lib/simple_model/attributes.rb in simple_model-1.2.26 vs lib/simple_model/attributes.rb in simple_model-1.2.27

- old
+ new

@@ -1,12 +1,30 @@ module SimpleModel module Attributes include ExtendCore extend ActiveSupport::Concern include ActiveModel::AttributeMethods + include ActiveModel::Dirty + + DEFAULT_ATTRIBUTE_SETTINGS = {:attributes_method => :attributes, + :allow_blank => false, + :initialize => true + }.freeze + + AVAILABLE_ATTRIBUTE_METHODS = { + :has_attribute => {:alias => :has_attributes, :options => {:allow_blank => true}}, + :has_boolean => {:cast_to => :to_b, :alias => :has_booleans, :options => {:allow_blank => true, :boolean => true}}, + :has_currency => {:cast_to => :to_d, :alias => :has_currencies}, + :has_date => {:cast_to => :to_date, :alias => :has_dates} , + :has_decimal => {:cast_to => :to_d, :alias => :has_decimals}, + :has_float => {:cast_to => :to_f, :alias => :has_floats}, + :has_int => {:cast_to => :to_i, :alias => :has_ints}, + :has_time => {:cast_to => :to_time, :alias => :has_times} + }.freeze + attr_accessor :attributes - + def initialize(*attrs) attrs = attrs.extract_options! attrs = attributes_for_init(attrs) attrs = self.class.before_initialize.call(self,attrs) if self.class.before_initialize set(attrs) @@ -34,54 +52,72 @@ send("#{attr}=",val) end end alias :set_attributes :set - def set_attribute(attr,val) - options = self.class.defined_attributes[attr] || {} - if allow_attribute_action?(val,options) - allow_blank = options[:allow_blank] - default = options[:default] - val = fetch_default_value(default) if (!allow_blank && default && val.blank?) - unless (val.blank? && !allow_blank) - val = options[:on_set].call(self,val) if options.key?(:on_set) + def set_attribute(attr,val,opts=nil) + opts ||= fetch_attribute_options(attr) + if allow_attribute_action?(val,opts) + allow_blank = opts[:allow_blank] + val = fetch_default_value(opts[:default]) unless skip_set_default?(attr,opts,val) + unless (opts[:boolean] ? (!allow_blank && val.blank? && (val != false)) : (!allow_blank && val.blank?)) + val = opts[:on_set].call(self,val) if opts.key?(:on_set) send("#{attr}_will_change!") if (initialized?(attr) && val != attributes[attr]) attributes[attr] = val - options[:after_set].call(self,val) if options[:after_set] + opts[:after_set].call(self,val) if opts[:after_set] end end end - def get_attribute(attr) + def get_attribute(attr,opts=nil) + opts ||= fetch_attribute_options(attr) val = attributes[attr] - options = self.class.defined_attributes[attr] || {} - if (options.key?(:default) && (!initialized?(attr) || (!options[:allow_blank] && val.blank?))) - val = attributes[attr] = fetch_default_value(options[:default]) - end - if options[:on_get] - options[:on_get].call(self,val) + val = attributes[attr] ||= fetch_default_value(opts[:default]) unless skip_get_default?(attr,opts,val) + if opts[:on_get] + opts[:on_get].call(self,val) else val end end + def fetch_attribute_options(attr) + self.class.defined_attributes[attr] || {} + end + def get_attribute?(attr) - return false unless val = get_attribute(attr) + return false unless val = send(attr) if val.respond_to?(:blank?) return !val.blank? - elsif val.respond_to?(:to_b) + elsif val.respond_to?(:to_b) return val.to_b end !val.nil? end - private - def attribute_defined?(attr) self.class.attribute_defined?(attr) end + # Rails 3.2 + required when searching for attributes in from inherited classes/models + def attribute(attr) + get_attribute(attr) + end + + private + + def skip_get_default?(attr,opts,val) + (val || !opts.key?(:default) || (opts[:boolean] && (val == false))) + end + + def skip_set_default?(attr,opts,val) + return true if (!opts.key?(:default) || + initialized?(attr) || + (opts[:boolean] && (val == false))) + blnk = val.blank? + (!blnk || (blnk && opts[:allow_blank])) + end + def fetch_default_value(arg) return send(arg) if (arg.is_a?(Symbol) && self.respond_to?(arg)) arg end @@ -102,70 +138,53 @@ def allow_init_default?(d,k,v) (v[:default] && v[:initialize] && (!d.key?(k) && !attributes_have_alias?(d,k))) end def attributes_have_alias?(attrs,attr) - !(self.class.alias_attributes.select{ |a, m| (m == attr.to_sym && attrs.key?(a)) }).empty? + base_meth = self.class.alias_attributes.rassoc(attr.to_sym) + base_meth && attrs.key?(base_meth[0]) + #!(self.class.alias_attributes.select{ |a, m| (m == attr.to_sym && attrs.key?(a)) }).empty? end def allow_attribute_action?(val,options) return true unless (options[:if] || options[:unless]) b = true - opt = options[:if] - if opt.is_a?(Symbol) - if opt == :blank - b = val.blank? - else - b = send(opt) + if opt = options[:if] + if opt.is_a?(Symbol) + if opt == :blank + b = val.blank? + else + b = send(opt) + end + elsif opt.is_a?(Proc) + b = opt.call(self,val) end - elsif opt.is_a?(Proc) - b = opt.call(self,val) end - opt = options[:unless] - if opt.is_a?(Symbol) - if opt == :blank - b = !val.blank? - else - b = !send(opt) + if opt = options[:unless] + if opt.is_a?(Symbol) + if opt == :blank + b = !val.blank? + else + b = !send(opt) + end + elsif opt.is_a?(Proc) + b = !opt.call(self,val) end - elsif opt.is_a?(Proc) - b = !opt.call(self,val) end b end - # Rails 3.2 + required when searching for attributes in from inherited classes/models - def attribute(attr) - get_attribute(attr) - end - module ClassMethods - DEFAULT_ATTRIBUTE_SETTINGS = {:attributes_method => :attributes, - :allow_blank => false, - :initialize => true - }.freeze - - AVAILABLE_ATTRIBUTE_METHODS = { - :has_attribute => {:alias => :has_attributes, :options => {:allow_blank => true}}, - :has_boolean => {:cast_to => :to_b, :alias => :has_booleans, :options => {:allow_blank => true}}, - :has_currency => {:cast_to => :to_d, :alias => :has_currencies}, - :has_date => {:cast_to => :to_date, :alias => :has_dates} , - :has_decimal => {:cast_to => :to_d, :alias => :has_decimals}, - :has_float => {:cast_to => :to_f, :alias => :has_floats}, - :has_int => {:cast_to => :to_i, :alias => :has_ints}, - :has_time => {:cast_to => :to_time, :alias => :has_times} - }.freeze - AVAILABLE_ATTRIBUTE_METHODS.each do |method,method_options| define_method(method) do |*attributes| options = attributes.extract_options! options = method_options[:options].merge(options) if method_options[:options] options = default_attribute_settings.merge(options) options[:on_set] = lambda {|obj,val| val.send(method_options[:cast_to]) } if method_options[:cast_to] create_attribute_methods(attributes,options) end - module_eval("alias #{method_options[:alias]} #{method}") + module_eval("alias #{method_options[:alias]} #{method}") if method_options[:alias] end # Creates a new instance where the attributes store is set to object # provided, which allows one to pass a session store hash or any other # hash-like object to be used for persistence. Typically used for modeling @@ -249,11 +268,11 @@ end end def define_reader_with_options(attr,options) define_method(attr) do - get_attribute(attr) + get_attribute(attr,options) end define_method("#{attr}?") do get_attribute?(attr) end end @@ -261,18 +280,18 @@ # Creates setter methods for the provided attributes # On set, it will mark the attribute as changed if the attributes has been # initialized. def define_setter_with_options(attr,options) define_method("#{attr}=") do |val| - set_attribute(attr,val) + set_attribute(attr,val,options) end end # Creates alias setter and getter for the supplied attribute using the supplied alias # See spec for example. def alias_attribute(new_alias,attr) - + # get to the base attribute while alias_attributes[attr] attr = alias_attributes[attr] end @@ -329,11 +348,11 @@ def inherited(base) base.defined_attributes = defined_attributes.merge(base.defined_attributes) base.alias_attributes = alias_attributes.merge(base.alias_attributes) super # Rails 3.0 Hack - if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0) + if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR < 1) base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was' base.attribute_method_affix :prefix => 'reset_', :suffix => '!' end end end # end ClassMethods @@ -341,18 +360,17 @@ # Rails 3.0 does some weird stuff with ActiveModel::Dirty so we need a # hack to keep things working when a class includes a module that has # ActiveModel::Dirty included def self.included(base) base.extend(Attributes::ClassMethods) - base.send(:include, ActiveModel::Dirty) base.send(:include, ActiveModel::Validations) base.send(:include, ActiveModel::Conversion) base.extend ActiveModel::Naming base.extend ActiveModel::Callbacks base.send(:include, ActiveModel::Validations::Callbacks) # Rails 3.0 Hack - if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0) + if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR < 1) base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was' base.attribute_method_affix :prefix => 'reset_', :suffix => '!' end end end