Tuesday, March 20

Immutable ActiveRecord Attributes

At last night's Ruby Meetup I posed the question of how to make an ActiveRecord attribute immutable, meaning the ability to set a value once and never allow it to change.

I got lots of theories but no solid answers. One person suggested using callbacks but the hooks aren't there for the logic he proposed. Another person suggested overwriting the assignment method, but I wondered about the update_attributes method undermining it. It turns out the latter calls the former so it's safe.

I wrote some unit tests today and started experimenting and found a solution that seems to work nicely (and consistently).

class Sender < ActiveRecord::Base
def email=(address)
if new_record?
write_attribute(:email, address)
else
raise 'Sender.email is immutable!'
end
end
end

4 comments:

Simon said...

Looks good to me. A bit of:

immutable :only => [...]

would be pretty nice I reckon...could whip that up for you if you like. Wouldn't want to steal your thunder though :)

xmlblog said...

Sorry I missed the meetup. Anyway, if you want to protect the attribute from mass assignment via update_attributes, just use attr_protected.

http://caboo.se/doc/classes/ActiveRecord/Base.html#M006884

Craig said...

Would it be wort the trouble to wrap ActiveRecord in a class that only exposes a read interface?

Nicolas said...

my first thought was the same as "xmlblog's" (attr_protected/attr_accessible) but i guess you have a reason why you want not to go this way... So why you try to circumvent attr_protected/attr_accessible?