How can I watch a variable in Ruby Pry? - ruby

I'm using Ruby 2.2.2, Pry and 'pry-byebug'. The continue statement removes any watched variables in have in pry-byebug:
[1] pry(main)> watch foo
Watching foo
watch: foo => 42
[2] pry(main)> watch
Listing all watched expressions:
1: foo => 42
[3] pry(main)> continue
[1] pry(main)> watch
No watched expressions
Losing them on every continue makes watched expressions pretty worthless. If I use next and step to the same point in the code instead, the watched expressions are still there; it's just the continue that causes the problem. At the moment I can't even find any documentation on the watch statement, so I don't know why this occurs.
More generally, I just want to print out the value of a set of expressions on every Pry prompt (or, perhaps, print them out if they've changed since the last time they were printed.) How can I achieve this in a Ruby debugger?

Using pry-moves you can use watch variable - this will display value of variable on each step.

You should be able to write a plugin that just shows the expressions after evaluating if that's exactly what you want.
Pry plugins
Which you could do something simple like
Pry.hooks.add_hook(:after_eval, "reporter") do |output, binding, pry|
$watch ||= []
$watch.each {|block| block.call}
end
$watch = [-> { puts "hello"}]
Or something along the lines of that, would report "hello" after each new evaluation. (this is untested however, currently on mobile)

Related

Pry (Ruby REPL): How can I turn automatic echo off

Since Ruby supports parallel assignments and automatic value return from functions, almost every assignment and method run ends up creating an output when working on REPLs like IRB and Pry.
Normally I prevent this echo effect by putting a semicolon at the end of each line. For instance:
JSON::parse(very_long_json_string);
This normally prevents REPL echo. But when working with very large enumerables even one mistake can generate enough output to make a mess on the screen and put all my useful command history out of memory before I have the reflex to hit the break.
Is there a way to turn this echo effect off by default in Pry? As mentioned in the comments below (#Stefan), the same can be achieved in IRB by setting conf.echo = false.
In IRB there is:
conf.echo = false
In Pry you could replace the print object with an empty proc:
_pry_.config.print = proc {}
You'll have to store the old print object in order to restore it.
In both cases, the result of the last expression is still available via _

Ruby REPL, determinate if a line it's part of a valid expression

I'm doing a Ruby REPL (just a hobby, won't be big and professional like pry).
I wrote a very simple REPL that works fine if the input it's just a single valid line of Ruby:
loop do
print "ruby> "
input = gets
puts "=> #{eval(input)}"
end
I want to support multiline inputs.
One approach that I'm thinking is to check for each input line if the code is a correct Ruby expression, part of a Ruby expression or invalid code.
valid_expression?("def foo; end") # => true, complete expression
valid_expression?("def foo") # => true, partial expression
valid_expression?("def ::foo") # => false
Anyway, I tried to understand other implementations1,2,3 but is really difficult/undocumented code. Maybe I can use RubyLex or Ripper.
Ideally, I'm interesting to use Ruby standard libraries without any external gem. It doesn't matter if only target for Ruby 2.x versions, but if there is a gem to do the work, I'll happy to use it.
Since you noticed that there is pry, you should look its source and learn from it. Actually, pry does internally use such method as you described. It is: MethodSource::CodeHelpers#complete_expression?.

Override '+' method

In the process of understanding ruby, I was trying to overide '+' with a default argument value. Something like this.
class C
def something(a = 5)
puts "Received: #{a}"
end
def +(b = 10)
puts "Received: #{b}"
end
end
Now
x = C.new
x.something #=> Received: 5
x.something(88) #=> Received: 88
x.+ #=> IRB shows ? whereas I was expecting an output 'Received: 10'
Is this because of operator precedence?
Problem with IRB (look like it doesn't handle such cases). If you create separate .rb file and run it you will get expected output:
Received: 5
Received: 88
Received: 10
IRB is parsing the + and expecting a second parameter for the binary operation. If you provide parenthesis it works correctly:
x.+() #=> Received: 10
IRb uses a different parser than Ruby does. So, in some weird corner cases, IRb may parse code differently than Ruby. If you want to see whether something is valid Ruby or not, you should ask Ruby not IRb.
The reason for this is mainly that Ruby always parses the entire file at once, so it always knows when an expression ends. IRb on the other hand, has to "guess" every time when you press ENTER whether you simply want to continue the expression on a new line or whether you wanted to evaluate the expression as-is. As a result, IRb cannot just use the Ruby parser, it needs to have its own. And Ruby's grammar is so complex that writing your own parser is really really hard. That's why such bugs and corner cases pop up from time to time even in a piece of software as old and as widely used as IRb.

IRB - Ruby 1.9.x hash syntax: {if: true} is not equal to {:if => true}

Long story short, I was writing a method that included an options argument, that will do certain stuff if the value for the key :if, evaluated to true. When I trying the hash in IRB using the new syntax I got a syntax error in IRB, the prompt stays open:
1.9.3p374 :010 > {if: true}
1.9.3p374 :011?>
Using the old syntax, works just fine:
1.9.3p374 :011 > {:if => true}
=> {:if=>true}
All keywords that start a statement, exhibit the same behavior. E.g. def, do, module, case
Other reserved words that occur in the middle and class work just fine: else, end
My question is: Is this expected behavior, a bug or a limitation?
It's challenging to reliably and unambiguously parse things in any language. This is especially true when you start using reserved words. And irb has to go beyond that and provide an interactive model on top of the parser, which is even harder. I personally don't think there's too much value in worrying about cases like this, either as a user of the language or as a maintainer. In my mind, it's better to simply figure out what works and avoid getting into these situations if possible.
You can see some similar behaviors in plain Ruby, outside irb. For example:
puts({if: true}) # no problem, behaves as expected in Ruby 1.9.3.
puts {if: true} # raises a syntax error in Ruby 1.9.3
To answer your question, is it "expected behavior, a bug or a limitation", I'd say you should ignore irb and compare it to plain Ruby, and if you do this, it works fine. That means it has to be an irb bug.
But is it possible or worthwhile to solve? #coreyward makes a good point in his comment that irb has to delay execution in most cases when it encounters an if. You'd have to look further to know for sure, but you may not be able to unambiguously interpret all cases like this.
My advice: avoid this construct altogether if you can, and don't use reserved words for labels if you can avoid it!
Here's a file you can run with plain Ruby (eg MRI). You should see {:if=>true} in the output to confirm it works.
{if: true}
foo = {if: true}
# if MRI is working, should be able to execute this file without trouble.
p foo

Ruby knows 'myvar' is a variable in myvar = 0 if false

I'm learning Ruby and I like playing with irb to discover new features and tricks. Today I was playing with variables and methods because I wanted to know which one took preference in front of the other one. Everything looked fine until I tried this:
def test
puts "hello"
end
test = "bye" if false
puts test
I was expecting this to return "hello" , but it doesn't. So, I suppose the parser is treating 'test' as a variable instead of as a method. I have two questions:
Is my assumption correct?
Is there any way to know if something is a variable or a method? Some method like test.is_variable?
test = "hello" if false
p test #=> nil
The local variable test is created anyway (with default value nil), and given that local variables overshadow methods with the same name, that's the value you get. Just an hour ago someone got bitten by a subtle variation of the theme. And don't you think this only happens with one-liner conditionals:
if false
test = "hello"
end
p test #=> nil
That's because Ruby defines variables when they are parsed (and not when they are executed).
There are at least two methods that help: methods and local_variables. I wouldn't recommend using them in real world programs, but they might be useful when learning Ruby.

Resources