module Dynamoid::Dirty

Support interface of Rails’ ActiveModel::Dirty module

The reason why not just include ActiveModel::Dirty - ActiveModel::Dirty conflicts either with @attributes or attributes in different Rails versions.

Separate implementation (or copy-pasting) is the best way to avoid endless monkey-patching

Documentation: api.rubyonrails.org/v4.2/classes/ActiveModel/Dirty.html

Public Instance Methods

attribute_changed?(name, options = {}) click to toggle source

Handle *_changed? for method_missing.

person.attribute_changed?(:name) # => true
person.attribute_changed?(:name, from: 'Alice')
person.attribute_changed?(:name, to: 'Bob')
person.attribute_changed?(:name, from: 'Alice', to: 'Bod')

@private @param name [Symbol] attribute name @param options [Hash] conditions on from and to value (optional) @option options [Symbol] :from previous attribute value @option options [Symbol] :to current attribute value

# File lib/dynamoid/dirty.rb, line 169
def attribute_changed?(name, options = {})
  result = changes_include?(name)
  result &&= options[:to] == read_attribute(name) if options.key?(:to)
  result &&= options[:from] == changed_attributes[name] if options.key?(:from)
  result
end
attribute_previous_change(name) click to toggle source

Handles *_previous_change for method_missing.

person = Person.create(name: 'Alice')
person.name = 'Bob'
person.save
person.attribute_previously_changed(:name) # => ["Alice", "Bob"]

@private @param name [Symbol] @return [Array]

# File lib/dynamoid/dirty.rb, line 219
def attribute_previous_change(name)
  previous_changes[name] if attribute_previously_changed?(name)
end
attribute_previously_changed?(name) click to toggle source

Handles *_previously_changed? for method_missing.

person = Person.create(name: 'Alice')
person.name = 'Bob'
person.save
person.attribute_changed?(:name) # => true

@private @param name [Symbol] attribute name @return [true|false]

# File lib/dynamoid/dirty.rb, line 205
def attribute_previously_changed?(name)
  previous_changes_include?(name)
end
attribute_was(name) click to toggle source

Handle *_was for method_missing.

person = Person.create(name: 'Alice')
person.name = 'Bob'
person.attribute_was(:name) # => "Alice"

@private @param name [Symbol] attribute name

# File lib/dynamoid/dirty.rb, line 184
def attribute_was(name)
  attribute_changed?(name) ? changed_attributes[name] : read_attribute(name)
end
changed() click to toggle source

Returns an array with names of the attributes with unsaved changes.

person = Person.new
person.changed # => []
person.name = 'Bob'
person.changed # => ["name"]

@return [Array]

# File lib/dynamoid/dirty.rb, line 98
def changed
  changed_attributes.keys
end
changed?() click to toggle source

Returns true if any attribute have unsaved changes, false otherwise.

person.changed? # => false
person.name = 'Bob'
person.changed? # => true

@return [true|false]

# File lib/dynamoid/dirty.rb, line 86
def changed?
  changed_attributes.present?
end
changed_attributes() click to toggle source

Returns a hash of the attributes with unsaved changes indicating their original values like attr => original value.

person.name # => "Bob"
person.name = 'Robert'
person.changed_attributes # => {"name" => "Bob"}

@return [ActiveSupport::HashWithIndifferentAccess]

# File lib/dynamoid/dirty.rb, line 134
def changed_attributes
  @changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
end
Also aliased as: attributes_changed_by_setter
changes() click to toggle source

Returns a hash of changed attributes indicating their original and new values like attr => [original value, new value].

person.changes # => {}
person.name = 'Bob'
person.changes # => { "name" => ["Bill", "Bob"] }

@return [ActiveSupport::HashWithIndifferentAccess]

# File lib/dynamoid/dirty.rb, line 110
def changes
  ActiveSupport::HashWithIndifferentAccess[changed.map { |name| [name, attribute_change(name)] }]
end
changes_applied() click to toggle source

Clears dirty data and moves changes to previous_changes.

# File lib/dynamoid/dirty.rb, line 145
def changes_applied
  @previously_changed = changes
  @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
end
clear_attribute_changes(names) click to toggle source

Remove changes information for the provided attributes.

@param attributes [Array] - a list of attributes to clear changes for

# File lib/dynamoid/dirty.rb, line 153
def clear_attribute_changes(names)
  attributes_changed_by_setter.except!(*names)
end
clear_changes_information() click to toggle source

Clear all dirty data: current changes and previous changes.

# File lib/dynamoid/dirty.rb, line 139
def clear_changes_information
  @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
  @changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
end
previous_changes() click to toggle source

Returns a hash of attributes that were changed before the model was saved.

person.name # => "Bob"
person.name = 'Robert'
person.save
person.previous_changes # => {"name" => ["Bob", "Robert"]}

@return [ActiveSupport::HashWithIndifferentAccess]

# File lib/dynamoid/dirty.rb, line 122
def previous_changes
  @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
end
reload(*) click to toggle source

@private

Calls superclass method
# File lib/dynamoid/dirty.rb, line 73
def reload(*)
  super.tap do
    clear_changes_information
  end
end
restore_attributes(names = changed) click to toggle source

Restore all previous data of the provided attributes.

@param attributes [Array] a list of attribute names

# File lib/dynamoid/dirty.rb, line 191
def restore_attributes(names = changed)
  names.each { |name| restore_attribute! name }
end
save(*) click to toggle source

@private

Calls superclass method
# File lib/dynamoid/dirty.rb, line 45
def save(*)
  super.tap do |status|
    changes_applied if status
  end
end
save!(*) click to toggle source

@private

Calls superclass method
# File lib/dynamoid/dirty.rb, line 52
def save!(*)
  super.tap do
    changes_applied
  end
end
update(*) click to toggle source

@private

Calls superclass method
# File lib/dynamoid/dirty.rb, line 59
def update(*)
  super.tap do
    clear_changes_information
  end
end
update!(*) click to toggle source

@private

Calls superclass method
# File lib/dynamoid/dirty.rb, line 66
def update!(*)
  super.tap do
    clear_changes_information
  end
end

Private Instance Methods

attribute_change(name) click to toggle source

Handle *_change for method_missing.

# File lib/dynamoid/dirty.rb, line 231
def attribute_change(name)
  [changed_attributes[name], read_attribute(name)] if attribute_changed?(name)
end
attribute_changed_by_setter?(name)
Alias for: changes_include?
attribute_will_change!(name) click to toggle source

Handle *_will_change! for method_missing.

# File lib/dynamoid/dirty.rb, line 236
def attribute_will_change!(name)
  return if attribute_changed?(name)

  begin
    value = read_attribute(name)
    value = value.duplicable? ? value.clone : value
  rescue TypeError, NoMethodError
  end

  set_attribute_was(name, value)
end
attributes_changed_by_setter()

This is necessary because ‘changed_attributes` might be overridden in other implemntations (e.g. in `ActiveRecord`)

Alias for: changed_attributes
changes_include?(name) click to toggle source
# File lib/dynamoid/dirty.rb, line 225
def changes_include?(name)
  attributes_changed_by_setter.include?(name)
end
Also aliased as: attribute_changed_by_setter?
previous_changes_include?(name) click to toggle source

Returns true if name were changed before the model was saved, false otherwise.

# File lib/dynamoid/dirty.rb, line 258
def previous_changes_include?(name)
  previous_changes.include?(name)
end
restore_attribute!(name) click to toggle source

Handle restore_*! for method_missing.

# File lib/dynamoid/dirty.rb, line 249
def restore_attribute!(name)
  if attribute_changed?(name)
    write_attribute(name, changed_attributes[name])
    clear_attribute_changes([name])
  end
end
set_attribute_was(name, old_value) click to toggle source

Force an attribute to have a particular “before” value

# File lib/dynamoid/dirty.rb, line 267
def set_attribute_was(name, old_value)
  attributes_changed_by_setter[name] = old_value
end