<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -72,12 +72,26 @@ module ActiveModel
       changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h }
     end
 
+    # Map of attributes that were changed when the model was saved.
+    #   person.name # =&gt; 'bob'
+    #   person.name = 'robert'
+    #   person.save
+    #   person.previous_changes # =&gt; {'name' =&gt; ['bob, 'robert']}
+    def previous_changes
+      previously_changed_attributes
+    end
+
     private
       # Map of change &lt;tt&gt;attr =&gt; original value&lt;/tt&gt;.
       def changed_attributes
         @changed_attributes ||= {}
       end
 
+      # Map of fields that were changed when the model was saved
+      def previously_changed_attributes
+        @previously_changed || {}
+      end
+
       # Handle &lt;tt&gt;*_changed?&lt;/tt&gt; for +method_missing+.
       def attribute_changed?(attr)
         changed_attributes.include?(attr)</diff>
      <filename>activemodel/lib/active_model/dirty.rb</filename>
    </modified>
    <modified>
      <diff>@@ -19,6 +19,7 @@ module ActiveRecord
       # Attempts to +save+ the record and clears changed attributes if successful.
       def save_with_dirty(*args) #:nodoc:
         if status = save_without_dirty(*args)
+          @previously_changed = changes
           changed_attributes.clear
         end
         status
@@ -26,12 +27,18 @@ module ActiveRecord
 
       # Attempts to &lt;tt&gt;save!&lt;/tt&gt; the record and clears changed attributes if successful.
       def save_with_dirty!(*args) #:nodoc:
-        save_without_dirty!(*args).tap { changed_attributes.clear }
+        save_without_dirty!(*args).tap do
+          @previously_changed = changes 
+          changed_attributes.clear 
+        end
       end
 
       # &lt;tt&gt;reload&lt;/tt&gt; the record and clears changed attributes.
       def reload_with_dirty(*args) #:nodoc:
-        reload_without_dirty(*args).tap { changed_attributes.clear }
+        reload_without_dirty(*args).tap do
+          previously_changed_attributes.clear
+          changed_attributes.clear
+        end
       end
 
       private</diff>
      <filename>activerecord/lib/active_record/attribute_methods/dirty.rb</filename>
    </modified>
    <modified>
      <diff>@@ -308,6 +308,84 @@ class DirtyTest &lt; ActiveRecord::TestCase
     end
   end
 
+  def test_previous_changes
+    # original values should be in previous_changes
+    pirate = Pirate.new
+  
+    assert_equal Hash.new, pirate.previous_changes
+    pirate.catchphrase = &quot;arrr&quot;
+    pirate.save!
+  
+    assert_equal 4, pirate.previous_changes.size
+    assert_equal [nil, &quot;arrr&quot;], pirate.previous_changes['catchphrase']
+    assert_equal [nil, pirate.id], pirate.previous_changes['id']
+    assert_nil pirate.previous_changes['updated_on'][0]
+    assert_not_nil pirate.previous_changes['updated_on'][1]
+    assert_nil pirate.previous_changes['created_on'][0]
+    assert_not_nil pirate.previous_changes['created_on'][1]
+    assert !pirate.previous_changes.key?('parrot_id')
+  
+    # original values should be in previous_changes
+    pirate = Pirate.new
+  
+    assert_equal Hash.new, pirate.previous_changes
+    pirate.catchphrase = &quot;arrr&quot;
+    pirate.save
+  
+    assert_equal 4, pirate.previous_changes.size
+    assert_equal [nil, &quot;arrr&quot;], pirate.previous_changes['catchphrase']
+    assert_equal [nil, pirate.id], pirate.previous_changes['id']
+    assert pirate.previous_changes.include?('updated_on')
+    assert pirate.previous_changes.include?('created_on')
+    assert !pirate.previous_changes.key?('parrot_id')
+  
+    pirate.catchphrase = &quot;Yar!!&quot;
+    pirate.reload
+    assert_equal Hash.new, pirate.previous_changes
+
+    pirate = Pirate.find_by_catchphrase(&quot;arrr&quot;)
+    pirate.catchphrase = &quot;Me Maties!&quot;
+    pirate.save!
+
+    assert_equal 2, pirate.previous_changes.size
+    assert_equal [&quot;arrr&quot;, &quot;Me Maties!&quot;], pirate.previous_changes['catchphrase']
+    assert_not_nil pirate.previous_changes['updated_on'][0]
+    assert_not_nil pirate.previous_changes['updated_on'][1]
+    assert !pirate.previous_changes.key?('parrot_id')
+    assert !pirate.previous_changes.key?('created_on')
+
+    pirate = Pirate.find_by_catchphrase(&quot;Me Maties!&quot;)
+    pirate.catchphrase = &quot;Thar She Blows!&quot;
+    pirate.save
+
+    assert_equal 2, pirate.previous_changes.size
+    assert_equal [&quot;Me Maties!&quot;, &quot;Thar She Blows!&quot;], pirate.previous_changes['catchphrase']
+    assert_not_nil pirate.previous_changes['updated_on'][0]
+    assert_not_nil pirate.previous_changes['updated_on'][1]
+    assert !pirate.previous_changes.key?('parrot_id')
+    assert !pirate.previous_changes.key?('created_on')
+
+    pirate = Pirate.find_by_catchphrase(&quot;Thar She Blows!&quot;)
+    pirate.update_attributes(:catchphrase =&gt; &quot;Ahoy!&quot;)
+
+    assert_equal 2, pirate.previous_changes.size
+    assert_equal [&quot;Thar She Blows!&quot;, &quot;Ahoy!&quot;], pirate.previous_changes['catchphrase']
+    assert_not_nil pirate.previous_changes['updated_on'][0]
+    assert_not_nil pirate.previous_changes['updated_on'][1]
+    assert !pirate.previous_changes.key?('parrot_id')
+    assert !pirate.previous_changes.key?('created_on')
+
+    pirate = Pirate.find_by_catchphrase(&quot;Ahoy!&quot;)
+    pirate.update_attribute(:catchphrase, &quot;Ninjas suck!&quot;)
+
+    assert_equal 2, pirate.previous_changes.size
+    assert_equal [&quot;Ahoy!&quot;, &quot;Ninjas suck!&quot;], pirate.previous_changes['catchphrase']
+    assert_not_nil pirate.previous_changes['updated_on'][0]
+    assert_not_nil pirate.previous_changes['updated_on'][1]
+    assert !pirate.previous_changes.key?('parrot_id')
+    assert !pirate.previous_changes.key?('created_on')    
+  end
+
   private
     def with_partial_updates(klass, on = true)
       old = klass.partial_updates?</diff>
      <filename>activerecord/test/cases/dirty_test.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>57f7308da4dff57639f8e67a830baab358aaa5df</id>
    </parent>
  </parents>
  <author>
    <name>Josh Sharpe</name>
    <login>crankharder</login>
    <email>josh.m.sharpe@gmail.com</email>
  </author>
  <url>http://github.com/rails/rails/commit/80989437dc1502f9194b0600941b6d70a3efa3b2</url>
  <id>80989437dc1502f9194b0600941b6d70a3efa3b2</id>
  <committed-date>2009-08-31T10:50:27-07:00</committed-date>
  <authored-date>2009-08-31T10:50:27-07:00</authored-date>
  <message>I added this feature so that a Map of changed fields could be retrieved
after a model had been saved. This is useful in the after_save callback
when you need to know what fields changed. At present there is no way
to do this other than have code in the before_save callback that takes
a copy of the changes Map, which I thought was a bit messy.

Example.

  person = Person.find_by_name('bob')
  person.name = 'robert'
  person.changes # =&gt; {'name' =&gt; ['bob, 'robert']}
  person.save
  person.changes # =&gt; {}
  person.previous_changes # =&gt; {'name' =&gt; ['bob, 'robert']}
  person.reload
  person.previous_changes # =&gt; {}

Signed-off-by: Joshua Peek &lt;josh@joshpeek.com&gt;</message>
  <tree>a899f747b89ed6b912ee7218d4f9e01f95fe2d25</tree>
  <committer>
    <name>Joshua Peek</name>
    <login>josh</login>
    <email>josh@joshpeek.com</email>
  </committer>
</commit>
