Class: Money::Allocation

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

Class Method Summary collapse

Class Method Details

.convert_to_big_decimal(number) ⇒ BigDecimal

Converts a given number to BigDecimal. This method supports inputs of BigDecimal, Rational, and other numeric types by ensuring they are all returned as BigDecimal instances for consistent handling.

Parameters:

  • number (Numeric, BigDecimal, Rational)

    The number to convert.

Returns:

  • (BigDecimal)

    The converted number as a BigDecimal.



58
59
60
61
62
63
64
65
66
# File 'lib/money/money/allocation.rb', line 58

def self.convert_to_big_decimal(number)
  if number.is_a? BigDecimal
    number
  elsif number.is_a? Rational
    BigDecimal(number.to_f.to_s)
  else
    BigDecimal(number.to_s)
  end
end

.generate(amount, parts, whole_amounts = true) ⇒ Array<Numeric>

Splits a given amount in parts. The allocation is based on the parts’ proportions or evenly if parts are numerically specified.

The results should always add up to the original amount.

Parameters:

  • amount (Numeric)

    The total amount to be allocated.

  • parts (Numeric, Array<Numeric>)

    Number of parts to split into or an array (proportions for allocation)

  • whole_amounts (Boolean) (defaults to: true)

    Specifies whether to allocate whole amounts only. Defaults to true.

Returns:

  • (Array<Numeric>)

    An array containing the allocated amounts.

Raises:

  • (ArgumentError)

    If parts is empty or not provided.



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/money/money/allocation.rb', line 16

def self.generate(amount, parts, whole_amounts = true)
  parts = if parts.is_a?(Numeric)
    Array.new(parts, 1)
  elsif parts.all?(&:zero?)
    Array.new(parts.count, 1)
  else
    parts.dup
  end

  raise ArgumentError, 'need at least one part' if parts.empty?

  if [amount, *parts].any? { |i| i.is_a?(BigDecimal) || i.is_a?(Float) || i.is_a?(Rational) }
    amount = convert_to_big_decimal(amount)
    parts.map! { |p| convert_to_big_decimal(p) }
  end

  result = []
  remaining_amount = amount

  until parts.empty? do
    parts_sum = parts.inject(0, :+)
    part = parts.pop

    current_split = 0
    if parts_sum > 0
      current_split = remaining_amount * part / parts_sum
      current_split = current_split.truncate if whole_amounts
    end

    result.unshift current_split
    remaining_amount -= current_split
  end

  result
end