in News

ANN: rails-undo-redo

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.

For a full how-to, read the Rails Undo Redo project page, or try the Rails Undo Redo demo.

18 Comments

  1. Hi Pascal,

    this sounds like a great addition. I am curious though: do you keep track of the revisions of a record ? Is it possible to visualize them ?

  2. The undo data is kept in a separate record independent from the original model. The same way Acts As Revisionable does it, and unlike something like Acts As Versioned.

    Assuming you know what the UndoManager is (UndoManager.current), then you can find the UndoActions (undo_manager.undo_actions), from there, the UndoRecords (undo_action.undo_records).

    The data is stored as a compressed blob, but the inspect method of UndoRecord will show what the object looks like.

    I’ll be documenting in more details how this works.

  3. This is perfect! The trouble with me working with rails is that i always tweak something accidentally. This should be of great help!

  4. It shows

    undefined local variable or method `undo_path’ for #

    I guess you forgot to include some routing changes.

  5. Indeed! Here are the missing routes

    map.undo 'undo', :controller => "welcome", :action => "undo"

    map.redo 'redo', :controller => "welcome", :action => "redo"

    In fact, any controller should do since undo and redo are added to all controllers. “

  6. Absolutely terrific!

    Works even with polymorphic associations. Do not forget to include, in your models, “acts_as_undoable” at each end of the relation though.

    Onno

  7. Sorry for my ignorance….
    On this page:
    http://blog.nanorails.com/rails-undo-redo

    It says: Define the undoable controllers:
    Add the undo/redo logic to all your controllers
    The code sample has: undoable_methods

    Is that just pseudo code? I mean is undoable_methods a method?

    I am assuming it is just pseudo code and to add undo\redo functionality to the controller you need the 3 things as explained in: Track changes

    Is this assumption correct?

    Another ‘lazy’ question. I take it there is no problem using this on a app that only provides xml, right?

    Thank you…

  8. @Onno: great to hear! I’ve only used on simpler associations, but it hooks in pretty deep, so I’m no surprised :)

    @Makr_001: yes, undoable_methods is actual code. It will add all the methods required for for controller to provide undo logic. See http://gitorious.org/projects/rur_demo/repos/mainline/blobs/master/app/controllers/projects_controller.rb for an example. This adds methods such as change, undo_redo_links, … to your controller and helper

    I would think that this is independent from the rendering format you use, yes.

  9. Thanks for the quick and helpful response.
    I will let you know how I get on….

  10. Well, sorry. Never had any chance to go very deep with Python. I’m sure a similar approach can be used when using a python ORM.

  11. Unfortunately the demo site for undo-redo is down. It tells me to contact you. Well, here’s me trying to tell you about it ;-)

  12. Ah, good point. That’s because the server ip changed and I did not update the dns. Hopefully, once the change propagates, everything should be back up.

    Thank you for pointing that out, Felix!

  13. Unfortunately the demo sevre is still down ;)
    It looks like you havn’t upgraded the project in a while. Is it still advisable to use with the current or edge Rails ?
    I am planning to use this in a production setting.

  14. The demo was not so popular, so I took it down. The code is more dependent on how active record works than anything else, but as far as production goes, you should make your own call and get familiar with the code.

Comments are closed.

  • Related Content by Tag