1

In rails 4.2.5: Is it possible, that a locale var will be declared, even if it occurs in a not executed block? See this example:

class Animal < ActiveRecord::Base

  def test

    # there is no local var `name`
    puts "name " + name.inspect
    puts "self.name " + self.name.inspect

    if false
      name = 'Hund'
    end

    # now there is a local var `name` (value `nil`)

    puts "name " + name.inspect
    puts "self.name " + self.name.inspect

  end
end

To test it:

>> Animal.new(:name=>'Katze').test

name "Katze"
self.name "Katze"
name nil
self.name "Katze"

We just were very surprised, that name suddenly was nil.

For which reason does it behave like this? Is this wanted behaviour? So we must not rely on name, but using self.name in future?

(No question, one should use self.name = "Hund", but why is that local var declared.)

Thanks and regards, Phil

Phil
  • 145
  • 8

2 Answers2

4

It's important to realize that when you call self.name you're not actually accessing a variable but rather sending a message to an accessor method

def name
  @name
end

It's also important to know how the Ruby parser handles local variables, especially when inside a conditional. This is an excerpt from the Well-Grounded Rubyist Chapter 6.1.2

When the Ruby parser sees the sequence identifier, equal-sign, value, as in this expression

x = 1

it allocates space for a local variable called x. The creation of the variable—not the assignment of a value to it, but the internal creation of a variable—always takes place as a result of this kind of expression, even if the code isn’t executed! Consider this example:

if false
  x = 1
end
p x # Output: nil
p y # Fatal Error: y is unknown

The assignment to x isn’t executed, because it’s wrapped in a failing conditional test. But the Ruby parser sees the sequence x = 1, from which it deduces that the program involves a local variable x. The parser doesn’t care whether x is ever assigned a value. Its job is just to scour the code for local vari ables for which space needs to be allocated. The result is that x inhabits a strange kind of variable limbo. It has been brought into being and initialized to nil. In that respect, it differs from a variable that has no existence at all; as you can see in the example, examining x gives you the value nil, whereas trying to inspect the non-existent variable y results in a fatal error. But although x exists, it has not played any role in the program. It exists only as an artifact of the parsing process.

Given the above information, the first time you call name.inpspect in your code, the Ruby parser knows that no such local variable has been defined so it looks to see whether a method by that name exists (it does as it's provided by ActiveRecord) and calls that method - so in the first case name.inspect and self.name.inspect are both calling the same accessor method.

The next time you call name.inspect, you're actually calling the local variable name which the Ruby parser has initialized to nil.

The takeaway is that you should always use self explicitly. Hope this helps. Cheers.

Bart Jedrocha
  • 11,450
  • 5
  • 43
  • 53
1

This is a Ruby thing, not a Rails thing.

Ruby does not define a closed scope for if blocks.

You can get more details of the reason here: https://softwareengineering.stackexchange.com/questions/58900/why-if-statements-do-not-introduce-scope-in-ruby-1-9#answer-60413

Community
  • 1
  • 1
Américo Duarte
  • 525
  • 3
  • 16