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
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
any other. Trouble is that when you assign stuff in Ruby (that is, a simple
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.