If you’ve been using Ruby for any length of time, then you should know that you
can override assignment on object attributes, right? It’s what you get from
attr_accessor
(by way of attr_writer
), and it’s what you get when you define
a something=
method (which is what attr_writer
really does).
>> class Foo
def bar= val
@bar = val
end
end
>> f = Foo.new
>> f.bar = :none
>> p f
#<Foo:0x319afc @bar=:none>
The setter is a method, completely normal, and you can call it with send
like
any other. Trouble is that when you assign stuff in Ruby (that is, a simple a =
b
statement), your setter doesn’t act like a normal method. First off, all
assignments return the right-hand-side of the assignment, instead of what the
return value of the setter would be.
>> def test= val
@test = val.to_f
end
>> self.test = "12.34"
=> "12.34"
>> self.send(:test=, "12.34")
=> 12.34
Secondly, if you use a getter method bare (like, say, foo
) without having
defined it first, Ruby will try to access the local variable foo
first. Seeing
it isn’t there, it’ll try to access the method on self
. And then if it isn’t
there, it’ll go to method_missing
. If you try this with a setter, though,
you’ll find it won’t get past the local variable… Ruby will just set it and
continue on. You need to explicitly call it through self
to get it to work.
>> def foo= val
puts val
@foo = val
end
>> foo = 1
=> 1
>> self.foo = 1
1
=> 1
Both of these quirks have caught me before in rather unexpected ways, most
annoyingly the first one, because I fully expected the return value of the
statement to be the return value of the setter. Getters work fine without being
referenced through an object, just remember to add self
before setters, or you
may find yourself wondering wtf is going on, since everything looks right.