money-rails 1.12.0 → 1.13.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 06fe320d8e4a586f4c77666ad7b84724dda8131d
4
- data.tar.gz: 78fa897739471e33e09f80a25ae127284434041f
2
+ SHA256:
3
+ metadata.gz: 618663a78af12610ff9901dd5ed3b3e5e5bf74d6d24b65141a52b2811179a0d8
4
+ data.tar.gz: 0724c64933122f73af34f1ad606c309e40c224a8b8636053ca437145527a8490
5
5
  SHA512:
6
- metadata.gz: 770d0661ee7e420da951020b5b63ad584d3e6d211e129855d0994f95f8a4a45f31df1ca0f4724855f757d5ab8afc417ab354f2bf8c4e3e8f8c7c065e6deebb6f
7
- data.tar.gz: 2d78aec95f0f07fbf2bfbf7d12d08ff8f7d938fb4337536de8377dba0305d7c53315d9053fb1eca66940fceec3a82a5a708edde784d1ce6cf5563e5cf3114dde
6
+ metadata.gz: d0b87a85f4fe133e3fc75d75722c42dbb445c05cc98ebe7d242d8b63a27d8b879d324d37a8ca9b1ee24fddc8c0381152cd04e449dc8f8c041ec1598371760551
7
+ data.tar.gz: c984f12a0162f918b6d5c67b009da28d37a96a63ee0db664a8778e03503d60064ad9e8227d2e1d4fd37f61098f235445f0b959b7612aa503a1b90416f70b69c0
@@ -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 an use it inside ```change```
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.currency_as_string # => EUR ```
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, 3.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
- desc "Run Tests against mongoid (version 5)"
52
- task(:mongoid5) { run_with_gemfile 'gemfiles/mongoid5.gemfile' }
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
- desc "Run Tests against mongoid (version 4)"
55
- task(:mongoid4) { run_with_gemfile 'gemfiles/mongoid4.gemfile' }
58
+ frameworks_versions[framework] ||= []
59
+ frameworks_versions[framework] << file_name
56
60
 
57
- desc "Run Tests against mongoid (version 3)"
58
- task(:mongoid3) { run_with_gemfile 'gemfiles/mongoid3.gemfile' }
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
- desc "Run Tests against all ORMs"
88
- task all: [:rails, :mongoid]
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"
@@ -0,0 +1,4 @@
1
+ da:
2
+ errors:
3
+ messages:
4
+ invalid_currency: skal være en gyldig valuta (f.eks. '100', '5%{decimal}24', eller '123%{thousands}456%{decimal}78'). Fik %{currency}
@@ -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
- def validate_each(record, attr, _value)
5
- reset_memoized_variables!
6
- @record = record
7
- @attr = attr
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
- subunit_attr = @record.class.monetized_attributes[@attr.to_s]
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 = :"#{@attr}_money_before_type_cast"
13
- @raw_value = @record.try(before_type_cast)
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 @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
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] && @raw_value.nil?
30
+ return if options[:allow_nil] && raw_value.nil?
24
31
 
25
- # Set this before we modify @raw_value below.
26
- stringy = @raw_value.present? && !@raw_value.is_a?(Numeric) && !@raw_value.is_a?(Money)
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
- @raw_value = @raw_value.to_s.gsub(symbol, "")
41
+ raw_value = raw_value.to_s.gsub(symbol, "")
31
42
  end
32
43
 
33
- normalize_raw_value!
34
- super(@record, @attr, @raw_value)
35
-
36
- if stringy && record_does_not_have_error?
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
- private
49
+ super(record, attr, normalized_raw_value)
45
50
 
46
- def record_does_not_have_error?
47
- return true unless @record.errors.has_key?(@attr)
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
- def reset_memoized_variables!
52
- [:currency, :decimal_mark, :thousands_separator, :symbol,
53
- :abs_raw_value, :decimal_pieces, :pieces_array].each do |var_name|
54
- ivar_name = :"@_#{var_name}"
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
- def currency
60
- @_currency ||= @record.public_send("currency_for_#{@attr}")
61
- end
60
+ private
62
61
 
63
- def decimal_mark
64
- character = currency.decimal_mark || '.'
65
- @_decimal_mark ||= Money.use_i18n ? I18n.t('number.currency.format.separator', default: character) : character
66
- end
62
+ DEFAULTS = {
63
+ decimal_mark: '.',
64
+ thousands_separator: ','
65
+ }.freeze
67
66
 
68
- def thousands_separator
69
- character = currency.thousands_separator || ','
70
- @_thousands_separator ||= Money.use_i18n ? I18n.t('number.currency.format.delimiter', default: character) : character
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
- def symbol
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 abs_raw_value
78
- @_abs_raw_value ||= @raw_value.to_s.sub(/^\s*-/, "").strip
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 = @attr.to_s.tr('.', '_').humanize
83
- attr_name = @record.class.human_attribute_name(@attr, default: 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
- @record.errors.add(@attr, :invalid_currency,
86
- { thousands: thousands_separator,
87
- decimal: decimal_mark,
88
- currency: abs_raw_value,
89
- attribute: attr_name })
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 decimal_pieces
93
- @_decimal_pieces ||= abs_raw_value.split(decimal_mark)
90
+ def value_has_too_many_decimal_points(details)
91
+ ![1, 2].include?(details.decimal_pieces.length)
94
92
  end
95
93
 
96
- def value_has_too_many_decimal_points
97
- ![1, 2].include?(decimal_pieces.length)
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 pieces_array
101
- @_pieces_array ||= decimal_pieces[0].split(thousands_separator.presence)
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 normalize_raw_value!
119
- # Cache abs_raw_value before normalizing because it's used in
120
- # many places and relies on the original @raw_value.
121
- abs_raw_value
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, _, _ = OptionsExtractor.extract attribute, table_name, accessor, options
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[name.to_sym].present?
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