ActsAsDiffable provides a dead-simple way to compare two instances of a class, including any or all associations, or more complex relationships.
The return is a hash*, suitable for digestion by case-based textualizers, JSON processors, etc., in the form { ‘attribute’ => [from, to] }
-
In the instance of no changes, a nil is returned
Usage
class Foo < ActiveRecord::Base
acts_as_diffable
end
> Foo.first.diff(Foo.last) => { 'bar' => ['foo', nil] }
Associations
For plural associations, a :diff_key option needs to be added to the association, defining how to relate disparate instances within each parent’s collections.
This can be a single field or a collection of fields in an array, and will be expressed as the key side of a hash with the value being the hash of attribute differences.
For singular association, there is no need to specify a way to organize and compare, so we only need to express which associations to include in the diff, by adding a :diff option to the association that evaluates to true.
class Foo < ActiveRecord::Base
acts_as_diffable
has_one :bar, :diff => true
has_many :fish, :diff_keypattern => :name
has_and_belongs_to_many :users, :diff_keypattern => [:firstname, :lastname]
end
> Foo.first.diff(Foo.last)
=> { 'bar' => { 'attr1' => ['a', 'b'],
'attr2' => [14, nil] },
'fish => { 'nemo' => {'fish_attr1' => [nil, 'zip'] },
'goldie' => {'fish_attr1' => ['zap', 'zop'] } },
'users' => { ['Jane', 'Doe'] => { 'firstname' => 'Jane',
'lastname' => 'Doe' },
['John', 'Doe'] => { '_deleted' => true } } }
More complex relationships
In addition to the marked associations, any method on the class that returns an ActiveRecord-ish object can be included in the diff by adding a manual_diff_definiton. For comparing collections of ActiveRecord objects, use the form:
manual_diff_definition :name, :eval => 'instance_eval_code',
:diff_key => [:key, :pattern]
For simpler singular comparisons, omit the diff_key option.
Have a lot of fun!