Merge pull request #10816 from bogdan/less-dirty-dirty

Make AM::Dirty less dirty to plugin into AR or other library
This commit is contained in:
Rafael Mendonça França 2013-09-23 10:59:05 -03:00
parent 782055674a
commit 9aa1a3d853
7 changed files with 32 additions and 26 deletions

View File

@ -1,3 +1,9 @@
* Added new API methods `reset_changes` and `changed_applied` to AM::Dirty
that control changes state. Previsously you needed to update internal
instance variables, but now API methods are available.
*Bogdan Gusiev*
* Fix has_secure_password. `password_confirmation` validations are triggered * Fix has_secure_password. `password_confirmation` validations are triggered
even if no `password_confirmation` is set. even if no `password_confirmation` is set.

View File

@ -14,13 +14,7 @@ module ActiveModel
# track. # track.
# * Call <tt>attr_name_will_change!</tt> before each change to the tracked # * Call <tt>attr_name_will_change!</tt> before each change to the tracked
# attribute. # attribute.
# # * Call <tt>changes_applied</tt> after the changes are persisted.
# If you wish to also track previous changes on save or update, you need to
# add:
#
# @previously_changed = changes
#
# inside of your save or update method.
# #
# A minimal implementation could be: # A minimal implementation could be:
# #
@ -39,8 +33,8 @@ module ActiveModel
# end # end
# #
# def save # def save
# @previously_changed = changes # # do persistence work
# @changed_attributes.clear # changes_applied
# end # end
# end # end
# #
@ -129,7 +123,7 @@ module ActiveModel
# person.save # person.save
# person.previous_changes # => {"name" => ["bob", "robert"]} # person.previous_changes # => {"name" => ["bob", "robert"]}
def previous_changes def previous_changes
@previously_changed @previously_changed ||= {}
end end
# Returns a hash of the attributes with unsaved changes indicating their original # Returns a hash of the attributes with unsaved changes indicating their original
@ -154,6 +148,18 @@ module ActiveModel
private private
# Removes current changes and makes them accessible through +previous_changes+.
def changes_applied
@previously_changed = changes
@changed_attributes = {}
end
# Removes all dirty data: current changes and previous changes
def reset_changes
@previously_changed = {}
@changed_attributes = {}
end
# Handle <tt>*_change</tt> for +method_missing+. # Handle <tt>*_change</tt> for +method_missing+.
def attribute_change(attr) def attribute_change(attr)
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr) [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)

View File

@ -29,8 +29,7 @@ class DirtyTest < ActiveModel::TestCase
end end
def save def save
@previously_changed = changes changes_applied
@changed_attributes.clear
end end
end end

View File

@ -19,8 +19,7 @@ module ActiveRecord
# Attempts to +save+ the record and clears changed attributes if successful. # Attempts to +save+ the record and clears changed attributes if successful.
def save(*) def save(*)
if status = super if status = super
@previously_changed = changes changes_applied
@changed_attributes.clear
end end
status status
end end
@ -28,16 +27,14 @@ module ActiveRecord
# Attempts to <tt>save!</tt> the record and clears changed attributes if successful. # Attempts to <tt>save!</tt> the record and clears changed attributes if successful.
def save!(*) def save!(*)
super.tap do super.tap do
@previously_changed = changes changes_applied
@changed_attributes.clear
end end
end end
# <tt>reload</tt> the record and clears changed attributes. # <tt>reload</tt> the record and clears changed attributes.
def reload(*) def reload(*)
super.tap do super.tap do
@previously_changed.clear reset_changes
@changed_attributes.clear
end end
end end
@ -48,11 +45,11 @@ module ActiveRecord
# The attribute already has an unsaved change. # The attribute already has an unsaved change.
if attribute_changed?(attr) if attribute_changed?(attr)
old = @changed_attributes[attr] old = changed_attributes[attr]
@changed_attributes.delete(attr) unless _field_changed?(attr, old, value) changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
else else
old = clone_attribute_value(:read_attribute, attr) old = clone_attribute_value(:read_attribute, attr)
@changed_attributes[attr] = old if _field_changed?(attr, old, value) changed_attributes[attr] = old if _field_changed?(attr, old, value)
end end
# Carry on. # Carry on.

View File

@ -413,8 +413,6 @@ module ActiveRecord
@aggregation_cache = {} @aggregation_cache = {}
@association_cache = {} @association_cache = {}
@attributes_cache = {} @attributes_cache = {}
@previously_changed = {}
@changed_attributes = {}
@readonly = false @readonly = false
@destroyed = false @destroyed = false
@marked_for_destruction = false @marked_for_destruction = false
@ -431,7 +429,7 @@ module ActiveRecord
# optimistic locking) won't get written unless they get marked as changed # optimistic locking) won't get written unless they get marked as changed
self.class.columns.each do |c| self.class.columns.each do |c|
attr, orig_value = c.name, c.default attr, orig_value = c.name, c.default
@changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr]) changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
end end
end end

View File

@ -434,7 +434,7 @@ module ActiveRecord
changes[self.class.locking_column] = increment_lock if locking_enabled? changes[self.class.locking_column] = increment_lock if locking_enabled?
@changed_attributes.except!(*changes.keys) changed_attributes.except!(*changes.keys)
primary_key = self.class.primary_key primary_key = self.class.primary_key
self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1 self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
end end

View File

@ -120,8 +120,8 @@ class Person
end end
def save def save
@previously_changed = changes
# do save work... # do save work...
changes_applied
end end
end end
``` ```