2012/1/16
Problem
If I do this:
puts foo foo = 3
there is always the doubt whether I'm accessing a local variable, or calling methods foo
and foo=
.
TL;DR
When you want to call an instance's own methods, use self
:
self.foo # Calls foo self.foo = 'bar' # Calls foo=
Example 1
def example1 'example1 method' end example1 #=> "example1 method" example1 = 'assigned value' example1 #=> "assigned value"
Here, we define a method, and then make an assignment. As we assign to a bareword, Ruby creates a new local variable.
As soon as a value is assigned to the local variable, the method no longer gets called.
Example 2
But, what if we also have an assignment method?
def example2 'example2 method' end def example2=(value) puts "example2= called" # (this never gets called) end example2 #=> "example2 method" example2 = 'assigned value' example2 #=> "assigned value"
Adding the method example2=
does not change things. When we assign to a bareword, Ruby takes it as assignment to a local variable.
Example with a Class
class Foo attr_accessor :bar def initialize @bar = 42 end def method1 puts bar end def method2 bar = 99 puts bar end def method3 bar = 99 puts self.bar end end foo = Foo.new foo.bar #=> 42 foo.method1 #=> 42 foo.method2 #=> 99 foo.method3 #=> 42
method2
is the problem case. bar
is assigned to, creating a local variable, so subsequent calls to bar
return 99.
method3
disambiguates by explicitly calling the bar
method on self
.
The Cause
There are two things going on here:
bareword assignment creates local variables,
local variables mask methods of the same name.
Refactoring Might Break Code
One solution is to use `self.method` only in cases where local variables mask methods. The problem with this approach is that code may be altered, introducing local variables, and so altering the behaviour of following code:
Original Code
class Foo attr_accessor :bar def baz puts bar end end foo = Foo.new foo.bar = 42 foo.baz #=> 42
Modified Code
class Foo attr_accessor :bar def baz bar = 99 # <= variable assignment introduced puts bar end end foo = Foo.new foo.bar = 42 foo.baz #=> 99
Solution
The best solution is to always call instance methods on self
.