money-rails 1.12.0 → 1.13.4
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/README.md +6 -5
- data/Rakefile +17 -35
- 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 +2 -2
- 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 +7 -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/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 +16 -13
- 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: 618663a78af12610ff9901dd5ed3b3e5e5bf74d6d24b65141a52b2811179a0d8
|
4
|
+
data.tar.gz: 0724c64933122f73af34f1ad606c309e40c224a8b8636053ca437145527a8490
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d0b87a85f4fe133e3fc75d75722c42dbb445c05cc98ebe7d242d8b63a27d8b879d324d37a8ca9b1ee24fddc8c0381152cd04e449dc8f8c041ec1598371760551
|
7
|
+
data.tar.gz: c984f12a0162f918b6d5c67b009da28d37a96a63ee0db664a8778e03503d60064ad9e8227d2e1d4fd37f61098f235445f0b959b7612aa503a1b90416f70b69c0
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,36 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 1.13.4
|
4
|
+
|
5
|
+
- Fix validator race condition
|
6
|
+
- Add Danish translation for errors
|
7
|
+
- Change Money fields to Decimal in Rails Admin
|
8
|
+
- Run hooks after active_record.initialize_database
|
9
|
+
- Add optional currency argument to "#currency_symbol" helper
|
10
|
+
- Rails 6.1 support
|
11
|
+
|
12
|
+
## 1.13.3
|
13
|
+
|
14
|
+
- Add Money#to_hash for JSON serialization
|
15
|
+
- Update initializer template with #locale_backend config
|
16
|
+
- Rollback support for remove_monetize / remove_money DB helpers
|
17
|
+
- Rails 6 support
|
18
|
+
|
19
|
+
## 1.13.2
|
20
|
+
|
21
|
+
- Make validation compatible with Money.locale_backend
|
22
|
+
|
23
|
+
## 1.13.1
|
24
|
+
|
25
|
+
- Add a guard clause for "blank form" input (Mongoid)
|
26
|
+
- Do not add extra errors in case attribute is not a number
|
27
|
+
- Use Money.locale_backend instead of Money.use_i18n
|
28
|
+
- Add money_only_cents helper method
|
29
|
+
|
30
|
+
## 1.13.0
|
31
|
+
|
32
|
+
- Bump money version to ~> 6.13.0
|
33
|
+
|
3
34
|
## 1.12.0
|
4
35
|
|
5
36
|
- Bump money version to ~> 6.12.0
|
data/README.md
CHANGED
@@ -22,7 +22,7 @@ welcome to contribute to the project.
|
|
22
22
|
|
23
23
|
Add this line to your application's Gemfile:
|
24
24
|
|
25
|
-
gem 'money-rails', '~>1'
|
25
|
+
gem 'money-rails', '~>1.12'
|
26
26
|
|
27
27
|
And then execute:
|
28
28
|
|
@@ -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
|
|
data/Rakefile
CHANGED
@@ -13,6 +13,7 @@ rescue Bundler::BundlerError => e
|
|
13
13
|
end
|
14
14
|
|
15
15
|
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
16
|
+
GEMFILES_PATH = 'gemfiles/*.gemfile'.freeze
|
16
17
|
|
17
18
|
load 'rails/tasks/engine.rake' if File.exist?(APP_RAKEFILE)
|
18
19
|
|
@@ -47,46 +48,27 @@ def run_with_gemfile(gemfile)
|
|
47
48
|
end
|
48
49
|
|
49
50
|
namespace :spec do
|
51
|
+
frameworks_versions = {}
|
50
52
|
|
51
|
-
|
52
|
-
|
53
|
+
Dir[GEMFILES_PATH].each do |gemfile|
|
54
|
+
file_name = File.basename(gemfile, '.gemfile')
|
55
|
+
framework, version = file_name.split(/(\d+)/)
|
56
|
+
major, minor = version.split(//)
|
53
57
|
|
54
|
-
|
55
|
-
|
58
|
+
frameworks_versions[framework] ||= []
|
59
|
+
frameworks_versions[framework] << file_name
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
desc "Run Tests against mongoid (version 2)"
|
61
|
-
task(:mongoid2) { run_with_gemfile 'gemfiles/mongoid2.gemfile' }
|
62
|
-
|
63
|
-
desc "Run Tests against rails 5.1"
|
64
|
-
task(:rails51) { run_with_gemfile 'gemfiles/rails51.gemfile' }
|
65
|
-
|
66
|
-
desc "Run Tests against rails 5.0"
|
67
|
-
task(:rails50) { run_with_gemfile 'gemfiles/rails50.gemfile' }
|
68
|
-
|
69
|
-
desc "Run Tests against rails 4.2"
|
70
|
-
task(:rails42) { run_with_gemfile 'gemfiles/rails42.gemfile' }
|
71
|
-
|
72
|
-
desc "Run Tests against rails 4.1"
|
73
|
-
task(:rails41) { run_with_gemfile 'gemfiles/rails41.gemfile' }
|
74
|
-
|
75
|
-
desc "Run Tests against rails 4"
|
76
|
-
task(:rails4) { run_with_gemfile 'gemfiles/rails4.gemfile' }
|
77
|
-
|
78
|
-
desc "Run Tests against rails 3"
|
79
|
-
task(:rails3) { run_with_gemfile 'gemfiles/rails3.gemfile' }
|
80
|
-
|
81
|
-
desc "Run Tests against mongoid 2 & 3 & 4, 5"
|
82
|
-
task mongoid: [:mongoid2, :mongoid3, :mongoid4, :mongoid5]
|
83
|
-
|
84
|
-
desc "Run Tests against rails 3 & 4 & 4.1 & 4.2 & 5.0"
|
85
|
-
task rails: [:rails3, :rails4, :rails41, :rails42, :rails50, :rails51]
|
61
|
+
desc "Run Tests against #{framework} #{[major, minor].compact.join('.')}"
|
62
|
+
task(file_name) { run_with_gemfile gemfile }
|
63
|
+
end
|
86
64
|
|
87
|
-
|
88
|
-
|
65
|
+
frameworks_versions.each do |framework, versions|
|
66
|
+
desc "Run Tests against all supported #{framework} versions"
|
67
|
+
task framework => versions
|
68
|
+
end
|
89
69
|
|
70
|
+
desc 'Run Tests against all ORMs'
|
71
|
+
task all: frameworks_versions.keys
|
90
72
|
end
|
91
73
|
|
92
74
|
desc "Update CONTRIBUTORS file"
|
@@ -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
|
@@ -11,8 +11,8 @@ module MoneyRails
|
|
11
11
|
|
12
12
|
def remove_monetize(table_name, accessor, options={})
|
13
13
|
[:amount, :currency].each do |attribute|
|
14
|
-
column_present, table_name, column_name,
|
15
|
-
remove_column table_name, column_name if column_present
|
14
|
+
column_present, table_name, column_name, type, _ = OptionsExtractor.extract attribute, table_name, accessor, options
|
15
|
+
remove_column table_name, column_name, type if column_present
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -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
|