5

Given this code:

class Something
  attr_accessor :my_variable

  def initialize
    @my_variable = 0
  end

  def foo
    my_variable = my_variable + 3
  end
end

s = Something.new
s.foo

I get this error:

test.rb:9:in `foo': undefined method `+' for nil:NilClass (NoMethodError)
    from test.rb:14:in `<main>'

If attr_accessor creates a method called my_variable (and ..=), why can't foo find the method? It works if I change it to self.my_variable, but why? Isn't self the default receiver?

ryeguy
  • 65,519
  • 58
  • 198
  • 260

4 Answers4

7

You're not calling the method there, you're actually referencing the same variable you're in the process of defining! This is a little gotcha in Ruby.

What would be better is if you referenced and set the instance variable instead:

@my_variable = @my_variable + 3

Or shorter:

@my_variable += 3

Or you could call the setter method, as you found (and Jits pointed) out:

self.my_variable += 3

This last one will call the my_variable= method defined by the attr_accessor, where the other two will only modify a variable. If you did it this way, you could override my_variable= to do something different to the value passed in:

def my_variable=(value)
  # do something here
  @my_variable = value
end

BONUS

Or you could call the method explicitly by passing an empty set of arguments through:

my_variable = my_variable() + 3

This is not "The Ruby Way" to go about it, but it's still interesting to know that you can still call a method this way if you have a local variable of the same name.

Community
  • 1
  • 1
Ryan Bigg
  • 106,965
  • 23
  • 235
  • 261
  • 1
    WRT the Bonus: You'd still need to either set the ivar directly or use the setter. That just sets a local. – Chuck May 30 '11 at 04:06
5
my_variable = my_variable + 3

... is a local variable assignment, which takes precedence.

Hence the need for self - in order to scope it to the object.

Jits
  • 9,647
  • 1
  • 34
  • 27
3

If you do my_variable in foo, that's assigning to the local variable my_variable, not calling the method my_variable=.

To assign the value as you desire, you do need to use self as you found out.

See also this question: Why do ruby setters need “self.” qualification within the class?

Community
  • 1
  • 1
Zabba
  • 64,285
  • 47
  • 179
  • 207
2

I think in this case the scope of the variable is only within the function, unless you prepend it with self. of @.

user94154
  • 16,176
  • 20
  • 77
  • 116