Skip to content

Commit dc4bc08

Browse files
committed
Refactor active record Monetizable module
1 parent 3a46b2e commit dc4bc08

File tree

1 file changed

+80
-69
lines changed

1 file changed

+80
-69
lines changed

Diff for: lib/money-rails/active_record/monetizable.rb

+80-69
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,9 @@ module ClassMethods
1212
def monetized_attributes
1313
monetized_attributes = @monetized_attributes || {}.with_indifferent_access
1414

15-
if superclass.respond_to?(:monetized_attributes)
16-
monetized_attributes.merge(superclass.monetized_attributes)
17-
else
18-
monetized_attributes
19-
end
15+
return monetized_attributes unless superclass.respond_to?(:monetized_attributes)
16+
17+
monetized_attributes.merge(superclass.monetized_attributes)
2018
end
2119

2220
def monetize(*fields)
@@ -32,7 +30,7 @@ def monetize(*fields)
3230
":with_currency or :with_model_currency")
3331
end
3432

35-
name = options[:as] || options[:target_name] || nil
33+
name = options[:as] || options[:target_name]
3634

3735
# Form target name for the money backed ActiveModel field:
3836
# if a target name is provided then use it
@@ -46,34 +44,38 @@ def monetize(*fields)
4644
if name == subunit_name
4745
raise ArgumentError, "monetizable attribute name cannot be the same as options[:as] parameter"
4846
end
49-
50-
elsif subunit_name =~ /#{MoneyRails::Configuration.amount_column[:postfix]}$/
51-
name = subunit_name.sub(/#{MoneyRails::Configuration.amount_column[:postfix]}$/, "")
5247
else
53-
raise ArgumentError, "Unable to infer the name of the monetizable attribute for '#{subunit_name}'. " \
54-
"Expected amount column postfix is '#{MoneyRails::Configuration.amount_column[:postfix]}'. " \
55-
"Use :as option to explicitly specify the name or change the amount column postfix in the initializer."
48+
amount_column_postfix = MoneyRails::Configuration.amount_column[:postfix]
49+
50+
if subunit_name =~ /#{amount_column_postfix}$/
51+
name = subunit_name.sub(/#{amount_column_postfix}$/, "")
52+
else
53+
raise ArgumentError, "Unable to infer the name of the monetizable attribute for '#{subunit_name}'. " \
54+
"Expected amount column postfix is '#{amount_column_postfix}'. " \
55+
"Use :as option to explicitly specify the name or change the amount column postfix in the initializer."
56+
end
5657
end
5758

5859
# Optional accessor to be run on an instance to detect currency
5960
instance_currency_name = options[:with_model_currency] ||
60-
options[:model_currency] ||
61-
MoneyRails::Configuration.currency_column[:column_name]
61+
options[:model_currency] ||
62+
MoneyRails::Configuration.currency_column[:column_name]
6263

6364
# Infer currency column from name and postfix
64-
if !instance_currency_name && MoneyRails::Configuration.currency_column[:postfix].present?
65-
instance_currency_name = "#{name}#{MoneyRails::Configuration.currency_column[:postfix]}"
66-
end
65+
currency_column_postfix = MoneyRails::Configuration.currency_column[:postfix]
6766

68-
instance_currency_name = instance_currency_name && instance_currency_name.to_s
67+
if instance_currency_name
68+
instance_currency_name = instance_currency_name.to_s
69+
elsif currency_column_postfix.present?
70+
instance_currency_name = "#{name}#{currency_column_postfix}"
71+
end
6972

7073
# This attribute allows per column currency values
7174
# Overrides row and default currency
72-
field_currency_name = options[:with_currency] ||
73-
options[:field_currency] || nil
75+
field_currency_name = options[:with_currency] || options[:field_currency]
7476

7577
# Create a reverse mapping of the monetized attributes
76-
track_monetized_attribute name, subunit_name
78+
track_monetized_attribute(name, subunit_name)
7779

7880
# Include numericality validations if needed.
7981
# There are two validation options:
@@ -97,56 +99,54 @@ def monetize(*fields)
9799
#
98100
# To disable validation entirely, use :disable_validation, E.g:
99101
# monetize :price_in_a_range_cents, disable_validation: true
100-
if (validation_enabled = MoneyRails.include_validations && !options[:disable_validation])
101-
102-
# This is a validation for the subunit
103-
if (subunit_numericality = options.fetch(:subunit_numericality, true))
104-
validates subunit_name, {
105-
allow_nil: options[:allow_nil],
106-
numericality: subunit_numericality
107-
}
102+
validation_enabled = MoneyRails.include_validations && !options[:disable_validation]
103+
104+
if validation_enabled
105+
validate_subunit_numericality = options.fetch(:subunit_numericality, true)
106+
validate_numericality = options.fetch(:numericality, true)
107+
108+
if validate_subunit_numericality
109+
validates subunit_name, allow_nil: options[:allow_nil],
110+
numericality: validate_subunit_numericality
108111
end
109112

110113
# Allow only Money objects or Numeric values!
111-
if (numericality = options.fetch(:numericality, true))
112-
validates name.to_sym, {
113-
allow_nil: options[:allow_nil],
114-
'money_rails/active_model/money' => numericality
115-
}
114+
if validate_numericality
115+
validates name.to_sym, allow_nil: options[:allow_nil],
116+
'money_rails/active_model/money' => validate_numericality
116117
end
117118
end
118119

119-
120120
# Getter for monetized attribute
121121
define_method name do |*args, **kwargs|
122-
read_monetized name, subunit_name, options, *args, **kwargs
122+
read_monetized(name, subunit_name, options, *args, **kwargs)
123123
end
124124

125125
# Setter for monetized attribute
126126
define_method "#{name}=" do |value|
127-
write_monetized name, subunit_name, value, validation_enabled, instance_currency_name, options
127+
write_monetized(name, subunit_name, value, validation_enabled, instance_currency_name, options)
128128
end
129129

130130
if validation_enabled
131131
# Ensure that the before_type_cast value is cleared when setting
132132
# the subunit value directly
133133
define_method "#{subunit_name}=" do |value|
134-
instance_variable_set "@#{name}_money_before_type_cast", nil
134+
instance_variable_set("@#{name}_money_before_type_cast", nil)
135135
write_attribute(subunit_name, value)
136136
end
137137
end
138138

139139
# Currency getter
140140
define_method "currency_for_#{name}" do
141-
currency_for name, instance_currency_name, field_currency_name
141+
currency_for(name, instance_currency_name, field_currency_name)
142142
end
143143

144144
attr_reader "#{name}_money_before_type_cast"
145145

146146
# Hook to ensure the reset of before_type_cast attr
147147
# TODO: think of a better way to avoid this
148148
after_save do
149-
instance_variable_set "@#{name}_money_before_type_cast", nil
149+
instance_variable_set("@#{name}_money_before_type_cast", nil)
150150
end
151151
end
152152
end
@@ -185,12 +185,12 @@ def read_monetized(name, subunit_name, options = nil, *args, **kwargs)
185185
kwargs = {}
186186
end
187187

188-
if kwargs.any?
189-
amount = public_send(subunit_name, *args, **kwargs)
190-
else
191-
# Ruby 2.x does not allow empty kwargs
192-
amount = public_send(subunit_name, *args)
193-
end
188+
amount = if kwargs.any?
189+
public_send(subunit_name, *args, **kwargs)
190+
else
191+
# Ruby 2.x does not allow empty kwargs
192+
public_send(subunit_name, *args)
193+
end
194194

195195
return if amount.nil? && options[:allow_nil]
196196
# Get the currency object
@@ -218,7 +218,8 @@ def read_monetized(name, subunit_name, options = nil, *args, **kwargs)
218218
end
219219

220220
if MoneyRails::Configuration.preserve_user_input
221-
value_before_type_cast = instance_variable_get "@#{name}_money_before_type_cast"
221+
value_before_type_cast = instance_variable_get("@#{name}_money_before_type_cast")
222+
222223
if errors.has_key?(name.to_sym)
223224
result.define_singleton_method(:to_s) { value_before_type_cast }
224225
result.define_singleton_method(:format) { |_| value_before_type_cast }
@@ -230,23 +231,21 @@ def read_monetized(name, subunit_name, options = nil, *args, **kwargs)
230231

231232
def write_monetized(name, subunit_name, value, validation_enabled, instance_currency_name, options)
232233
# Keep before_type_cast value as a reference to original input
233-
instance_variable_set "@#{name}_money_before_type_cast", value
234+
instance_variable_set("@#{name}_money_before_type_cast", value)
234235

235236
# Use nil or get a Money object
236237
if options[:allow_nil] && value.blank?
237238
money = nil
239+
elsif value.is_a?(Money)
240+
money = value
238241
else
239-
if value.is_a?(Money)
240-
money = value
241-
else
242-
begin
243-
money = value.to_money(public_send("currency_for_#{name}"))
244-
rescue NoMethodError
245-
return nil
246-
rescue Money::Currency::UnknownCurrency, Monetize::ParseError => e
247-
raise MoneyRails::Error, e.message if MoneyRails.raise_error_on_money_parsing
248-
return nil
249-
end
242+
begin
243+
money = value.to_money(public_send("currency_for_#{name}"))
244+
rescue NoMethodError
245+
return nil
246+
rescue Money::Currency::UnknownCurrency, Monetize::ParseError => e
247+
raise MoneyRails::Error, e.message if MoneyRails.raise_error_on_money_parsing
248+
return nil
250249
end
251250
end
252251

@@ -273,26 +272,38 @@ def write_monetized(name, subunit_name, value, validation_enabled, instance_curr
273272
public_send("#{instance_currency_name}=", money_currency.iso_code)
274273
else
275274
current_currency = public_send("currency_for_#{name}")
275+
276276
if current_currency != money_currency.id
277-
raise ReadOnlyCurrencyException.new("Can't change readonly currency '#{current_currency}' to '#{money_currency}' for field '#{name}'") if MoneyRails.raise_error_on_money_parsing
277+
if MoneyRails.raise_error_on_money_parsing
278+
raise ReadOnlyCurrencyException,
279+
"Can't change readonly currency '#{current_currency}' to '#{money_currency}' for field '#{name}'"
280+
end
281+
278282
return nil
279283
end
280284
end
281285
end
282286

283287
# Save and return the new Money object
284-
instance_variable_set "@#{name}", money
288+
instance_variable_set("@#{name}", money)
285289
end
286290

287291
def currency_for(name, instance_currency_name, field_currency_name)
288-
if instance_currency_name.present? && respond_to?(instance_currency_name) &&
289-
Money::Currency.find(public_send(instance_currency_name))
290-
291-
Money::Currency.find(public_send(instance_currency_name))
292-
elsif field_currency_name.respond_to?(:call)
293-
Money::Currency.find(field_currency_name.call(self))
294-
elsif field_currency_name
295-
Money::Currency.find(field_currency_name)
292+
if instance_currency_name.present? && respond_to?(instance_currency_name)
293+
currency_name = public_send(instance_currency_name)
294+
currency = Money::Currency.find(currency_name)
295+
296+
return currency if currency
297+
end
298+
299+
if field_currency_name
300+
currency_name = if field_currency_name.respond_to?(:call)
301+
field_currency_name.call(self)
302+
else
303+
field_currency_name
304+
end
305+
306+
Money::Currency.find(currency_name)
296307
elsif self.class.respond_to?(:currency)
297308
self.class.currency
298309
else

0 commit comments

Comments
 (0)