# frozen_string_literal: true # Released under the MIT License. # Copyright, 2012-2023, by Samuel Williams. # Copyright, 2020, by Tim Craft. # Copyright, 2021, by Adam Daniels. module Latinum module Formatters # Formats a currency using a standard decimal notation. class PlainFormatter def initialize(name:) @name = name end # Parse a string into an amount. # @returns [BigDecimal] The parsed amount. def parse(string) BigDecimal(string) end # Formats the amount using a general notation. # e.g. "5.0 NZD". # @returns [String] The formatted string. def format(amount) "#{amount.to_s('F')} #{@name}" end # Converts the amount directly to an integer, truncating any decimal part. # @parameter amount [BigDecimal] The amount to convert to an integral. # @returns [Integer] The converted whole number integer. def to_integral(amount) amount.to_i end # Converts the amount to a decimal. # @parameter amount [Integer] The amount to convert to a decimal. # @returns [BigDecimal] The converted amount. def from_integral(amount) amount.to_d end end # Formats a currency using a standard decimal notation. class DecimalCurrencyFormatter def initialize(**options) @symbol = options[:symbol] || '$' @separator = options[:separator] || '.' @delimiter = options[:delimiter] || ',' @places = options[:precision] || 2 @zero = options[:zero] || '0' @name = options[:name] end def round(amount) return amount.round(@places) end # Parse a string into an amount using the configured separator and delimiter. # @returns [BigDecimal] The parsed amount. def parse(string) BigDecimal( string.gsub(/[^\-0-9#{@separator}]/, '').gsub(@separator, '.') ) end # Formats the amount using the configured symbol, separator, delimiter, and places. # e.g. "$5,000.00 NZD". Rounds the amount to the specified number of decimal places. # @returns [String] The formatted string. def format(amount, places: @places, **options) # Round to the desired number of places. Truncation used to be the default. amount = amount.round(places).to_d integral, fraction = amount.abs.to_s('F').split(/\./, 2) # The sign of the number sign = '-' if amount < 0 # Decimal places, e.g. the '.00' in '$10.00' fraction = fraction[0...places].ljust(places, @zero) # Grouping, e.g. the ',' in '$10,000.00' remainder = integral.size % 3 groups = integral[remainder..-1].scan(/.{3}/).to_a groups.unshift(integral[0...remainder]) if remainder > 0 symbol = options.fetch(:symbol, @symbol) value = "#{sign}#{symbol}#{groups.join(@delimiter)}" name = options.fetch(:name, @name) suffix = name ? " #{name}" : '' if places > 0 "#{value}#{@separator}#{fraction}#{suffix}" else "#{value}#{suffix}" end end # Converts the amount directly to an integer, truncating any decimal part, taking into account the number of specified decimal places. # @parameter amount [BigDecimal] The amount to convert to an integral. # @returns [Integer] The converted whole number integer. def to_integral(amount) (amount * 10**@places).to_i end # Converts the amount to a decimal, taking into account the number of specified decimal places. # @parameter amount [Integer] The amount to convert to a decimal. # @returns [BigDecimal] The converted amount. def from_integral(amount) (amount.to_d / 10**@places) end end end end