# ActsAsAddressable module ActsAsAddressable extend ActiveSupport::Concern module ActiveRecord def acts_as_addressable(*options) @acts_as_addressable_opts = options || [] include ::ActsAsAddressable end end included do has_many :addresses, :as => :addressable, :class_name => 'Effective::Address', :dependent => :delete_all, :autosave => true # Setup validations and methods categories = @acts_as_addressable_opts.try(:flatten) || [] if categories.first.kind_of?(Hash) # We were passed some validation requirements categories = categories.first categories.each do |category, options| # billing, shipping category = category.to_s.gsub('_address', '') self.send(:define_method, "#{category}_address") { effective_address(category) } self.send(:define_method, "#{category}_addresses") { effective_addresses(category) } validates "#{category}_address", :effective_address_valid => true if options.kind_of?(Hash) validates "#{category}_address", :presence => options.fetch(:presence, false) validates "#{category}_address", :effective_address_full_name_presence => options.fetch(:use_full_name, EffectiveAddresses.use_full_name) if options.fetch(:singular, false) self.send(:define_method, "#{category}_address=") { |atts| set_singular_effective_address(category, atts) } else self.send(:define_method, "#{category}_address=") { |atts| set_effective_address(category, atts) } end else # Probably just true or false passed validates "#{category}_address", :presence => (options == true) validates "#{category}_address", :effective_address_full_name_presence => EffectiveAddresses.use_full_name # Multiple Addresses self.send(:define_method, "#{category}_address=") { |atts| set_effective_address(category, atts) } end end else # No validation requirements passed categories.each do |category| category = category.to_s.gsub('_address', '') self.send(:define_method, "#{category}_address") { effective_address(category) } self.send(:define_method, "#{category}_addresses") { effective_addresses(category) } self.send(:define_method, "#{category}_address=") { |atts| set_effective_address(category, atts) } validates "#{category}_address", :effective_address_valid => true validates "#{category}_address", :effective_address_full_name_presence => EffectiveAddresses.use_full_name end end end module ClassMethods end def effective_addresses(category) category = category.to_s addresses.select { |address| address.category == category } end def effective_address(category) effective_addresses(category).last end def set_effective_address(category, atts) raise ArgumentError.new("Effective::Address #{category}_address= expecting an Effective::Address or Hash of attributes") unless (atts.kind_of?(Effective::Address) || atts.kind_of?(Hash)) atts = HashWithIndifferentAccess.new(atts.kind_of?(Effective::Address) ? atts.attributes : atts) add = Effective::Address.new( :category => category, :full_name => atts[:full_name], :address1 => atts[:address1], :address2 => atts[:address2], :city => atts[:city], :state_code => atts[:state_code], :country_code => atts[:country_code], :postal_code => atts[:postal_code] ) self.addresses << add unless (add.empty? || add == effective_address(category)) end def set_singular_effective_address(category, atts) raise ArgumentError.new("Effective::Address #{category}_address= expecting an Effective::Address or Hash of attributes") unless (atts.kind_of?(Effective::Address) || atts.kind_of?(Hash)) atts = HashWithIndifferentAccess.new(atts.kind_of?(Effective::Address) ? atts.attributes : atts) (effective_address(category) || self.addresses.build()).tap do |existing| existing.category = category existing.full_name = atts[:full_name] existing.address1 = atts[:address1] existing.address2 = atts[:address2] existing.city = atts[:city] existing.state_code = atts[:state_code] existing.country_code = atts[:country_code] existing.postal_code = atts[:postal_code] end end end