lib/has_eav.rb in has_eav-1.0.1 vs lib/has_eav.rb in has_eav-1.1.0

- old
+ new

@@ -1,12 +1,12 @@ module ActiveRecord module ActsAs module HasEav - def self.included(base) - base.extend ActiveRecord::ActsAs::HasEav::ClassMethods + def self.included(base) + base.extend ActiveRecord::ActsAs::HasEav::ClassMethods end - + module ClassMethods # Specify that the ActiveModel is an EAV model # # == Usage # # specifiy eav_attributes at instance level @@ -34,187 +34,202 @@ klass = opts.delete :through raise( "Eav Class cannot be nil. Specify a class using " + "has_eav :through => :class" ) if klass.blank? - + class_eval do after_save :save_eav_attributes end - + @eav_class = klass.to_s.camelize.constantize @eav_attributes = {} - + yield if block_given? - + send :include, ActiveRecord::ActsAs::HasEav::InstanceMethods end - + # Add an other attribute to the class list def eav_attribute name, type = String name = name.to_s if !name.is_a? String - + self.class_eav_attributes[name] = type end - + # class accessor - when the superclass != AR::Base asume we are in STI # mode def class_eav_attributes # :nodoc: superclass != ActiveRecord::Base ? superclass.class_eav_attributes : - @eav_attributes + @eav_attributes end - + # class accessor - when the superclass != AR::Base asume we are in STI # mode def eav_class # :nodoc: superclass != ActiveRecord::Base ? superclass.eav_class : @eav_class - end + end end # /ClassMethods - + module InstanceMethods # get to the eav class def eav_class self.class.eav_class end - + # get the class eav attributes def class_eav_attributes self.class.class_eav_attributes end - + # Override this to get some usable attributes - # + # # Cowardly refusing to adhere to all def instance_eav_attributes [] - end - + end + # override method missing, but only kick in when super fails with a # NoMethodError # def method_missing method_symbol, *args, &block super rescue NoMethodError => e method_name = method_symbol.to_s attribute_name = method_name.gsub(/[\?=]$/, '') - + raise e unless eav_attributes_list.include? attribute_name - + attribute = self.eav_attributes.select { |a| a.name == attribute_name }.first - + if method_name =~ /\=$/ value = args[0] - + if attribute if value return attribute.send(:write_attribute, "value", value) - + elsif value.nil? return attribute.destroy - + end - + else @eav_attributes << eav_class.new( :name => attribute_name, :value => "#{value}" ) - + return cast_eav_value(value, attribute_name) - + end elsif method_name =~ /\?$/ return ( attribute and attribute.value == true ) ? true : false - + else - return attribute ? + return attribute ? cast_eav_value(attribute.value, attribute_name) : nil - + end - + raise e - end - + end + # override respond_to? def respond_to? method_symbol, is_private=false if super == false method_name = method_symbol.to_s.gsub(/[\?=]$/, '') return true if eav_attributes_list.include? method_name - + false else true end - end - + end + # get all the eav attribute instances available for this model instance # - # eg: if you model says 'has_eav :through => :post_attribute' these are + # eg: if you model says 'has_eav :through => :post_attribute' these are # all PostAttribute's # def eav_attributes @eav_attributes ||= eav_class.all( :conditions => { self_key => self.id } ) end - + # save the list of eav_attribute back to the database def save_eav_attributes # :nodoc: eav_attributes.select { |a| a.changed? }.each do |a| if a.new_record? a.send( :write_attribute, self_key, self.id ) end - + a.save! end end - - # override changed - if any of the eav_attributes has changed, the + + # override changed - if any of the eav_attributes has changed, the # object has changed. # def changed? - eav_attributes.each do |attribute| + eav_attributes.each do |attribute| return true if ( attribute.changed? || attribute.new_record? ) end - + super end - + # get a complete list of eav_attributes (class + instance) def eav_attributes_list # :nodoc: ( self.instance_eav_attributes + self.class_eav_attributes.keys ).collect { |attribute| attribute.to_s }.uniq end - + # get the key to my <3 def self_key # :nodoc: klass = self.class if klass.superclass != ActiveRecord::Base klass = klass.superclass end - + "#{klass.name.underscore}_id".to_sym end - + # cast an eav value to it's desired class def cast_eav_value value, attribute # :nodoc: attributes = self.class_eav_attributes.stringify_keys return value unless attributes.keys.include?(attribute) - - return ( - eval("#{attributes[attribute]} '#{value}'") || - eval("#{attributes[attribute]}.new('#{value}')") || - value - ) + + + begin + # for core types [eg: String 'foo'] + eval("#{attributes[attribute]} '#{value}'") + + rescue + begin + # for BigDecimal [eg: BigDecimal.new("123.45")] + eval("#{attributes[attribute]}.new('#{value}')") + + rescue + begin + # for date/time classes [eg: Date.parse("2011-03-20")] + eval("#{attributes[attribute]}.parse('#{value}')") + rescue + value + end + + end + end end - + protected :save_eav_attributes, :self_key, :cast_eav_value - + end # /InstanceMethods end # /HasEav end # /ActsAs end # /ActiveRecord \ No newline at end of file