The test:
def test_cannot_change_country_name_once_constructed
country = Country.new(:name => 'UK')
assert_raise RuntimeError do
country.name = 'USA'
end
end
The class:
class Country < ActiveRecord::Base
extend ImmutableModel
immutable :name
...
The implementation:
module ImmutableModel
def immutable(attr_name)
define_method "#{attr_name}=" do |new_value|
read_attribute(attr_name).nil? ? write_attribute(attr_name, new_value) : raise(RuntimeError, "You can't change #{attr_name} once it has been set")
end
end
end










I can see the utility in this. It’s not bad, but…
Wouldn’t this be more inline with ActiveRecord if it was a validation instead?
I’m guessing that might not be trivial with AR. Dunno. In DataMapper I could just check if the attribute is #dirty?(attr_name) ;-)
I’ve been slowing putting together a plugin called SecureAssociations http://code.google.com/p/secure-associations/ I’ve been meaning to add immutable as an option for belongs_to associations. This post has inspired me to finally add that functionality. Would you mind if I used this code?
Nice, concise code… though I also wonder how useful it is to throw an exception… seems very Java-esque.
I was also pondering your use of “extend”. Isn’t it more common to use “include”? If I understand correctly, “include” adds a module’s methods to a class; “extend” adds a module’s methods to an object instance… though in this usage the object would be the class Country… and hence the same… maybe??