Ruby version: ruby 2.6.7p197 (2021-04-05 revision 67941) [x86_64-linux]
Rails gem version: 6.0.1
Simple question, I have this example on rails console
As expected, if I try to call a non existent variable, I got an error
irb(main):007:0> value
# Traceback (most recent call last):
# 2: from (irb):7
# 1: from (irb):7:in `rescue in irb_binding'
# NameError (undefined local variable or method `value' for main:Object)
But if I try to do the following example
(value = 1) if value.present?
It returns nil for some reason, the first scenario this happenned there was no parenthesis, I thought it was defining the variable and then returning a value of nil, but then I tried with it and just happened again (I tried other variable because ruby counted that as a defined variable)
OBS: I tried the same scenario on raw irb and it raised me an error, this only happens on rails console
EDIT: It only raised an error because I didn't realized that '.present?' is a rails method, but if I change my syntax to
(value = 1) if value
the same behaviour happens
Why does that happen? Shouldn't a NameError be raised?
CodePudding user response:
Ruby works such way, please look the docs
The local variable is created when the parser encounters the assignment, not when the assignment occurs:
a = 0 if false # does not assign to a p local_variables # prints [:a] p a # prints nil
And other interesting thing:
Another commonly confusing case is when using a modifier if:
p a if a = 0.zero?
Rather than printing “true” you receive a
NameError, “undefined local variable or method 'a'”
. Since ruby parses the bare a left of the if first and has not yet seen an assignment to a it assumes you wish to call a method. Ruby then sees the assignment to a and will assume you are referencing a local method.The confusion comes from the out-of-order execution of the expression. First the local variable is assigned-to then you attempt to call a nonexistent method.
So you can