18

Is there a way to undo/revert any local changes to an Activerecord object. For example:

user = User.first
user.name # "Fred"
user.name = "Sam"
user.name_was # "Fred"

user.revert
user.name # "Fred"

I know I could do user.reload but I shouldn't have to hit the database to do this since the old values are stored in the state of the object.

Preferably a Rails 3 solution.

Tbabs
  • 445
  • 2
  • 5
  • 8
  • I guess you can assign `nil` to user e.g. `user=nil` which is equivalent to revert (but not necessarily the old values) – Bala Sep 06 '13 at 17:57
  • I didn't realize that the old values were retained in the object. Can you point me to any documentation on how to access them? – Peter Alfvin Sep 06 '13 at 17:59
  • 1
    `user.changes` @PeterAlfvin returns a Hash of the changes of the object: `{:attribute => ["original_value", "new_value_before_save"]}` – MrYoshiji Sep 06 '13 at 18:01
  • 1
    In addition to what MrYoshiji said, for every member your AR object has there is a *_was member that shows what the value used to be if it changed e.g. if you have `name` there is also a `name_was` member. http://stackoverflow.com/questions/13906635/what-is-the-activemodel-method-attribute-was-used-for @PeterAlfvin – Tbabs Sep 06 '13 at 18:06

3 Answers3

31

As mentioned in this answer Rails 4.2 introduced the restore_attributes method in ActiveModel::Dirty:

user = User.first
user.name   # "Fred"
user.status # "Active"

user.name = "Sam"
user.status = "Inactive"
user.restore_attributes
user.name   # "Fred"
user.status # "Active"

If you want to restore a specific attribute, restore_attribute! can be used:

user.name = "Sam"
user.status = "Inactive"
user.restore_name!
user.name   # "Fred"
user.status # "Inactive"
Community
  • 1
  • 1
Peter Miller
  • 411
  • 5
  • 4
24

In case anyone ends up here, restore_attributes is the way to do it as of rails 4.2:

http://api.rubyonrails.org/classes/ActiveModel/Dirty.html#method-i-restore_attributes

Jim
  • 1,062
  • 11
  • 24
9

You could just loop through the 'changes' and reset them. There might be a method that does this, but I didn't look.

> c = Course.first
> c.name
=> "Athabasca Country Club"
> c.name = "Foo Bar"
=> "Foo Bar"
> c.changes
=> {"name"=>["Athabasca Country Club", "Foo Bar"]}
> c.changes.each {|k,vs| c[k] = vs.first}
> c.name
=> "Athabasca Country Club"

Actually looks like there is a "name_reset!" method you could call...

> c.changes.each {|k,vs| c.send("#{k}_reset!")}
Philip Hallstrom
  • 19,673
  • 2
  • 42
  • 46
  • Id rather use `c.changes.each {|k,vs| c.send("#{k}=", vs.first)}` instead of the hash notation to set attributes to an object. – MrYoshiji Sep 06 '13 at 18:05
  • 1
    I believe the "reset" methods are actually called `"reset_#{name}!"`. I also suggest using `public_send` to call public methods - it's a good habit to avoid accidental calling of private methods. – Kelvin Nov 21 '18 at 19:54