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)