Module: ActiveRecord::Dirty

Track unsaved attribute changes.

A newly instantiated object is unchanged:

  person = Person.find_by_name('uncle bob')
  person.changed?       # => false

Change the name:

  person.name = 'Bob'
  person.changed?       # => true
  person.name_changed?  # => true
  person.name_was       # => 'uncle bob'
  person.name_change    # => ['uncle bob', 'Bob']
  person.name = 'Bill'
  person.name_change    # => ['uncle bob', 'Bill']

Save the changes:

  person.save
  person.changed?       # => false
  person.name_changed?  # => false

Assigning the same value leaves the attribute unchanged:

  person.name = 'Bill'
  person.name_changed?  # => false
  person.name_change    # => nil

Which attributes have changed?

  person.name = 'bob'
  person.changed        # => ['name']
  person.changes        # => { 'name' => ['Bill', 'bob'] }

Before modifying an attribute in-place:

  person.name_will_change!
  person.name << 'by'
  person.name_change    # => ['uncle bob', 'uncle bobby']

Public Class Methods


included (base)

    # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 37
37:     def self.included(base)
38:       base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
39:       base.alias_method_chain :write_attribute, :dirty
40:       base.alias_method_chain :save,            :dirty
41:       base.alias_method_chain :save!,           :dirty
42:       base.alias_method_chain :update,          :dirty
43: 
44:       base.superclass_delegating_accessor :partial_updates
45:       base.partial_updates = false
46:     end

Public Instance Methods


changed ()

List of attributes with unsaved changes.

  person.changed # => []
  person.name = 'bob'
  person.changed # => ['name']
    # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 60
60:     def changed
61:       changed_attributes.keys
62:     end

changed? ()

Do any attributes have unsaved changes?

  person.changed? # => false
  person.name = 'bob'
  person.changed? # => true
    # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 52
52:     def changed?
53:       !changed_attributes.empty?
54:     end

changes ()

Map of changed attrs => [original value, new value]

  person.changes # => {}
  person.name = 'bob'
  person.changes # => { 'name' => ['bill', 'bob'] }
    # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 68
68:     def changes
69:       changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h }
70:     end

save_with_dirty (*args)

Clear changed attributes after they are saved.

    # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 74
74:     def save_with_dirty(*args) #nodoc
75:       save_without_dirty(*args)
76:     ensure
77:       changed_attributes.clear
78:     end

save_with_dirty! (*args)

Clear changed attributes after they are saved.

    # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 81
81:     def save_with_dirty!(*args) #nodoc
82:       save_without_dirty!(*args)
83:     ensure
84:       changed_attributes.clear
85:     end

Private Instance Methods


attribute_change (attr)

Handle *_change for method_missing.

     # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 99
 99:       def attribute_change(attr)
100:         [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
101:       end

attribute_changed? (attr)

Handle *_changed? for method_missing.

    # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 94
94:       def attribute_changed?(attr)
95:         changed_attributes.include?(attr)
96:       end

attribute_was (attr)

Handle *_was for method_missing.

     # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 104
104:       def attribute_was(attr)
105:         attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
106:       end

attribute_will_change! (attr)

Handle *_will_change! for method_missing.

     # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 109
109:       def attribute_will_change!(attr)
110:         changed_attributes[attr] = clone_attribute_value(:read_attribute, attr)
111:       end

changed_attributes ()

Map of change attr => original value.

    # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 89
89:       def changed_attributes
90:         @changed_attributes ||= {}
91:       end

update_with_dirty ()

     # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 134
134:       def update_with_dirty
135:         if partial_updates?
136:           update_without_dirty(changed)
137:         else
138:           update_without_dirty
139:         end
140:       end

write_attribute_with_dirty (attr, value)

Wrap write_attribute to remember original attribute value.

     # File vendor/rails/activerecord/lib/active_record/dirty.rb, line 114
114:       def write_attribute_with_dirty(attr, value)
115:         attr = attr.to_s
116: 
117:         # The attribute already has an unsaved change.
118:         unless changed_attributes.include?(attr)
119:           old = clone_attribute_value(:read_attribute, attr)
120: 
121:           # Remember the original value if it's different.
122:           typecasted = if column = column_for_attribute(attr)
123:                          column.type_cast(value)
124:                        else
125:                          value
126:                        end
127:           changed_attributes[attr] = old unless old == typecasted
128:         end
129: 
130:         # Carry on.
131:         write_attribute_without_dirty(attr, value)
132:       end