To this day, very few web apps provide any undo/redo capabilities. This is really not such a good thing for usability. There are a few exceptions like gmail, but it does not go far enough to provide what every one is used on their desktop.
Ever since I read Assaf’s post on the subject, and left hungry for more, as Assaf only helps you on the UI side and leaves you all the heavy lifting, I have been thinking that there must be a way to make it a lot easier to implement undo/redo in a consistent manner, and in a way that is as easy as rails has gotten us used to.
After a few failed attempts, and building on the work of Brian Durand and his Acts As Revisionable and ActsAsTrashable plugins, that I’ve reused (and probably abused as well), I have come to realize a few (obvious?) things, namely that:
for Undo/Redo to work, it needs to
- Work across all models
- Capture the list of changed objects (UndoRecords)
- Group these UndoRecords per User Action (UndoActions)
Then undo and redo are just a simple matter of replaying the UndoRecords in the right order.
All my previous attempts, and most other plugins I got inspiration from focused on one model, and even though you need this as a building block, for any real application, you need to be able to undo changes across multiple models (even beyond belongs_to, has_many type changes).
When you look at the types of changes to a model in details, you really have 3 types of changes Create, Update and Destroy (these are the C-U-D in CRUD, even if you are not doing REST), and undoing each one requires special handling, so this is also tracked and managed by UndoRecord.
To make a long story short, I’ve packaged all these ideas into a new Rails plugin: Rails Undo Redo (and very soon a gem as well, because this can help, and that’s also easy, or very easy), you can now easily transform any Rails Application using Active Record into a full fledged multi level undo/redo application like most desktop application.