Immutable ActiveRecord Attributes

Posted by Paul Ingles
Monday, September 10, 2007 19:21:00 GMT

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
Comments

Leave a response

  1. Sam SmootSeptember 10, 2007 @ 10:45 PM

    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) ;-)

  2. Jordan McKibleSeptember 11, 2007 @ 02:34 AM

    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?

  3. BenSeptember 11, 2007 @ 09:07 AM

    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??