Prevent an ActiveRecord attribute from being changed after creation

Sometimes you can make your life easier by not allowing a record attribute to be changed after the record was created. An example for this is when your model represents a child node in a composition Show archive.org snapshot and has logic that is hard to support for cases when the container changes.

Here is an example for a container Region composed of many children of type Holiday. After saving a Holiday it caches the current number of holidays in its region:

class Region < ActiveRecord::Base

  has_many :holidays
  validates_numericality_of :holiday_count

end

class Holiday < ActiveRecord::Base

  belongs_to :region
  after_save :update_holiday_count_in_region
  after_destroy :update_holiday_count_in_region

  private

  def update_holiday_count
    region.update_attribute :holiday_count, region.holidays.count
  end
  
end

Note that there are probably better days to implement the holiday_count mechanism, but humor me for the sake of the example.

The #update_holiday_count method in the code above has a bug when a holiday's region changes. Only the new region is updated with the current holiday count, while we would actually have to update both the old and new region.

While you can write #update_holiday_count in a way that supports a region change, you can also use the attached Modularity Show archive.org snapshot trait to forbid #region_id from being changed after the Holiday was created:

class Holiday < ActiveRecord::Base

  belongs_to :region
  does 'frozen_attribute', :region_id

  # ...

end

The attached trait will add a validation error if #region_id is changed during an update.

Henning Koch