0

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?

4
  • 2
    "I tried the same scenario on raw irb and it raised me an error, this only happens on rails console" that error did not happen to be a NoMethodError did it? Because value = 1 if value.nil? works just fine. The parser recognizes the first value and initializes as nil then processes the right hand side. e.g. b = 7 if 1 == 2; b #=> nil Commented Feb 11, 2022 at 15:27
  • @engineersmnky, Yes, I fixed the post example, but the same behaviour still happens if I pass the variable itself, the point is; why there is no NameError exception being raised? Commented Feb 11, 2022 at 16:53
  • @engineersmnky I forgot to mention, "why there is no NameError exception being raised? WITH the parenthesis" Commented Feb 11, 2022 at 17:41
  • I think I already explained this the parse reads top to bottom, left to right, so when it sees value assignment, it treats it as a local variable even though the assignment has not occurred yet. Then the interpreter sweeps through and it evaluates the modifier if first (as it should) and since value has already been determined to be a local variable (albeit without a value) there is no NameError. Commented Feb 11, 2022 at 20:40

1 Answer 1

2

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

Sign up to request clarification or add additional context in comments.

1 Comment

They might also benefit from reviewing the official getting started docs for ruby and the rails guides

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.