Track dirty for not-persisted attribute in an ActiveRecord object in rails

Santiago Palladino picture Santiago Palladino · Apr 14, 2011 · Viewed 9.1k times · Source

I have an object that inherits from ActiveRecord, yet it has an attribute that is not persisted in the DB, like:

 class Foo < ActiveRecord::Base
   attr_accessor :bar
 end

I would like to be able to track changes to 'bar', with methods like 'bar_changed?', as provided by ActiveModel Dirty. The problem is that when I try to implement Dirty on this object, as described in the docs, I'm getting an error as both ActiveRecord and ActiveModel have defined define_attribute_methods, but with different number of parameters, so I'm getting an error when trying to invoke define_attribute_methods [:bar].

I have tried aliasing define_attribute_methods before including ActiveModel::Dirty, but with no luck: I get a not defined method error.

Any ideas on how to deal with this? Of course I could write the required methods manually, but I was wondering if it was possible to do using Rails modules, by extending ActiveModel functionality to attributes not handled by ActiveRecord.

Answer

Alessandro picture Alessandro · May 16, 2012

I'm using the attribute_will_change! method and things seem to be working fine.

It's a private method defined in active_model/dirty.rb, but ActiveRecord mixes it in all models.

This is what I ended up implementing in my model class:

def bar
  @bar ||= init_bar
end
def bar=(value)
  attribute_will_change!('bar') if bar != value
  @bar = value
end
def bar_changed?
  changed.include?('bar')
end

The init_bar method is just used to initialise the attribute. You may or may not need it.

I didn't need to specify any other method (such as define_attribute_methods) or include any modules. You do have to reimplement some of the methods yourself, but at least the behaviour will be mostly consistent with ActiveModel.

I admit I haven't tested it thoroughly yet, but so far I've encountered no issues.