If you are a frequent reader, please congratulate me next time you see me on my first ever GiHub commit. I have been a long time user of GitHub, but I haven’t contributed anything back yet. I decided to start with the project acts_as_revisable, because I have some ideas for it that I think are useful.
The commit tonight adds a “revisable_changes” column, which records the attributes “before” and “after” values in the revision record. This data is calculated from the ActiveRecord instance method “changes”. To demonstrate:
# User.create :username => 'ben' #[nil, "ben"]}> # User.find(1).update_attributes :username => 'bob' #["ben", "bob"]}>
This should serve as a good base to build summaries in a human readable format in an application. After tossing around a few different word choices to describe the changes, I decided that the better approach was to just record the data that changed in a structured format and leave it up to the implementation. Such an implementation may look something like this:
def changes_summary self.revisable_changes.map do |attribute, values| before, after = values[0], values[1] if before.blank? and after "Added #{attribute} as #{after}" elsif before and after.blank? "Removed #{attribute} value of #{before}" else "Changed #{attribute} from #{before} to #{after}" end end.join('; ') end
Using our user instance above, this method out return: “Changed username from ben to bob”.
Other plans for acts_as_revisable in the future include tracking associations of a model. For example, if a user has many permissions, and the permissions change, the revisable information will include the permissions for that user at that point in time. I haven’t worked out the specifics of this yet, as it could potentially generate a lot of unwanted data. I plan on parsing the data from ActiveRecord’s “reflect_on_all_associations” method to gather the associations. I will then provide a way for a user to configure which associations should, or should not be tracked. Then I will iterate through these objects using ActiveRecord hooks and somehow record the state. Either a shadow table, or by serializing the associations in a column. The trick here will be to marshal the objects back to the live data when a revert is called. More soon…