Why is my ruby loop not ending? - ruby

I am new to ruby!
Here is my code:
require 'sdk'
def get_teams_from_evident
teams_api = SDK::Team.all
teams = []
loop do
teams_api.each do |team|
puts team.name
teams << team
end
if teams_api.next_page?
team_api = teams_api.page(teams_api.next_page_number)
else
break
end
end
teams
end
When i run it with
$ruby my_script.rb
the program never exits, the loop remains hanging...
What am I doing wrong?

As the comments have said, the only way your loop can end is if teams_api.next_page? returns false. If the loop continues to loop for ever, then you can infer that teams_api.next_page? always returns true. That doesn't seem logical though, your next step should be to check the documentation of #next_page?.
That's not the only way your loop can hang though. I've been fooled before, thinking my loop was spinning when in fact a function call inside the loop had frozen. You could add puts 'loop start' at the start of the loop to make sure, or you could use a gem like Byebug to step through the code.

Related

How can I stop Ruby if/elsif statement from looping?

I am teaching myself Ruby by making a small game in order to test out how I feel about the language. My code was going rather smoothly until I encountered a problem in which the first decision of the game loops instead of progressing forwards.
This code is what I have been using for a short part in the Exposition of my game.
def getup_or_sleep?
puts"Cole";
puts"----";
puts"Will you get up or go back to sleep?";
decision = gets
if decision == "sleep";
puts"Cole";
puts"----";
puts"You decide to go back sleep. It is far too early.";
elsif decision == "get up";
Exposition.stretch
else;
Exposition.getup_or_sleep?
end
This is the expected result I was hoping to achieve:
Cole
Will you get up or go back to sleep?
If player chooses 'sleep'
1)Cole
You decide to go back to sleep, it is far too early.
*I plan to make a new method to direct the user to, but I first want to fix this issue.
**if player chooses 'get up'
->>> to stretch method which is inside of the same class.
I'm new to coding so I may be confused on a few things. Thanks in advance! =)
Your method calls itself recursively because it all conditions fail, and it always falls back to the else branch.
This happens because gets reads input from the user and returns the input including the invisible newline character that is added when the user hits the enter key. But your conditions do not include such new line characters.
A common Ruby idiom is to call gets.chomp to get user input, where the chomp removes the newline character from the input.
Just change this line decision = gets to
decision = gets.chomp
to fix your issue.
Apart from that, your code isn't following Ruby idioms, for example, Ruby does not require a ; at the end of a line, or you usually add a whitespace between a method name and its argument, like in puts "Cole". Therefore, I suggest formatting your code like this:
def getup_or_sleep?
puts 'Cole'
puts '----'
puts 'Will you get up or go back to sleep?'
decision = gets.chomp
if decision == 'sleep'
puts 'Cole'
puts '----'
puts 'You decide to go back sleep. It is far too early.'
elsif decision == 'get up'
Exposition.stretch
else
Exposition.getup_or_sleep?
end
end
Or with a case block and some duplication extracted into a method:
def greeting
puts 'Cole'
puts '----'
end
def getup_or_sleep?
greeting
puts 'Will you get up or go back to sleep?'
case gets.chomp
when 'sleep'
greeting
puts 'You decide to go back sleep. It is far too early.'
when 'get up'
Exposition.stretch
else
Exposition.getup_or_sleep?
end
end

(Ruby) while loop stops my code from running

I'm trying to run the following code (my goal was to make a "game" where i make two objects and have them "fight" each other):
#player definition
Player=Struct.new(:health, :dmg)
active_player=Player.new(10, 2)
puts "test"
#monster definition
class Monster
attr_accessor :health, :damage
def initialize(health, damage)
#health=health
#damage=damage
end
end
big_spider=Monster.new(4, 1)
player_win=false
monster_win=false
#the fight itself
while (!player_win) or (!monster_win)
big_spider.health-=active_player.dmg
active_player.health-=big_spider.damage
if big_spider.health<=0
player_win=true
elsif active_player.health<=0
monster_win=true
end
end
#prints out who wins
case monster_win
when true
puts "the monster wins"
when false
puts "the player wins"
else
puts "error"
end
gets.chomp #is there to ensure that the program doesn't exit immediately after execution(yes i have tried removing it)
When I try to run that code (from Windows 10 PowerShell) it prints out "test" and then stops as if it was asking for input, however when I try to type anything it doesn't appear on the screen and I can't react with PowerShell in any other way than closing/minimising it (I even try pressing Ctrl+D as when exiting irb). I tried running the code in many other ways (through Notepad++, from file explorer, and through cmd.exe), yet i still ran into the same problem. When I comment out the lines from the "the fight itself" comment to the end of the code the program prints out "test",then asks for input and then stops executing. I haven't really found anyone with a similar issue anywhere.
That loop spins until BOTH win. With your current code, this is impossible. You want to change it to something like
while !player_win && !monster_win
Personally, I prefer to write such loops like this:
loop do
break if player_win
break if monster_win
...
end

ruby script freezes when calling class method

I have this program,which I am running from the command line, that freezes after this method.This method works but at the end it freezes,and I am left with a constantly flashing cursor.
def self.chooseteams(numberofteams)
i=1
while i<numberofteams
puts "choose a team"
team=gets.chomp
##c<<team
i+=1
end
end
The program then freezes and fails to call the next method which is.It is meant to perform its function and when the condition is met,pass on to the next method.My guess is that it is caught in a infinite loop,however,I can't see it!
def self.secondfixer(numberofteams)
until ##listofDrawnTeams.length==numberofteams do
firstPick = ##c.sample
##listofDrawnTeams<<firstPick
##listofDrawnTeams.uniq!
end
end
The methods are all called at the end of the program
Genfix.gener(64)
Genfix.fixer(64)
Genfix.chooseteams(32)
Genfix.secondfixer(32)
Genfix.fixer(32)
In your method secondfixer, your until loop might not ever complete if you have duplicate teams.
Using ## variables is really odd - it's rarely used in ruby programming.

Shortest Ruby one-liner to execute a statement exactly once

I'm looking for the shortest, most simple Ruby one-liner to execute a statement exactly once. Idea is to use this while debugging to quickly add a debug statement to a loop that gets executed exactly once.
Best I've come up with so far:
puts "do something interesting exactly once!" if (once ||= "0").next! == "1"
Can you come up with something even shorter?
Added for clarification:
The idea for the questions was to focus on the "do it once" part and not so much on the "do something interesting" part. It should be assumed that the code do be executed once could be anything, not just a puts statement.
The ideal solution would also work in different kinds of loop constructs. E.g. as was pointed out my initial solution only works if the once variable is already defined outside the loop context or if the loop context used doesn't create a new lexical scope.
The original use case that triggered this question was slightly different - it looked more like below. But I though the above, simpler example would more easily explain the kind of solution I was looking for.
def process
do_some_preprocessing()
raise SomeError if <once> # added temp. for debugging purposes - the <once> part is what this question is about!
dangerous_operation() # this can raise SomeError under certain conditions
rescue SomeError
attempt_to_rescue() and retry
end
Well, you could abuse lambdas and closures.
->{puts "do something interesting exactly once!";x=->{x}}[][]
#=> do something interesting exactly once!
#=> #<Proc:0x5465282c#(irb):10 (lambda)>
The original contents of the lambda are only run once; any subsequent invocations will simply return an empty proc.
You could alternately abuse globals for a more true "one-liner", but it's awful.
$x ||= puts("do something interesting exactly once!") || 1
debug = ["do something interesting exactly once!"]
puts debug.pop # "do something interesting exactly once!"
puts debug.pop # nil
(answer edited to reflect the discussion in comments)
Your code won't do what you want it to do, it will depend of the looping construct you use.
This will work:
puts "do something interesting exactly once!" if once = once.nil?
But with this one, you'll have to define once before: once = nil (same thing for your own code). This is because otherwise, the scope of the once variable will be restrained to the block within an each loop, causing it to fail. This would work just fine within a for loop (the way you must have tested it):
(1..3).each do # 3.times would behave just the same
puts "once has not been defined before! Won't work!" if once = once.nil?
end
# >once has not been defined before! Won't work!
# once has not been defined before! Won't work!
# once has not been defined before! Won't work!
for i in 1..3 do
puts "Works because of the way for loops treat vars within loop" if once = once.nil?
end
# >Works because of the way for loops treat vars within loop
To avoid that problem without having to initialize the variable first, you can make once global:
(1..3).each do
puts "$once's scope is not restrained to the 'each' loop! Works!" if $once = $once.nil?
end
# >$once's scope is not restrained to the 'each' loop! Works!
The original idea generates code-smell. It results in code that will leave someone else scratching their head, which isn't a good thing. Generating code that is obvious and easy to understand will make your, and other programmer's, job easier.
Writing code that takes a while to figure out will take you a while to figure out in the future if you're debugging so be kind to your future self.
I'd stick with the standard way, using a simple flag:
once = false
2.times do
puts "do something interesting exactly once!" unless once
once ||= true
end
Which results in this output:
# >> do something interesting exactly once!

How to use Dir.mktmpdir with a block in a hook with rspec?

I want to create a tmpdir in a before-each hook and use its path in an rspec example. I want to use the block form of Dir.mktmpdir so the dir is removed at the end of the example.
Problems:
I can't let the block exit in the before hook, or the dir is removed before my example can run.
I can't wrap a block around my example. I tried using an around
hook, but that doesn't share instance variables with examples (the
doc confirms this behavior).
Currently I'm using continuations (Fibers would be better if I were on 1.9) to jump out of the block, then jump back in so mktmpdir can clean up.
Is there an easier way to accomplish this, without moving mktmpdir inside each example? It's true that I can remove the dir in the after-hook, but I'm also looking for a general solution to this type of problem - I don't always know what cleanup code is supposed to run when the block exits.
FYI, my continuation code, encapsulated into a class:
class SuspendableBlock
def initialize
end
def run(&block)
raise LocalJumpError unless block_given?
callcc {|#run_cc|
yield
#resume_cc.call if #resume_cc
}
nil
end
# saves the suspend point & causes run to return immediately
def suspend
raise "run must be called first" unless #run_cc
callcc {|#suspend_cc|
#run_cc.call(#suspend_cc)
}
nil
end
# jumps back to after the suspend point to finish the block.
# after the block exits, return immediately from resume.
def resume
raise "suspend must be called first" unless #suspend_cc
callcc {|#resume_cc|
#suspend_cc.call(#resume_cc)
}
nil
end
end
Usage:
before :each do
#sb = SuspendableBlock.new
#sb.run do
Dir.mktmpdir do |dir|
#tmpdir_path = Pathname.new(dir)
#sb.suspend
end
end
end
after :each do
#sb.resume
end
it "should use a tmp dir" do
p #tmpdir_path
end
From what I read (never tested it) continuations are really inefficient.
While I cannot help you on continuations you could use Thread to mimic Fibers: https://github.com/tmm1/fiber18.
One library which already does that is em-spec (https://github.com/tmm1/em-spec), with it each test is ran in a fiber you may be able to modify it to match your needs.

Resources