lib/couchrest/model/dirty.rb in couchrest_model-2.1.0.rc1 vs lib/couchrest/model/dirty.rb in couchrest_model-2.2.0.beta1

- old
+ new

@@ -1,39 +1,113 @@ -# encoding: utf-8 - -I18n.load_path << File.join( - File.dirname(__FILE__), "validations", "locale", "en.yml" -) - module CouchRest module Model # This applies to both Model::Base and Model::CastedModel module Dirty extend ActiveSupport::Concern - include ActiveModel::Dirty included do - # internal dirty setting - overrides global setting. - # this is used to temporarily disable dirty tracking when setting - # attributes directly, for performance reasons. - self.send(:attr_accessor, :disable_dirty) + # The original attributes data hash, used for comparing changes. + self.send(:attr_reader, :original_change_data) end def use_dirty? - doc = base_doc - doc && !doc.disable_dirty + # Use the configuration option. + !disable_dirty_tracking end - def couchrest_attribute_will_change!(attr) - return if attr.nil? || !use_dirty? - attribute_will_change!(attr) - couchrest_parent_will_change! + # Provide an array of changes according to the hashdiff gem of the raw + # json hash data. + # If dirty tracking is disabled, this will always return nil. + def changes + if original_change_data.nil? + nil + else + HashDiff.diff(original_change_data, current_change_data) + end end - def couchrest_parent_will_change! - casted_by.couchrest_attribute_will_change!(casted_by_property.name) if casted_by_property + # Has this model changed? If dirty tracking is disabled, this method + # will always return true. + def changed? + diff = changes + diff.nil? || !diff.empty? end + def clear_changes_information + if use_dirty? + # Recursively clear all change information + self.class.properties.each do |property| + val = read_attribute(property) + if val.respond_to?(:clear_changes_information) + val.clear_changes_information + end + end + @original_change_data = current_change_data + else + @original_change_data = nil + end + end + + protected + + def current_change_data + as_couch_json.as_json + end + + module ClassMethods + + def create_dirty_property_methods(property) + create_dirty_property_change_method(property) + create_dirty_property_changed_method(property) + create_dirty_property_was_method(property) + end + + # For #property_change. + # Tries to be a bit more efficient by directly comparing the properties + # current value with that stored in the original change data. This also + # maintains compatibility with ActiveModel change results. + def create_dirty_property_change_method(property) + define_method("#{property.name}_change") do + val = read_attribute(property.name) + if val.respond_to?(:changes) + val.changes + else + if original_change_data.nil? + nil + else + orig = original_change_data[property.name] + cur = val.as_json + if orig != cur + [orig, cur] + else + [] + end + end + end + end + end + + # For #property_was value. + # Uses the original raw value, if available. + def create_dirty_property_was_method(property) + define_method("#{property.name}_was") do + if original_change_data.nil? + nil + else + original_change_data[property.name] + end + end + end + + # For #property_changed? + def create_dirty_property_changed_method(property) + define_method("#{property.name}_changed?") do + changes = send("#{property.name}_change") + changes.nil? || !changes.empty? + end + end + + end end end end