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.
Write the bar= method yourself and use an instance variable to track changes.
ActiveRecord
has the#attribute
method (source) which once invoked from your class will letActiveModel::Dirty
to create methods such asbar_was
,bar_changed?
, and many others.Thus you would have to call
attribute :bar
within any class that extends fromActiveRecord
(orApplicationRecord
for most recent versions of Rails) in order to create those helper methods uponbar
.Edit: Note that this approach should not be mixed with
attr_accessor :bar
Edit 2: Another note is that unpersisted attributes defined with
attribute
(egattribute :bar, :string
) will be blown away on save. If you need attrs to hang around after save (as I did), you actually can (carefully) mix withattr_reader
, like so:I figured out a solution that worked for me...
Save this file as
lib/active_record/nonpersisted_attribute_methods.rb
: https://gist.github.com/4600209Then you can do something like this:
However, it looks like we get a warning when we do it this way:
So I don't know if this approach will break or be harder in Rails 4...
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:
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.