When, during a pry debug session, I want to inspect step-by-step execution of FooClass.new.foo I'd do this in pry console
$ FooClass.new.foo #this gives me path and line of the method
break /path/to/foo_class.rb:LINE_WHERE_FOO_IS_DEFINED
FooClass.new.foo
This works but I need to look for the path, line and it leaves a breakpoint I sometimes have to remove.
There's a faster way:
break FooClass#foo
FooClass.new.foo
but it's still two steps, and the breakpoint stays.
Is there a way to do it in one command, like
step-into FooClass.new.foo
which would start a Pry subsession, enter the method execution and after it exits/finishes I'm back in the original session with no extra breakpoints left?
In other words: I'm in the middle of debugging, and see a method called few lines before (I can't step into it immediately). I don't want to put a binding.pry in the source (it may take a lot of time to start the debugging session again).
The problem has hidden complexity. For instance, one thing you can do is:
pry Foo.new.foo
or
Foo.new.foo.pry
This puts you into a binding on the return value of the function foo, but that is not what you want. It also shows there is complexity when your line of code Foo.new.foo runs, for a function to know what exactly to put a binding on.
So that's really the problem I feel; it would take understanding of the expression on the line to understand where to put a breakpoint. If you imagine it was working like the step function.
If your code Foo.new.foo was already a break point (e.g. you had a function with that code in and you put a breakpoint on a line like that) then you typed step, you would go into Foo.new Foo.initialize instance of foo foo.foo and so on. So, any code trying to do what you would like it to do would have a very hard time understanding where in the expression tree to stop and put your into pry.
This is why you have to tell pry where to stop using break in this context.
Related
when i write method missing in Object class i'm getting the output different in each interface.
the code is
class Object
def method_missing(hgh)
puts self
end
end
when i use REPL like irb, i get
when i use the command line, i get no error, any reasons would be helpful, thanks in advance
The tl;dr answer is that both are correct. Just more stuff happen in the REPL.
When you run the code from the command line like:
ruby script.rb
All that happens is that it's evaluated.
Whereas REPLs like IRB read your input, evaluate it and print it in a loop.
In this case evaluating your code literally broke the REPL and resulted in the subsequent print failing.
Now you may be a bit confused by this. "There is a print in both cases, I use puts!". The print I'm referring to here is the result that gets visualised after each evaluation. In this case the method definition result (=> :method_missing).
It might not only be the printing itself. It can be the ton of other under the hood code that the REPL has to execute to keep state like "what code was defined on which line" and so on.
Think of what you just did - you made it so that every object has every method possible to return nil. That is not just for the code you write in the REPL. It's for the code of the REPL itself as well.
I have the following (mildly-anonymised) evaluate_script line in a javascript capybara test, within a ruby rspec:
page.execute_script("$('selector').addClass('-class')")
It isn't working, e.g. the following prints false :
page.execute_script("console.log($('selector').hasClass('-class')")
If I put a debugger (binding.pry) after that line, and then run the addClass line in the debugger -- literally the same line that's just executed; copied and pasted -- then it does work.
It doesn't seem to be a timing issue -- putting sleeps after the addClass line doesn't change anything.
It's very repeatable: the exact line that doesn't do anything in the script then suddenly works if I replace it with a binding.pry and run the line from pry. Which AFAIK should be impossible.
So: what could cause the same thing to work when run from a debugger, and not when run not from the debugger?
Capybara driver is poltergeist on phantomJS.
Cheers :)
OK, so turns out it was a timing issue after all.
I had been assuming that an addClass call would work no matter when in page load it was done, as long as jQuery was loaded and the element was present, which they were.
Which means while I'd tried adding a sleep after the addClass, in case it was a race condition between adding a class and testing for it - what I hadn't tried was putting a sleep before adding the class. Which fixed it. So apparently that assumption was false.
So the difference running the line from the debugger made was just that it gave it a few seconds before running it!
To avoid sleeping a random amount of time, I added a spinner:
def spin_until_thing_done(selector)
Timeout.timeout(Capybara.default_wait_time) do
loop do
do_thing(selector)
return if thing_done?(selector)
sleep 0.1
end
end
end
def do_thing(selector)
page.execute_script("$('#{selector}').addClass('-class')")
end
def thing_done?(selector)
page.evaluate_script("$('#{selector}').hasClass('-clas')")
end
I'm trying to write a test to prevent you from doing the same thing twice (in this case, playing on the same square twice in tictactoe), but RSpec syntax is confusing me and I can't see a solution in their docs. I've tried a few approaches with no success, which have made me realise I don't understand RSpec syntax as well as I thought.
Because the tests are for a recursive method, a lot of the errors aren't as descriptive as I'd like since they cause the program to crash, which is making it hard to isolate the problem.
The method I'm trying to test/create will initially look something like this:
def your_turn
your_turn unless play_on(square_at(get_row, get_square))
end
(play_on returns nil if it doesn't successfully place a marker)
In this case I'm testing a module for which, your_turn, get_row and get_square are that module's methods, but play_on and square_at are methods from the class into which it's getting integrated, so I'm stubbing where necessary (ditto the get methods, since I don't want to get prompted by RSpec).
The first test I tried was this:
it 'repeats the process if a player can't play on that square' do
allow(human).to receive(:play_on).and_return(nil, :square)
expect(human).to receive(:your_turn).twice
human.your_turn
end
The test fails, and says it was only received once. The way I expected this to unfold was on the initial your_turn call, RSpec would stub play_on, return nil, setting off the unless statement and repeating the your_turn call - after which it would return a symbol and prevent any further recursion. What have I misunderstood?
After that effort, I came up with a monstrosity that seems to work, but I'm not entirely sure why:
allow(human).to receive(:get_row).and_return(1,2)
allow(human).to receive(:get_square).and_return(2,2)
allow(human).to receive(:square_at).with(1, 2).and_return(:nil)
allow(human).to receive(:square_at).with(2, 2).and_return(:square)
allow(human).to receive(:play_on).with(:square).and_return(:square)
expect(human).to receive(:play_on).twice
human.your_turn
The trouble here (other than this being ugly) is I got it to work more through brute force than understanding, and there's a couple of things I'm not sure of:
Why can I not remove eg the first two lines without the prompt returning to RSpec? Since on the third and fourth lines I'm stubbing the method square_at, which would call the get_ methods, why are they still being called?
Similarly, the fifth line third and fourth line seemed to cover the third and fourth, but I initially had the last 'allow' return(nil, :square), expecting that to work. That ended up calling your_turn three times where I was expecting two, so I removed the nil and it passed. The way I parse it, without the nil, play_on will return :square immediately, so the unless logic won't be triggered and the method will terminate. Again, what have I misunderstood?
Is there a dryer way of dealing with the two 'square_at' allowing lines?
Thanks all,
Sasha
I don't understand your questions (specially the second and the third), but I believe that a big part of your problem is thinking that just because you stubed one method call, Ruby won't evaluate the method call arguments.
The get_row and get_square methods are called before square_at. Ruby "resolves" statements from "right to left", so it calls first get_row, then get_square and finally square_at passing the result of the two get_ methods. That could potentially break your tests depending on what those two methods do, and explains why you need the first two stubs.
I am writing Ruby in Emacs, but my Emacs skills are actually pretty low. What I can do, is open the project, TDD using M-x rinari-test, or play inferior Ruby in the second window using M-x run-ruby. Now I woul like to start using debugger from StdLib. I am able to summon it from irb by saying:
require 'debug'
Upon which I get into a prompt
(rdb:1)
but there my aptitude ends. I don't even know how to step into a file. Typing 'help' brought a screenful, but it didn't help me to finally start debugging my buggy gem. Online, everybody writes about things such as "rdebug" or "ruby-debug" or whatever which I firstly don't want to use and secondly, being a muggle, I am unable to install on my Debian. Please help!!!
You really need to try reading the output of help in the debugger. It explains the commands nicely.
For instance, for practice, try this at the command-line, not inside an editor/IDE:
ruby -rdebug -e 'p 1'
h
Ruby's debugger will output the help summary:
Debugger help v.-0.002b
Commands
b[reak] [file:|class:]<line|method>
b[reak] [class.]<line|method>
set breakpoint to some position
wat[ch] <expression> set watchpoint to some expression
cat[ch] (<exception>|off) set catchpoint to an exception
b[reak] list breakpoints
cat[ch] show catchpoint
del[ete][ nnn] delete some or all breakpoints
disp[lay] <expression> add expression into display expression list
undisp[lay][ nnn] delete one particular or all display expressions
c[ont] run until program ends or hit breakpoint
s[tep][ nnn] step (into methods) one line or till line nnn
n[ext][ nnn] go over one line or till line nnn
w[here] display frames
f[rame] alias for where
l[ist][ (-|nn-mm)] list program, - lists backwards
nn-mm lists given lines
up[ nn] move to higher frame
down[ nn] move to lower frame
fin[ish] return to outer frame
tr[ace] (on|off) set trace mode of current thread
tr[ace] (on|off) all set trace mode of all threads
q[uit] exit from debugger
v[ar] g[lobal] show global variables
v[ar] l[ocal] show local variables
v[ar] i[nstance] <object> show instance variables of object
v[ar] c[onst] <object> show constants of object
m[ethod] i[nstance] <obj> show methods of object
m[ethod] <class|module> show instance methods of class or module
th[read] l[ist] list all threads
th[read] c[ur[rent]] show current thread
th[read] [sw[itch]] <nnn> switch thread context to nnn
th[read] stop <nnn> stop thread nnn
th[read] resume <nnn> resume thread nnn
p expression evaluate expression and print its value
h[elp] print this help
<everything else> evaluate
The important commands to start with are s, n, c and b, and q.
s steps into a method.
n steps over a method.
c number runs (continue) until you reach line number.
b number sets a breakpoint on line number. After setting your breakpoints use c to continue running until that line is executed.
q exits the debugger.
Personally, I use the debugger gem. Others use PRY, which is similar to IRB, but with debugger-like extensions.
Knowing how to use a debugger is a good skill. There are problems you can trace down quickly using a debugger, that will take longer trying to use puts statements, because you can see what a variable contains interactively, or loop conditionally until a variable contains a certain value.
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.