Class: Money::Allocator

Inherits:
SimpleDelegator
  • Object
show all
Defined in:
lib/money/allocator.rb

Instance Method Summary collapse

Constructor Details

#initialize(money) ⇒ Allocator

Returns a new instance of Allocator.



3
4
5
# File 'lib/money/allocator.rb', line 3

def initialize(money)
  super
end

Instance Method Details

#allocate(splits, strategy = :roundrobin) ⇒ Object

Examples:

left over pennies distributed reverse order when using roundrobin_reverse strategy

Money.new(10.01, "USD").allocate([0.5, 0.5], :roundrobin_reverse)
  #=> [#<Money value:5.00 currency:USD>, #<Money value:5.01 currency:USD>]


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/money/allocator.rb', line 39

def allocate(splits, strategy = :roundrobin)
  if all_rational?(splits)
    allocations = splits.inject(0) { |sum, n| sum + n }
  else
    allocations = splits.inject(0) { |sum, n| sum + Helpers.value_to_decimal(n) }
  end

  if (allocations - BigDecimal("1")) > Float::EPSILON
    raise ArgumentError, "splits add to more than 100%"
  end

  amounts, left_over = amounts_from_splits(allocations, splits)

  left_over.to_i.times do |i|
    amounts[allocation_index_for(strategy, amounts.length, i)] += 1
  end

  amounts.collect { |subunits| Money.from_subunits(subunits, currency) }
end

#allocate_max_amounts(maximums) ⇒ Object

Allocates money between different parties up to the maximum amounts specified. Left over pennies will be assigned round-robin up to the maximum specified. Pennies are dropped when the maximums are attained.

Examples:

Money.new(30.75).allocate_max_amounts([Money.new(26), Money.new(4.75)])
  #=> [Money.new(26), Money.new(4.75)]

Money.new(30.75).allocate_max_amounts([Money.new(26), Money.new(4.74)]
  #=> [Money.new(26), Money.new(4.74)]

Money.new(30).allocate_max_amounts([Money.new(15), Money.new(15)]
  #=> [Money.new(15), Money.new(15)]

Money.new(1).allocate_max_amounts([Money.new(33), Money.new(33), Money.new(33)])
  #=> [Money.new(0.34), Money.new(0.33), Money.new(0.33)]

Money.new(100).allocate_max_amounts([Money.new(5), Money.new(2)])
  #=> [Money.new(5), Money.new(2)]


78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/money/allocator.rb', line 78

def allocate_max_amounts(maximums)
  allocation_currency = extract_currency(maximums + [self.__getobj__])
  maximums = maximums.map { |max| max.to_money(allocation_currency) }
  maximums_total = maximums.reduce(Money.new(0, allocation_currency), :+)

  splits = maximums.map do |max_amount|
    next(0) if maximums_total.zero?
    Money.rational(max_amount, maximums_total)
  end

  total_allocatable = [
    value * allocation_currency.subunit_to_unit,
    maximums_total.value * allocation_currency.subunit_to_unit
  ].min

  subunits_amounts, left_over = amounts_from_splits(1, splits, total_allocatable)

  subunits_amounts.each_with_index do |amount, index|
    break unless left_over > 0

    max_amount = maximums[index].value * allocation_currency.subunit_to_unit
    next unless amount < max_amount

    left_over -= 1
    subunits_amounts[index] += 1
  end

  subunits_amounts.map { |cents| Money.from_subunits(cents, allocation_currency) }
end