Module: Dynamoid::Dirty

Extended by:
ActiveSupport::Concern
Includes:
ActiveModel::AttributeMethods
Included in:
Components
Defined in:
lib/dynamoid/dirty.rb

Overview

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

Defined Under Namespace

Modules: ClassMethods, DeepDupper

Instance Method Summary collapse

Instance Method Details

#assign_attributes_from_database(attributes_from_database) ⇒ Object



232
233
234
# File 'lib/dynamoid/dirty.rb', line 232

def assign_attributes_from_database(attributes_from_database)
  @attributes_from_database = HashWithIndifferentAccess.new(attributes_from_database)
end

#attribute_changed?(name, options = {}) ⇒ Boolean

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')

Parameters:

  • name (Symbol)

    attribute name

  • options (Hash) (defaults to: {})

    conditions on from and to value (optional)

Options Hash (options):

  • :from (Symbol)

    previous attribute value

  • :to (Symbol)

    current attribute value

Returns:

  • (Boolean)


177
178
179
180
181
182
# File 'lib/dynamoid/dirty.rb', line 177

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) ⇒ Array

Handles *_previous_change for method_missing.

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

Parameters:

  • name (Symbol)

Returns:

  • (Array)


227
228
229
# File 'lib/dynamoid/dirty.rb', line 227

def attribute_previous_change(name)
  previous_changes[name] if attribute_previously_changed?(name)
end

#attribute_previously_changed?(name) ⇒ true|false

Handles *_previously_changed? for method_missing.

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

Parameters:

  • name (Symbol)

    attribute name

Returns:

  • (true|false)


213
214
215
# File 'lib/dynamoid/dirty.rb', line 213

def attribute_previously_changed?(name)
  previous_changes_include?(name)
end

#attribute_was(name) ⇒ Object

Handle *_was for method_missing.

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

Parameters:

  • name (Symbol)

    attribute name



192
193
194
# File 'lib/dynamoid/dirty.rb', line 192

def attribute_was(name)
  attribute_changed?(name) ? changed_attributes[name] : read_attribute(name)
end

#changedArray[String]

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

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

Returns:

  • (Array[String])


101
102
103
# File 'lib/dynamoid/dirty.rb', line 101

def changed
  changed_attributes.keys
end

#changed?true|false

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

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

Returns:

  • (true|false)


89
90
91
# File 'lib/dynamoid/dirty.rb', line 89

def changed?
  changed_attributes.present?
end

#changed_attributesActiveSupport::HashWithIndifferentAccess

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"}

Returns:

  • (ActiveSupport::HashWithIndifferentAccess)


137
138
139
# File 'lib/dynamoid/dirty.rb', line 137

def changed_attributes
  attributes_changed_by_setter.merge(attributes_changed_in_place)
end

#changesActiveSupport::HashWithIndifferentAccess

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"] }

Returns:

  • (ActiveSupport::HashWithIndifferentAccess)


113
114
115
# File 'lib/dynamoid/dirty.rb', line 113

def changes
  ActiveSupport::HashWithIndifferentAccess[changed_attributes.map { |name, old_value| [name, [old_value, read_attribute(name)]] }]
end

#changes_appliedObject

Clears dirty data and moves changes to previous_changes.



149
150
151
152
153
# File 'lib/dynamoid/dirty.rb', line 149

def changes_applied
  @previously_changed = changes
  @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
  @attributes_from_database = HashWithIndifferentAccess.new(DeepDupper.dup_attributes(@attributes, self.class))
end

#clear_attribute_changes(names) ⇒ Object

Remove changes information for the provided attributes.

Parameters:

  • attributes (Array[String])
    • a list of attributes to clear changes for



158
159
160
161
162
163
# File 'lib/dynamoid/dirty.rb', line 158

def clear_attribute_changes(names)
  attributes_changed_by_setter.except!(*names)

  slice = HashWithIndifferentAccess.new(@attributes).slice(*names)
  attributes_from_database.merge!(DeepDupper.dup_attributes(slice, self.class))
end

#clear_changes_informationObject

Clear all dirty data: current changes and previous changes.



142
143
144
145
146
# File 'lib/dynamoid/dirty.rb', line 142

def clear_changes_information
  @previously_changed = ActiveSupport::HashWithIndifferentAccess.new
  @attributes_changed_by_setter = ActiveSupport::HashWithIndifferentAccess.new
  @attributes_from_database = HashWithIndifferentAccess.new(DeepDupper.dup_attributes(@attributes, self.class))
end

#previous_changesActiveSupport::HashWithIndifferentAccess

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"]}

Returns:

  • (ActiveSupport::HashWithIndifferentAccess)


125
126
127
# File 'lib/dynamoid/dirty.rb', line 125

def previous_changes
  @previously_changed ||= ActiveSupport::HashWithIndifferentAccess.new
end

#reloadObject



76
77
78
79
80
# File 'lib/dynamoid/dirty.rb', line 76

def reload(*)
  super.tap do
    clear_changes_information
  end
end

#restore_attributes(names = changed) ⇒ Object

Restore all previous data of the provided attributes.

Parameters:

  • attributes (Array[Symbol])

    a list of attribute names



199
200
201
# File 'lib/dynamoid/dirty.rb', line 199

def restore_attributes(names = changed)
  names.each { |name| restore_attribute! name }
end

#saveObject



48
49
50
51
52
# File 'lib/dynamoid/dirty.rb', line 48

def save(*)
  super.tap do |status|
    changes_applied if status
  end
end

#save!Object



55
56
57
58
59
# File 'lib/dynamoid/dirty.rb', line 55

def save!(*)
  super.tap do
    changes_applied
  end
end

#updateObject



62
63
64
65
66
# File 'lib/dynamoid/dirty.rb', line 62

def update(*)
  super.tap do
    clear_changes_information
  end
end

#update!Object



69
70
71
72
73
# File 'lib/dynamoid/dirty.rb', line 69

def update!(*)
  super.tap do
    clear_changes_information
  end
end