lib/couchrest/model/properties.rb in couchrest_model-1.1.0.beta2 vs lib/couchrest/model/properties.rb in couchrest_model-1.1.0.beta3

- old
+ new

@@ -4,11 +4,13 @@ module Properties extend ActiveSupport::Concern included do extlib_inheritable_accessor(:properties) unless self.respond_to?(:properties) + extlib_inheritable_accessor(:property_by_name) unless self.respond_to?(:property_by_name) self.properties ||= [] + self.property_by_name ||= {} raise "You can only mixin Properties in a class responding to [] and []=, if you tried to mixin CastedModel, make sure your class inherits from Hash or responds to the proper methods" unless (method_defined?(:[]) && method_defined?(:[]=)) end # Returns the Class properties # @@ -35,16 +37,39 @@ def read_attribute(property) self[find_property!(property).to_s] end # Store a casted value in the current instance of an attribute defined - # with a property. + # with a property and update dirty status def write_attribute(property, value) prop = find_property!(property) - self[prop.to_s] = prop.is_a?(String) ? value : prop.cast(self, value) + value = prop.is_a?(String) ? value : prop.cast(self, value) + attribute_will_change!(prop.name) if use_dirty? && self[prop.name] != value + self[prop.name] = value end + def []=(key,value) + return super(key,value) unless use_dirty? + + has_changes = self.changed? + if !has_changes && self.respond_to?(:get_unique_id) + check_id_change = true + old_id = get_unique_id + end + + ret = super(key, value) + + if check_id_change + # if we have set an attribute that results in the _id changing (unique_id), + # force changed? to return true so that the record can be saved + new_id = get_unique_id + changed_attributes["_id"] = new_id if old_id != new_id + end + + ret + end + # Takes a hash as argument, and applies the values by using writer methods # for each key. It doesn't save the document at the end. Raises a NoMethodError if the corresponding methods are # missing. In case of error, no attributes are changed. def update_attributes_without_saving(hash) # Remove any protected and update all the rest. Any attributes @@ -52,33 +77,51 @@ attrs = remove_protected_attributes(hash) directly_set_attributes(attrs) end alias :attributes= :update_attributes_without_saving + # 'attributes' needed for Dirty + alias :attributes :properties_with_values - private + def set_attributes(hash) + attrs = remove_protected_attributes(hash) + directly_set_attributes(attrs) + end + + protected + + def find_property(property) + property.is_a?(Property) ? property : self.class.property_by_name[property.to_s] + end + # The following methods should be accessable by the Model::Base Class, but not by anything else! def apply_all_property_defaults return if self.respond_to?(:new?) && (new? == false) # TODO: cache the default object + # Never mark default options as dirty! + dirty, self.disable_dirty = self.disable_dirty, true self.class.properties.each do |property| write_attribute(property, property.default_value) end + self.disable_dirty = dirty end def prepare_all_attributes(doc = {}, options = {}) + self.disable_dirty = !!options[:directly_set_attributes] apply_all_property_defaults if options[:directly_set_attributes] directly_set_read_only_attributes(doc) else doc = remove_protected_attributes(doc) end - directly_set_attributes(doc) unless doc.nil? + res = doc.nil? ? doc : directly_set_attributes(doc) + self.disable_dirty = false + res end def find_property!(property) - prop = property.is_a?(Property) ? property : self.class.properties.detect {|p| p.to_s == property.to_s} + prop = find_property(property) raise ArgumentError, "Missing property definition for #{property.to_s}" if prop.nil? prop end # Set all the attributes and return a hash with the attributes @@ -105,19 +148,16 @@ write_attribute(attribute_name, hash.delete(attribute_name)) end end end - def set_attributes(hash) - attrs = remove_protected_attributes(hash) - directly_set_attributes(attrs) - end module ClassMethods def property(name, *options, &block) + raise "Invalid property definition, '#{name}' already used for CouchRest Model type field" if name.to_s == model_type_key.to_s opts = { } type = options.shift if type.class != Hash opts[:type] = type opts.merge!(options.shift || {}) @@ -170,9 +210,10 @@ create_property_setter(property) unless property.read_only == true if property.type_class.respond_to?(:validates_casted_model) validates_casted_model property.name end properties << property + property_by_name[property.to_s] = property property end # defines the getter for the property (and optional aliases) def create_property_getter(property)