class Money::Allocator
Public Class Methods
new(money)
click to toggle source
Calls superclass method
# File lib/money/allocator.rb, line 6 def initialize(money) super end
Public Instance Methods
allocate(splits, strategy = :roundrobin)
click to toggle source
@example 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>]
# File lib/money/allocator.rb, line 42 def allocate(splits, strategy = :roundrobin) splits.map!(&:to_r) allocations = splits.inject(0, :+) 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)
click to toggle source
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.
@example
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)]
# 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(Rational(0)) if maximums_total.zero? Money.rational(max_amount, maximums_total) end total_allocatable = [maximums_total.subunits, self.subunits].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
Private Instance Methods
all_rational?(splits)
click to toggle source
# File lib/money/allocator.rb, line 129 def all_rational?(splits) splits.all? { |split| split.is_a?(Rational) } end
allocation_index_for(strategy, length, idx)
click to toggle source
# File lib/money/allocator.rb, line 133 def allocation_index_for(strategy, length, idx) case strategy when :roundrobin idx % length when :roundrobin_reverse length - (idx % length) - 1 else raise ArgumentError, "Invalid strategy. Valid options: :roundrobin, :roundrobin_reverse" end end
amounts_from_splits(allocations, splits, subunits_to_split = subunits)
click to toggle source
# File lib/money/allocator.rb, line 115 def amounts_from_splits(allocations, splits, subunits_to_split = subunits) raise ArgumentError, "All splits values must be of type Rational." unless all_rational?(splits) left_over = subunits_to_split amounts = splits.collect do |ratio| frac = (subunits_to_split * ratio / allocations.to_r).floor left_over -= frac frac end [amounts, left_over] end
extract_currency(money_array)
click to toggle source
# File lib/money/allocator.rb, line 107 def extract_currency(money_array) currencies = money_array.lazy.select { |money| money.is_a?(Money) }.reject(&:no_currency?).map(&:currency).to_a.uniq if currencies.size > 1 raise ArgumentError, "operation not permitted for Money objects with different currencies #{currencies.join(', ')}" end currencies.first || NULL_CURRENCY end