Module: ConcernsOnRails::Support::Money

Defined in:
lib/concerns_on_rails/support/money.rb

Overview

Formats an integer subunit amount (e.g. cents) as a human-readable money string. Pure and stateless; used by Models::Monetizable. Uses BigDecimal throughout so there is no binary-float rounding drift.

Class Method Summary collapse

Class Method Details

.delimit(integer_string, delimiter) ⇒ Object

Insert the thousands delimiter into a non-negative integer string.



33
34
35
# File 'lib/concerns_on_rails/support/money.rb', line 33

def delimit(integer_string, delimiter)
  integer_string.reverse.gsub(/(\d{3})(?=\d)/, "\\1#{delimiter}").reverse
end

.format(cents, options = {}) ⇒ Object

format(199999) => “$1,999.99” format(-500, unit: “£”) => “-£5.00” format(1234, unit: “¥”, precision: 0, subunit_to_unit: 1) => “¥1,234”



14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/concerns_on_rails/support/money.rb', line 14

def format(cents, options = {})
  unit      = options.fetch(:unit, "$")
  precision = options.fetch(:precision, 2)
  delimiter = options.fetch(:delimiter, ",")
  separator = options.fetch(:separator, ".")
  subunit   = options.fetch(:subunit_to_unit, 100)

  decimal = BigDecimal(cents.to_s) / subunit
  # BigDecimal#round returns an Integer for precision <= 0, so re-wrap it
  # in a BigDecimal before #to_s("F") (Integer#to_s would read "F" as a radix).
  rounded = BigDecimal(decimal.abs.round(precision).to_s)
  whole, _, frac = rounded.to_s("F").partition(".")
  whole = delimit(whole, delimiter)
  number = precision.positive? ? "#{whole}#{separator}#{frac.ljust(precision, '0')[0, precision]}" : whole

  "#{'-' if decimal.negative?}#{unit}#{number}"
end