lib/active_data/model/attributable.rb in active_data-0.1.0 vs lib/active_data/model/attributable.rb in active_data-0.2.0

- old
+ new

@@ -3,70 +3,73 @@ module Attributable extend ActiveSupport::Concern included do class_attribute :_attributes, :instance_reader => false, :instance_writer => false - self._attributes = ActiveSupport::HashWithIndifferentAccess.new + self._attributes = {} - extend generated_class_attributes_methods - include generated_instance_attributes_methods - delegate :attribute_default, :to => 'self.class' end module ClassMethods + def attribute name, options = {}, &block + attribute = build_attribute(name, options, &block) + self._attributes = _attributes.merge(attribute.name => attribute) + + attribute.generate_instance_methods generated_instance_attributes_methods + attribute.generate_class_methods generated_class_attributes_methods + attribute + end + def build_attribute name, options = {}, &block klass = case options[:type].to_s when 'Localized' ActiveData::Attributes::Localized else ActiveData::Attributes::Base end klass.new name, options, &block end - def attribute name, options = {}, &block - attribute = build_attribute(name, options, &block) - self._attributes = _attributes.merge(attribute.name => attribute) - - attribute.generate_instance_methods generated_instance_attributes_methods - attribute.generate_singleton_methods generated_class_attributes_methods - attribute - end - def generated_class_attributes_methods - @generated_class_attributes_methods ||= Module.new + @generated_class_attributes_methods ||= Module.new.tap { |proxy| extend proxy } end def generated_instance_attributes_methods - @generated_instance_attributes_methods ||= Module.new + @generated_instance_attributes_methods ||= Module.new.tap { |proxy| include proxy } end def initialize_attributes - _attributes.inject(ActiveSupport::HashWithIndifferentAccess.new) do |result, (name, value)| - result[name] = nil - result - end + Hash[_attributes.map { |(name, _)| [name, nil] }] end end def read_attribute name - attribute = self.class._attributes[name] - source = @attributes[name].nil? ? attribute.default_value(self) : @attributes[name] - attribute.type_cast source + name = name.to_sym + if attributes_cache.key? name + attributes_cache[name] + else + attribute = self.class._attributes[name] + value = attribute.type_cast @attributes[name] + use_default = attribute.default_blank? && value.blank? || value.nil? + + attributes_cache[name] = use_default ? attribute.default_value(self) : value + end end alias_method :[], :read_attribute def has_attribute? name - @attributes.key? name + @attributes.key? name.to_sym end def read_attribute_before_type_cast name - @attributes[name] + @attributes[name.to_sym] end def write_attribute name, value + name = name.to_sym + attributes_cache.delete name @attributes[name] = value end alias_method :[]=, :write_attribute def attributes @@ -85,32 +88,35 @@ end def attributes= attributes assign_attributes(attributes) end + alias_method :update_attributes, :attributes= - def update_attributes attributes - self.attributes = attributes + def write_attributes attributes + attributes.each { |(name, value)| send("#{name}=", value) } end def reverse_update_attributes attributes reverse_assign_attributes(attributes) end private + def attributes_cache + @attributes_cache ||= {} + end + def assign_attributes attributes (attributes.presence || {}).each do |(name, value)| - send("#{name}=", value) if respond_to?("#{name}=") + send("#{name}=", value) if has_attribute?(name) || respond_to?("#{name}=") end - self.attributes end def reverse_assign_attributes attributes (attributes.presence || {}).each do |(name, value)| send("#{name}=", value) if respond_to?("#{name}=") && respond_to?(name) && send(name).blank? end - self.attributes end end end end