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
.