module ActiveModel::Dirty

def attribute_change(attr)

Handle *_change for +method_missing+.
def attribute_change(attr)
  [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
end

def attribute_changed?(attr)

Handle *_changed? for +method_missing+.
def attribute_changed?(attr)
  changed_attributes.include?(attr)
end

def attribute_was(attr)

Handle *_was for +method_missing+.
def attribute_was(attr)
  attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end

def attribute_will_change!(attr)

Handle *_will_change! for +method_missing+.
def attribute_will_change!(attr)
  begin
    value = __send__(attr)
    value = value.duplicable? ? value.clone : value
  rescue TypeError, NoMethodError
  end
  changed_attributes[attr] = value
end

def changed

person.changed # => ['name']
person.name = 'bob'
person.changed # => []
List of attributes with unsaved changes.
def changed
  changed_attributes.keys
end

def changed?

person.changed? # => true
person.name = 'bob'
person.changed? # => false
Do any attributes have unsaved changes?
def changed?
  !changed_attributes.empty?
end

def changed_attributes

Map of change attr => original value.
def changed_attributes
  @changed_attributes ||= {}
end

def changes

person.changes # => { 'name' => ['bill', 'bob'] }
person.name = 'bob'
person.changes # => {}
Map of changed attrs => [original value, new value].
def changes
  changed.inject(HashWithIndifferentAccess.new){ |h, attr| h[attr] = attribute_change(attr); h }
end

def previous_changes

person.previous_changes # => {'name' => ['bob, 'robert']}
person.save
person.name = 'robert'
person.name # => 'bob'
Map of attributes that were changed when the model was saved.
def previous_changes
  @previously_changed
end

def reset_attribute!(attr)

Handle reset_*! for +method_missing+.
def reset_attribute!(attr)
  __send__("#{attr}=", changed_attributes[attr]) if attribute_changed?(attr)
end