Why isn't my variable getting defined, and ending this loop? - ruby

I ran across this issue. I have a work around, so I'm specifically asking WHY this doesn't work? Why doesn't the variable get defined in the first time through the loop, and then exit the loop?
Assuming it's the scope, which I'm convinced it is, then why is foo defined after breaking the loop? Am I just seeing an artifact of irb here?
voin0017:[/home/acowell/src/local/goldrhel] ruby -v
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]
voin0017:[/home/acowell/src/local/goldrhel] irb
irb(main):001:0> while defined?(foo).nil? ; foo = 1 ; end
^CIRB::Abort: abort then interrupt!
from (irb):1:in `call'
from (irb):1
from /opt/chef/embedded/bin/irb:12:in `<main>'
irb(main):002:0> p foo
1
=> 1
irb(main):003:0>

The defined? method does not test if a value is defined for a variable, but instead if that variable is defined within the scope you're testing it in.
The code you've written here is basically nonsensical:
while (defined?(foo).nil?)
foo = 1
end
The foo variable is never "defined" outside of the context of the while block so it spins forever waiting for that to happen. You end up defining it repeatedly inside the context of the while loop, but that defined? check does not test that. It is getting defined, just not where you expected it to be.
If you add a little code to see what's going on you get this:
puts defined?(foo).inspect
# => nil
while (true)
foo = 1
puts defined?(foo).inspect
# => "local-variable"
break
end
# Once the while completes, the variable is defined outside that scope.
puts defined?(foo).inspect
# => "local-variable"
The general pattern you're going in idiomatic Ruby code looks more like this
foo = nil
while (!foo)
foo = 1
end
As a note, it's highly unusual to see defined? used in Ruby applications as variables are either used, or not used. It's in situations where you have some automatic code generation and block rebinding going on that you want to test local variables before using them. The most common case for this rare behaviour is within Rails partials where you may have passed in options via the :local argument. These appear as local variables if defined, but may not exist, so you need to test to be sure.

Whether or not a variable is defined depends upon not only the scope, but also on its location within the ruby script. That is, a variable is defined only if it has been defined previously within the parse, not the execution.
Here's an example:
begin
puts "Is foo defined? #{defined?(foo).inspect}" # foo is never defined here
foo ||= 1
puts "but now foo is #{foo}" # foo is always defined here
foo += 1
end while foo <= 3
Output:
Is foo defined? nil
but now foo is 1
Is foo defined? nil
but now foo is 2
Is foo defined? nil
but now foo is 3
Because foo has not been defined previously within the script on the first line of the loop, it is undefined at that point, and remains undefined, even if it is assigned to and the same line is returned to at a later point during execution.
This is why foo in the while condition of the question is always undefined:
while defined?(foo).nil? # foo is always undefined
foo = 1
end
and will loop forever. In contrast, this loop only executes once:
begin
foo = 1
end while defined?(foo).nil? # foo is defined
because foo is assigned to previously in the parse.
Edit:
Only loops that require a block seem to isolate its local variables from living outside of it. E.g. loop, upto, each, inject, map, times, etc. These all require use of the keyword do and end, or curly braces, which delimit the block. In contrast, while, until, and for do not, and so variables defined within them continue to live outside of them. This is demonstrated here:
while true
foo_while = 1
break
end
puts "foo_while: #{defined?(foo_while).inspect}"
until false
foo_until = 1
break
end
puts "foo_until: #{defined?(foo_until).inspect}"
for i in 0..2
foo_for = 1
break
end
puts "foo_for: #{defined?(foo_for).inspect}"
loop do
foo_loop = 1
break
end
puts "foo_loop: #{defined?(foo_loop).inspect}"
1.upto(2) do |i|
foo_upto = 1
break
end
puts "foo_upto: #{defined?(foo_upto).inspect}"
[1,2,3].each do |i|
foo_each = 1
break
end
puts "foo_each: #{defined?(foo_each).inspect}"
[1,2,3].inject do |i,j|
foo_inject = 1
break
end
puts "foo_inject: #{defined?(foo_inject).inspect}"
[1,2,3].map do |i|
foo_map = 1
break
end
puts "foo_map: #{defined?(foo_map).inspect}"
3.times do
foo_times = 1
break
end
puts "foo_times: #{defined?(foo_times).inspect}"
Output:
foo_while: "local-variable"
foo_until: "local-variable"
foo_for: "local-variable"
foo_loop: nil
foo_upto: nil
foo_each: nil
foo_inject: nil
foo_map: nil
foo_times: nil

Related

Can a Ruby method access the implicit block argument?

The implicit block argument passed to a Ruby method can be executed using yield, or its existence can be checked using block_given?. I'm trying to procify this implicit block to pass it to another method.
Is this possible?
(It's access to the implicit block argument I'm asking about. Replacing this with an explicit argument won't cut it.)
You can procify it, and more importantly give it a name so you can reference it, using the & ampersand unary prefix sigil in the parameter list of the method, like so:
#implicit, anonymous, cannot be referenced:
def foo
yield 23 if block_given?
end
foo {|i| puts i }
# 23
#explicit, named, can be referenced:
def bar(&blk)
yield 23 if block_given? # still works
blk.(42) if blk # but now it also has a name and is a `Proc`
# since we have the block available as an object, we can inspect it
p blk.arity, blk.parameters, blk.source_location, blk.binding
b = blk.binding
p b.local_variables.map {|var| [var, b.local_variable_get(var)] }.to_h
end
quux = "Hello"
bar { |a, b, c = nil, d: nil, &e| puts a }
# 23
# 42
# 2
# [[:opt, :a], [:opt, :b], [:opt, :c], [:key, :d], [:block, :e]]
# ["(irb)", 24]
# #<Binding:0x00007fb091051308>
# { :quux => "Hello" }
Those are your two choices:
implicit, anonymous, not an object
explicit, named, Proc
There used to be an undocumented trick that was actually an unintended side-effect of how Proc::new was implemented in MRI: Proc::new did not check whether you passed a block or not, it simply assumed that you passed a block and would take the first block off the top of the internal VM stack. So, if you didn't pass a block to Proc::new, it would actually end up creating a Proc for the implicit block that was passed to the method (since that was the one which just happened to be on the top of the stack).
But, that was never portable, never guaranteed, never worked in all Ruby implementations, and AFAIK no longer works in YARV.
You can refer to the block argument via Proc.new. From the docs:
::new may be called without a block only within a method with an attached block, in which case that block is converted to the Proc object.
Example:
def bar
yield * 2
end
def foo
bar(&Proc.new)
end
foo(123)
#=> 456
Note that Proc.new raises an ArgumentError when called without passing a block.
Take a look at this answer. In your case it would be something like:
def outer
wrapper = lambda { |something|
p 'Do something crazy in this wrapper'
yield(something)
}
other_method(&wrapper)
end
def other_method
yield(5)
end
outer { |x| puts x + 3 }
With that you get:
"Do something crazy in this wrapper"
8
=> nil

# symbol before var name in Ruby script

I'm not entirely sure how to Google this question, so I come here with an example. I'm looking at a selenium script that has an excerpt like the following:
def setup
#starting_url = "https://www.example.com"
#restricted_url = "https://www.example.com/restricted"
#user_email = "foo#bar.com"
#user_password = "notarealpassword"
#headless_mode = false
#page_timeout = 15 # seconds
#log_file = 'log/development.log'
#lineup_file = 'data/lineup1.csv'
... more code
end
My question is, why does every variable here get prefixed with an # symbol? This method is not part of a class. It is being written in the global scope. I understand the variables have significance in with an # symbol in the case of an explicit class, but what about here?
Those variables become instance variables in the scope of the main object. They will be available inside other methods also defined at the global scope. Local variables defined within methods at the global scope would go out of scope as soon as the method returned.
Illustration:
def foo
lvar = 1
#ivar = 2
end
def bar
puts #ivar # will print 2 after foo() is called
puts lvar # will throw NameError
end
#ivar # => nil
foo # initializes #ivar
#ivar # => 2
bar # prints 2, throws NameError
lvar # throws NameError

Ruby: Variables defined in If/else statement are accessible outside of if/else? [duplicate]

This question already has answers here:
Why can I refer to a variable outside of an if/unless/case statement that never ran?
(3 answers)
Closed 5 years ago.
def foo
#bar = nil
if true
bar = 1
else
bar = 2
end
bar #<-- shouldn't this refer to nil since the bar from the if statement is removed from the stack?
end
puts foo # prints "1"
I always thought you had to make a temporary variable and define it as nil or an initial value so that variables defined inside an if/else statement would persist outside the scope of the if/else statement and not disappear off the stack?? Why does it print 1 and not nil?
Variables are local to a function, class or module defintion, a proc, a block.
In ruby if is an expression and the branches don't have their own scope.
Also note that whenever the parser sees a variable assignment, it will create a variable in the scope, even if that code path isn't executed:
def test
if false
a = 1
end
puts a
end
test
# ok, it's nil.
It's bit similar to JavaScript, though it doesn't hoist the variable to the top of the scope:
def test
puts a
a = 1
end
test
# NameError: undefined local variable or method `a' for ...
So even if what you were saying were true, it still wouldn't be nil.

Get value into Ruby code block without it being the last statement [duplicate]

This question already has answers here:
Pass arguments by reference to a block with the splat operator
(4 answers)
Closed 8 years ago.
I would like to return a value from a yield in a Ruby code block. In other words, I would like to pass a value from inside the blocking into the block itself.
def bar
puts "in block"
foo = 1
# The next line is wrong,
# but this is just to show that I want to assign a value to foo
foo = yield(foo)
puts "done block #{foo}"
end
puts "start"
bar do |shoop|
puts "doing other stuff"
shoop = 2
puts "doing more stuff"
# The following commented-out line would work,
# but I would rather not force the programmer
# to always put a variable at the end
#shoop
end
puts "done"
What I would like to see for output is:
start
in block
doing other stuff
doing more stuff
done block 2
done
Is there any way to accomplish this without relying on the implicit return value at the end of a Ruby block? I believe this is possible, because I've seen this type of behavior in Sinatra code blocks before.
You can implicitly return values using instance variables, like it is done in Rails:
def bar
yield
puts "The block has assigned #value to #{#value}"
end
bar do
#value = 42
nil # Do not return value intentionally
end
This code outputs:
The block has assigned #value to 42
UPDATE
Another nice option is to use helper methods, many frameworks do it too:
def bar
def render(value)
#value = value
end
yield
puts "The block has rendered #{#value}"
end
bar do
render 42
nil # Do not return value intentionally
end
This code outputs:
The block has rendered 42
UPDATE 2
Please see an important addition from #Marek Lipka https://stackoverflow.com/a/19542149/203174
I must warn you that #Daniel Vartanov answer applies only if self in the method and in the method call scope is the same. Otherwise, the block should be called in the context of self in method, that is:
def bar(&block)
instance_eval(&block)
puts "The block has assigned #value to #{#value}"
end
One can abuse Object#tap:
bar do |shoop|
puts "doing other stuff"
2.tap do
puts "doing more stuff"
end
end
Tap yield to the block, but results in the object that was tapped (in this case, 2).
It's a fun trick, and occasionally helpful, but a simple temporary variable usually is plainer and easier to read.

Simple Ruby puzzle on variable reference in a method

Why does this method return 1 rather than dying from infinite recursion?
def foo
foo ||= 1
end
foo # => 1
Rewritten the following way it does die:
def foo
foo.nil? ? 1 : foo
end
In the first case, foo ||= 1 refers to a local variable. Ruby always creates a local variable when you do assignment on a bareword, which is why you have to write self.foo = ... if you want to invoke a writer method defined as def foo=(value). The ||= operator is, after all, just a fancy assignment operator.
In the second case, there is no assignment, so when it hits foo.nil?, Ruby interprets the bareword foo as a method call, and blows up.

Resources