money-rails 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +12 -3
- data/lib/money-rails/active_model/validator.rb +5 -8
- data/lib/money-rails/active_record/migration_extensions/schema_statements_pg_rails4.rb +21 -0
- data/lib/money-rails/active_record/migration_extensions/table_pg_rails4.rb +21 -0
- data/lib/money-rails/active_record/monetizable.rb +194 -186
- data/lib/money-rails/helpers/action_view_extension.rb +1 -0
- data/lib/money-rails/hooks.rb +19 -1
- data/lib/money-rails/mongoid/money.rb +10 -2
- data/lib/money-rails/version.rb +1 -1
- data/money-rails.gemspec +1 -1
- data/spec/active_record/monetizable_spec.rb +31 -19
- data/spec/dummy/app/models/product.rb +10 -4
- data/spec/dummy/db/migrate/20150107061030_add_delivery_fee_cents_and_restock_fee_cents_to_product.rb +6 -0
- data/spec/dummy/db/migrate/20150126231442_add_reduced_price_to_products.rb +6 -0
- data/spec/dummy/db/schema.rb +5 -1
- data/spec/helpers/action_view_extension_spec.rb +16 -1
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f6d1b9abaf2dfb8fa5d9ede9509fe5dd3ef323a5
|
4
|
+
data.tar.gz: 34b2f71bba5a7773e2b85571fda0892f643cb0c0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8038c315b04d1d026b2582c1ac76a1c904e4e9ec5a2462fcbfcdfcca283475e1ee778621a34dbd5acc7d28b608350f4a216cf1805bb669f99134cb340bca4b5
|
7
|
+
data.tar.gz: 9109fd349499fe439531743e309a6babd40e7bc1ca3a218a3955e4f87985e4046caeaef5f46ce705293f48a2b0544bb0865e5c9a81f8e072ce852f27f7bfd415
|
data/README.md
CHANGED
@@ -107,10 +107,17 @@ class MonetizeItem < ActiveRecord::Migration
|
|
107
107
|
end
|
108
108
|
```
|
109
109
|
|
110
|
+
Notice. Default value of currency field, generated by migration's helper, is USD. To override these defaults, you need change default_currency in money initializer and run migrations.
|
111
|
+
|
110
112
|
The ```add_money``` helper is reversible, so you an use it inside ```change```
|
111
113
|
migrations. If you're writing separate ```up``` and ```down``` methods, you
|
112
114
|
can use the ```remove_money``` helper.
|
113
115
|
|
116
|
+
##### Notice for Rails >= 4.2 and PG adapter
|
117
|
+
|
118
|
+
Due the adding `money` column type for postgres in Rails 4.2 you will need to use `add_monetize` for add money column,
|
119
|
+
`t.monetize` for add column in `create_table` or `change_table` block and `remove_monetize` for removing column.
|
120
|
+
|
114
121
|
#### Allow nil values
|
115
122
|
|
116
123
|
If you want to allow nil and/or blank values to a specific
|
@@ -377,7 +384,7 @@ MoneyRails.configure do |config|
|
|
377
384
|
|
378
385
|
# Specify a rounding mode
|
379
386
|
# Any one of:
|
380
|
-
#
|
387
|
+
#
|
381
388
|
# BigDecimal::ROUND_UP,
|
382
389
|
# BigDecimal::ROUND_DOWN,
|
383
390
|
# BigDecimal::ROUND_HALF_UP,
|
@@ -385,9 +392,9 @@ MoneyRails.configure do |config|
|
|
385
392
|
# BigDecimal::ROUND_HALF_EVEN,
|
386
393
|
# BigDecimal::ROUND_CEILING,
|
387
394
|
# BigDecimal::ROUND_FLOOR
|
388
|
-
#
|
395
|
+
#
|
389
396
|
# set to BigDecimal::ROUND_HALF_EVEN by default
|
390
|
-
#
|
397
|
+
#
|
391
398
|
# config.rounding_mode = BigDecimal::ROUND_HALF_UP
|
392
399
|
|
393
400
|
# Set default money format globally.
|
@@ -434,6 +441,8 @@ _For examples below, `@money_object == <Money fractional:650 currency:USD>`_
|
|
434
441
|
#### `no_cents_if_whole`
|
435
442
|
|
436
443
|
`humanized_money` and `humanized_money_with_symbol` will not render the cents part if it contains only zeros, unless `config.no_cents_if_whole` is set to `false` in the `money.rb` configuration (default: true).
|
444
|
+
Note that the `config.default_format` will be overwritten by `config.no_cents_if_whole`.
|
445
|
+
So `humanized_money` will ignore `config.default_format = { no_cents_if_whole: false }` if you don't set `config.no_cents_if_whole = false`.
|
437
446
|
|
438
447
|
### Testing
|
439
448
|
|
@@ -6,21 +6,18 @@ module MoneyRails
|
|
6
6
|
@record = record
|
7
7
|
@attr = attr
|
8
8
|
|
9
|
-
# If subunit is not set then no need to validate as it is an
|
10
|
-
# indicator that no assignment has been done onto the virtual
|
11
|
-
# money field.
|
12
9
|
subunit_attr = @record.class.monetized_attributes[@attr.to_sym]
|
13
|
-
return unless @record.changed_attributes.keys.include? subunit_attr
|
14
10
|
|
15
11
|
# WARNING: Currently this is only defined in ActiveRecord extension!
|
16
12
|
before_type_cast = :"#{@attr}_money_before_type_cast"
|
17
13
|
@raw_value = @record.try(before_type_cast)
|
18
14
|
|
19
15
|
# If raw value is nil and changed subunit is nil, then
|
20
|
-
# nil is a assigned value,
|
16
|
+
# nil is a assigned value, else we should treat the
|
21
17
|
# subunit value as the one assigned.
|
22
|
-
if @raw_value.nil? && @record.
|
23
|
-
|
18
|
+
if @raw_value.nil? && @record.public_send(subunit_attr)
|
19
|
+
subunit_value = @record.public_send(subunit_attr)
|
20
|
+
@raw_value = subunit_value.to_f / currency.subunit_to_unit
|
24
21
|
end
|
25
22
|
|
26
23
|
return if options[:allow_nil] && @raw_value.nil?
|
@@ -51,7 +48,7 @@ module MoneyRails
|
|
51
48
|
end
|
52
49
|
|
53
50
|
def currency
|
54
|
-
@_currency ||= @record.
|
51
|
+
@_currency ||= @record.public_send("currency_for_#{@attr}")
|
55
52
|
end
|
56
53
|
|
57
54
|
def decimal_mark
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MoneyRails
|
2
|
+
module ActiveRecord
|
3
|
+
module MigrationExtensions
|
4
|
+
module SchemaStatements
|
5
|
+
def add_monetize(table_name, accessor, options={})
|
6
|
+
[:amount, :currency].each do |attribute|
|
7
|
+
column_present, *opts = OptionsExtractor.extract attribute, table_name, accessor, options
|
8
|
+
add_column *opts if column_present
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_monetize(table_name, accessor, options={})
|
13
|
+
[:amount, :currency].each do |attribute|
|
14
|
+
column_present, table_name, column_name, _, _ = OptionsExtractor.extract attribute, table_name, accessor, options
|
15
|
+
remove_column table_name, column_name if column_present
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MoneyRails
|
2
|
+
module ActiveRecord
|
3
|
+
module MigrationExtensions
|
4
|
+
module Table
|
5
|
+
def monetize(accessor, options={})
|
6
|
+
[:amount, :currency].each do |attribute|
|
7
|
+
column_present, _, *opts = OptionsExtractor.extract attribute, :no_table, accessor, options
|
8
|
+
column *opts if column_present
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def remove_monetize(accessor, options={})
|
13
|
+
[:amount, :currency].each do |attribute|
|
14
|
+
column_present, _, column_name, _, _ = OptionsExtractor.extract attribute, :no_table, accessor, options
|
15
|
+
remove column_name if column_present
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -8,224 +8,232 @@ module MoneyRails
|
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
-
def monetize(
|
12
|
-
options =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
options[:
|
19
|
-
|
20
|
-
"
|
21
|
-
|
22
|
-
|
11
|
+
def monetize(*fields)
|
12
|
+
options = fields.extract_options!
|
13
|
+
|
14
|
+
fields.each do |field|
|
15
|
+
# Stringify model field name
|
16
|
+
subunit_name = field.to_s
|
17
|
+
|
18
|
+
if options[:field_currency] || options[:target_name] ||
|
19
|
+
options[:model_currency]
|
20
|
+
ActiveSupport::Deprecation.warn("You are using the old " \
|
21
|
+
"argument keys of the monetize command! Instead use :as, " \
|
22
|
+
":with_currency or :with_model_currency")
|
23
|
+
end
|
23
24
|
|
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
|
-
|
25
|
+
# Optional accessor to be run on an instance to detect currency
|
26
|
+
instance_currency_name = options[:with_model_currency] ||
|
27
|
+
options[:model_currency] ||
|
28
|
+
MoneyRails::Configuration.currency_column[:column_name]
|
29
|
+
|
30
|
+
instance_currency_name = instance_currency_name &&
|
31
|
+
instance_currency_name.to_s
|
32
|
+
|
33
|
+
# This attribute allows per column currency values
|
34
|
+
# Overrides row and default currency
|
35
|
+
field_currency_name = options[:with_currency] ||
|
36
|
+
options[:field_currency] || nil
|
37
|
+
|
38
|
+
name = options[:as] || options[:target_name] || nil
|
39
|
+
|
40
|
+
# Form target name for the money backed ActiveModel field:
|
41
|
+
# if a target name is provided then use it
|
42
|
+
# if there is a "{column.postfix}" suffix then just remove it to create the target name
|
43
|
+
# if none of the previous is the case then use a default suffix
|
44
|
+
if name
|
45
|
+
name = name.to_s
|
46
|
+
elsif subunit_name =~ /#{MoneyRails::Configuration.amount_column[:postfix]}$/
|
47
|
+
name = subunit_name.sub(/#{MoneyRails::Configuration.amount_column[:postfix]}$/, "")
|
48
|
+
else
|
49
|
+
# FIXME: provide a better default
|
50
|
+
name = [subunit_name, "money"].join("_")
|
51
|
+
end
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
57
|
-
@monetized_attributes[name] = subunit_name
|
58
|
-
class << self
|
59
|
-
def monetized_attributes
|
60
|
-
@monetized_attributes || superclass.monetized_attributes
|
53
|
+
# Create a reverse mapping of the monetized attributes
|
54
|
+
@monetized_attributes ||= {}.with_indifferent_access
|
55
|
+
if @monetized_attributes[name].present?
|
56
|
+
raise ArgumentError, "#{self} already has a monetized attribute called '#{name}'"
|
61
57
|
end
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
# 1. Subunit field validation (e.g. cents should be > 100)
|
68
|
-
# 2. Money field validation (e.g. euros should be > 10)
|
69
|
-
#
|
70
|
-
# All the options which are available for Rails numericality
|
71
|
-
# validation, are also available for both types.
|
72
|
-
# E.g.
|
73
|
-
# monetize :price_in_a_range_cents, :allow_nil => true,
|
74
|
-
# :subunit_numericality => {
|
75
|
-
# :greater_than_or_equal_to => 0,
|
76
|
-
# :less_than_or_equal_to => 10000,
|
77
|
-
# },
|
78
|
-
# :numericality => {
|
79
|
-
# :greater_than_or_equal_to => 0,
|
80
|
-
# :less_than_or_equal_to => 100,
|
81
|
-
# :message => "Must be greater than zero and less than $100"
|
82
|
-
# }
|
83
|
-
#
|
84
|
-
# To disable validation entirely, use :disable_validation, E.g:
|
85
|
-
# monetize :price_in_a_range_cents, :disable_validation => true
|
86
|
-
if validation_enabled = MoneyRails.include_validations && !options[:disable_validation]
|
87
|
-
|
88
|
-
subunit_validation_options =
|
89
|
-
unless options.has_key? :subunit_numericality
|
90
|
-
true
|
91
|
-
else
|
92
|
-
options[:subunit_numericality]
|
58
|
+
@monetized_attributes[name] = subunit_name
|
59
|
+
class << self
|
60
|
+
def monetized_attributes
|
61
|
+
@monetized_attributes || superclass.monetized_attributes
|
93
62
|
end
|
63
|
+
end unless respond_to? :monetized_attributes
|
64
|
+
|
65
|
+
# Include numericality validations if needed.
|
66
|
+
# There are two validation options:
|
67
|
+
#
|
68
|
+
# 1. Subunit field validation (e.g. cents should be > 100)
|
69
|
+
# 2. Money field validation (e.g. euros should be > 10)
|
70
|
+
#
|
71
|
+
# All the options which are available for Rails numericality
|
72
|
+
# validation, are also available for both types.
|
73
|
+
# E.g.
|
74
|
+
# monetize :price_in_a_range_cents, :allow_nil => true,
|
75
|
+
# :subunit_numericality => {
|
76
|
+
# :greater_than_or_equal_to => 0,
|
77
|
+
# :less_than_or_equal_to => 10000,
|
78
|
+
# },
|
79
|
+
# :numericality => {
|
80
|
+
# :greater_than_or_equal_to => 0,
|
81
|
+
# :less_than_or_equal_to => 100,
|
82
|
+
# :message => "Must be greater than zero and less than $100"
|
83
|
+
# }
|
84
|
+
#
|
85
|
+
# To disable validation entirely, use :disable_validation, E.g:
|
86
|
+
# monetize :price_in_a_range_cents, :disable_validation => true
|
87
|
+
if validation_enabled = MoneyRails.include_validations && !options[:disable_validation]
|
88
|
+
|
89
|
+
subunit_validation_options =
|
90
|
+
unless options.has_key? :subunit_numericality
|
91
|
+
true
|
92
|
+
else
|
93
|
+
options[:subunit_numericality]
|
94
|
+
end
|
94
95
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
96
|
+
money_validation_options =
|
97
|
+
unless options.has_key? :numericality
|
98
|
+
true
|
99
|
+
else
|
100
|
+
options[:numericality]
|
101
|
+
end
|
101
102
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
103
|
+
# This is a validation for the subunit
|
104
|
+
validates subunit_name, {
|
105
|
+
:allow_nil => options[:allow_nil],
|
106
|
+
:numericality => subunit_validation_options
|
107
|
+
}
|
108
|
+
|
109
|
+
# Allow only Money objects or Numeric values!
|
110
|
+
validates name.to_sym, {
|
111
|
+
:allow_nil => options[:allow_nil],
|
112
|
+
'money_rails/active_model/money' => money_validation_options
|
113
|
+
}
|
114
|
+
end
|
114
115
|
|
115
116
|
|
116
|
-
|
117
|
+
define_method name do |*args|
|
117
118
|
|
118
|
-
|
119
|
-
|
119
|
+
# Get the cents
|
120
|
+
amount = public_send(subunit_name, *args)
|
120
121
|
|
121
|
-
|
122
|
-
|
122
|
+
# Get the currency object
|
123
|
+
attr_currency = public_send("currency_for_#{name}")
|
123
124
|
|
124
|
-
|
125
|
-
|
125
|
+
# Get the cached value
|
126
|
+
memoized = instance_variable_get("@#{name}")
|
126
127
|
|
127
|
-
|
128
|
-
|
129
|
-
|
128
|
+
# Dont create a new Money instance if the values haven't been changed.
|
129
|
+
return memoized if memoized && memoized.cents == amount &&
|
130
|
+
memoized.currency == attr_currency
|
130
131
|
|
131
|
-
|
132
|
-
|
132
|
+
# If amount is NOT nil (or empty string) load the amount in a Money
|
133
|
+
amount = Money.new(amount, attr_currency) unless amount.blank?
|
133
134
|
|
134
|
-
|
135
|
-
|
136
|
-
|
135
|
+
# Cache and return the value (it may be nil)
|
136
|
+
instance_variable_set "@#{name}", amount
|
137
|
+
end
|
137
138
|
|
138
|
-
|
139
|
+
define_method "#{name}=" do |value|
|
139
140
|
|
140
|
-
|
141
|
-
|
141
|
+
# Lets keep the before_type_cast value
|
142
|
+
instance_variable_set "@#{name}_money_before_type_cast", value
|
142
143
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
else
|
147
|
-
if value.is_a?(Money)
|
148
|
-
money = value
|
144
|
+
# Use nil or get a Money object
|
145
|
+
if options[:allow_nil] && value.blank?
|
146
|
+
money = nil
|
149
147
|
else
|
150
|
-
|
151
|
-
money = value
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
148
|
+
if value.is_a?(Money)
|
149
|
+
money = value
|
150
|
+
else
|
151
|
+
begin
|
152
|
+
money = value.to_money(public_send("currency_for_#{name}"))
|
153
|
+
rescue NoMethodError
|
154
|
+
return nil
|
155
|
+
rescue ArgumentError
|
156
|
+
raise if MoneyRails.raise_error_on_money_parsing
|
157
|
+
return nil
|
158
|
+
rescue Money::Currency::UnknownCurrency
|
159
|
+
raise if MoneyRails.raise_error_on_money_parsing
|
160
|
+
return nil
|
161
|
+
end
|
160
162
|
end
|
161
163
|
end
|
162
|
-
end
|
163
164
|
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
165
|
+
# Update cents
|
166
|
+
if !validation_enabled
|
167
|
+
# We haven't defined our own subunit writer, so we can invoke
|
168
|
+
# the regular writer, which works with store_accessors
|
169
|
+
public_send("#{subunit_name}=", money.try(:cents))
|
170
|
+
elsif self.class.respond_to?(:attribute_aliases) &&
|
171
|
+
self.class.attribute_aliases.key?(subunit_name)
|
172
|
+
# If the attribute is aliased, make sure we write to the original
|
173
|
+
# attribute name or an error will be raised.
|
174
|
+
# (Note: 'attribute_aliases' doesn't exist in Rails 3.x, so we
|
175
|
+
# can't tell if the attribute was aliased.)
|
176
|
+
original_name = self.class.attribute_aliases[subunit_name.to_s]
|
177
|
+
write_attribute(original_name, money.try(:cents))
|
178
|
+
else
|
179
|
+
write_attribute(subunit_name, money.try(:cents))
|
180
|
+
end
|
176
181
|
|
177
|
-
|
182
|
+
money_currency = money.try(:currency)
|
178
183
|
|
179
|
-
|
180
|
-
|
181
|
-
|
184
|
+
# Update currency iso value if there is an instance currency attribute
|
185
|
+
if instance_currency_name.present? &&
|
186
|
+
respond_to?("#{instance_currency_name}=")
|
182
187
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
+
public_send("#{instance_currency_name}=", money_currency.try(:iso_code))
|
189
|
+
else
|
190
|
+
current_currency = public_send("currency_for_#{name}")
|
191
|
+
if money_currency && current_currency != money_currency.id
|
192
|
+
raise "Can't change readonly currency '#{current_currency}' to '#{money_currency}' for field '#{name}'"
|
193
|
+
end
|
188
194
|
end
|
189
|
-
end
|
190
195
|
|
191
|
-
|
192
|
-
|
193
|
-
|
196
|
+
# Save and return the new Money object
|
197
|
+
instance_variable_set "@#{name}", money
|
198
|
+
end
|
194
199
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
200
|
+
if validation_enabled
|
201
|
+
# Ensure that the before_type_cast value is cleared when setting
|
202
|
+
# the subunit value directly
|
203
|
+
define_method "#{subunit_name}=" do |value|
|
204
|
+
instance_variable_set "@#{name}_money_before_type_cast", nil
|
205
|
+
write_attribute(subunit_name, value)
|
206
|
+
end
|
202
207
|
end
|
203
|
-
end
|
204
208
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
209
|
+
define_method "currency_for_#{name}" do
|
210
|
+
instance_currency_name_with_postfix = "#{name}#{MoneyRails::Configuration.currency_column[:postfix]}"
|
211
|
+
|
212
|
+
if instance_currency_name.present? &&
|
213
|
+
respond_to?(instance_currency_name) &&
|
214
|
+
Money::Currency.find(public_send(instance_currency_name))
|
215
|
+
|
216
|
+
Money::Currency.find(public_send(instance_currency_name))
|
217
|
+
elsif field_currency_name
|
218
|
+
Money::Currency.find(field_currency_name)
|
219
|
+
elsif respond_to?(instance_currency_name_with_postfix) &&
|
220
|
+
Money::Currency.find(public_send(instance_currency_name_with_postfix))
|
221
|
+
|
222
|
+
Money::Currency.find(public_send(instance_currency_name_with_postfix))
|
223
|
+
elsif self.class.respond_to?(:currency)
|
224
|
+
self.class.currency
|
225
|
+
else
|
226
|
+
Money.default_currency
|
227
|
+
end
|
218
228
|
end
|
219
|
-
end
|
220
229
|
|
221
|
-
|
222
|
-
instance_variable_get "@#{name}_money_before_type_cast"
|
223
|
-
end
|
230
|
+
attr_reader "#{name}_money_before_type_cast"
|
224
231
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
232
|
+
# Hook to ensure the reset of before_type_cast attr
|
233
|
+
# TODO: think of a better way to avoid this
|
234
|
+
after_save do
|
235
|
+
instance_variable_set "@#{name}_money_before_type_cast", nil
|
236
|
+
end
|
229
237
|
end
|
230
238
|
end
|
231
239
|
|
@@ -15,6 +15,7 @@ module MoneyRails
|
|
15
15
|
:no_cents_if_whole => MoneyRails::Configuration.no_cents_if_whole.nil? ? true : MoneyRails::Configuration.no_cents_if_whole,
|
16
16
|
:symbol => false
|
17
17
|
}.merge(options)
|
18
|
+
options.delete(:symbol) if options[:disambiguate]
|
18
19
|
|
19
20
|
if value.is_a?(Money)
|
20
21
|
value.format(options)
|
data/lib/money-rails/hooks.rb
CHANGED
@@ -6,8 +6,26 @@ module MoneyRails
|
|
6
6
|
require 'money-rails/active_model/validator'
|
7
7
|
require 'money-rails/active_record/monetizable'
|
8
8
|
::ActiveRecord::Base.send :include, MoneyRails::ActiveRecord::Monetizable
|
9
|
+
if ::Rails::VERSION::MAJOR >= 4
|
10
|
+
rails42 = case
|
11
|
+
when ::Rails::VERSION::MAJOR < 5 && ::Rails::VERSION::MINOR >= 2
|
12
|
+
true
|
13
|
+
when ::Rails::VERSION::MAJOR >= 5
|
14
|
+
true
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
postgresql_with_money = rails42 && ['activerecord-jdbcpostgresql-adapter', 'postgresql'].include?(::ActiveRecord::Base.connection.instance_values["config"][:adapter])
|
19
|
+
end
|
9
20
|
|
10
|
-
|
21
|
+
require "money-rails/active_record/migration_extensions/options_extractor"
|
22
|
+
%w{schema_statements table}.each do |file|
|
23
|
+
if postgresql_with_money
|
24
|
+
require "money-rails/active_record/migration_extensions/#{file}_pg_rails4"
|
25
|
+
else
|
26
|
+
require "money-rails/active_record/migration_extensions/#{file}"
|
27
|
+
end
|
28
|
+
end
|
11
29
|
::ActiveRecord::Migration.send :include, MoneyRails::ActiveRecord::MigrationExtensions::SchemaStatements
|
12
30
|
::ActiveRecord::ConnectionAdapters::TableDefinition.send :include, MoneyRails::ActiveRecord::MigrationExtensions::Table
|
13
31
|
::ActiveRecord::ConnectionAdapters::Table.send :include, MoneyRails::ActiveRecord::MigrationExtensions::Table
|
@@ -14,7 +14,11 @@ class Money
|
|
14
14
|
# this custom class from it.
|
15
15
|
def demongoize(object)
|
16
16
|
if object.is_a?(Hash)
|
17
|
-
|
17
|
+
if object.respond_to?(:deep_symbolize_keys)
|
18
|
+
object = object.deep_symbolize_keys
|
19
|
+
else
|
20
|
+
object = object.symbolize_keys
|
21
|
+
end
|
18
22
|
object.has_key?(:cents) ? ::Money.new(object[:cents], object[:currency_iso]) : nil
|
19
23
|
else
|
20
24
|
nil
|
@@ -27,7 +31,11 @@ class Money
|
|
27
31
|
case
|
28
32
|
when object.is_a?(Money) then object.mongoize
|
29
33
|
when object.is_a?(Hash) then
|
30
|
-
|
34
|
+
if object.respond_to?(:deep_symbolize_keys!)
|
35
|
+
object.deep_symbolize_keys!
|
36
|
+
elsif object.respond_to?(:symbolize_keys!)
|
37
|
+
object.symbolize_keys!
|
38
|
+
end
|
31
39
|
::Money.new(object[:cents], object[:currency_iso]).mongoize
|
32
40
|
when object.respond_to?(:to_money) then
|
33
41
|
begin
|
data/lib/money-rails/version.rb
CHANGED
data/money-rails.gemspec
CHANGED
@@ -33,5 +33,5 @@ Gem::Specification.new do |s|
|
|
33
33
|
|
34
34
|
s.add_development_dependency "rails", ">= 3.0"
|
35
35
|
s.add_development_dependency "rspec-rails", "~> 3.0"
|
36
|
-
s.add_development_dependency 'database_cleaner', ['>= 0.8.0']
|
36
|
+
s.add_development_dependency 'database_cleaner', ['>= 0.8.0', '< 1.4.0']
|
37
37
|
end
|
@@ -8,7 +8,9 @@ if defined? ActiveRecord
|
|
8
8
|
let(:product) do
|
9
9
|
Product.create(:price_cents => 3000, :discount => 150,
|
10
10
|
:bonus_cents => 200, :optional_price => 100,
|
11
|
-
:sale_price_amount => 1200
|
11
|
+
:sale_price_amount => 1200, :delivery_fee_cents => 100,
|
12
|
+
:restock_fee_cents => 2000,
|
13
|
+
:reduced_price_cents => 1500, :reduced_price_currency => :lvl)
|
12
14
|
end
|
13
15
|
|
14
16
|
let(:service) do
|
@@ -25,6 +27,11 @@ if defined? ActiveRecord
|
|
25
27
|
expect(product.bonus).to be_an_instance_of(Money)
|
26
28
|
end
|
27
29
|
|
30
|
+
it "attaches Money objects to multiple model fields" do
|
31
|
+
expect(product.delivery_fee).to be_an_instance_of(Money)
|
32
|
+
expect(product.restock_fee).to be_an_instance_of(Money)
|
33
|
+
end
|
34
|
+
|
28
35
|
it "returns the expected money amount as a Money object" do
|
29
36
|
expect(product.price).to eq(Money.new(3000, "USD"))
|
30
37
|
end
|
@@ -75,7 +82,7 @@ if defined? ActiveRecord
|
|
75
82
|
end
|
76
83
|
|
77
84
|
it "skips numericality validation when disabled" do
|
78
|
-
product.
|
85
|
+
product.accessor_price_cents = 'not_valid'
|
79
86
|
expect(product.save).to be_truthy
|
80
87
|
end
|
81
88
|
|
@@ -91,13 +98,13 @@ if defined? ActiveRecord
|
|
91
98
|
after { MoneyRails.raise_error_on_money_parsing = false }
|
92
99
|
|
93
100
|
it "raises exception when a String value with hyphen is assigned" do
|
94
|
-
expect { product.
|
101
|
+
expect { product.accessor_price = "10-235" }.to raise_error
|
95
102
|
end
|
96
103
|
end
|
97
104
|
|
98
105
|
context "when MoneyRails.raise_error_on_money_parsing is false (default)" do
|
99
106
|
it "does not raise exception when a String value with hyphen is assigned" do
|
100
|
-
expect { product.
|
107
|
+
expect { product.accessor_price = "10-235" }.not_to raise_error
|
101
108
|
end
|
102
109
|
end
|
103
110
|
|
@@ -193,6 +200,15 @@ if defined? ActiveRecord
|
|
193
200
|
expect(product.errors[:price_in_a_range].first).to match(/Must be greater than zero and less than \$100/)
|
194
201
|
end
|
195
202
|
|
203
|
+
it "fails validation if linked attribute changed" do
|
204
|
+
product = Product.create(:price => Money.new(3210, "USD"), :discount => 150,
|
205
|
+
:validates_method_amount => 100,
|
206
|
+
:bonus_cents => 200, :optional_price => 100)
|
207
|
+
expect(product.valid?).to be_truthy
|
208
|
+
product.optional_price = 50
|
209
|
+
expect(product.valid?).to be_falsey
|
210
|
+
end
|
211
|
+
|
196
212
|
it "fails validation with the proper error message using validates :money" do
|
197
213
|
product.validates_method_amount = "-12"
|
198
214
|
expect(product.valid?).to be_falsey
|
@@ -297,20 +313,6 @@ if defined? ActiveRecord
|
|
297
313
|
I18n.locale = old_locale
|
298
314
|
end
|
299
315
|
|
300
|
-
it "defaults to Money::Currency format when no I18n information is present" do
|
301
|
-
old_locale = I18n.locale
|
302
|
-
|
303
|
-
I18n.locale = "zxsw"
|
304
|
-
Money.default_currency = Money::Currency.find('EUR')
|
305
|
-
expect("12,00".to_money).to eq(Money.new(1200, :eur))
|
306
|
-
transaction = Transaction.new(amount: "12,00", tax: "13,00")
|
307
|
-
expect(transaction.amount_cents).to eq(1200)
|
308
|
-
expect(transaction.valid?).to be_truthy
|
309
|
-
|
310
|
-
# reset locale setting
|
311
|
-
I18n.locale = old_locale
|
312
|
-
end
|
313
|
-
|
314
316
|
it "doesn't allow nil by default" do
|
315
317
|
product.price_cents = nil
|
316
318
|
expect(product.save).to be_falsey
|
@@ -364,6 +366,10 @@ if defined? ActiveRecord
|
|
364
366
|
expect(product.bonus.currency).to eq(Money::Currency.find(:gbp))
|
365
367
|
end
|
366
368
|
|
369
|
+
it "uses currency postfix to determine attribute that stores currency" do
|
370
|
+
expect(product.reduced_price.currency).to eq(Money::Currency.find(:lvl))
|
371
|
+
end
|
372
|
+
|
367
373
|
it "correctly assigns Money objects to the attribute" do
|
368
374
|
product.price = Money.new(2500, :USD)
|
369
375
|
expect(product.save).to be_truthy
|
@@ -395,6 +401,13 @@ if defined? ActiveRecord
|
|
395
401
|
expect(service.discount.currency_as_string).to eq("EUR")
|
396
402
|
end
|
397
403
|
|
404
|
+
it "correctly assigns objects to a accessor attribute" do
|
405
|
+
product.accessor_price = 1.23
|
406
|
+
expect(product.save).to be_truthy
|
407
|
+
expect(product.accessor_price.cents).to eq(123)
|
408
|
+
expect(product.accessor_price_cents).to eq(123)
|
409
|
+
end
|
410
|
+
|
398
411
|
it "overrides default, model currency with the value of :with_currency in fixnum assignments" do
|
399
412
|
product.bonus = 25
|
400
413
|
expect(product.save).to be_truthy
|
@@ -467,7 +480,6 @@ if defined? ActiveRecord
|
|
467
480
|
expect(product.optional_price).to be_nil
|
468
481
|
end
|
469
482
|
|
470
|
-
|
471
483
|
context "when the monetized field is an aliased attribute" do
|
472
484
|
it "writes the subunits to the original (unaliased) column" do
|
473
485
|
pending if Rails::VERSION::MAJOR < 4
|
@@ -5,6 +5,9 @@ class Product < ActiveRecord::Base
|
|
5
5
|
# Use money-rails macros
|
6
6
|
monetize :price_cents
|
7
7
|
|
8
|
+
# Use money-rails macro with multiple fields
|
9
|
+
monetize :delivery_fee_cents, :restock_fee_cents, :allow_nil => true
|
10
|
+
|
8
11
|
# Use a custom name for the Money attribute
|
9
12
|
monetize :discount, :as => "discount_value"
|
10
13
|
|
@@ -29,18 +32,21 @@ class Product < ActiveRecord::Base
|
|
29
32
|
:message => "Must be greater than zero and less than $100"
|
30
33
|
}
|
31
34
|
|
32
|
-
attr_accessor :
|
33
|
-
monetize :
|
35
|
+
attr_accessor :accessor_price_cents
|
36
|
+
monetize :accessor_price_cents, disable_validation: true
|
34
37
|
|
35
38
|
monetize :validates_method_amount_cents, allow_nil: true
|
36
39
|
|
37
40
|
validates :validates_method_amount, :money => {
|
38
41
|
:greater_than => 0,
|
39
|
-
:less_than_or_equal_to =>
|
42
|
+
:less_than_or_equal_to => ->(product) { product.optional_price.to_f },
|
40
43
|
:message => 'Must be greater than zero and less than $100',
|
41
|
-
}
|
44
|
+
}, allow_nil: true
|
42
45
|
|
43
46
|
alias_attribute :renamed_cents, :aliased_cents
|
44
47
|
|
45
48
|
monetize :renamed_cents, allow_nil: true
|
49
|
+
|
50
|
+
# Using postfix to determine currency column (reduced_price_currency)
|
51
|
+
monetize :reduced_price_cents, :allow_nil => true
|
46
52
|
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended that you check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(version:
|
14
|
+
ActiveRecord::Schema.define(version: 20150126231442) do
|
15
15
|
|
16
16
|
create_table "dummy_products", force: true do |t|
|
17
17
|
t.string "currency"
|
@@ -32,6 +32,10 @@ ActiveRecord::Schema.define(version: 20141005075025) do
|
|
32
32
|
t.integer "price_in_a_range_cents"
|
33
33
|
t.integer "validates_method_amount_cents"
|
34
34
|
t.integer "aliased_cents"
|
35
|
+
t.integer "delivery_fee_cents"
|
36
|
+
t.integer "restock_fee_cents"
|
37
|
+
t.integer "reduced_price_cents"
|
38
|
+
t.string "reduced_price_currency"
|
35
39
|
end
|
36
40
|
|
37
41
|
create_table "services", force: true do |t|
|
@@ -8,8 +8,9 @@ describe 'MoneyRails::ActionViewExtension', :type => :helper do
|
|
8
8
|
end
|
9
9
|
|
10
10
|
describe '#humanized_money' do
|
11
|
+
let(:money_object){ Money.new(12500) }
|
11
12
|
let(:options) { {} }
|
12
|
-
subject { helper.humanized_money
|
13
|
+
subject { helper.humanized_money money_object, options }
|
13
14
|
it { is_expected.to be_a String }
|
14
15
|
it { is_expected.not_to include Money.default_currency.symbol }
|
15
16
|
it { is_expected.not_to include Money.default_currency.decimal_mark }
|
@@ -26,6 +27,20 @@ describe 'MoneyRails::ActionViewExtension', :type => :helper do
|
|
26
27
|
end
|
27
28
|
it { is_expected.to include Money.default_currency.symbol }
|
28
29
|
end
|
30
|
+
|
31
|
+
context 'with a currency with an alternate symbol' do
|
32
|
+
let(:money_object) { Money.new(125_00, 'SGD') }
|
33
|
+
|
34
|
+
context 'with symbol options' do
|
35
|
+
let(:options) { { :symbol => true } }
|
36
|
+
it { is_expected.to include Money::Currency.new(:sgd).symbol }
|
37
|
+
|
38
|
+
context 'with disambiguate options' do
|
39
|
+
let(:options) { { :symbol => true, :disambiguate => true } }
|
40
|
+
it { is_expected.to include Money::Currency.new(:sgd).disambiguate_symbol }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
29
44
|
end
|
30
45
|
|
31
46
|
describe '#humanized_money_with_symbol' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: money-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Loupasakis
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2015-01-29 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: money
|
@@ -103,6 +103,9 @@ dependencies:
|
|
103
103
|
- - ">="
|
104
104
|
- !ruby/object:Gem::Version
|
105
105
|
version: 0.8.0
|
106
|
+
- - "<"
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
version: 1.4.0
|
106
109
|
type: :development
|
107
110
|
prerelease: false
|
108
111
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -110,6 +113,9 @@ dependencies:
|
|
110
113
|
- - ">="
|
111
114
|
- !ruby/object:Gem::Version
|
112
115
|
version: 0.8.0
|
116
|
+
- - "<"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: 1.4.0
|
113
119
|
description: This library provides integration of RubyMoney - Money gem with Rails
|
114
120
|
email:
|
115
121
|
- alup.rubymoney@gmail.com
|
@@ -128,7 +134,9 @@ files:
|
|
128
134
|
- lib/money-rails/active_model/validator.rb
|
129
135
|
- lib/money-rails/active_record/migration_extensions/options_extractor.rb
|
130
136
|
- lib/money-rails/active_record/migration_extensions/schema_statements.rb
|
137
|
+
- lib/money-rails/active_record/migration_extensions/schema_statements_pg_rails4.rb
|
131
138
|
- lib/money-rails/active_record/migration_extensions/table.rb
|
139
|
+
- lib/money-rails/active_record/migration_extensions/table_pg_rails4.rb
|
132
140
|
- lib/money-rails/active_record/monetizable.rb
|
133
141
|
- lib/money-rails/configuration.rb
|
134
142
|
- lib/money-rails/engine.rb
|
@@ -187,6 +195,8 @@ files:
|
|
187
195
|
- spec/dummy/db/migrate/20130124023419_add_price_in_a_range_cents_to_products.rb
|
188
196
|
- spec/dummy/db/migrate/20140110194016_add_validates_method_amount_cents_to_products.rb
|
189
197
|
- spec/dummy/db/migrate/20141005075025_add_aliased_attr_to_products.rb
|
198
|
+
- spec/dummy/db/migrate/20150107061030_add_delivery_fee_cents_and_restock_fee_cents_to_product.rb
|
199
|
+
- spec/dummy/db/migrate/20150126231442_add_reduced_price_to_products.rb
|
190
200
|
- spec/dummy/db/schema.rb
|
191
201
|
- spec/dummy/db/structure.sql
|
192
202
|
- spec/dummy/public/404.html
|
@@ -273,6 +283,8 @@ test_files:
|
|
273
283
|
- spec/dummy/db/migrate/20130124023419_add_price_in_a_range_cents_to_products.rb
|
274
284
|
- spec/dummy/db/migrate/20140110194016_add_validates_method_amount_cents_to_products.rb
|
275
285
|
- spec/dummy/db/migrate/20141005075025_add_aliased_attr_to_products.rb
|
286
|
+
- spec/dummy/db/migrate/20150107061030_add_delivery_fee_cents_and_restock_fee_cents_to_product.rb
|
287
|
+
- spec/dummy/db/migrate/20150126231442_add_reduced_price_to_products.rb
|
276
288
|
- spec/dummy/db/schema.rb
|
277
289
|
- spec/dummy/db/structure.sql
|
278
290
|
- spec/dummy/public/404.html
|