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