lib/couchrest/model/properties.rb in couchrest_model-2.1.0.rc1 vs lib/couchrest/model/properties.rb in couchrest_model-2.2.0.beta1
- old
+ new
@@ -7,101 +7,87 @@
included do
class_attribute(:properties) unless self.respond_to?(:properties)
class_attribute(:properties_by_name) unless self.respond_to?(:properties_by_name)
self.properties ||= []
self.properties_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
# Provide an attribute hash ready to be sent to CouchDB but with
# all the nil attributes removed.
def as_couch_json
super.delete_if{|k,v| v.nil?}
end
- # Returns the Class properties with their values
- #
- # ==== Returns
- # Array:: the list of properties with their values
- def properties_with_values
- props = {}
- properties.each { |property| props[property.name] = read_attribute(property.name) }
- props
- end
-
# Read the casted value of an attribute defined with a property.
- #
- # ==== Returns
- # Object:: the casted attibutes value.
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 and update dirty status
+ # with a property.
def write_attribute(property, value)
prop = find_property!(property)
value = prop.cast(self, value)
- couchrest_attribute_will_change!(prop.name) if use_dirty? && self[prop.name] != value
self[prop.name] = value
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
- # which do not have a property will simply be ignored.
- attrs = remove_protected_attributes(hash)
- directly_set_attributes(attrs)
+ # Returns a hash of this object's attributes with a defined property.
+ # This is effectively an accessor to the underlying CouchRest
+ # attributes hash.
+ def read_attributes
+ @_attributes
end
- alias :attributes= :update_attributes_without_saving
+ alias :attributes :read_attributes
- # 'attributes' needed for Dirty
- alias :attributes :properties_with_values
-
- def set_attributes(hash)
+ # Takes a hash as argument, and applies the values by using writer
+ # methods respecting protected properties.
+ def write_attributes(hash)
attrs = remove_protected_attributes(hash)
directly_set_attributes(attrs)
+ self
end
+ alias :attributes= :write_attributes
+ # Takes the provided attribute hash and sets all properties, assuming
+ # that the data is from a trusted source, such as the database.
+ def write_all_attributes(attrs = {})
+ directly_set_read_only_attributes(attrs)
+ directly_set_attributes(attrs, true)
+ self
+ end
+
protected
def find_property(property)
property.is_a?(Property) ? property : self.class.properties_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
+ def find_property!(property)
+ find_property(property) or
+ raise ArgumentError, "Missing property definition for #{property.to_s}"
end
- def prepare_all_attributes(attrs = {}, options = {})
- self.disable_dirty = !!options[:directly_set_attributes]
+ def write_attributes_for_initialization(attrs = {}, opts = {})
apply_all_property_defaults
- if options[:directly_set_attributes]
- directly_set_read_only_attributes(attrs)
- directly_set_attributes(attrs, true)
+ if opts[:write_all_attributes]
+ # Assume coming from a database, so we clear change information after
+ write_all_attributes(attrs)
+ clear_changes_information
else
- attrs = remove_protected_attributes(attrs)
- directly_set_attributes(attrs)
+ # Not from a persisted source, clear the change data in advance and do
+ # not set protected or read-only attributes.
+ clear_changes_information
+ write_attributes(attrs)
end
- self.disable_dirty = false
- self
end
- def find_property!(property)
- prop = find_property(property)
- raise ArgumentError, "Missing property definition for #{property.to_s}" if prop.nil?
- prop
+ # Apply each property's default value to the attributes. This should
+ # only ever be called on initialization.
+ def apply_all_property_defaults
+ self.class.properties.each do |property|
+ write_attribute(property, property.default_value)
+ end
end
# Set all the attributes and return a hash with the attributes
# that have not been accepted.
def directly_set_attributes(hash, mass_assign = false)
@@ -114,16 +100,19 @@
multi_parameter_attributes << [ key, value ]
false
elsif self.respond_to?("#{key}=")
self.send("#{key}=", value)
elsif mass_assign || mass_assign_any_attribute
- couchrest_attribute_will_change!(key) if use_dirty? && self[key] != value
self[key] = value
end
end
- assign_multiparameter_attributes(multi_parameter_attributes, hash) unless multi_parameter_attributes.empty?
+ # Handle attributes provided in an embedded object format, such
+ # as a web-form.
+ unless multi_parameter_attributes.empty?
+ assign_multiparameter_attributes(multi_parameter_attributes, hash)
+ end
end
def directly_set_read_only_attributes(hash)
property_list = self.properties.map{|p| p.name}
hash.each do |attribute_name, attribute_value|
@@ -211,9 +200,12 @@
create_property_setter(property) unless property.read_only == true
if property.type.respond_to?(:validates_casted_model)
validates_casted_model property.name
end
+
+ # Dirty!
+ create_dirty_property_methods(property)
properties << property
properties_by_name[property.to_s] = property
property
end