shopify-money 3.0.0 → 3.0.2
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/.github/workflows/tests.yml +3 -3
- data/.gitignore +2 -1
- data/.rubocop.yml +36 -0
- data/.ruby-version +1 -1
- data/Gemfile +4 -1
- data/Gemfile.lock +165 -124
- data/Rakefile +2 -2
- data/config/currency_historic.yml +44 -2
- data/config/currency_iso.yml +38 -64
- data/lib/money/allocator.rb +13 -9
- data/lib/money/config.rb +8 -0
- data/lib/money/core_extensions.rb +2 -1
- data/lib/money/currency/loader.rb +4 -3
- data/lib/money/currency.rb +12 -3
- data/lib/money/deprecations.rb +22 -4
- data/lib/money/errors.rb +1 -0
- data/lib/money/helpers.rb +2 -1
- data/lib/money/money.rb +55 -30
- data/lib/money/null_currency.rb +11 -3
- data/lib/money/parser/accounting.rb +1 -0
- data/lib/money/parser/fuzzy.rb +14 -13
- data/lib/money/parser/locale_aware.rb +3 -6
- data/lib/money/parser/simple.rb +2 -1
- data/lib/money/rails/job_argument_serializer.rb +0 -1
- data/lib/money/railtie.rb +5 -3
- data/lib/money/splitter.rb +20 -24
- data/lib/money/version.rb +2 -1
- data/lib/money.rb +1 -0
- data/lib/money_column/active_record_hooks.rb +9 -6
- data/lib/money_column/active_record_type.rb +7 -4
- data/lib/money_column/railtie.rb +2 -1
- data/lib/money_column.rb +1 -0
- data/lib/rubocop/cop/money/missing_currency.rb +16 -23
- data/lib/rubocop/cop/money/zero_money.rb +7 -13
- data/lib/shopify-money.rb +1 -0
- data/money.gemspec +6 -6
- data/spec/config_spec.rb +18 -0
- data/spec/money_spec.rb +38 -0
- data/spec/rubocop/cop/money/missing_currency_spec.rb +7 -7
- data/spec/rubocop/cop/money/zero_money_spec.rb +2 -2
- metadata +18 -42
data/lib/money/parser/fuzzy.rb
CHANGED
@@ -1,43 +1,44 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Money
|
3
4
|
module Parser
|
4
5
|
class Fuzzy
|
5
6
|
class MoneyFormatError < ArgumentError; end
|
6
7
|
|
7
|
-
MARKS =
|
8
|
+
MARKS = ['.', ',', '·', '’', '˙', "'", ' '].freeze
|
8
9
|
|
9
10
|
ESCAPED_MARKS = Regexp.escape(MARKS.join)
|
10
11
|
ESCAPED_NON_SPACE_MARKS = Regexp.escape((MARKS - [' ']).join)
|
11
12
|
ESCAPED_NON_DOT_MARKS = Regexp.escape((MARKS - ['.']).join)
|
12
13
|
ESCAPED_NON_COMMA_MARKS = Regexp.escape((MARKS - [',']).join)
|
13
14
|
|
14
|
-
NUMERIC_REGEX =
|
15
|
+
NUMERIC_REGEX = %r{(
|
15
16
|
[\+\-]?
|
16
17
|
[\d#{ESCAPED_NON_SPACE_MARKS}][\d#{ESCAPED_MARKS}]*
|
17
|
-
)
|
18
|
+
)}ix
|
18
19
|
|
19
20
|
# 1,234,567.89
|
20
|
-
DOT_DECIMAL_REGEX =
|
21
|
+
DOT_DECIMAL_REGEX = %r{\A
|
21
22
|
[\+\-]?
|
22
23
|
(?:
|
23
24
|
(?:\d+)
|
24
25
|
(?:[#{ESCAPED_NON_DOT_MARKS}]\d{3})+
|
25
26
|
(?:\.\d{2,})?
|
26
27
|
)
|
27
|
-
\z
|
28
|
+
\z}ix
|
28
29
|
|
29
30
|
# 1.234.567,89
|
30
|
-
COMMA_DECIMAL_REGEX =
|
31
|
+
COMMA_DECIMAL_REGEX = %r{\A
|
31
32
|
[\+\-]?
|
32
33
|
(?:
|
33
34
|
(?:\d+)
|
34
35
|
(?:[#{ESCAPED_NON_COMMA_MARKS}]\d{3})+
|
35
36
|
(?:\,\d{2,})?
|
36
37
|
)
|
37
|
-
\z
|
38
|
+
\z}ix
|
38
39
|
|
39
40
|
# 12,34,567.89
|
40
|
-
INDIAN_NUMERIC_REGEX =
|
41
|
+
INDIAN_NUMERIC_REGEX = %r{\A
|
41
42
|
[\+\-]?
|
42
43
|
(?:
|
43
44
|
(?:\d+)
|
@@ -45,17 +46,17 @@ class Money
|
|
45
46
|
(?:\,\d{3})
|
46
47
|
(?:\.\d{2})?
|
47
48
|
)
|
48
|
-
\z
|
49
|
+
\z}ix
|
49
50
|
|
50
51
|
# 1,1123,4567.89
|
51
|
-
CHINESE_NUMERIC_REGEX =
|
52
|
+
CHINESE_NUMERIC_REGEX = %r{\A
|
52
53
|
[\+\-]?
|
53
54
|
(?:
|
54
55
|
(?:\d+)
|
55
56
|
(?:\,\d{4})+
|
56
57
|
(?:\.\d{2})?
|
57
58
|
)
|
58
|
-
\z
|
59
|
+
\z}ix
|
59
60
|
|
60
61
|
def self.parse(input, currency = nil, **options)
|
61
62
|
new.parse(input, currency, **options)
|
@@ -111,11 +112,11 @@ class Money
|
|
111
112
|
# remove end of string mark
|
112
113
|
number.sub!(/[#{ESCAPED_MARKS}]\z/, '')
|
113
114
|
|
114
|
-
if amount = number[DOT_DECIMAL_REGEX] || number[INDIAN_NUMERIC_REGEX] || number[CHINESE_NUMERIC_REGEX]
|
115
|
+
if (amount = number[DOT_DECIMAL_REGEX] || number[INDIAN_NUMERIC_REGEX] || number[CHINESE_NUMERIC_REGEX])
|
115
116
|
return amount.tr(ESCAPED_NON_DOT_MARKS, '')
|
116
117
|
end
|
117
118
|
|
118
|
-
if amount = number[COMMA_DECIMAL_REGEX]
|
119
|
+
if (amount = number[COMMA_DECIMAL_REGEX])
|
119
120
|
return amount.tr(ESCAPED_NON_COMMA_MARKS, '').sub(',', '.')
|
120
121
|
end
|
121
122
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Money
|
3
4
|
module Parser
|
4
5
|
class LocaleAware
|
@@ -7,18 +8,14 @@ class Money
|
|
7
8
|
class << self
|
8
9
|
# The +Proc+ called to get the current locale decimal separator. In Rails apps this defaults to the same lookup
|
9
10
|
# ActionView's +number_to_currency+ helper will use to format the monetary amount for display.
|
10
|
-
|
11
|
-
@decimal_separator_resolver
|
12
|
-
end
|
11
|
+
attr_reader :decimal_separator_resolver
|
13
12
|
|
14
13
|
# Set the default +Proc+ to determine the current locale decimal separator.
|
15
14
|
#
|
16
15
|
# @example
|
17
16
|
# Money::Parser::LocaleAware.decimal_separator_resolver =
|
18
17
|
# ->() { MyFormattingLibrary.current_locale.decimal.separator }
|
19
|
-
|
20
|
-
@decimal_separator_resolver = proc
|
21
|
-
end
|
18
|
+
attr_writer :decimal_separator_resolver
|
22
19
|
|
23
20
|
# Parses an input string, normalizing some non-ASCII characters to their equivalent ASCII, then discarding any
|
24
21
|
# character that is not a digit, hyphen-minus or the decimal separator. To prevent user confusion, make sure
|
data/lib/money/parser/simple.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
class Money
|
3
4
|
module Parser
|
4
5
|
class Simple
|
5
|
-
SIGNED_DECIMAL_MATCHER = /\A-?\d*(?:\.\d*)?\z
|
6
|
+
SIGNED_DECIMAL_MATCHER = /\A-?\d*(?:\.\d*)?\z/
|
6
7
|
|
7
8
|
class << self
|
8
9
|
# Parses an input string using BigDecimal, it always expects a dot character as a decimal separator and
|
data/lib/money/railtie.rb
CHANGED
@@ -3,9 +3,11 @@
|
|
3
3
|
class Money
|
4
4
|
class Railtie < Rails::Railtie
|
5
5
|
initializer "shopify-money.setup_active_job_serializer" do
|
6
|
-
ActiveSupport.on_load
|
7
|
-
|
8
|
-
|
6
|
+
ActiveSupport.on_load(:active_job) do
|
7
|
+
if defined?(ActiveJob::Serializers)
|
8
|
+
require_relative "rails/job_argument_serializer"
|
9
|
+
ActiveJob::Serializers.add_serializers(::Money::Rails::JobArgumentSerializer)
|
10
|
+
end
|
9
11
|
end
|
10
12
|
end
|
11
13
|
|
data/lib/money/splitter.rb
CHANGED
@@ -11,7 +11,7 @@ class Money
|
|
11
11
|
@split = nil
|
12
12
|
end
|
13
13
|
|
14
|
-
protected attr_writer
|
14
|
+
protected attr_writer(:split)
|
15
15
|
|
16
16
|
def split
|
17
17
|
@split ||= begin
|
@@ -35,19 +35,17 @@ class Money
|
|
35
35
|
each do |money|
|
36
36
|
return money
|
37
37
|
end
|
38
|
+
elsif count >= size
|
39
|
+
to_a
|
38
40
|
else
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
result =
|
43
|
-
index
|
44
|
-
|
45
|
-
result[index] = money
|
46
|
-
index += 1
|
47
|
-
break if index == count
|
48
|
-
end
|
49
|
-
result
|
41
|
+
result = Array.new(count)
|
42
|
+
index = 0
|
43
|
+
each do |money|
|
44
|
+
result[index] = money
|
45
|
+
index += 1
|
46
|
+
break if index == count
|
50
47
|
end
|
48
|
+
result
|
51
49
|
end
|
52
50
|
end
|
53
51
|
|
@@ -56,20 +54,18 @@ class Money
|
|
56
54
|
reverse_each do |money|
|
57
55
|
return money
|
58
56
|
end
|
57
|
+
elsif count >= size
|
58
|
+
to_a
|
59
59
|
else
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
result =
|
64
|
-
index
|
65
|
-
|
66
|
-
result[index] = money
|
67
|
-
index += 1
|
68
|
-
break if index == count
|
69
|
-
end
|
70
|
-
result.reverse!
|
71
|
-
result
|
60
|
+
result = Array.new(count)
|
61
|
+
index = 0
|
62
|
+
reverse_each do |money|
|
63
|
+
result[index] = money
|
64
|
+
index += 1
|
65
|
+
break if index == count
|
72
66
|
end
|
67
|
+
result.reverse!
|
68
|
+
result
|
73
69
|
end
|
74
70
|
end
|
75
71
|
|
data/lib/money/version.rb
CHANGED
data/lib/money.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module MoneyColumn
|
3
4
|
class CurrencyReadOnlyError < StandardError; end
|
4
5
|
|
@@ -82,7 +83,7 @@ module MoneyColumn
|
|
82
83
|
currency_column: currency_column,
|
83
84
|
currency: currency,
|
84
85
|
currency_read_only: currency_read_only,
|
85
|
-
coerce_null: coerce_null
|
86
|
+
coerce_null: coerce_null,
|
86
87
|
)
|
87
88
|
|
88
89
|
if options[:currency_column]
|
@@ -96,11 +97,11 @@ module MoneyColumn
|
|
96
97
|
|
97
98
|
attribute(column_string, MoneyColumn::ActiveRecordType.new)
|
98
99
|
|
99
|
-
define_method
|
100
|
+
define_method(column) do
|
100
101
|
read_money_attribute(column_string)
|
101
102
|
end
|
102
103
|
|
103
|
-
define_method
|
104
|
+
define_method("#{column}=") do |money|
|
104
105
|
write_money_attribute(column_string, money)
|
105
106
|
end
|
106
107
|
end
|
@@ -109,8 +110,10 @@ module MoneyColumn
|
|
109
110
|
private
|
110
111
|
|
111
112
|
def normalize_money_column_options(options)
|
112
|
-
raise ArgumentError,
|
113
|
-
|
113
|
+
raise ArgumentError,
|
114
|
+
'cannot set both :currency_column and :currency options' if options[:currency] && options[:currency_column]
|
115
|
+
raise ArgumentError,
|
116
|
+
'must set one of :currency_column or :currency options' unless options[:currency] || options[:currency_column]
|
114
117
|
|
115
118
|
if options[:currency]
|
116
119
|
options[:currency] = Money::Currency.find!(options[:currency]).to_s.freeze
|
@@ -126,7 +129,7 @@ module MoneyColumn
|
|
126
129
|
def clear_cache_on_currency_change(currency_column)
|
127
130
|
return if money_column_options.any? { |_, opt| opt[:currency_column] == currency_column }
|
128
131
|
|
129
|
-
define_method
|
132
|
+
define_method("#{currency_column}=") do |value|
|
130
133
|
clear_money_column_cache
|
131
134
|
super(value)
|
132
135
|
end
|
@@ -1,7 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
|
3
|
+
module MoneyColumn
|
4
|
+
class ActiveRecordType < ActiveRecord::Type::Decimal
|
5
|
+
def serialize(money)
|
6
|
+
return unless money
|
7
|
+
super(money.to_d)
|
8
|
+
end
|
6
9
|
end
|
7
10
|
end
|
data/lib/money_column/railtie.rb
CHANGED
data/lib/money_column.rb
CHANGED
@@ -3,7 +3,8 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Money
|
6
|
-
class MissingCurrency <
|
6
|
+
class MissingCurrency < Base
|
7
|
+
extend RuboCop::Cop::AutoCorrector
|
7
8
|
# `Money.new()` without a currency argument cannot guarantee correctness:
|
8
9
|
# - no error raised for cross-currency computation (e.g. 5 CAD + 5 USD)
|
9
10
|
# - #subunits returns wrong values for 0 and 3 decimals currencies
|
@@ -32,48 +33,40 @@ module RuboCop
|
|
32
33
|
PATTERN
|
33
34
|
|
34
35
|
def on_send(node)
|
36
|
+
receiver, method, _ = *node
|
37
|
+
|
35
38
|
money_new(node) do |amount, currency_arg|
|
36
39
|
return if amount&.splat_type?
|
37
40
|
return if currency_arg
|
38
41
|
|
39
|
-
add_offense(node, message: 'Money is missing currency argument')
|
40
|
-
end
|
41
|
-
|
42
|
-
if to_money_block?(node) || to_money_without_currency?(node)
|
43
|
-
add_offense(node, message: 'to_money is missing currency argument')
|
44
|
-
end
|
45
|
-
end
|
46
|
-
alias on_csend on_send
|
47
|
-
|
48
|
-
def autocorrect(node)
|
49
|
-
receiver, method, _ = *node
|
50
|
-
|
51
|
-
lambda do |corrector|
|
52
|
-
money_new(node) do |amount, currency_arg|
|
53
|
-
return if currency_arg
|
54
|
-
|
42
|
+
add_offense(node, message: 'Money is missing currency argument') do |corrector|
|
55
43
|
corrector.replace(
|
56
44
|
node.loc.expression,
|
57
|
-
"#{receiver.source}.#{method}(#{amount&.source || 0}, #{replacement_currency})"
|
45
|
+
"#{receiver.source}.#{method}(#{amount&.source || 0}, #{replacement_currency})",
|
58
46
|
)
|
59
47
|
end
|
48
|
+
end
|
60
49
|
|
61
|
-
|
62
|
-
|
63
|
-
elsif to_money_block?(node)
|
50
|
+
if to_money_block?(node)
|
51
|
+
add_offense(node, message: 'to_money is missing currency argument') do |corrector|
|
64
52
|
corrector.replace(
|
65
53
|
node.loc.expression,
|
66
|
-
"#{receiver.source}.#{method} { |x| x.to_money(#{replacement_currency}) }"
|
54
|
+
"#{receiver.source}.#{method} { |x| x.to_money(#{replacement_currency}) }",
|
67
55
|
)
|
68
56
|
end
|
57
|
+
elsif to_money_without_currency?(node)
|
58
|
+
add_offense(node, message: 'to_money is missing currency argument') do |corrector|
|
59
|
+
corrector.insert_after(node.loc.expression, "(#{replacement_currency})")
|
60
|
+
end
|
69
61
|
end
|
70
62
|
end
|
63
|
+
alias_method :on_csend, :on_send
|
71
64
|
|
72
65
|
private
|
73
66
|
|
74
67
|
def replacement_currency
|
75
68
|
if cop_config['ReplacementCurrency']
|
76
|
-
"'#{cop_config[
|
69
|
+
"'#{cop_config["ReplacementCurrency"]}'"
|
77
70
|
else
|
78
71
|
'Money::NULL_CURRENCY'
|
79
72
|
end
|
@@ -3,7 +3,9 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Money
|
6
|
-
class ZeroMoney <
|
6
|
+
class ZeroMoney < Base
|
7
|
+
extend RuboCop::Cop::AutoCorrector
|
8
|
+
|
7
9
|
# `Money.zero` and it's alias `empty`, with or without currency
|
8
10
|
# argument is removed in favour of the more explicit Money.new
|
9
11
|
# syntax. Supplying it with a real currency is preferred for
|
@@ -31,21 +33,13 @@ module RuboCop
|
|
31
33
|
PATTERN
|
32
34
|
|
33
35
|
def on_send(node)
|
34
|
-
money_zero(node) do |currency_arg|
|
35
|
-
add_offense(node, message: format(MSG, currency: replacement_currency(currency_arg)))
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
def autocorrect(node)
|
40
36
|
receiver, _ = *node
|
41
37
|
|
42
|
-
|
43
|
-
|
44
|
-
replacement_currency = replacement_currency(currency_arg)
|
45
|
-
|
38
|
+
money_zero(node) do |currency_arg|
|
39
|
+
add_offense(node, message: format(MSG, currency: replacement_currency(currency_arg))) do |corrector|
|
46
40
|
corrector.replace(
|
47
41
|
node.loc.expression,
|
48
|
-
"#{receiver.source}.new(0, #{replacement_currency})"
|
42
|
+
"#{receiver.source}.new(0, #{replacement_currency(currency_arg)})",
|
49
43
|
)
|
50
44
|
end
|
51
45
|
end
|
@@ -55,7 +49,7 @@ module RuboCop
|
|
55
49
|
|
56
50
|
def replacement_currency(currency_arg)
|
57
51
|
return currency_arg.first.source unless currency_arg.empty?
|
58
|
-
return "'#{cop_config[
|
52
|
+
return "'#{cop_config["ReplacementCurrency"]}'" if cop_config['ReplacementCurrency']
|
59
53
|
|
60
54
|
'Money::NULL_CURRENCY'
|
61
55
|
end
|
data/lib/shopify-money.rb
CHANGED
data/money.gemspec
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
2
|
# frozen_string_literal: true
|
3
|
+
|
3
4
|
require_relative "lib/money/version"
|
4
5
|
|
5
6
|
Gem::Specification.new do |s|
|
@@ -16,16 +17,15 @@ Gem::Specification.new do |s|
|
|
16
17
|
s.metadata['allowed_push_host'] = "https://rubygems.org"
|
17
18
|
|
18
19
|
s.add_development_dependency("bundler")
|
19
|
-
s.add_development_dependency("simplecov", ">= 0")
|
20
|
-
s.add_development_dependency("rails", "~> 6.0")
|
21
|
-
s.add_development_dependency("rspec", "~> 3.2")
|
22
20
|
s.add_development_dependency("database_cleaner", "~> 2.0")
|
21
|
+
s.add_development_dependency("rails", "~> 7.2")
|
22
|
+
s.add_development_dependency("rspec", "~> 3.2")
|
23
|
+
s.add_development_dependency("simplecov", ">= 0")
|
23
24
|
s.add_development_dependency("sqlite3")
|
24
25
|
|
25
|
-
s.required_ruby_version = '>= 3.
|
26
|
+
s.required_ruby_version = '>= 3.1'
|
26
27
|
|
27
|
-
s.files =
|
28
|
+
s.files = %x(git ls-files).split($/)
|
28
29
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
29
|
-
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
30
30
|
s.require_paths = ["lib"]
|
31
31
|
end
|
data/spec/config_spec.rb
CHANGED
@@ -2,6 +2,24 @@
|
|
2
2
|
require 'spec_helper'
|
3
3
|
|
4
4
|
RSpec.describe "Money::Config" do
|
5
|
+
describe 'thread safety' do
|
6
|
+
it 'does not share the same config across threads' do
|
7
|
+
configure(legacy_deprecations: false, default_currency: 'USD') do
|
8
|
+
expect(Money.config.legacy_deprecations).to eq(false)
|
9
|
+
expect(Money.config.default_currency).to eq('USD')
|
10
|
+
thread = Thread.new do
|
11
|
+
Money.config.legacy_deprecations!
|
12
|
+
Money.default_currency = "EUR"
|
13
|
+
expect(Money.config.legacy_deprecations).to eq(true)
|
14
|
+
expect(Money.config.default_currency).to eq("EUR")
|
15
|
+
end
|
16
|
+
thread.join
|
17
|
+
expect(Money.config.legacy_deprecations).to eq(false)
|
18
|
+
expect(Money.config.default_currency).to eq('USD')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
5
23
|
describe 'legacy_deprecations' do
|
6
24
|
it "respects the default currency" do
|
7
25
|
configure(default_currency: 'USD', legacy_deprecations: true) do
|
data/spec/money_spec.rb
CHANGED
@@ -62,6 +62,14 @@ RSpec.describe "Money" do
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
+
it "#without_legacy_deprecations bypasses the legacy deprecations and gives the real behavior" do
|
66
|
+
configure(legacy_deprecations: true) do
|
67
|
+
Money.without_legacy_deprecations do
|
68
|
+
expect{ Money.new(1, 'USD').to_money('CAD') }.to raise_error(Money::IncompatibleCurrencyError)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
65
73
|
it "#to_money raises when changing currency" do
|
66
74
|
expect{ Money.new(1, 'USD').to_money('CAD') }.to raise_error(Money::IncompatibleCurrencyError)
|
67
75
|
end
|
@@ -385,6 +393,36 @@ RSpec.describe "Money" do
|
|
385
393
|
expect(JSON.dump(Money.new(1.00, "CAD"))).to eq('{"value":"1.00","currency":"CAD"}')
|
386
394
|
end
|
387
395
|
|
396
|
+
describe ".from_hash" do
|
397
|
+
it "is the inverse operation of #to_h" do
|
398
|
+
one_cad = Money.new(1, "CAD")
|
399
|
+
expect(Money.from_hash(one_cad.to_h)).to eq(one_cad)
|
400
|
+
end
|
401
|
+
|
402
|
+
it "creates Money object from hash with expected keys" do
|
403
|
+
expect(Money.from_hash({ value: 1.01, currency: "CAD" })).to eq(Money.new(1.01, "CAD"))
|
404
|
+
end
|
405
|
+
|
406
|
+
it "raises if Hash does not have the expected keys" do
|
407
|
+
expect { Money.from_hash({ "val": 1.0 }) }.to raise_error(KeyError)
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
describe ".from_json" do
|
412
|
+
it "is the inverse operation of #to_json" do
|
413
|
+
one_cad = Money.new(1, "CAD")
|
414
|
+
expect(Money.from_json(one_cad.to_json)).to eq(one_cad)
|
415
|
+
end
|
416
|
+
|
417
|
+
it "creates Money object from JSON-encoded string" do
|
418
|
+
expect(Money.from_json('{ "value": 1.01, "currency": "CAD" }')).to eq(Money.new(1.01, "CAD"))
|
419
|
+
end
|
420
|
+
|
421
|
+
it "raises if JSON string is malformed" do
|
422
|
+
expect { Money.from_json('{ "val": 1.0 }') }.to raise_error(KeyError)
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
388
426
|
it "supports absolute value" do
|
389
427
|
expect(Money.new(-1.00).abs).to eq(Money.new(1.00))
|
390
428
|
end
|
@@ -12,7 +12,7 @@ RSpec.describe RuboCop::Cop::Money::MissingCurrency do
|
|
12
12
|
it 'registers an offense and corrects for Money.new without a currency argument' do
|
13
13
|
expect_offense(<<~RUBY)
|
14
14
|
Money.new(1)
|
15
|
-
^^^^^^^^^^^^ Money is missing currency argument
|
15
|
+
^^^^^^^^^^^^ Money/MissingCurrency: Money is missing currency argument
|
16
16
|
RUBY
|
17
17
|
|
18
18
|
expect_correction(<<~RUBY)
|
@@ -36,7 +36,7 @@ RSpec.describe RuboCop::Cop::Money::MissingCurrency do
|
|
36
36
|
it 'registers an offense and corrects for Money.new without a currency argument' do
|
37
37
|
expect_offense(<<~RUBY)
|
38
38
|
Money.new
|
39
|
-
^^^^^^^^^ Money is missing currency argument
|
39
|
+
^^^^^^^^^ Money/MissingCurrency: Money is missing currency argument
|
40
40
|
RUBY
|
41
41
|
|
42
42
|
expect_correction(<<~RUBY)
|
@@ -47,7 +47,7 @@ RSpec.describe RuboCop::Cop::Money::MissingCurrency do
|
|
47
47
|
it 'registers an offense and corrects for Money.from_amount without a currency argument' do
|
48
48
|
expect_offense(<<~RUBY)
|
49
49
|
Money.from_amount(1)
|
50
|
-
^^^^^^^^^^^^^^^^^^^^ Money is missing currency argument
|
50
|
+
^^^^^^^^^^^^^^^^^^^^ Money/MissingCurrency: Money is missing currency argument
|
51
51
|
RUBY
|
52
52
|
|
53
53
|
expect_correction(<<~RUBY)
|
@@ -64,7 +64,7 @@ RSpec.describe RuboCop::Cop::Money::MissingCurrency do
|
|
64
64
|
it 'registers an offense and corrects for Money.from_cents without a currency argument' do
|
65
65
|
expect_offense(<<~RUBY)
|
66
66
|
Money.from_cents(1)
|
67
|
-
^^^^^^^^^^^^^^^^^^^ Money is missing currency argument
|
67
|
+
^^^^^^^^^^^^^^^^^^^ Money/MissingCurrency: Money is missing currency argument
|
68
68
|
RUBY
|
69
69
|
|
70
70
|
expect_correction(<<~RUBY)
|
@@ -81,7 +81,7 @@ RSpec.describe RuboCop::Cop::Money::MissingCurrency do
|
|
81
81
|
it 'registers an offense and corrects for to_money without a currency argument' do
|
82
82
|
expect_offense(<<~RUBY)
|
83
83
|
'1'.to_money
|
84
|
-
^^^^^^^^^^^^ to_money is missing currency argument
|
84
|
+
^^^^^^^^^^^^ Money/MissingCurrency: to_money is missing currency argument
|
85
85
|
RUBY
|
86
86
|
|
87
87
|
expect_correction(<<~RUBY)
|
@@ -92,7 +92,7 @@ RSpec.describe RuboCop::Cop::Money::MissingCurrency do
|
|
92
92
|
it 'registers an offense and corrects for safe navigation to_money without a currency argument' do
|
93
93
|
expect_offense(<<~RUBY)
|
94
94
|
item&.to_money
|
95
|
-
^^^^^^^^^^^^^^ to_money is missing currency argument
|
95
|
+
^^^^^^^^^^^^^^ Money/MissingCurrency: to_money is missing currency argument
|
96
96
|
RUBY
|
97
97
|
|
98
98
|
expect_correction(<<~RUBY)
|
@@ -109,7 +109,7 @@ RSpec.describe RuboCop::Cop::Money::MissingCurrency do
|
|
109
109
|
it 'registers an offense and corrects for to_money block pass form' do
|
110
110
|
expect_offense(<<~RUBY)
|
111
111
|
['1'].map(&:to_money)
|
112
|
-
^^^^^^^^^^^^^^^^^^^^^ to_money is missing currency argument
|
112
|
+
^^^^^^^^^^^^^^^^^^^^^ Money/MissingCurrency: to_money is missing currency argument
|
113
113
|
RUBY
|
114
114
|
|
115
115
|
expect_correction(<<~RUBY)
|
@@ -12,7 +12,7 @@ RSpec.describe RuboCop::Cop::Money::ZeroMoney do
|
|
12
12
|
it 'registers an offense and corrects Money.zero without currency' do
|
13
13
|
expect_offense(<<~RUBY)
|
14
14
|
Money.zero
|
15
|
-
^^^^^^^^^^ Money.zero is removed, use `Money.new(0, Money::NULL_CURRENCY)`.
|
15
|
+
^^^^^^^^^^ Money/ZeroMoney: Money.zero is removed, use `Money.new(0, Money::NULL_CURRENCY)`.
|
16
16
|
RUBY
|
17
17
|
|
18
18
|
expect_correction(<<~RUBY)
|
@@ -23,7 +23,7 @@ RSpec.describe RuboCop::Cop::Money::ZeroMoney do
|
|
23
23
|
it 'registers an offense and corrects Money.zero with currency' do
|
24
24
|
expect_offense(<<~RUBY)
|
25
25
|
Money.zero('CAD')
|
26
|
-
^^^^^^^^^^^^^^^^^ Money.zero is removed, use `Money.new(0, 'CAD')`.
|
26
|
+
^^^^^^^^^^^^^^^^^ Money/ZeroMoney: Money.zero is removed, use `Money.new(0, 'CAD')`.
|
27
27
|
RUBY
|
28
28
|
|
29
29
|
expect_correction(<<~RUBY)
|