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.



77
78
79
80
81
82
83
84
85
# File 'lib/money/money/allocation.rb', line 77

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, decimal_cutoff = true) ⇒ Array<Numeric>

Allocates a specified amount into parts based on their proportions or distributes it evenly when the number of parts is specified numerically.

The total of the allocated amounts will always equal the original amount.

The parts can be specified as: Numeric — performs the split between a given number of parties evenly Array — allocates the amounts proportionally to the given array

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)

  • decimal_cutoff (Boolean, Integer) (defaults to: true)

    Controls per-split precision:

    • When true, splits are truncated to whole amounts.
    • When an Integer N, splits are rounded to N decimal places.
    • When false or nil, no rounding/truncation is applied. Defaults to true (whole amounts).

Returns:

  • (Array<Numeric>)

    An array containing the allocated amounts.

Raises:

  • (ArgumentError)

    If parts is empty or not provided.



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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/money/money/allocation.rb', line 24

def self.generate(amount, parts, decimal_cutoff = 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
  round_to_whole = decimal_cutoff.is_a?(TrueClass)
  round_to_precision = decimal_cutoff.is_a?(Integer)

  parts_sum = parts.sum

  until parts.empty? do
    part = parts.pop

    current_split = 0
    if parts_sum > 0
      current_split = remaining_amount * part / parts_sum
      current_split =
        if round_to_whole
          current_split.truncate
        elsif round_to_precision
          current_split.round(decimal_cutoff)
        else
          current_split
        end
    end

    result.unshift current_split
    remaining_amount -= current_split
    parts_sum -= part
  end

  result
end