ruby script freezes when calling class method - ruby

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.

Related

(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

Why does having a large array cause NoMethodError to take longer to be raised

I have the following Ruby code where each instance of BigClass creates a array with instances of BigClass (up until a maximum depth).
class BigClass
# Increase this depending on your computer performance
MAX_DEPTH = 8
def initialize(depth = 0)
#my_arr = []
5.times do |i|
unless depth > MAX_DEPTH
#my_arr << BigClass.new(depth+1)
end
end
end
end
big_class = BigClass.new
puts "Reaches here"
# This line should throw an error but instead freezes
big_class.blah
puts "Doesn't reach here"
When calling a method which doesn't exist, I expect to receive a NoMethodError pretty much instantly, however, it seems that the larger the contents of the array in big_class the longer it takes for the error to be produced.
Why is this?
A few options I considered:
Maybe Ruby does some kind of cleanup or calls some hooks for all created instances before throwing an error. But if I make the error happen from a different object or inside an existing method, it instantly raises the error.
Maybe the Ruby method lookup for some reason includes instance variables lookup, but after reading this GitHub Gist I can't find anything to indicate that that would be the cause
Something I have noticed is that when it prints "Reached here" if I quit (CTRL-C) it will immediately print out the NoMethodError. So it doesn't seem to be an issue with finding whether or not the method exists, otherwise it wouldn't know that that is the error when I quit.
I think I worked it out.
If I override the #inspect method of BigClass it no longer causes an issue. This makes me think that something internally is calling #inspect when handling the error. Since the default inspect will include the instance variables, and call inspect on them as well, if there are many instance variables and they all have many instance variables, then it could take a very long time to process.
The below code shows a version which runs as I would expect:
class BigClass
# Increase this depending on your computer performance
MAX_DEPTH = 8
def initialize(depth = 0)
#my_arr = []
5.times do |i|
unless depth > MAX_DEPTH
#my_arr << BigClass.new(depth+1)
end
end
end
def inspect
"now it works"
end
end
big_class = BigClass.new
puts "Reaches here"
# Correctly raises an error now
big_class.blah_blah_blah
puts "Doesn't reach here"

Why is my ruby loop not ending?

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.

Calling Block multiple times in Cucumber Around Hook (Ruby)

I'm trying to run a scenario several (30) times in order to get a nice statistical sample. However the block is only executing once; each subsequent time results in the scenario being called and not executing (although it says that the scenario did successfully complete with a time of around 5 ms).
Around('#mass_benchmark') do |scenario, block|
$seconds_taken = "SECONDS TAKEN NOT SET"
#time_array = []
30.times do
before_hook(scenario)
block.call
after_hook(scenario)
#time_array << $seconds_taken
end
write_time_array_to_file(#time_array, scenario_name)
end
The tag #mass_benchmark executes this block, as opposed to ~#mass_benchmark, which just executes the scenario normally. The methods before_hook and after_hook replicate the Before ('~#mass_benchmark') and After ('~#mass_benchmark') hooks (which actually just call the same method).
The variable $seconds_taken is set around the specific area for which I am timing. I am not timing the whole test there, just a critical portion of it; the remainder of the test is getting to that point, etc, which is not to be part of the timed portion, so I cannot just move the timing portion outside of this.
The issue may be with something I'm doing in those methods, but as far as I can tell, everything works normally (as indicated by well-placed puts statements). Any ideas are appreciated!
Currently Cucumber does not seem to support calling the block twice in an around hook. This can be demonstrated by the following feature file:
Feature: This scenario will print a line
Scenario: Print a line
When I print a line
And step definitions:
Around do |scenario, block|
Kernel.puts "START AROUND, status=#{scenario.status}"
block.call
Kernel.puts "BETWEEN CALLS, status=#{scenario.status}"
block.call
Kernel.puts "END AROUND, status=#{scenario.status}"
end
When /^I print a line$/ do
Kernel.puts "IN THE STEP DEFINITION"
end
When this is executed, Cucumber will print:
Scenario: Print line # features/test1.feature:3
START AROUND, status=skipped
IN THE STEP DEFINITION
When I print a line # features/test.rb:9
BETWEEN CALLS, status=passed
When I print a line # features/test.rb:9
END AROUND, status=passed
Evidently since the status of the scenario is already "passed", Cucumber does not re-execute it, though the output formatter receives the steps. I have not found any way to "reset" the status in the scenario API to get them to be re-run.
There are other problems with around hooks as well, for example you cannot set variables to the World in around hooks (like you can in before hooks). See also Cucumber issues 52 and 116 for more gory details.
One possibility might be to keep the passed-in block as it is, and call the ".call" method on a duplicate?
Something like (untested):
Around do |scenario, block|
30.times do
duplicate = block.dup
before_hook(scenario)
duplicate.call
after_hook(scenario)
end
end
Just make sure not to use ".clone" on the block, since clone will create an object with the same Id, resulting in every change made to the duplicate also affecting the original.

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