lib/mongomodel/concerns/attribute_methods/dirty.rb in mongomodel-0.5.5 vs lib/mongomodel/concerns/attribute_methods/dirty.rb in mongomodel-0.5.6
- old
+ new
@@ -1,42 +1,124 @@
module MongoModel
module AttributeMethods
module Dirty
extend ActiveSupport::Concern
-
- include ActiveModel::Dirty
-
+
+ include ActiveModel::AttributeMethods
+
+ OPTION_NOT_GIVEN = Object.new
+ private_constant :OPTION_NOT_GIVEN
+
included do
+ attribute_method_suffix "_changed?", "_change", "_will_change!", "_was"
+ attribute_method_suffix "_previously_changed?", "_previous_change"
+ attribute_method_affix prefix: "restore_", suffix: "!"
+
before_save { @previously_changed = changes }
after_save { changed_attributes.clear }
end
-
+
+ def changed?
+ changed_attributes.present?
+ end
+
+ def changed
+ changed_attributes.keys
+ end
+
+ def changes
+ ActiveSupport::HashWithIndifferentAccess[changed.map { |attr| [attr, attribute_change(attr)] }]
+ end
+
+ def previous_changes
+ @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
+ end
+
+ def changed_attributes
+ @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
+ end
+
+ def attribute_changed?(attr, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN)
+ !!changes_include?(attr) &&
+ (to == OPTION_NOT_GIVEN || to == __send__(attr)) &&
+ (from == OPTION_NOT_GIVEN || from == changed_attributes[attr])
+ end
+
+ def attribute_was(attr)
+ attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
+ end
+
+ def attribute_previously_changed?(attr)
+ previous_changes.include?(attr)
+ end
+
+ def restore_attributes(attributes = changed)
+ attributes.each { |attr| restore_attribute! attr }
+ end
+
def write_attribute(key, value)
attr = key.to_sym
-
+
# The attribute already has an unsaved change.
if changed_attributes.include?(attr)
old = changed_attributes[attr]
changed_attributes.delete(attr) if value == old
else
old = clone_attribute_value(attr)
changed_attributes[attr] = old unless value == old
end
-
+
super
end
-
+
# Returns the attributes as they were before any changes were made to the document.
def original_attributes
{}.with_indifferent_access.merge(attributes).merge(changed_attributes)
end
-
- protected
- def changed_attributes
- @changed_attributes ||= {}.with_indifferent_access
- end
-
+
private
+ def changes_include?(attr_name)
+ attributes_changed_by_setter.include?(attr_name)
+ end
+ alias attribute_changed_by_setter? changes_include?
+
+ def attribute_change(attr)
+ [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
+ end
+
+ def attribute_previous_change(attr)
+ previous_changes[attr] if attribute_previously_changed?(attr)
+ end
+
+ def attribute_will_change!(attr)
+ return if attribute_changed?(attr)
+
+ begin
+ value = __send__(attr)
+ value = value.duplicable? ? value.clone : value
+ rescue TypeError, NoMethodError
+ end
+
+ set_attribute_was(attr, value)
+ end
+
+ def restore_attribute!(attr)
+ if attribute_changed?(attr)
+ __send__("#{attr}=", changed_attributes[attr])
+ clear_attribute_changes([attr])
+ end
+ end
+
+ alias_method :attributes_changed_by_setter, :changed_attributes
+
+ def set_attribute_was(attr, old_value)
+ attributes_changed_by_setter[attr] = old_value
+ end
+
+ def clear_attribute_changes(attributes)
+ attributes_changed_by_setter.except!(*attributes)
+ end
+
def clone_attribute_value(attribute_name)
value = self[attribute_name.to_sym]
value.duplicable? ? value.clone : value
rescue TypeError, NoMethodError
value