I did something quite evil today at work.
A customer wants to be able to roll back changes made by its moderators. If there's a difference of opinion they want to roll back a change. Makes sense. (No, it isn't a wiki.)
I decided that a second-best effort (instead of doing:
-
AdminEditLog.create(:user => @session_user, :description => "Set object=#{@object.inspect}")
I'd do:
-
old = Marshal.dump(@object)
-
@object.update_attributes(params[:object])
-
AdminEditLog.create(:user => @session_user, :old => old, :current => Marshal.dump(@object.reload))
Why is it evil? It requires two BLOB fields in MySQL, stores the ENTIRE object, twice and can't be searched upon.
The next iteration will likely just be a diff. Something like:
-
def object_diff(o,c)
-
changes = {}
-
c.attributes.each do |k,v|
-
if o[k] != v
-
changes[k.to_sym] = v
-
end
-
end
-
changes
-
end
-
Marshal.dump(object_diff(old_obj,new_obj)) # Store this, works for the arbitrary models
It is just a little better. They want to revert back to it so there's no need to do the diff after, upon reverting.
It works and I was impressed how easy it was because the revert action is simply to Marshal.load the old object and to just .save it.
Ruby is love. Just don't abuse it by stuffing crap into the database!


Eviiiiil!
A different approach would be using acts_as_versioned to keep trac of changes using
version.
No marshal-dump-thing needed lol
Comment by Rodrigo Kochenburger — July 5, 2007 @ 00:58
acts_as_versioned, from what I understand of the plugin, isn’t what we wanted. The client wanted to only have a revision history of a certain subset of users and not the general userbase.
Comment by Lisa Seelye — July 5, 2007 @ 22:34