shopify-money 0.12.0 → 0.14.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -3
  3. data/Gemfile +2 -1
  4. data/README.md +20 -1
  5. data/Rakefile +1 -0
  6. data/bin/console +1 -0
  7. data/config/currency_iso.json +1 -1
  8. data/dev.yml +1 -1
  9. data/lib/money.rb +4 -0
  10. data/lib/money/allocator.rb +144 -0
  11. data/lib/money/core_extensions.rb +1 -0
  12. data/lib/money/currency.rb +1 -0
  13. data/lib/money/currency/loader.rb +1 -0
  14. data/lib/money/deprecations.rb +1 -0
  15. data/lib/money/errors.rb +1 -0
  16. data/lib/money/helpers.rb +5 -15
  17. data/lib/money/money.rb +8 -111
  18. data/lib/money/null_currency.rb +1 -0
  19. data/lib/money/version.rb +2 -1
  20. data/lib/money_accessor.rb +1 -0
  21. data/lib/money_column.rb +1 -0
  22. data/lib/money_column/active_record_hooks.rb +2 -1
  23. data/lib/money_column/active_record_type.rb +1 -0
  24. data/lib/money_column/railtie.rb +1 -0
  25. data/lib/rubocop/cop/money.rb +3 -0
  26. data/lib/rubocop/cop/money/missing_currency.rb +75 -0
  27. data/lib/shopify-money.rb +2 -0
  28. data/money.gemspec +7 -3
  29. data/spec/accounting_money_parser_spec.rb +1 -0
  30. data/spec/allocator_spec.rb +148 -0
  31. data/spec/core_extensions_spec.rb +2 -1
  32. data/spec/currency/loader_spec.rb +1 -0
  33. data/spec/currency_spec.rb +1 -0
  34. data/spec/helpers_spec.rb +1 -6
  35. data/spec/money_accessor_spec.rb +1 -0
  36. data/spec/money_column_spec.rb +1 -0
  37. data/spec/money_parser_spec.rb +1 -0
  38. data/spec/money_spec.rb +13 -96
  39. data/spec/null_currency_spec.rb +1 -0
  40. data/spec/rubocop/cop/money/missing_currency_spec.rb +108 -0
  41. data/spec/rubocop_helper.rb +10 -0
  42. data/spec/schema.rb +1 -0
  43. data/spec/spec_helper.rb +1 -5
  44. metadata +20 -24
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class Money
2
3
  class NullCurrency
3
4
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class Money
2
- VERSION = "0.12.0"
3
+ VERSION = "0.14.2"
3
4
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module MoneyAccessor
2
3
  def self.included(base)
3
4
  base.extend(ClassMethods)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require_relative 'money_column/active_record_hooks'
2
3
  require_relative 'money_column/active_record_type'
3
4
  require_relative 'money_column/railtie'
@@ -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?
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  class MoneyColumn::ActiveRecordType < ActiveRecord::Type::Decimal
2
3
  def serialize(money)
3
4
  return nil unless money
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module MoneyColumn
2
3
  class Railtie < Rails::Railtie
3
4
  ActiveSupport.on_load :active_record do
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop/cop/money/missing_currency'
@@ -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
@@ -0,0 +1,2 @@
1
+ # frozen_string_literal: true
2
+ require_relative 'money'
@@ -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", "~> 5.0")
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.3.6")
21
- s.add_development_dependency("bigdecimal", "~> 1.3.2")
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) }
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  RSpec.describe AccountingMoneyParser do
@@ -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.new("-0.000").to_money).to eq(Money.zero)
60
+ expect(BigDecimal("-0.000").to_money).to eq(Money.zero)
60
61
  end
61
62
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  RSpec.describe Money::Currency::Loader do
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  RSpec.describe "Currency" do
@@ -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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  class NormalObject
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  class MoneyRecord < ActiveRecord::Base
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'spec_helper'
2
3
 
3
4
  RSpec.describe MoneyParser do
@@ -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 takes no action when one gets all" do
587
- expect(Money.new(5).allocate([1])).to eq([Money.new(5)])
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 "#allocate does not lose dollars with non-decimal currency" do
597
- moneys = Money.new(5, 'JPY').allocate([0.3,0.7])
598
- expect(moneys[0]).to eq(Money.new(2, 'JPY'))
599
- expect(moneys[1]).to eq(Money.new(3, 'JPY'))
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 "#allocate does not lose dollars with three decimal currency" do
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.new("1000000000000000000", Money::Helpers::MAX_DECIMAL))
832
- expect(Money.from_amount(Rational("999999999999999999.99")).value).to eql(BigDecimal.new("999999999999999999.99", Money::Helpers::MAX_DECIMAL))
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