lib/simple_model/attributes.rb in simple_model-1.2.17 vs lib/simple_model/attributes.rb in simple_model-1.2.18
- old
+ new
@@ -1,30 +1,27 @@
module SimpleModel
module Attributes
include ExtendCore
extend ActiveSupport::Concern
include ActiveModel::AttributeMethods
-
+ attr_accessor :attributes
+
def initialize(*attrs)
attrs = attrs.extract_options!
attrs = attributes_with_for_init(attrs)
attrs = self.class.before_initialize.call(self,attrs) if self.class.before_initialize
set(attrs)
self.class.after_initialize.call(self) if self.class.after_initialize
end
- # Returns true if attribute has been initialized
- def initialized?(attr)
- attributes.key?(attr.to_sym)
- end
-
def attributes
@attributes ||= HashWithIndifferentAccess.new
end
- def attributes=attrs
- @attributes = attrs
+ # Returns true if attribute has been initialized
+ def initialized?(attr)
+ self.attributes.key?(attr.to_sym)
end
def get(attr)
self.send(attr)
end
@@ -37,12 +34,51 @@
self.send("#{attr.to_s}=",val)
end
end
alias :set_attributes :set
+ def set_attribute(attr,val)
+ options = self.class.defined_attributes[attr] || {}
+ if allow_attribute_action?(self,val,options)
+ val = fetch_default_value(options[:default]) if (!options[:allow_blank] && options.key?(:default) && val.blank?)
+ val = options[:on_set].call(self,val) unless (!options.key?(:on_set) || (val.blank? && !options[:allow_blank]) )
+ will_change = "#{attr}_will_change!".to_sym
+ self.send(will_change) if (initialized?(attr) && val != self.attributes[attr])
+ self.attributes[attr] = val
+ options[:after_set].call(self,val) if options[:after_set]
+ end
+ end
+
+ def get_attribute(attr)
+ val = self.attributes[attr]
+ options = self.class.defined_attributes[attr] || {}
+ if (options.key?(:default) && (!self.initialized?(attr) || (!options[:allow_blank] && val.blank?)))
+ val = self.attributes[attr] = fetch_default_value(options[:default])
+ end
+ if options[:on_get]
+ options[:on_get].call(self,val)
+ else
+ val
+ end
+ end
+
+ def get_attribute?(attr)
+ val = get_attribute(attr)
+ if val.respond_to?(:to_b)
+ val = val.to_b
+ else
+ val = !val.blank? if val.respond_to?(:blank?)
+ end
+ val
+ end
+
private
+ def attribute_defined?(attr)
+ self.class.attribute_defined?(attr)
+ end
+
def fetch_default_value(arg)
return self.send(arg) if (arg.is_a?(Symbol) && self.respond_to?(arg))
arg
end
@@ -56,16 +92,16 @@
end
end
d
end
+ # Only set default if there is a default value, initializing is allow and
+ # new attributes do not have a value to set and
def allow_set_default?(d,k,v)
- (v[:default] && v[:initialize] && (d[k].blank? && (self.class.alias_attributes[k].blank? || d.key?(self.class.alias_attributes[k]) && d[self.class.alias_attributes[k]].blank?)))
+ (v[:default] && (v[:initialize] != false) && (!d.key?(k) && !d.key?(self.class.alias_attributes[k])))
end
- private
-
def allow_attribute_action?(obj,val,options)
return true if (options[:if].blank? && options[:unless].blank?)
b = true
if options[:if].is_a?(Symbol)
if options[:if] == :blank
@@ -90,10 +126,35 @@
def attribute(name)
attributes[name.to_sym]
end
module ClassMethods
+ DEFAULT_ATTRIBUTE_SETTINGS = {:attributes_method => :attributes,
+ :allow_blank => true,
+ :initialize => true
+ }.freeze
+
+ AVAILABLE_ATTRIBUTE_METHODS = {
+ :has_attribute => {:alias => :has_attributes},
+ :has_boolean => {:cast_to => :to_b, :alias => :has_booleans},
+ :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 = default_attribute_settings.merge(attributes.extract_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}")
+ 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
# session stores for authorization or shopping carts
# EX:
@@ -104,14 +165,14 @@
# end
# helper_method :session_user
# end
#
def new_with_store(session_hash)
- new = self.new()
- new.attributes = session_hash
- new.set(new.send(:attributes_with_for_init,session_hash))
- new
+ nw = self.new()
+ nw.attributes = session_hash
+ nw.set(nw.send(:attributes_with_for_init,session_hash))
+ nw
end
def alias_attributes
@alias_attributes ||= HashWithIndifferentAccess.new
end
@@ -139,16 +200,11 @@
# * :default - the default value for the attribute, can be a symbol that is sent for a method
# * :initialize - informations the object whether or not it should initialize the attribute with :default value, defaults to true
# ** If :initialize is set to false you must set :allow_blank to false or it will never set the default value
# * :allow_blank - when set to false, if an attributes value is blank attempts to set the default value, defaults to true
def default_attribute_settings
- @default_attribute_settings ||= {:attributes_method => :attributes,
- :on_set => lambda {|obj,attr| attr},
- :on_get => lambda {|obj,attr| attr},
- :allow_blank => true,
- :initialize => true
- }
+ @default_attribute_settings ||= DEFAULT_ATTRIBUTE_SETTINGS
end
def default_attribute_settings=default_attribute_settings
@default_attribute_settings = default_attribute_settings
end
@@ -157,90 +213,59 @@
# at once, so we must set @attribute_methods_generated to nil to allow the
# re-run to occur ONLY IN RAILS 3.0.
def add_defined_attribute(attr,options)
self.defined_attributes[attr] = options
@attribute_methods_generated = nil #if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0)
- define_attribute_methods(self.defined_attributes.keys)
+ define_attribute_methods(defined_attributes_keys)
end
+ # We don't want to call define_attribute_methods on methods defined in the parent class
+ def defined_attributes_keys
+ dak = self.defined_attributes.keys
+ dak = dak - self.superclass.defined_attributes.keys if self.superclass.respond_to?(:defined_attributes)
+ dak
+ end
+
# builds the setter and getter methods
def create_attribute_methods(attributes,options)
unless attributes.blank?
attributes.each do |attr|
- define_reader_with_options(attr,options)
define_setter_with_options(attr,options)
+ define_reader_with_options(attr,options)
+ add_defined_attribute(attr,options)
end
end
end
def define_reader_with_options(attr,options)
- add_defined_attribute(attr,options)
- options = default_attribute_settings.merge(options) if options[:on_get].blank?
define_method(attr) do
- val = self.attributes[attr]
- if (options.key?(:default) && (!self.initialized?(attr) || (!options[:allow_blank] && val.blank?)))
- val = self.attributes[attr] = fetch_default_value(options[:default])
- end
- options[:on_get].call(self,val)
+ get_attribute(attr)
end
define_method("#{attr.to_s}?") do
- # return false unless initialized?(attr) THIS BROKE STUFF
- val = self.send(attr)
- if val.respond_to?(:to_b)
- val = val.to_b
- else
- val = !val.blank? if val.respond_to?(:blank?)
- end
- val
+ get_attribute?(attr)
end
end
# 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)
- add_defined_attribute(attr,options)
- options = default_attribute_settings.merge(options) if (options[:on_set].blank? || options[:after_set].blank?)
define_method("#{attr.to_s}=") do |val|
- if allow_attribute_action?(self,val,options)
- val = fetch_default_value(options[:default]) if (!options[:allow_blank] && options.key?(:default) && val.blank?)
- val = options[:on_set].call(self,val) unless (val.blank? && !options[:allow_blank] )
- will_change = "#{attr}_will_change!".to_sym
- self.send(will_change) if (initialized?(attr) && val != self.attributes[attr])
- self.attributes[attr] = val
- options[:after_set].call(self,val) if options[:after_set]
- end
+ set_attribute(attr,val)
end
end
- AVAILABLE_ATTRIBUTE_METHODS = {
- :has_attribute => {:alias => :has_attributes},
- :has_boolean => {:cast_to => :to_b, :alias => :has_booleans},
- :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}
- }
-
- AVAILABLE_ATTRIBUTE_METHODS.each do |method,method_options|
- define_method(method) do |*attributes|
- options = default_attribute_settings.merge(attributes.extract_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}")
- end
-
# Creates alias setter and getter for the supplied attribute using the supplied alias
# See spec for example.
def alias_attribute(new_alias,attribute)
alias_attributes[attribute] = new_alias
define_method(new_alias) do
self.send(attribute)
end
+ define_method("#{new_alias}?") do
+ self.send("#{attribute}?")
+ end
define_method("#{new_alias.to_s}=") do |*args, &block|
self.send("#{attribute.to_s}=",*args, &block)
end
end
@@ -278,18 +303,19 @@
# Must inherit super's defined_attributes and alias_attributes
# Rails 3.0 does some weird stuff with ActiveModel::Dirty so we need a
# hack to keep things working when a class inherits from a super that
# has ActiveModel::Dirty included
def inherited(base)
- base.alias_attributes = self.alias_attributes.merge(base.alias_attributes)
+ base.defined_attributes = self.defined_attributes.dup
+ base.alias_attributes = self.alias_attributes.dup
super
# Rails 3.0 Hack
if (ActiveModel::VERSION::MAJOR == 3 && ActiveModel::VERSION::MINOR == 0)
base.attribute_method_suffix '_changed?', '_change', '_will_change!', '_was'
base.attribute_method_affix :prefix => 'reset_', :suffix => '!'
end
end
- end
+ end # end ClassMethods
# 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)