ref: master
plugins/suppliers/lib/default_delegate.rb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
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_chain default_setting, :presence alias_method_chain "#{default_setting}=", :presence 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_chain field, :default alias_method_chain "#{field}=", :default include DefaultDelegate::InstanceMethods end end module InstanceMethods end end ApplicationRecord.extend DefaultDelegate::ClassMethods |