# encoding: utf-8 module Mongoid #:nodoc: module Dirty #:nodoc: extend ActiveSupport::Concern module InstanceMethods #:nodoc: # Gets the changes for a specific field. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.attribute_change("title") # [ "Sir", "Madam" ] # # Returns: # # An +Array+ containing the old and new values. def attribute_change(name) @modifications[name] end # Determines if a specific field has chaged. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.attribute_changed?("title") # true # # Returns: # # +true+ if changed, +false+ if not. def attribute_changed?(name) @modifications.include?(name) end # Gets the old value for a specific field. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.attribute_was("title") # "Sir" # # Returns: # # The old field value. def attribute_was(name) change = @modifications[name] change ? change[0] : nil end # Gets the names of all the fields that have changed in the document. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.changed # returns [ "title" ] # # Returns: # # An +Array+ of changed field names. def changed @modifications.keys end # Alerts to whether the document has been modified or not. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.changed? # returns true # # Returns: # # +true+ if changed, +false+ if not. def changed? !@modifications.empty? end # Gets all the modifications that have happened to the object as a +Hash+ # with the keys being the names of the fields, and the values being an # +Array+ with the old value and new value. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.changes # returns { "title" => [ "Sir", "Madam" ] } # # Returns: # # A +Hash+ of changes. def changes @modifications end # Call this method after save, so the changes can be properly switched. # # Example: # # person.move_changes def move_changes @previous_modifications = @modifications.dup @modifications = {} end # Gets all the new values for each of the changed fields, to be passed to # a MongoDB $set modifier. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.setters # returns { "title" => "Madam" } # # Returns: # # A +Hash+ of new values. def setters @modifications.inject({}) do |sets, (field, changes)| key = embedded ? "#{position}.#{field}" : field sets[key] = changes[1]; sets end end # Gets all the modifications that have happened to the object before the # object was saved. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.save! # person.previous_changes # returns { "title" => [ "Sir", "Madam" ] } # # Returns: # # A +Hash+ of changes before save. def previous_changes @previous_modifications end # Resets a changed field back to its old value. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.reset_attribute!("title") # person.title # "Sir" # # Returns: # # The old field value. def reset_attribute!(name) value = attribute_was(name) if value @attributes[name] = value @modifications.delete(name) end end # Sets up the modifications hash. This occurs just after the document is # instantiated. # # Example: # # document.setup_notifications def setup_modifications @modifications ||= {} @previous_modifications ||= {} end protected # Audit the change of a field's value. def modify(name, old_value, new_value) @attributes[name] = new_value @modifications[name] = [ old_value, new_value ] if @modifications end end module ClassMethods #:nodoc: # Add the dynamic dirty methods. These are custom methods defined on a # field by field basis that wrap the dirty attribute methods. # # Example: # # person = Person.new(:title => "Sir") # person.title = "Madam" # person.title_change # [ "Sir", "Madam" ] # person.title_changed? # true # person.title_was # "Sir" # person.reset_title! def add_dirty_methods(name) define_method("#{name}_change") { attribute_change(name) } define_method("#{name}_changed?") { attribute_changed?(name) } define_method("#{name}_was") { attribute_was(name) } define_method("reset_#{name}!") { reset_attribute!(name) } end end end end