Skip to content

Commit 9030ec3

Browse files
committed
Define methods on dynamic module
Currently you cannot override the generated methods and access super ie: ```ruby monetize :price_cents def price=(value) # stuff super # NoMethodError end ``` This happens because the method is defined on the class directly so their is no super method to call. A solution today is to create an alias (`alias :old_price= :price=`) before overriding the method as is recommending in issue #507. Instead, a new module can be created, included into the class, and the methods defined on it allowing `super` to work as expected.
1 parent df7a030 commit 9030ec3

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

lib/money-rails/active_record/monetizable.rb

Lines changed: 24 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ class ReadOnlyCurrencyException < MoneyRails::Error; end
99
extend ActiveSupport::Concern
1010

1111
module ClassMethods
12+
MODULE_NAME = :DynamicMoneyAttributes
13+
1214
def monetized_attributes
1315
monetized_attributes = @monetized_attributes || {}.with_indifferent_access
1416

@@ -116,23 +118,31 @@ def monetize(*fields)
116118
end
117119
end
118120

119-
120-
# Getter for monetized attribute
121-
define_method name do |*args, **kwargs|
122-
read_monetized name, subunit_name, options, *args, **kwargs
121+
if const_defined?(MODULE_NAME, false)
122+
mod = const_get(MODULE_NAME)
123+
else
124+
mod = const_set(MODULE_NAME, Module.new)
125+
include mod
123126
end
124127

125-
# Setter for monetized attribute
126-
define_method "#{name}=" do |value|
127-
write_monetized name, subunit_name, value, validation_enabled, instance_currency_name, options
128-
end
128+
mod.module_eval do
129+
# Getter for monetized attribute
130+
define_method name do |*args, **kwargs|
131+
read_monetized name, subunit_name, options, *args, **kwargs
132+
end
133+
134+
# Setter for monetized attribute
135+
define_method "#{name}=" do |value|
136+
write_monetized name, subunit_name, value, validation_enabled, instance_currency_name, options
137+
end
129138

130-
if validation_enabled
131-
# Ensure that the before_type_cast value is cleared when setting
132-
# the subunit value directly
133-
define_method "#{subunit_name}=" do |value|
134-
instance_variable_set "@#{name}_money_before_type_cast", nil
135-
write_attribute(subunit_name, value)
139+
if validation_enabled
140+
# Ensure that the before_type_cast value is cleared when setting
141+
# the subunit value directly
142+
define_method "#{subunit_name}=" do |value|
143+
instance_variable_set "@#{name}_money_before_type_cast", nil
144+
write_attribute(subunit_name, value)
145+
end
136146
end
137147
end
138148

spec/active_record/monetizable_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,23 @@ def update_product(*attributes)
3030

3131
context ".monetized_attributes" do
3232

33+
it "allows adds methods to the inheritance chain" do
34+
class MyProduct < ActiveRecord::Base
35+
self.table_name = :products
36+
monetize :price_cents
37+
attr_reader :side_effect
38+
39+
def price=(value)
40+
@side_effect = true
41+
super
42+
end
43+
end
44+
45+
p = MyProduct.new(price: 10)
46+
expect(p.price).to eq Money.new(10_00)
47+
expect(p.side_effect).to be_truthy
48+
end
49+
3350
class InheritedMonetizeProduct < Product
3451
monetize :special_price_cents
3552
end

0 commit comments

Comments
 (0)