Tuesday, January 13, 2009

Overloading the ActiveRecord Setter

Sometimes, you want to do some extra work when you set an attribute on your ActiveRecord model instance.

You can't just do the following, because you'll just end up recursively calling the very setter that you're defining:


class Square < ActiveRecord::Base
def side=(value)
area = value * value
side = value
end
end

...and you can't do the following because you'll only modify a copy of the attributes hash of the model instance, which will not be written through to the database when the model instance is saved. ( check out ActiveRecord::Base::attributes in active_record/base.rb):

class Square < ActiveRecord::Base
def side=(value)
self.attributes['side'] = value
self.area = value * value
end
end

After looking through the source for ActiveRecord::Base and some testing, I've found a method that's worked for me and appears to be the preferred way of doing it:


class Square < ActiveRecord::Base
def side=(value)
self.area = value * value
# This would still work because we're calling the default setter for area
write_attribute('side', value)
end
end

Alternatively, you could do "self['side'] = value" instead of write_attribute. The index assignment operatore actually does the same thing.