plugins/suppliers/lib/default_delegate.rb
module DefaultDelegate
module ClassMethods
def default_delegate_setting field, options, &block
extend ActsAsHavingSettings::ClassMethods
prefix = options[:prefix] || :default
default_setting = "#{prefix}_#{field}"
settings_items default_setting, default: options[:default], type: :boolean
options[:default_setting] = default_setting
default_delegate field, options
end
# TODO: add some documentation about the methods being added
def default_delegate field, options = {}
class_attribute :default_delegate_enable unless respond_to? :default_delegate_enable
self.default_delegate_enable = true if self.default_delegate_enable.nil?
# rake db:migrate run?
return unless self.table_exists?
# Rails doesn't define getters for attributes
define_method field do
self[field]
end if field.to_s.in? self.column_names and not self.method_defined? field
define_method "#{field}=" do |value|
self[field] = value
end if field.to_s.in? self.column_names and not self.method_defined? "#{field}="
original_field_method = "original_own_#{field}".freeze
alias_method original_field_method, field
own_field = "own_#{field}".freeze
define_method own_field do
# we prefer the value from dabatase here, and the getter may give a default value
# e.g. Product#name defaults to Product#product_category.name
if field.to_s.in? self.class.column_names then self[field] else self.send original_field_method end
end
alias_method "#{own_field}=", "#{field}="
delegated_field = "delegated_#{field}".freeze
to = options[:to].freeze
define_method delegated_field do
case to
when Symbol
object = self.send to
object.send field if object and object.respond_to? field
when Proc then instance_exec &to
end
end
alias_method "original_#{field}", delegated_field
own_field_blank = "own_#{field}_blank?".freeze
define_method own_field_blank do
own = self.send own_field
# blank? also covers false, use nil? and empty? instead
own.nil? or (own.respond_to? :empty? and own.empty?)
end
own_field_present = "own_#{field}_present?".freeze
define_method own_field_present do
not self.send own_field_blank
end
default_if = options[:default_if].freeze
own_field_is_default = "own_#{field}_default?".freeze
define_method own_field_is_default do
default = self.send own_field_blank
default ||= case default_if
when Proc then instance_exec &default_if
when :equal?
self.send(own_field).equal? self.send(delegated_field)
when Symbol then self.send default_if
else false
end
end
default_setting = options[:default_setting] || "#{options[:prefix] || :default}_#{field}"
# as a field may use other field's default_setting, check for definition
default_setting_with_presence = "#{default_setting}_with_presence".freeze
unless self.method_defined? default_setting_with_presence
define_method default_setting_with_presence do
original_setting = self.send "#{default_setting}_without_presence"
# if the setting is false, see if it should be true; if it is true, respect it.
original_setting = self.send own_field_is_default unless original_setting
original_setting
end
define_method "#{default_setting_with_presence}=" do |value|
# this ensures latter the getter won't get a different
self.send "#{own_field}=", nil if value
self.send "#{default_setting}_without_presence=", value
end
alias_method default_setting, :presence
alias_method ("#{default_setting}="_without_presence).to_sym, :("#{default_setting}=").to_sym
alias_method :("#{default_setting}=").to_sym, ("#{default_setting}="_with_presence).to_sym
end
define_method "#{field}_with_default" do
return self.send own_field unless self.default_delegate_enable
if self.send default_setting
# delegated_field may return nil, so use own instead
# this is the case with some associations (e.g. Product#product_qualifiers)
# FIXME: this shouldn't be necessary, it seems to happens only in certain cases
# (product creation, product global search, etc)
self.send(delegated_field) || self.send(own_field)
else self.send(own_field)
end
end
define_method "#{field}_with_default=" do |*args|
return self.send "#{own_field}=", *args unless self.default_delegate_enable
own = self.send "#{own_field}=", *args
# break/set the default setting automatically, used for interfaces
# that don't have the default setting (e.g. manage_products)
self.send "#{default_setting}=", self.send(own_field_is_default)
own
end
alias_method field, :default
alias_method "#{field}=", :default
include DefaultDelegate::InstanceMethods
end
end
module InstanceMethods
end
end
ApplicationRecord.extend DefaultDelegate::ClassMethods