money-rails 1.13.0 → 1.14.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 +5 -5
- data/CHANGELOG.md +31 -0
- data/LICENSE +18 -18
- data/README.md +6 -5
- data/Rakefile +7 -0
- data/config/locales/money.da.yml +4 -0
- data/lib/generators/templates/money.rb +23 -0
- data/lib/money-rails/active_model/validator.rb +82 -78
- data/lib/money-rails/active_record/migration_extensions/schema_statements_pg_rails4.rb +4 -3
- data/lib/money-rails/active_record/migration_extensions/table_pg_rails4.rb +2 -1
- data/lib/money-rails/active_record/monetizable.rb +1 -1
- data/lib/money-rails/configuration.rb +1 -2
- data/lib/money-rails/helpers/action_view_extension.rb +10 -3
- data/lib/money-rails/hooks.rb +7 -1
- data/lib/money-rails/money.rb +6 -1
- data/lib/money-rails/mongoid/money.rb +28 -19
- data/lib/money-rails/rails_admin.rb +1 -1
- data/lib/money-rails/railtie.rb +1 -1
- data/lib/money-rails/version.rb +1 -1
- data/money-rails.gemspec +8 -1
- data/spec/active_record/monetizable_spec.rb +40 -30
- data/spec/configuration_spec.rb +3 -3
- data/spec/dummy/app/assets/config/manifest.js +0 -0
- data/spec/dummy/db/schema.rb +30 -31
- data/spec/helpers/action_view_extension_spec.rb +33 -0
- data/spec/money_spec.rb +40 -0
- data/spec/mongoid/{four_spec.rb → mongoid_spec.rb} +10 -3
- data/spec/mongoid/two_spec.rb +2 -2
- metadata +33 -16
- data/spec/mongoid/five_spec.rb +0 -117
- data/spec/mongoid/three_spec.rb +0 -122
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8226863e954671b603c22fac3eae1667b81ba88eb3425b8c9cd798e2cfa2f303
|
4
|
+
data.tar.gz: cc9c7c8fbc27bcc9343ad457f596b15594a576d0da93448d3bfbabe1caedcfbb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ed641980d3b776a3b2ec80d9ff1e7247415248eeff0bdc3fbc7077ad7700171fdcbda6c4c13bf39c2cda063a9fdf2575b660f9625fb506e8d39a815b96ab02f
|
7
|
+
data.tar.gz: 301fbd73c779ac54d60e1be7137df6e0a89974b0372d1667fe9eb69c2a8e40c309768bcda6d885f1d6e7168512d2e62a61562ea2189dcd1bba14c53d58b1b891
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.14.0
|
4
|
+
|
5
|
+
- Tweaks to support Ruby 3.0
|
6
|
+
|
7
|
+
## 1.13.4
|
8
|
+
|
9
|
+
- Fix validator race condition
|
10
|
+
- Add Danish translation for errors
|
11
|
+
- Change Money fields to Decimal in Rails Admin
|
12
|
+
- Run hooks after active_record.initialize_database
|
13
|
+
- Add optional currency argument to "#currency_symbol" helper
|
14
|
+
- Rails 6.1 support
|
15
|
+
|
16
|
+
## 1.13.3
|
17
|
+
|
18
|
+
- Add Money#to_hash for JSON serialization
|
19
|
+
- Update initializer template with #locale_backend config
|
20
|
+
- Rollback support for remove_monetize / remove_money DB helpers
|
21
|
+
- Rails 6 support
|
22
|
+
|
23
|
+
## 1.13.2
|
24
|
+
|
25
|
+
- Make validation compatible with Money.locale_backend
|
26
|
+
|
27
|
+
## 1.13.1
|
28
|
+
|
29
|
+
- Add a guard clause for "blank form" input (Mongoid)
|
30
|
+
- Do not add extra errors in case attribute is not a number
|
31
|
+
- Use Money.locale_backend instead of Money.use_i18n
|
32
|
+
- Add money_only_cents helper method
|
33
|
+
|
3
34
|
## 1.13.0
|
4
35
|
|
5
36
|
- Bump money version to ~> 6.13.0
|
data/LICENSE
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
Copyright (c) 2012 Andreas Loupasakis
|
2
|
-
|
3
1
|
MIT License
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
Copyright (c) 2012 Andreas Loupasakis
|
4
|
+
Copyright (c) 2021 Shane Emmons
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
12
|
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
|
13
|
+
The above copyright notice and this permission notice shall be included in all
|
14
|
+
copies or substantial portions of the Software.
|
15
15
|
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
OF
|
22
|
-
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
22
|
+
SOFTWARE.
|
data/README.md
CHANGED
@@ -116,7 +116,7 @@ end
|
|
116
116
|
|
117
117
|
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.
|
118
118
|
|
119
|
-
The ```add_money``` helper is reversible, so you
|
119
|
+
The ```add_money``` helper is reversible, so you can use it inside ```change```
|
120
120
|
migrations. If you're writing separate ```up``` and ```down``` methods, you
|
121
121
|
can use the ```remove_money``` helper.
|
122
122
|
|
@@ -306,7 +306,7 @@ object using EUR as their currency, instead of the default USD.
|
|
306
306
|
|
307
307
|
By passing the option ```:with_currency``` to the ```monetize``` macro call,
|
308
308
|
with a currency code (symbol or string) or a callable object (object that responds to the method ```call```) that returns a currency code, as its value, you can define a currency in a more granular
|
309
|
-
way. This will you attach the given currency only to the specified monetized model
|
309
|
+
way. This will let you attach the given currency only to the specified monetized model
|
310
310
|
attribute (allowing you to, for example, monetize different attributes of the same model with different currencies.).
|
311
311
|
|
312
312
|
This allows you to override both the model level and the global
|
@@ -326,7 +326,7 @@ end
|
|
326
326
|
```
|
327
327
|
|
328
328
|
In this case ```product.bonus``` will return a Money object with GBP as its
|
329
|
-
currency, whereas ```product.discount.
|
329
|
+
currency, whereas ```product.discount.currency.to_s # => EUR ```
|
330
330
|
|
331
331
|
As mentioned earlier you can use an object that responds to the method ```call``` and accepts the model instance as a parameter. That means you can use a ```Proc``` or ```lambda``` (we would recommend ```lambda``` over ```Proc``` because of their [different control flow characteristics](https://stackoverflow.com/questions/1740046/whats-the-difference-between-a-proc-and-a-lambda-in-ruby)) or even define a separate ```class``` with an instance or class method (maybe even a ```module```) to return the currency code:
|
332
332
|
|
@@ -502,6 +502,7 @@ _For examples below, `@money_object == <Money fractional:650 currency:USD>`_
|
|
502
502
|
| `humanized_money_with_symbol @money_object` | $6.50 |
|
503
503
|
| `money_without_cents @money_object` | 6 |
|
504
504
|
| `money_without_cents_and_with_symbol @money_object` | $6 |
|
505
|
+
| `money_only_cents @money_object` | 50 |
|
505
506
|
|
506
507
|
#### `no_cents_if_whole`
|
507
508
|
|
@@ -552,7 +553,7 @@ For examples on using the test_helpers look at
|
|
552
553
|
## Supported ORMs/ODMs
|
553
554
|
|
554
555
|
* ActiveRecord (>= 3.x)
|
555
|
-
* Mongoid (2.x
|
556
|
+
* Mongoid (>= 2.x)
|
556
557
|
|
557
558
|
## Supported Ruby interpreters
|
558
559
|
|
@@ -586,4 +587,4 @@ If you are testing against mongoid, make sure to have the mongod process running
|
|
586
587
|
|
587
588
|
## License
|
588
589
|
|
589
|
-
MIT License. Copyright
|
590
|
+
MIT License. Copyright 2021 RubyMoney.
|
data/Rakefile
CHANGED
@@ -55,6 +55,13 @@ namespace :spec do
|
|
55
55
|
framework, version = file_name.split(/(\d+)/)
|
56
56
|
major, minor = version.split(//)
|
57
57
|
|
58
|
+
# Ruby 3 exclusions
|
59
|
+
if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0')
|
60
|
+
# Rails 5 does not support ruby-3.0.0 https://github.com/rails/rails/issues/40938#issuecomment-751569171
|
61
|
+
# Mongoid gem does not yet support ruby-3.0.0 https://github.com/mongodb/mongoid#compatibility
|
62
|
+
next if framework == 'mongoid' || (framework == 'rails' && version == "5")
|
63
|
+
end
|
64
|
+
|
58
65
|
frameworks_versions[framework] ||= []
|
59
66
|
frameworks_versions[framework] << file_name
|
60
67
|
|
@@ -83,6 +83,29 @@ MoneyRails.configure do |config|
|
|
83
83
|
# sign_before_symbol: nil
|
84
84
|
# }
|
85
85
|
|
86
|
+
# If you would like to use I18n localization (formatting depends on the
|
87
|
+
# locale):
|
88
|
+
# config.locale_backend = :i18n
|
89
|
+
#
|
90
|
+
# Example (using default localization from rails-i18n):
|
91
|
+
#
|
92
|
+
# I18n.locale = :en
|
93
|
+
# Money.new(10_000_00, 'USD').format # => $10,000.00
|
94
|
+
# I18n.locale = :es
|
95
|
+
# Money.new(10_000_00, 'USD').format # => $10.000,00
|
96
|
+
#
|
97
|
+
# For the legacy behaviour of "per currency" localization (formatting depends
|
98
|
+
# only on currency):
|
99
|
+
# config.locale_backend = :currency
|
100
|
+
#
|
101
|
+
# Example:
|
102
|
+
# Money.new(10_000_00, 'USD').format # => $10,000.00
|
103
|
+
# Money.new(10_000_00, 'EUR').format # => €10.000,00
|
104
|
+
#
|
105
|
+
# In case you don't need localization and would like to use default values
|
106
|
+
# (can be redefined using config.default_format):
|
107
|
+
# config.locale_backend = nil
|
108
|
+
|
86
109
|
# Set default raise_error_on_money_parsing option
|
87
110
|
# It will be raise error if assigned different currency
|
88
111
|
# The default value is false
|
@@ -1,113 +1,108 @@
|
|
1
1
|
module MoneyRails
|
2
2
|
module ActiveModel
|
3
3
|
class MoneyValidator < ::ActiveModel::Validations::NumericalityValidator
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
class Details < Struct.new(:raw_value, :thousands_separator, :decimal_mark)
|
5
|
+
def abs_raw_value
|
6
|
+
@abs_raw_value ||= raw_value.to_s.sub(/^\s*-/, "").strip
|
7
|
+
end
|
8
|
+
|
9
|
+
def decimal_pieces
|
10
|
+
@decimal_pieces ||= abs_raw_value.split(decimal_mark)
|
11
|
+
end
|
12
|
+
end
|
8
13
|
|
9
|
-
|
14
|
+
def validate_each(record, attr, _value)
|
15
|
+
subunit_attr = record.class.monetized_attributes[attr.to_s]
|
16
|
+
currency = record.public_send("currency_for_#{attr}")
|
10
17
|
|
11
18
|
# WARNING: Currently this is only defined in ActiveRecord extension!
|
12
|
-
before_type_cast = :"#{
|
13
|
-
|
19
|
+
before_type_cast = :"#{attr}_money_before_type_cast"
|
20
|
+
raw_value = record.try(before_type_cast)
|
14
21
|
|
15
22
|
# If raw value is nil and changed subunit is nil, then
|
16
23
|
# nil is a assigned value, else we should treat the
|
17
24
|
# subunit value as the one assigned.
|
18
|
-
if
|
19
|
-
subunit_value =
|
20
|
-
|
25
|
+
if raw_value.nil? && record.public_send(subunit_attr)
|
26
|
+
subunit_value = record.public_send(subunit_attr)
|
27
|
+
raw_value = subunit_value.to_f / currency.subunit_to_unit
|
21
28
|
end
|
22
29
|
|
23
|
-
return if options[:allow_nil] &&
|
30
|
+
return if options[:allow_nil] && raw_value.nil?
|
24
31
|
|
25
|
-
# Set this before we modify
|
26
|
-
stringy =
|
32
|
+
# Set this before we modify raw_value below.
|
33
|
+
stringy = raw_value.present? && !raw_value.is_a?(Numeric) && !raw_value.is_a?(Money)
|
27
34
|
|
28
35
|
if stringy
|
36
|
+
# TODO: This is supporting legacy behaviour where a symbol can come from a i18n locale,
|
37
|
+
# however practical implications of that are most likely non-existent
|
38
|
+
symbol = lookup(:symbol, currency) || currency.symbol
|
39
|
+
|
29
40
|
# remove currency symbol
|
30
|
-
|
41
|
+
raw_value = raw_value.to_s.gsub(symbol, "")
|
31
42
|
end
|
32
43
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
add_error if
|
38
|
-
value_has_too_many_decimal_points ||
|
39
|
-
thousand_separator_after_decimal_mark ||
|
40
|
-
invalid_thousands_separation
|
41
|
-
end
|
42
|
-
end
|
44
|
+
# Cache abs_raw_value before normalizing because it's used in
|
45
|
+
# many places and relies on the original raw_value.
|
46
|
+
details = generate_details(raw_value, currency)
|
47
|
+
normalized_raw_value = normalize(details)
|
43
48
|
|
44
|
-
|
49
|
+
super(record, attr, normalized_raw_value)
|
45
50
|
|
46
|
-
|
47
|
-
return
|
48
|
-
!@record.errors.added?(@attr, :not_a_number)
|
49
|
-
end
|
51
|
+
return unless stringy
|
52
|
+
return if record_already_has_error?(record, attr, normalized_raw_value)
|
50
53
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
remove_instance_variable(ivar_name) if instance_variable_defined?(ivar_name)
|
56
|
-
end
|
54
|
+
add_error!(record, attr, details) if
|
55
|
+
value_has_too_many_decimal_points(details) ||
|
56
|
+
thousand_separator_after_decimal_mark(details) ||
|
57
|
+
invalid_thousands_separation(details)
|
57
58
|
end
|
58
59
|
|
59
|
-
|
60
|
-
@_currency ||= @record.public_send("currency_for_#{@attr}")
|
61
|
-
end
|
60
|
+
private
|
62
61
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
DEFAULTS = {
|
63
|
+
decimal_mark: '.',
|
64
|
+
thousands_separator: ','
|
65
|
+
}.freeze
|
67
66
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
71
|
-
end
|
67
|
+
def generate_details(raw_value, currency)
|
68
|
+
thousands_separator = lookup(:thousands_separator, currency)
|
69
|
+
decimal_mark = lookup(:decimal_mark, currency)
|
72
70
|
|
73
|
-
|
74
|
-
@_symbol ||= Money.use_i18n ? I18n.t('number.currency.format.unit', default: currency.symbol) : currency.symbol
|
71
|
+
Details.new(raw_value, thousands_separator, decimal_mark)
|
75
72
|
end
|
76
73
|
|
77
|
-
def
|
78
|
-
|
74
|
+
def record_already_has_error?(record, attr, raw_value)
|
75
|
+
record.errors.added?(attr, :not_a_number, value: raw_value)
|
79
76
|
end
|
80
77
|
|
81
|
-
def add_error
|
82
|
-
attr_name =
|
83
|
-
attr_name =
|
78
|
+
def add_error!(record, attr, details)
|
79
|
+
attr_name = attr.to_s.tr('.', '_').humanize
|
80
|
+
attr_name = record.class.human_attribute_name(attr, default: attr_name)
|
84
81
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
82
|
+
record.errors.add(attr, :invalid_currency,
|
83
|
+
thousands: details.thousands_separator,
|
84
|
+
decimal: details.decimal_mark,
|
85
|
+
currency: details.abs_raw_value,
|
86
|
+
attribute: attr_name
|
87
|
+
)
|
90
88
|
end
|
91
89
|
|
92
|
-
def
|
93
|
-
|
90
|
+
def value_has_too_many_decimal_points(details)
|
91
|
+
![1, 2].include?(details.decimal_pieces.length)
|
94
92
|
end
|
95
93
|
|
96
|
-
def
|
97
|
-
|
94
|
+
def thousand_separator_after_decimal_mark(details)
|
95
|
+
details.thousands_separator.present? &&
|
96
|
+
details.decimal_pieces.length == 2 &&
|
97
|
+
details.decimal_pieces[1].include?(details.thousands_separator)
|
98
98
|
end
|
99
99
|
|
100
|
-
def
|
101
|
-
|
102
|
-
end
|
100
|
+
def invalid_thousands_separation(details)
|
101
|
+
pieces_array = details.decimal_pieces[0].split(details.thousands_separator.presence)
|
103
102
|
|
104
|
-
def thousand_separator_after_decimal_mark
|
105
|
-
thousands_separator.present? && decimal_pieces.length == 2 && decimal_pieces[1].include?(thousands_separator)
|
106
|
-
end
|
107
|
-
|
108
|
-
def invalid_thousands_separation
|
109
103
|
return false if pieces_array.length <= 1
|
110
104
|
return true if pieces_array[0].length > 3
|
105
|
+
|
111
106
|
pieces_array[1..-1].any? do |thousands_group|
|
112
107
|
thousands_group.length != 3
|
113
108
|
end
|
@@ -115,16 +110,25 @@ module MoneyRails
|
|
115
110
|
|
116
111
|
# Remove thousands separators, normalize decimal mark,
|
117
112
|
# remove whitespaces and _ (E.g. 99 999 999 or 12_300_200.20)
|
118
|
-
def
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
@raw_value = @raw_value.to_s
|
124
|
-
.gsub(thousands_separator, '')
|
125
|
-
.gsub(decimal_mark, '.')
|
113
|
+
def normalize(details)
|
114
|
+
details.raw_value
|
115
|
+
.to_s
|
116
|
+
.gsub(details.thousands_separator, '')
|
117
|
+
.gsub(details.decimal_mark, '.')
|
126
118
|
.gsub(/[\s_]/, '')
|
127
119
|
end
|
120
|
+
|
121
|
+
def lookup(key, currency)
|
122
|
+
if locale_backend
|
123
|
+
locale_backend.lookup(key, currency) || DEFAULTS[key]
|
124
|
+
else
|
125
|
+
DEFAULTS[key]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def locale_backend
|
130
|
+
Money.locale_backend
|
131
|
+
end
|
128
132
|
end
|
129
133
|
end
|
130
134
|
end
|
@@ -5,14 +5,15 @@ module MoneyRails
|
|
5
5
|
def add_monetize(table_name, accessor, options={})
|
6
6
|
[:amount, :currency].each do |attribute|
|
7
7
|
column_present, *opts = OptionsExtractor.extract attribute, table_name, accessor, options
|
8
|
-
|
8
|
+
constraints = opts.pop
|
9
|
+
add_column(*opts, **constraints) if column_present
|
9
10
|
end
|
10
11
|
end
|
11
12
|
|
12
13
|
def remove_monetize(table_name, accessor, options={})
|
13
14
|
[:amount, :currency].each do |attribute|
|
14
|
-
column_present, table_name, column_name,
|
15
|
-
remove_column table_name, column_name if column_present
|
15
|
+
column_present, table_name, column_name, type, _ = OptionsExtractor.extract attribute, table_name, accessor, options
|
16
|
+
remove_column table_name, column_name, type if column_present
|
16
17
|
end
|
17
18
|
end
|
18
19
|
end
|
@@ -5,7 +5,8 @@ module MoneyRails
|
|
5
5
|
def monetize(accessor, options={})
|
6
6
|
[:amount, :currency].each do |attribute|
|
7
7
|
column_present, _, *opts = OptionsExtractor.extract attribute, :no_table, accessor, options
|
8
|
-
|
8
|
+
constraints = opts.pop
|
9
|
+
column(*opts, **constraints) if column_present
|
9
10
|
end
|
10
11
|
end
|
11
12
|
|
@@ -209,7 +209,7 @@ module MoneyRails
|
|
209
209
|
|
210
210
|
if MoneyRails::Configuration.preserve_user_input
|
211
211
|
value_before_type_cast = instance_variable_get "@#{name}_money_before_type_cast"
|
212
|
-
if errors
|
212
|
+
if errors.has_key?(name.to_sym)
|
213
213
|
result.define_singleton_method(:to_s) { value_before_type_cast }
|
214
214
|
result.define_singleton_method(:format) { |_| value_before_type_cast }
|
215
215
|
end
|
@@ -7,7 +7,6 @@ module MoneyRails
|
|
7
7
|
# MoneyRails configuration module.
|
8
8
|
# This is extended by MoneyRails to provide configuration settings.
|
9
9
|
module Configuration
|
10
|
-
|
11
10
|
# Start a MoneyRails configuration block in an initializer.
|
12
11
|
#
|
13
12
|
# example: Provide a default currency for the application
|
@@ -59,7 +58,7 @@ module MoneyRails
|
|
59
58
|
# MoneyRails.configure do |config|
|
60
59
|
# config.default_bank = EuCentralBank.new
|
61
60
|
# end
|
62
|
-
delegate :default_bank=, :default_bank, to: :Money
|
61
|
+
delegate :default_bank=, :default_bank, :locale_backend, :locale_backend=, to: :Money
|
63
62
|
|
64
63
|
# Provide exchange rates
|
65
64
|
delegate :add_rate, to: :Money
|