module ActiveRecord::Dirty
def self.included(base)
def self.included(base) base.attribute_method_suffix *DIRTY_SUFFIXES base.alias_method_chain :write_attribute, :dirty base.alias_method_chain :save, :dirty base.alias_method_chain :save!, :dirty base.alias_method_chain :update, :dirty base.alias_method_chain :reload, :dirty base.class_attribute :partial_updates base.partial_updates = true base.send(:extend, ClassMethods) end
def attribute_change(attr)
def attribute_change(attr) [changed_attributes[attr], __send__(attr)] if attribute_changed?(attr) end
def attribute_changed?(attr)
def attribute_changed?(attr) changed_attributes.include?(attr) end
def attribute_was(attr)
def attribute_was(attr) attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr) end
def attribute_will_change!(attr)
def attribute_will_change!(attr) changed_attributes[attr] = clone_attribute_value(:read_attribute, attr) end
def changed
person.name = 'bob'
person.changed # => []
List of attributes with unsaved changes.
def changed changed_attributes.keys end
def changed?
person.name = 'bob'
person.changed? # => false
Do any attributes have unsaved changes?
def changed? !changed_attributes.empty? end
def changed_attributes
def changed_attributes @changed_attributes ||= {} end
def changes
person.name = 'bob'
person.changes # => {}
Map of changed attrs => [original value, new value].
def changes changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h } end
def field_changed?(attr, old, value)
def field_changed?(attr, old, value) if column = column_for_attribute(attr) if column.number? && column.null && (old.nil? || old == 0) && value.blank? # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values. # Hence we don't record it as a change if the value changes from nil to ''. # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll # be typecast back to 0 (''.to_i => 0) value = nil else value = column.type_cast(value) end end old != value end
def reload_with_dirty(*args) #:nodoc:
reload the record and clears changed attributes.
def reload_with_dirty(*args) #:nodoc: record = reload_without_dirty(*args) changed_attributes.clear record end
def save_with_dirty(*args) #:nodoc:
Attempts to +save+ the record and clears changed attributes if successful.
def save_with_dirty(*args) #:nodoc: if status = save_without_dirty(*args) changed_attributes.clear end status end
def save_with_dirty!(*args) #:nodoc:
Attempts to save! the record and clears changed attributes if successful.
def save_with_dirty!(*args) #:nodoc: status = save_without_dirty!(*args) changed_attributes.clear status end
def update_with_dirty
def update_with_dirty if partial_updates? # Serialized attributes should always be written in case they've been # changed in place. update_without_dirty(changed | (attributes.keys & self.class.serialized_attributes.keys)) else update_without_dirty end end
def write_attribute_with_dirty(attr, value)
def write_attribute_with_dirty(attr, value) attr = attr.to_s # The attribute already has an unsaved change. if changed_attributes.include?(attr) old = changed_attributes[attr] changed_attributes.delete(attr) unless field_changed?(attr, old, value) else old = clone_attribute_value(:read_attribute, attr) changed_attributes[attr] = old if field_changed?(attr, old, value) end # Carry on. write_attribute_without_dirty(attr, value) end