module Mongoid::Changeable

Defines behavior for dirty tracking.

Constants

ATTRIBUTE_UNCHANGED

a singleton object to represent an optional ‘to` or `from` value that was not explicitly provided to attribute_changed?

Public Instance Methods

attribute_before_last_save(attr) click to toggle source

Returns the original value of an attribute before the last save.

This method is useful in after callbacks to get the original value of

an attribute before the save that triggered the callbacks to run.

@param [ Symbol | String ] attr The name of the attribute.

@return [ Object ] Value of the attribute before the last save.

# File lib/mongoid/changeable.rb, line 141
def attribute_before_last_save(attr)
  attr = database_field_name(attr)
  attributes_before_last_save[attr]
end
changed() click to toggle source

Get the changed attributes for the document.

@example Get the changed attributes.

model.changed

@return [ Array<String> ] The changed attributes.

# File lib/mongoid/changeable.rb, line 14
def changed
  changed_attributes.keys.select { |attr| attribute_change(attr) }
end
changed?() click to toggle source

Has the document changed?

@example Has the document changed?

model.changed?

@return [ true | false ] If the document is changed.

# File lib/mongoid/changeable.rb, line 24
def changed?
  changes.values.any? { |val| val } || children_changed?
end
changed_attributes() click to toggle source

Get the attribute changes.

@example Get the attribute changes.

model.changed_attributes

@return [ Hash<String, Object> ] The attribute changes.

# File lib/mongoid/changeable.rb, line 43
def changed_attributes
  @changed_attributes ||= {}
end
changes() click to toggle source

Get all the changes for the document.

@example Get all the changes.

model.changes

@return [ Hash<String, Array<Object, Object> ] The changes.

# File lib/mongoid/changeable.rb, line 53
def changes
  changed.each_with_object({}) do |attr, changes|
    change = attribute_change(attr)
    changes[attr] = change if change
  end.with_indifferent_access
end
children_changed?() click to toggle source

Have any children (embedded documents) of this document changed?

@note This intentionally only considers children and not descendants.

@return [ true | false ] If any children have changed.

# File lib/mongoid/changeable.rb, line 33
def children_changed?
  _children.any?(&:changed?)
end
move_changes() click to toggle source

Call this method after save, so the changes can be properly switched.

This will unset the memoized children array, set new record flag to false, set the document as validated, and move the dirty changes.

@example Move the changes to previous.

person.move_changes
# File lib/mongoid/changeable.rb, line 67
def move_changes
  @changes_before_last_save = @previous_changes
  @previous_changes = changes
  @attributes_before_last_save = @previous_attributes
  @previous_attributes = attributes.dup
  reset_atomic_updates!
  changed_attributes.clear
end
post_persist() click to toggle source

Things that need to execute after a document has been persisted.

@example Handle post persistence.

document.post_persist
# File lib/mongoid/changeable.rb, line 80
def post_persist
  reset_persisted_descendants
  reset_attributes_before_type_cast
  move_changes
end
previous_changes() click to toggle source

Get the previous changes on the document.

@example Get the previous changes.

model.previous_changes

@return [ Hash<String, Array<Object, Object> ] The previous changes.

# File lib/mongoid/changeable.rb, line 92
def previous_changes
  @previous_changes ||= {}
end
remove_change(name) click to toggle source

Remove a change from the dirty attributes hash. Used by the single field atomic updaters.

@example Remove a flagged change.

model.remove_change(:field)

@param [ Symbol | String ] name The name of the field.

# File lib/mongoid/changeable.rb, line 103
def remove_change(name)
  changed_attributes.delete(name.to_s)
end
saved_change_to_attribute(attr) click to toggle source

Returns the change to an attribute during the last save.

@param [ Symbol | String ] attr The name of the attribute.

@return [ Array<Object> | nil ] If the attribute was changed, returns

an array containing the original value and the saved value, otherwise nil.
# File lib/mongoid/changeable.rb, line 152
def saved_change_to_attribute(attr)
  attr = database_field_name(attr)
  previous_changes[attr]
end
saved_change_to_attribute?(attr, from: Utils::PLACEHOLDER, to: Utils::PLACEHOLDER) click to toggle source

Returns whether this attribute changed during the last save.

This method is useful in after callbacks, to see the change

in an attribute during the save that triggered the callbacks to run.

@param [ String ] attr The name of the attribute. @param [ Object ] from The object the attribute was changed from (optional). @param [ Object ] to The object the attribute was changed to (optional).

@return [ true | false ] Whether the attribute has changed during the last save.

# File lib/mongoid/changeable.rb, line 167
def saved_change_to_attribute?(attr, from: Utils::PLACEHOLDER, to: Utils::PLACEHOLDER)
  changes = saved_change_to_attribute(attr)
  return false unless changes.is_a?(Array)

  return true if Utils.placeholder?(from) && Utils.placeholder?(to)
  return changes.first == from if Utils.placeholder?(to)
  return changes.last == to if Utils.placeholder?(from)

  changes.first == from && changes.last == to
end
setters() click to toggle source

Gets all the new values for each of the changed fields, to be passed to a MongoDB $set modifier.

@example Get the setters for the atomic updates.

person = Person.new(:title => "Sir")
person.title = "Madam"
person.setters # returns { "title" => "Madam" }

@return [ Hash ] A Hash of atomic setters.

# File lib/mongoid/changeable.rb, line 116
def setters
  mods = {}
  changes.each_pair do |name, changes|
    next unless changes

    old, new = changes
    field = fields[name]
    key = atomic_attribute_name(name)
    if field&.resizable?
      field.add_atomic_changes(self, name, key, mods, new, old)
    else
      mods[key] = new unless atomic_unsets.include?(key)
    end
  end
  mods
end
will_save_change_to_attribute?(attr, **kwargs) click to toggle source

Returns whether this attribute change the next time we save.

This method is useful in validations and before callbacks to determine

if the next call to save will change a particular attribute.

@param [ String ] attr The name of the attribute. @param **kwargs The optional keyword arguments.

@option **kwargs [ Object ] :from The object the attribute was changed from. @option **kwargs [ Object ] :to The object the attribute was changed to.

@return [ true | false ] Whether the attribute change the next time we save.

# File lib/mongoid/changeable.rb, line 190
def will_save_change_to_attribute?(attr, **kwargs)
  attribute_changed?(attr, **kwargs)
end

Private Instance Methods

attribute_change(attr) click to toggle source

Get the old and new value for the provided attribute.

@example Get the attribute change.

model.attribute_change("name")

@param [ String ] attr The name of the attribute.

@return [ Array<Object> ] The old and new values.

# File lib/mongoid/changeable.rb, line 219
def attribute_change(attr)
  attr = database_field_name(attr)
  [ changed_attributes[attr], attributes[attr] ] if attribute_changed?(attr)
end
attribute_changed?(attr, from: ATTRIBUTE_UNCHANGED, to: ATTRIBUTE_UNCHANGED) click to toggle source

Determine if a specific attribute has changed.

@example Has the attribute changed?

model.attribute_changed?("name")

@param [ String ] attr The name of the attribute. @param [ Object ] from The object the attribute was changed from (optional). @param [ Object ] to The object the attribute was changed to (optional).

@return [ true | false ] Whether the attribute has changed.

# File lib/mongoid/changeable.rb, line 257
def attribute_changed?(attr, from: ATTRIBUTE_UNCHANGED, to: ATTRIBUTE_UNCHANGED)
  attr = database_field_name(attr)
  return false unless changed_attributes.key?(attr)
  return false if changed_attributes[attr] == attributes[attr]
  return false if from != changed_attributes[attr]
  return false if to != attributes[attr]

  true
end
attribute_changed_from_default?(attr) click to toggle source

Get whether or not the field has a different value from the default.

@example Is the field different from the default?

model.attribute_changed_from_default?

@param [ String ] attr The name of the attribute.

@return [ true | false ] If the attribute differs.

# File lib/mongoid/changeable.rb, line 275
def attribute_changed_from_default?(attr)
  return false unless (field = fields[attr])

  attributes[attr] != field.eval_default(self)
end
attribute_previously_was(attr) click to toggle source

Get the previous attribute value that was changed before the document was saved.

It the document has not been saved yet, or was just loaded from database, this method returns nil for all attributes.

@param [ String ] attr The attribute name.

@return [ Object | nil ] Attribute value before the document was saved,

or nil if the document has not been saved yet.
# File lib/mongoid/changeable.rb, line 302
def attribute_previously_was(attr)
  attr = database_field_name(attr)
  if previous_changes.key?(attr)
    previous_changes[attr].first
  else
    previous_attributes[attr]
  end
end
attribute_was(attr) click to toggle source

Get the previous value for the attribute.

@example Get the previous value.

model.attribute_was("name")

@param [ String ] attr The attribute name.

# File lib/mongoid/changeable.rb, line 287
def attribute_was(attr)
  attr = database_field_name(attr)
  attribute_changed?(attr) ? changed_attributes[attr] : attributes[attr]
end
attribute_will_change!(attr) click to toggle source

Flag an attribute as going to change.

@example Flag the attribute.

model.attribute_will_change!("name")

@param [ String ] attr The name of the attribute.

@return [ Object ] The old value.

# File lib/mongoid/changeable.rb, line 319
def attribute_will_change!(attr)
  return if changed_attributes.key?(attr)

  changed_attributes[attr] = read_raw_attribute(attr).__deep_copy__
end
attributes_before_last_save() click to toggle source
# File lib/mongoid/changeable.rb, line 207
def attributes_before_last_save
  @attributes_before_last_save ||= {}
end
changes_before_last_save() click to toggle source
# File lib/mongoid/changeable.rb, line 203
def changes_before_last_save
  @changes_before_last_save ||= {}
end
previous_attributes() click to toggle source

Get attributes of the document before the document was saved.

@return [ Hash ] Previous attributes

# File lib/mongoid/changeable.rb, line 199
def previous_attributes
  @previous_attributes ||= {}
end
reset_attribute!(attr) click to toggle source

Set the attribute back to its old value.

@example Reset the attribute.

model.reset_attribute!("name")

@param [ String ] attr The name of the attribute.

@return [ Object ] The old value.

# File lib/mongoid/changeable.rb, line 333
def reset_attribute!(attr)
  attr = database_field_name(attr)
  attributes[attr] = changed_attributes.delete(attr) if attribute_changed?(attr)
end
reset_attribute_to_default!(attr) click to toggle source
# File lib/mongoid/changeable.rb, line 338
def reset_attribute_to_default!(attr)
  attr = database_field_name(attr)
  if (field = fields[attr])
    __send__("#{attr}=", field.eval_default(self))
  else
    __send__("#{attr}=", nil)
  end
end
reset_attributes_before_type_cast() click to toggle source
# File lib/mongoid/changeable.rb, line 347
def reset_attributes_before_type_cast
  @attributes_before_type_cast = @attributes.dup
end