You are here: Browse Railsplugins Acts As Diff
= acts_as_diff
Suppose you have a database table that you don’t want to change (e.g. you bought an expensive database), but you still want to store modifications to that table. Then acts_as_diff is for you!
Let’s say you bought a database containing all songs of all times. There are the tables “artist”, “albums” and “songs”, with the corrensponding models “Artist”, “Album” and Song>
create_table :artists do |t| t.column :first_name, :string t.column :last_name, :string t.column :birthday, :date end
create_table :albums do |t| t.column :artist_id, :integer t.column :name, :strong end
create_table :songs do |t| t.column :album_id, :integer t.column :name, :string t.column :duration, :time end
class Artist < ActiveRecord::Base has_many :albums end
class Album < ActiveRecord::Base belongs_to :artist has_many :songs end
class Song < ActiveRecord::Base belongs_to :album end
You would like to keep your own modifications in different tables, but you want that transparently to your application. For each of the tables, create a diff-table with the same columns as the original tables except a) leave out the foreign keys and b) reference the original table in the diff table:
create_table :artist_diffs do |t| t.column :artist_id, :integer t.column :first_name, :string t.column :last_name, :string t.column :birthday, :date end
create_table :album_diffs do |t| t.column :album_id, :integer t.column :name, :strong end
create_table :song_diffs do |t| t.column :song_id, :integer t.column :name, :string t.column :duration, :time end
Now the interesting part: The models for the diff tables reference the models for the original tables with the “is_diff” statement:
class ArtistDiff < ActiveRecord::Base is_diff :to => :artist end
class AlbumDiff < ActiveRecord::Base is_diff :to => :album end
class SongDiff < ActiveRecord::Base is_diff :to => :song end
Also, the models for the original tables need to know about the diff tables:
class Artist < ActiveRecord::Base # ... has_diff :in => :artist_diff end
class Album < ActiveRecord::Base # ... has_diff :in => :album_diff end
class Song < ActiveRecord::Base # ... has_diff :in => :song_diff end
Now, every change that you save to the Artist, Album or Song models will be saved in the diff tables. Let’s try it:
jimi = Artist.find(:first) => #<artist:0×32ff078>”Jimi”, “last_name”=>”Hendrix”, “id”=>”1”, “birthday”=>”1942-11-27”}>
jimi.update_attributes(:first_name => “James Marshall”) => #<artist:0×32ff078>”Jimi”, “last_name”=>”Hendrix”, “id”=>”1”, “birthday”=>”1942-11-27”}>
Note that the first name did not change. But, when accessed directly, you get:
jimi.first_name => “James Marshall”
This value was saved in the diff table, which can be accessed through the “diff” method:
jimi.diff => #<artistdiff:0×32ff082>”1”, “first_name”=>”James Marshall”, “last_name”=>nil, “id”=>”1”, “birthday”=>nil}>
Note that the diff table contains only the values for the changed columns. If you want to rollback the changes you have made, simply destroy the record in the diff table, or set the column to nil:
jimi.diff.destroy => #<artistdiff:0×32ff082>”1”, “first_name”=>”James Marshall”, “last_name”=>nil, “id”=>”1”, “birthday”=>nil}>
jimi.first_name => “Jimi”
Now, the first name is the original again.
InstallationWe don't have a public SVN server yet, so you have to unpack this plugin to vendor/plugins inside your rails project.
UsagePlugin usage in short (for the longer explanation, see above):
- For each table that you don’t want to modify directly, create a diff table (e.g. for artists, create artist_diffs) - In the model for the original table, add the has_diff statement (e.g. has_diff :in => :artist_diff) - In the model for the diff table, add the is_diff statement (e.g. is_diff :to => :artist)
To disable the plugin temporarily, use the class method disable_diff_table. This is useful, if you want to update the original table. Call the class method enable_diff_table after you’re done.
StatusThis plugin is in a very early state and has not yet been used in a production environment, i.e. there are bugs. Feel free to fix them and to send patches :-)
TODO- Methods to explicitly access the original or the modified data - Method to clear all or selected modifications for a given record - Possibility to explicitly set a column to nil in the diff table (right now, nil in the diff table means “no change”) - Performance improvements for large queries (possibly using :include) - Documentation - Tests
LicenseReleased under the MIT license.
AuthorsNOTE: This description has been extracted from the Plugin README and so the formatting may need updating to make browser friendly