shopify-money 0.12.0 → 0.14.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/.travis.yml +2 -3
- data/Gemfile +2 -1
- data/README.md +20 -1
- data/Rakefile +1 -0
- data/bin/console +1 -0
- data/config/currency_iso.json +1 -1
- data/dev.yml +1 -1
- data/lib/money.rb +4 -0
- data/lib/money/allocator.rb +144 -0
- data/lib/money/core_extensions.rb +1 -0
- data/lib/money/currency.rb +1 -0
- data/lib/money/currency/loader.rb +1 -0
- data/lib/money/deprecations.rb +1 -0
- data/lib/money/errors.rb +1 -0
- data/lib/money/helpers.rb +5 -15
- data/lib/money/money.rb +8 -111
- data/lib/money/null_currency.rb +1 -0
- data/lib/money/version.rb +2 -1
- data/lib/money_accessor.rb +1 -0
- data/lib/money_column.rb +1 -0
- data/lib/money_column/active_record_hooks.rb +2 -1
- data/lib/money_column/active_record_type.rb +1 -0
- data/lib/money_column/railtie.rb +1 -0
- data/lib/rubocop/cop/money.rb +3 -0
- data/lib/rubocop/cop/money/missing_currency.rb +75 -0
- data/lib/shopify-money.rb +2 -0
- data/money.gemspec +7 -3
- data/spec/accounting_money_parser_spec.rb +1 -0
- data/spec/allocator_spec.rb +148 -0
- data/spec/core_extensions_spec.rb +2 -1
- data/spec/currency/loader_spec.rb +1 -0
- data/spec/currency_spec.rb +1 -0
- data/spec/helpers_spec.rb +1 -6
- data/spec/money_accessor_spec.rb +1 -0
- data/spec/money_column_spec.rb +1 -0
- data/spec/money_parser_spec.rb +1 -0
- data/spec/money_spec.rb +13 -96
- data/spec/null_currency_spec.rb +1 -0
- data/spec/rubocop/cop/money/missing_currency_spec.rb +108 -0
- data/spec/rubocop_helper.rb +10 -0
- data/spec/schema.rb +1 -0
- data/spec/spec_helper.rb +1 -5
- metadata +20 -24
data/lib/money/null_currency.rb
CHANGED
data/lib/money/version.rb
CHANGED
data/lib/money_accessor.rb
CHANGED
data/lib/money_column.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
module MoneyColumn
|
2
3
|
module ActiveRecordHooks
|
3
4
|
def self.included(base)
|
@@ -57,7 +58,7 @@ module MoneyColumn
|
|
57
58
|
if options[:currency_read_only]
|
58
59
|
currency_source = Money::Helpers.value_to_currency(currency_raw_source)
|
59
60
|
if currency_raw_source && !money.currency.compatible?(currency_source)
|
60
|
-
Money.deprecate("[money_column] currency mismatch between #{currency_source} and #{money.currency}.")
|
61
|
+
Money.deprecate("[money_column] currency mismatch between #{currency_source} and #{money.currency} in column #{column}.")
|
61
62
|
end
|
62
63
|
else
|
63
64
|
self[options[:currency_column]] = money.currency.to_s unless money.no_currency?
|
data/lib/money_column/railtie.rb
CHANGED
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Money
|
6
|
+
class MissingCurrency < Cop
|
7
|
+
# `Money.new()` without a currency argument cannot guarantee correctness:
|
8
|
+
# - no error raised for cross-currency computation (e.g. 5 CAD + 5 USD)
|
9
|
+
# - #subunits returns wrong values for 0 and 3 decimals currencies
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# # bad
|
13
|
+
# Money.new(123.45)
|
14
|
+
# Money.new
|
15
|
+
# "1,234.50".to_money
|
16
|
+
#
|
17
|
+
# # good
|
18
|
+
# Money.new(123.45, 'CAD')
|
19
|
+
# "1,234.50".to_money('CAD')
|
20
|
+
#
|
21
|
+
|
22
|
+
def_node_matcher :money_new, <<~PATTERN
|
23
|
+
(send (const nil? :Money) {:new :from_amount :from_cents} $...)
|
24
|
+
PATTERN
|
25
|
+
|
26
|
+
def_node_matcher :to_money_without_currency?, <<~PATTERN
|
27
|
+
(send _ :to_money)
|
28
|
+
PATTERN
|
29
|
+
|
30
|
+
def_node_matcher :to_money_block?, <<~PATTERN
|
31
|
+
(send _ _ (block_pass (sym :to_money)))
|
32
|
+
PATTERN
|
33
|
+
|
34
|
+
def on_send(node)
|
35
|
+
money_new(node) do |_amount, currency_arg|
|
36
|
+
return if currency_arg
|
37
|
+
|
38
|
+
add_offense(node, message: 'Money is missing currency argument')
|
39
|
+
end
|
40
|
+
|
41
|
+
if to_money_block?(node) || to_money_without_currency?(node)
|
42
|
+
add_offense(node, message: 'to_money is missing currency argument')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def autocorrect(node)
|
47
|
+
currency = cop_config['ReplacementCurrency']
|
48
|
+
return unless currency
|
49
|
+
|
50
|
+
receiver, method, _ = *node
|
51
|
+
|
52
|
+
lambda do |corrector|
|
53
|
+
money_new(node) do |amount, currency_arg|
|
54
|
+
return if currency_arg
|
55
|
+
|
56
|
+
corrector.replace(
|
57
|
+
node.loc.expression,
|
58
|
+
"#{receiver.source}.#{method}(#{amount&.source || 0}, '#{currency}')"
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
if to_money_without_currency?(node)
|
63
|
+
corrector.insert_after(node.loc.expression, "('#{currency}')")
|
64
|
+
elsif to_money_block?(node)
|
65
|
+
corrector.replace(
|
66
|
+
node.loc.expression,
|
67
|
+
"#{receiver.source}.#{method} { |x| x.to_money('#{currency}') }"
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/money.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
require_relative "lib/money/version"
|
3
4
|
|
4
5
|
Gem::Specification.new do |s|
|
@@ -12,13 +13,16 @@ Gem::Specification.new do |s|
|
|
12
13
|
s.licenses = "MIT"
|
13
14
|
s.summary = "Shopify's money gem"
|
14
15
|
|
16
|
+
s.metadata['allowed_push_host'] = "https://rubygems.org"
|
17
|
+
|
15
18
|
s.add_development_dependency("bundler", ">= 1.5")
|
16
19
|
s.add_development_dependency("simplecov", ">= 0")
|
17
|
-
s.add_development_dependency("rails", "~>
|
20
|
+
s.add_development_dependency("rails", "~> 6.0")
|
18
21
|
s.add_development_dependency("rspec", "~> 3.2")
|
19
22
|
s.add_development_dependency("database_cleaner", "~> 1.6")
|
20
|
-
s.add_development_dependency("sqlite3", "~> 1.
|
21
|
-
|
23
|
+
s.add_development_dependency("sqlite3", "~> 1.4.2")
|
24
|
+
|
25
|
+
s.required_ruby_version = '>= 2.6'
|
22
26
|
|
23
27
|
s.files = `git ls-files`.split($/)
|
24
28
|
s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
RSpec.describe "Allocator" do
|
5
|
+
describe "allocate"do
|
6
|
+
specify "#allocate takes no action when one gets all" do
|
7
|
+
expect(new_allocator(5).allocate([1])).to eq([Money.new(5)])
|
8
|
+
end
|
9
|
+
|
10
|
+
specify "#allocate does not lose pennies" do
|
11
|
+
moneys = new_allocator(0.05).allocate([0.3,0.7])
|
12
|
+
expect(moneys[0]).to eq(Money.new(0.02))
|
13
|
+
expect(moneys[1]).to eq(Money.new(0.03))
|
14
|
+
end
|
15
|
+
|
16
|
+
specify "#allocate does not lose dollars with non-decimal currency" do
|
17
|
+
moneys = new_allocator(5, 'JPY').allocate([0.3,0.7])
|
18
|
+
expect(moneys[0]).to eq(Money.new(2, 'JPY'))
|
19
|
+
expect(moneys[1]).to eq(Money.new(3, 'JPY'))
|
20
|
+
end
|
21
|
+
|
22
|
+
specify "#allocate does not lose dollars with three decimal currency" do
|
23
|
+
moneys = new_allocator(0.005, 'JOD').allocate([0.3,0.7])
|
24
|
+
expect(moneys[0]).to eq(Money.new(0.002, 'JOD'))
|
25
|
+
expect(moneys[1]).to eq(Money.new(0.003, 'JOD'))
|
26
|
+
end
|
27
|
+
|
28
|
+
specify "#allocate does not lose pennies even when given a lossy split" do
|
29
|
+
moneys = new_allocator(1).allocate([0.333,0.333, 0.333])
|
30
|
+
expect(moneys[0].subunits).to eq(34)
|
31
|
+
expect(moneys[1].subunits).to eq(33)
|
32
|
+
expect(moneys[2].subunits).to eq(33)
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "#allocate requires total to be less than 1" do
|
36
|
+
expect { new_allocator(0.05).allocate([0.5,0.6]) }.to raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
|
39
|
+
specify "#allocate will use rationals if provided" do
|
40
|
+
splits = [128400,20439,14589,14589,25936].map{ |num| Rational(num, 203953) } # sums to > 1 if converted to float
|
41
|
+
expect(new_allocator(2.25).allocate(splits)).to eq([Money.new(1.42), Money.new(0.23), Money.new(0.16), Money.new(0.16), Money.new(0.28)])
|
42
|
+
end
|
43
|
+
|
44
|
+
specify "#allocate will convert rationals with high precision" do
|
45
|
+
ratios = [Rational(1, 1), Rational(0)]
|
46
|
+
expect(new_allocator("858993456.12").allocate(ratios)).to eq([Money.new("858993456.12"), Money.empty])
|
47
|
+
ratios = [Rational(1, 6), Rational(5, 6)]
|
48
|
+
expect(new_allocator("3.00").allocate(ratios)).to eq([Money.new("0.50"), Money.new("2.50")])
|
49
|
+
end
|
50
|
+
|
51
|
+
specify "#allocate doesn't raise with weird negative rational ratios" do
|
52
|
+
rate = Rational(-5, 1201)
|
53
|
+
expect { new_allocator(1).allocate([rate, 1 - rate]) }.not_to raise_error
|
54
|
+
end
|
55
|
+
|
56
|
+
specify "#allocate fills pennies from beginning to end with roundrobin strategy" do
|
57
|
+
moneys = new_allocator(0.05).allocate([0.3,0.7], :roundrobin)
|
58
|
+
expect(moneys[0]).to eq(Money.new(0.02))
|
59
|
+
expect(moneys[1]).to eq(Money.new(0.03))
|
60
|
+
end
|
61
|
+
|
62
|
+
specify "#allocate fills pennies from end to beginning with roundrobin_reverse strategy" do
|
63
|
+
moneys = new_allocator(0.05).allocate([0.3,0.7], :roundrobin_reverse)
|
64
|
+
expect(moneys[0]).to eq(Money.new(0.01))
|
65
|
+
expect(moneys[1]).to eq(Money.new(0.04))
|
66
|
+
end
|
67
|
+
|
68
|
+
specify "#allocate raise ArgumentError when invalid strategy is provided" do
|
69
|
+
expect { new_allocator(0.03).allocate([0.5, 0.5], :bad_strategy_name) }.to raise_error(ArgumentError, "Invalid strategy. Valid options: :roundrobin, :roundrobin_reverse")
|
70
|
+
end
|
71
|
+
|
72
|
+
specify "#allocate does not raise ArgumentError when invalid splits types are provided" do
|
73
|
+
moneys = new_allocator(0.03).allocate([0.5, 0.5], :roundrobin)
|
74
|
+
expect(moneys[0]).to eq(Money.new(0.02))
|
75
|
+
expect(moneys[1]).to eq(Money.new(0.01))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'allocate_max_amounts' do
|
80
|
+
specify "#allocate_max_amounts returns the weighted allocation without exceeding the maxima when there is room for the remainder" do
|
81
|
+
expect(
|
82
|
+
new_allocator(30.75).allocate_max_amounts([Money.new(26), Money.new(4.75)]),
|
83
|
+
).to eq([Money.new(26), Money.new(4.75)])
|
84
|
+
end
|
85
|
+
|
86
|
+
specify "#allocate_max_amounts returns the weighted allocation without exceeding the maxima when there is room for the remainder with currency" do
|
87
|
+
expect(
|
88
|
+
new_allocator(3075, 'JPY').allocate_max_amounts([Money.new(2600, 'JPY'), Money.new(475, 'JPY')]),
|
89
|
+
).to eq([Money.new(2600, 'JPY'), Money.new(475, 'JPY')])
|
90
|
+
end
|
91
|
+
|
92
|
+
specify "#allocate_max_amounts legal computation with no currency objects" do
|
93
|
+
expect(
|
94
|
+
new_allocator(3075, 'JPY').allocate_max_amounts([2600, 475]),
|
95
|
+
).to eq([Money.new(2600, 'JPY'), Money.new(475, 'JPY')])
|
96
|
+
|
97
|
+
expect(
|
98
|
+
new_allocator(3075, Money::NULL_CURRENCY).allocate_max_amounts([Money.new(2600, 'JPY'), Money.new(475, 'JPY')]),
|
99
|
+
).to eq([Money.new(2600, 'JPY'), Money.new(475, 'JPY')])
|
100
|
+
end
|
101
|
+
|
102
|
+
specify "#allocate_max_amounts illegal computation across currencies" do
|
103
|
+
expect {
|
104
|
+
new_allocator(3075, 'USD').allocate_max_amounts([Money.new(2600, 'JPY'), Money.new(475, 'JPY')])
|
105
|
+
}.to raise_error(ArgumentError)
|
106
|
+
end
|
107
|
+
|
108
|
+
specify "#allocate_max_amounts drops the remainder when returning the weighted allocation without exceeding the maxima when there is no room for the remainder" do
|
109
|
+
expect(
|
110
|
+
new_allocator(30.75).allocate_max_amounts([Money.new(26), Money.new(4.74)]),
|
111
|
+
).to eq([Money.new(26), Money.new(4.74)])
|
112
|
+
end
|
113
|
+
|
114
|
+
specify "#allocate_max_amounts returns the weighted allocation when there is no remainder" do
|
115
|
+
expect(
|
116
|
+
new_allocator(30).allocate_max_amounts([Money.new(15), Money.new(15)]),
|
117
|
+
).to eq([Money.new(15), Money.new(15)])
|
118
|
+
end
|
119
|
+
|
120
|
+
specify "#allocate_max_amounts allocates the remainder round-robin when the maxima are not reached" do
|
121
|
+
expect(
|
122
|
+
new_allocator(1).allocate_max_amounts([Money.new(33), Money.new(33), Money.new(33)]),
|
123
|
+
).to eq([Money.new(0.34), Money.new(0.33), Money.new(0.33)])
|
124
|
+
end
|
125
|
+
|
126
|
+
specify "#allocate_max_amounts allocates up to the maxima specified" do
|
127
|
+
expect(
|
128
|
+
new_allocator(100).allocate_max_amounts([Money.new(5), Money.new(2)]),
|
129
|
+
).to eq([Money.new(5), Money.new(2)])
|
130
|
+
end
|
131
|
+
|
132
|
+
specify "#allocate_max_amounts supports all-zero maxima" do
|
133
|
+
expect(
|
134
|
+
new_allocator(3).allocate_max_amounts([Money.empty, Money.empty, Money.empty]),
|
135
|
+
).to eq([Money.empty, Money.empty, Money.empty])
|
136
|
+
end
|
137
|
+
|
138
|
+
specify "#allocate_max_amounts allocates the right amount without rounding error" do
|
139
|
+
expect(
|
140
|
+
new_allocator(24.2).allocate_max_amounts([Money.new(46), Money.new(46), Money.new(50), Money.new(50),Money.new(50)]),
|
141
|
+
).to eq([Money.new(4.6), Money.new(4.6), Money.new(5), Money.new(5), Money.new(5)])
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def new_allocator(amount, currency = nil)
|
146
|
+
Money::Allocator.new(Money.new(amount, currency))
|
147
|
+
end
|
148
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
RSpec.shared_examples_for "an object supporting to_money" do
|
@@ -56,6 +57,6 @@ RSpec.describe BigDecimal do
|
|
56
57
|
it_should_behave_like "an object supporting to_money"
|
57
58
|
|
58
59
|
it "parses a zero BigDecimal to Money.zero" do
|
59
|
-
expect(BigDecimal
|
60
|
+
expect(BigDecimal("-0.000").to_money).to eq(Money.zero)
|
60
61
|
end
|
61
62
|
end
|
data/spec/currency_spec.rb
CHANGED
data/spec/helpers_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
RSpec.describe Money::Helpers do
|
@@ -49,12 +50,6 @@ RSpec.describe Money::Helpers do
|
|
49
50
|
expect(subject.value_to_decimal('invalid')).to eq(0)
|
50
51
|
end
|
51
52
|
|
52
|
-
it 'returns the bigdecimal representation of numbers while they are deprecated' do
|
53
|
-
expect(Money).to receive(:deprecate).exactly(2).times
|
54
|
-
expect(subject.value_to_decimal('1.23abc')).to eq(amount)
|
55
|
-
expect(subject.value_to_decimal("1.23\n23")).to eq(amount)
|
56
|
-
end
|
57
|
-
|
58
53
|
it 'raises on invalid object' do
|
59
54
|
expect { subject.value_to_decimal(OpenStruct.new(amount: 1)) }.to raise_error(ArgumentError)
|
60
55
|
end
|
data/spec/money_accessor_spec.rb
CHANGED
data/spec/money_column_spec.rb
CHANGED
data/spec/money_parser_spec.rb
CHANGED
data/spec/money_spec.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
require 'spec_helper'
|
2
3
|
require 'yaml'
|
3
4
|
|
@@ -583,113 +584,29 @@ RSpec.describe "Money" do
|
|
583
584
|
end
|
584
585
|
|
585
586
|
describe "allocation"do
|
586
|
-
specify "#allocate
|
587
|
-
|
587
|
+
specify "#allocate is calculated by Money::Allocator#allocate" do
|
588
|
+
expected = [Money.new(1), [Money.new(1)]]
|
589
|
+
expect_any_instance_of(Money::Allocator).to receive(:allocate).with([0.5, 0.5], :roundrobin).and_return(expected)
|
590
|
+
expect(Money.new(2).allocate([0.5, 0.5])).to eq(expected)
|
588
591
|
end
|
589
592
|
|
590
|
-
specify "#allocate does not lose pennies" do
|
593
|
+
specify "#allocate does not lose pennies (integration test)" do
|
591
594
|
moneys = Money.new(0.05).allocate([0.3,0.7])
|
592
595
|
expect(moneys[0]).to eq(Money.new(0.02))
|
593
596
|
expect(moneys[1]).to eq(Money.new(0.03))
|
594
597
|
end
|
595
598
|
|
596
|
-
specify "#
|
597
|
-
|
598
|
-
|
599
|
-
expect(
|
599
|
+
specify "#allocate_max_amounts is calculated by Money::Allocator#allocate_max_amounts" do
|
600
|
+
expected = [Money.new(1), [Money.new(1)]]
|
601
|
+
expect_any_instance_of(Money::Allocator).to receive(:allocate_max_amounts).and_return(expected)
|
602
|
+
expect(Money.new(2).allocate_max_amounts([0.5, 0.5])).to eq(expected)
|
600
603
|
end
|
601
604
|
|
602
|
-
specify "#
|
603
|
-
moneys = Money.new(0.005, 'JOD').allocate([0.3,0.7])
|
604
|
-
expect(moneys[0]).to eq(Money.new(0.002, 'JOD'))
|
605
|
-
expect(moneys[1]).to eq(Money.new(0.003, 'JOD'))
|
606
|
-
end
|
607
|
-
|
608
|
-
specify "#allocate does not lose pennies even when given a lossy split" do
|
609
|
-
moneys = Money.new(1).allocate([0.333,0.333, 0.333])
|
610
|
-
expect(moneys[0].subunits).to eq(34)
|
611
|
-
expect(moneys[1].subunits).to eq(33)
|
612
|
-
expect(moneys[2].subunits).to eq(33)
|
613
|
-
end
|
614
|
-
|
615
|
-
specify "#allocate requires total to be less than 1" do
|
616
|
-
expect { Money.new(0.05).allocate([0.5,0.6]) }.to raise_error(ArgumentError)
|
617
|
-
end
|
618
|
-
|
619
|
-
specify "#allocate will use rationals if provided" do
|
620
|
-
splits = [128400,20439,14589,14589,25936].map{ |num| Rational(num, 203953) } # sums to > 1 if converted to float
|
621
|
-
expect(Money.new(2.25).allocate(splits)).to eq([Money.new(1.42), Money.new(0.23), Money.new(0.16), Money.new(0.16), Money.new(0.28)])
|
622
|
-
end
|
623
|
-
|
624
|
-
specify "#allocate will convert rationals with high precision" do
|
625
|
-
ratios = [Rational(1, 1), Rational(0)]
|
626
|
-
expect(Money.new("858993456.12").allocate(ratios)).to eq([Money.new("858993456.12"), Money.empty])
|
627
|
-
ratios = [Rational(1, 6), Rational(5, 6)]
|
628
|
-
expect(Money.new("3.00").allocate(ratios)).to eq([Money.new("0.50"), Money.new("2.50")])
|
629
|
-
end
|
630
|
-
|
631
|
-
specify "#allocate doesn't raise with weird negative rational ratios" do
|
632
|
-
rate = Rational(-5, 1201)
|
633
|
-
expect { Money.new(1).allocate([rate, 1 - rate]) }.not_to raise_error
|
634
|
-
end
|
635
|
-
|
636
|
-
specify "#allocate_max_amounts returns the weighted allocation without exceeding the maxima when there is room for the remainder" do
|
605
|
+
specify "#allocate_max_amounts returns the weighted allocation without exceeding the maxima when there is room for the remainder (integration test)" do
|
637
606
|
expect(
|
638
607
|
Money.new(30.75).allocate_max_amounts([Money.new(26), Money.new(4.75)]),
|
639
608
|
).to eq([Money.new(26), Money.new(4.75)])
|
640
609
|
end
|
641
|
-
|
642
|
-
specify "#allocate_max_amounts returns the weighted allocation without exceeding the maxima when there is room for the remainder with currency" do
|
643
|
-
expect(
|
644
|
-
Money.new(3075, 'JPY').allocate_max_amounts([Money.new(2600, 'JPY'), Money.new(475, 'JPY')]),
|
645
|
-
).to eq([Money.new(2600, 'JPY'), Money.new(475, 'JPY')])
|
646
|
-
end
|
647
|
-
|
648
|
-
specify "#allocate_max_amounts legal computation with no currency objects" do
|
649
|
-
expect(
|
650
|
-
Money.new(3075, 'JPY').allocate_max_amounts([2600, 475]),
|
651
|
-
).to eq([Money.new(2600, 'JPY'), Money.new(475, 'JPY')])
|
652
|
-
|
653
|
-
expect(
|
654
|
-
Money.new(3075, Money::NULL_CURRENCY).allocate_max_amounts([Money.new(2600, 'JPY'), Money.new(475, 'JPY')]),
|
655
|
-
).to eq([Money.new(2600, 'JPY'), Money.new(475, 'JPY')])
|
656
|
-
end
|
657
|
-
|
658
|
-
specify "#allocate_max_amounts illegal computation across currencies" do
|
659
|
-
expect {
|
660
|
-
Money.new(3075, 'USD').allocate_max_amounts([Money.new(2600, 'JPY'), Money.new(475, 'JPY')])
|
661
|
-
}.to raise_error(ArgumentError)
|
662
|
-
end
|
663
|
-
|
664
|
-
specify "#allocate_max_amounts drops the remainder when returning the weighted allocation without exceeding the maxima when there is no room for the remainder" do
|
665
|
-
expect(
|
666
|
-
Money.new(30.75).allocate_max_amounts([Money.new(26), Money.new(4.74)]),
|
667
|
-
).to eq([Money.new(26), Money.new(4.74)])
|
668
|
-
end
|
669
|
-
|
670
|
-
specify "#allocate_max_amounts returns the weighted allocation when there is no remainder" do
|
671
|
-
expect(
|
672
|
-
Money.new(30).allocate_max_amounts([Money.new(15), Money.new(15)]),
|
673
|
-
).to eq([Money.new(15), Money.new(15)])
|
674
|
-
end
|
675
|
-
|
676
|
-
specify "#allocate_max_amounts allocates the remainder round-robin when the maxima are not reached" do
|
677
|
-
expect(
|
678
|
-
Money.new(1).allocate_max_amounts([Money.new(33), Money.new(33), Money.new(33)]),
|
679
|
-
).to eq([Money.new(0.34), Money.new(0.33), Money.new(0.33)])
|
680
|
-
end
|
681
|
-
|
682
|
-
specify "#allocate_max_amounts allocates up to the maxima specified" do
|
683
|
-
expect(
|
684
|
-
Money.new(100).allocate_max_amounts([Money.new(5), Money.new(2)]),
|
685
|
-
).to eq([Money.new(5), Money.new(2)])
|
686
|
-
end
|
687
|
-
|
688
|
-
specify "#allocate_max_amounts supports all-zero maxima" do
|
689
|
-
expect(
|
690
|
-
Money.new(3).allocate_max_amounts([Money.empty, Money.empty, Money.empty]),
|
691
|
-
).to eq([Money.empty, Money.empty, Money.empty])
|
692
|
-
end
|
693
610
|
end
|
694
611
|
|
695
612
|
describe "split" do
|
@@ -828,8 +745,8 @@ RSpec.describe "Money" do
|
|
828
745
|
end
|
829
746
|
|
830
747
|
it "accepts Rational number" do
|
831
|
-
expect(Money.from_amount(Rational("999999999999999999.999")).value).to eql(BigDecimal
|
832
|
-
expect(Money.from_amount(Rational("999999999999999999.99")).value).to eql(BigDecimal
|
748
|
+
expect(Money.from_amount(Rational("999999999999999999.999")).value).to eql(BigDecimal("1000000000000000000", Money::Helpers::MAX_DECIMAL))
|
749
|
+
expect(Money.from_amount(Rational("999999999999999999.99")).value).to eql(BigDecimal("999999999999999999.99", Money::Helpers::MAX_DECIMAL))
|
833
750
|
end
|
834
751
|
|
835
752
|
it "raises ArgumentError with unsupported argument" do
|