module ActiveRecord module Acts module Changed def self.included(base) base.extend(ClassMethods) end module ClassMethods def acts_as_changed return if self.included_modules.include?(ActiveRecord::Acts::Changed::InstanceMethods) include InstanceMethods alias_method :initialize_without_changed_attributes, :initialize alias_method :initialize, :initialize_with_changed_attributes class << self alias_method :instantiate_without_changed_attributes, :instantiate alias_method :instantiate, :instantiate_with_changed_attributes end alias_method :write_attribute_without_changed, :write_attribute alias_method :write_attribute, :write_attribute_with_changed alias_method :method_missing_without_changed, :method_missing alias_method :method_missing, :method_missing_with_changed alias_method :reload_without_clear_original_attributes, :reload alias_method :reload, :reload_with_clear_original_attributes alias_method :read_attribute_without_freeze, :read_attribute alias_method :read_attribute, :read_attribute_with_freeze end def instantiate_with_changed_attributes(record) object = instantiate_without_changed_attributes(record) object.instance_variable_set('@changed_attributes', []) object end end module InstanceMethods def initialize_with_changed_attributes(attributes = nil, &block) @changed_attributes = [] initialize_without_changed_attributes(attributes, &block) end def write_attribute_with_changed(attr_name, value) attr_name = attr_name.to_s original_value = self[attr_name] write_attribute_without_changed(attr_name, value) @changed_attributes << attr_name unless @changed_attributes.include?(attr_name) or original_value == self[attr_name] end def read_attribute_with_freeze(attr_name) read_attribute_without_freeze(attr_name).freeze end def update_without_callbacks return true unless changed? connection.update( "UPDATE #{self.class.table_name} " + "SET #{quoted_comma_pair_list(connection, changed_attributes_with_quotes)} " + "WHERE #{self.class.primary_key} = #{quote(id)}", "#{self.class.name} Update" ) true end def reset_attributes @changed_attributes.clear end def changed? @changed_attributes.any? end def changed_attributes @changed_attributes.dup end def method_missing_with_changed(method_id, *arguments) if md = /_changed\?$/.match(method_id.to_s) @changed_attributes.include? md.pre_match else method_missing_without_changed method_id, *arguments end end def reload_with_clear_original_attributes reload_without_clear_original_attributes @changed_attributes.clear end private def changed_attributes_with_quotes attributes_with_quotes.delete_if do |attribute, value| !@changed_attributes.include? attribute end end end end end end ActiveRecord::Base.send(:include, ActiveRecord::Acts::Changed)