class Strings::Numeral

Constants

AND
CARDINALS
CARDINAL_TO_ORDINAL
CARDINAL_TO_ROMAN
CARDINAL_TO_SHORT_ORDINAL
CURRENCIES
DECIMAL_SLOTS
HUNDRED
NEGATIVE
POINT
SCALES
SPACE
VERSION
ZERO

Public Class Methods

cardinalise(num, **options)
Alias for: cardinalize
cardinalize(num, **options) click to toggle source
# File lib/strings/numeral.rb, line 211
def cardinalize(num, **options)
  instance.cardinalize(num, **options)
end
Also aliased as: cardinalise
instance() click to toggle source
# File lib/strings/numeral.rb, line 201
def self.instance
  @instance ||= Numeral.new
end
monetise(num, **options)
Alias for: monetize
monetize(num, **options) click to toggle source
# File lib/strings/numeral.rb, line 225
def monetize(num, **options)
  instance.monetize(num, **options)
end
Also aliased as: monetise
new() { |configuration| ... } click to toggle source

Create numeral with custom configuration

@yieldparam [Configuration]

@return [Numeral]

@api public

# File lib/strings/numeral.rb, line 243
def initialize
  @configuration = Configuration.new
  if block_given?
    yield @configuration
  end
end
numeralise(num, **options)
Alias for: numeralize
numeralize(num, **options) click to toggle source
# File lib/strings/numeral.rb, line 206
def numeralize(num, **options)
  instance.numeralize(num, **options)
end
Also aliased as: numeralise
ordinalise(num, **options)
Alias for: ordinalize
ordinalize(num, **options) click to toggle source
# File lib/strings/numeral.rb, line 216
def ordinalize(num, **options)
  instance.ordinalize(num, **options)
end
Also aliased as: ordinalise
ordinalize_short(num) click to toggle source
# File lib/strings/numeral.rb, line 221
def ordinalize_short(num)
  instance.ordinalize_short(num)
end
romanise(num)
Alias for: romanize
romanize(num) click to toggle source
# File lib/strings/numeral.rb, line 230
def romanize(num)
  instance.romanize(num)
end
Also aliased as: romanise

Public Instance Methods

cardinalise(num, **options)
Alias for: cardinalize
cardinalize(num, **options) click to toggle source

Convert a number to a cardinal numeral

@example

cardinalize(1234)
# => one thousand, two hundred thirty four

@param [Numeric,String] num

@return [String]

@api public

# File lib/strings/numeral.rb, line 276
def cardinalize(num, **options)
  convert_numeral(num, **options)
end
Also aliased as: cardinalise
monetise(num, **options)
Alias for: monetize
monetize(num, **options) click to toggle source

Convert a number into a monetary numeral

@example

monetize(123.45)
# => "one hundred twenty three dollars and forty five cents"

@param [Numeric,String] num

the number to convert

@return [String]

@api public

# File lib/strings/numeral.rb, line 356
def monetize(num, **options)
  sep = options.fetch(:separator, @configuration.separator)
  curr_name = options.fetch(:currency, @configuration.currency)
  n = "%0.2f" % num.to_s
  decimals = (num.to_i.abs != num.to_f.abs)
  sentence = convert_numeral(n, **options.merge(trailing_zeros: true))
  dec_num = n.split(".")[1]
  curr = CURRENCIES[curr_name.to_s.downcase.to_sym]
  separators = [AND, POINT, sep].compact

  if decimals
    regex = /(\w+) (#{Regexp.union(separators)})/
    sentence.sub!(regex, "\\1 #{curr[:units]} \\2")
  else
    sentence += SPACE + (num.to_i == 1 ? curr[:unit] : curr[:units])
  end

  if decimals
    slots = Regexp.union(DECIMAL_SLOTS.map { |slot| slot.chomp('s') })
    regex = /(#{slots})s?/i
    suffix = dec_num.to_i == 1 ? curr[:decimal_unit] : curr[:decimal_units]
    if sentence.sub!(regex, suffix).nil?
      sentence += SPACE + suffix
    end
  end

  sentence
end
Also aliased as: monetise
numeralize(num, **options) click to toggle source

Convert a number to a numeral

@param [Numeric,String] num

the number to convert

@api public

# File lib/strings/numeral.rb, line 256
def numeralize(num, **options)
  case options.delete(:term)
  when /ord/
    ordinalize(num, **options)
  else
    cardinalize(num, **options)
  end
end
ordinalise(num, **options)
Alias for: ordinalize
ordinalize(num, **options) click to toggle source

Convert a number to an ordinal numeral

@example

ordinalize(1234)
# => one thousand, two hundred thirty fourth

ordinalize(12, short: true) # => 12th

@param [Numeric,String] num

the number to convert

@return [String]

@api public

# File lib/strings/numeral.rb, line 295
def ordinalize(num, **options)
  if options[:short]
    ordinalize_short(num)
  else
    decimals = (num.to_i.abs != num.to_f.abs)
    sentence = convert_numeral(num, **options)
    separators = [AND, POINT,
                  options.fetch(:separator, @configuration.separator)].compact

    if decimals && sentence =~ /(\w+) (#{Regexp.union(separators)})/
      last_digits = $1
      separator = $2
      replacement = CARDINAL_TO_ORDINAL[last_digits]
      pattern = /#{last_digits} #{separator}/
      suffix = "#{replacement} #{separator}"
    elsif sentence =~ /(\w+)$/
      last_digits = $1
      replacement = CARDINAL_TO_ORDINAL[last_digits]
      pattern = /#{last_digits}$/
      suffix = replacement
    end

    if replacement
      sentence.sub(pattern, suffix)
    else
      sentence
    end
  end
end
Also aliased as: ordinalise
ordinalize_short(num) click to toggle source

Convert a number to a short ordinal form

@example

ordinalize_short(123) # => 123rd

@param [Numeric, String] num

the number to convert

@return [String]

@api private

# File lib/strings/numeral.rb, line 337
def ordinalize_short(num)
  num_abs = num.to_i.abs

  num.to_i.to_s + (CARDINAL_TO_SHORT_ORDINAL[num_abs % 100] ||
    CARDINAL_TO_SHORT_ORDINAL[num_abs % 10])
end
romanize(num) click to toggle source

Convert a number to a roman numeral

@example

romanize(2020) # => "MMXX"

@param [Integer] num

the number to convert

@return [String]

@api public

# File lib/strings/numeral.rb, line 397
def romanize(num)
  n = num.to_i

  if n < 1 || n > 4999
    raise Error, "'#{n}' is out of range"
  end

  CARDINAL_TO_ROMAN.keys.reverse_each.reduce([]) do |word, card|
    while n >= card
      n -= card
      word << CARDINAL_TO_ROMAN[card]
    end
    word
  end.join
end

Private Instance Methods

convert_decimals(num, **options) click to toggle source

Convert decimal part to words

@param [String] trailing_zeros

whether or not to keep trailing zeros, defaults to `false`

@return [String]

@api private

# File lib/strings/numeral.rb, line 467
def convert_decimals(num, **options)
  delimiter = options.fetch(:delimiter, @configuration.delimiter)
  decimal = options.fetch(:decimal, @configuration.decimal)
  trailing_zeros = options.fetch(:trailing_zeros, @configuration.trailing_zeros)

  dec_num = num.to_s.split(".")[1]
  dec_num.gsub!(/0+$/, "") unless trailing_zeros

  case decimal
  when :fraction
    unit = DECIMAL_SLOTS[dec_num.to_s.length - 1]
    unit = unit[0...-1] if dec_num.to_i == 1 # strip off 's'
    convert_to_words(dec_num.to_i).join(delimiter) + SPACE + unit
  when :digit
    dec_num.chars.map do |n|
      (v = convert_tens(n.to_i)).empty? ? ZERO : v
    end.join(SPACE)
  else
    raise Error, "Unknown decimal option '#{decimal.inspect}'"
  end
end
convert_hundreds(num) click to toggle source

Convert 3 digit number to equivalent word

@return [String]

@api private

# File lib/strings/numeral.rb, line 521
def convert_hundreds(num)
  word = []
  hundreds = (num % 1000) / 100
  tens = num % 100

  if !hundreds.zero?
    word << convert_tens(hundreds)
    word << HUNDRED
  end

  if !tens.zero?
    word << convert_tens(tens)
  end

  word.join(SPACE)
end
convert_numeral(num, **options) click to toggle source

Convert a number into a numeral

@param [Numeric] num

the number to convert to numeral

@param [String] delimiter

sets the thousand's delimiter, defaults to `, `

@param [String] decimal

the decimal word conversion, defaults to `:fraction`

@param [String] separator

sets the separator between the fractional and integer numerals,
defaults to `and` for fractions and `point` for digits

@return [String]

the number as numeral

@api private

# File lib/strings/numeral.rb, line 431
def convert_numeral(num, **options)
  delimiter = options.fetch(:delimiter, @configuration.delimiter)
  decimal = options.fetch(:decimal, @configuration.decimal)
  separator = options.fetch(:separator, @configuration.separator)

  negative = num.to_i < 0
  n = num.to_i.abs
  decimals = (n != num.to_f.abs)

  sentence = convert_to_words(n).join(delimiter)

  if sentence.empty?
    sentence = ZERO
  end

  if negative
    sentence = NEGATIVE + SPACE + sentence
  end

  if decimals
    sentence = sentence + SPACE +
      (separator.nil? ? (decimal == :fraction ? AND : POINT) : separator) +
      SPACE + convert_decimals(num, **options)
  end

  sentence
end
convert_tens(num) click to toggle source

Convert number in 0..99 range to equivalent word

@return [String]

@api private

# File lib/strings/numeral.rb, line 543
def convert_tens(num)
  word = []
  tens = num % 100

  if tens.to_s.size < 2 || tens <= 20
    word << CARDINALS[tens]
  else
    word << CARDINALS[(tens / 10) * 10]
    word << CARDINALS[tens % 10] unless (tens % 10).zero?
  end

  word.join(SPACE)
end
convert_to_words(n) click to toggle source

Convert an integer to number words

@param [Integer] n

@return [Array]

@api public

# File lib/strings/numeral.rb, line 496
def convert_to_words(n)
  words = []

  SCALES.each_with_index do |scale, i|
    mod = n % 1000

    word = []
    word << convert_hundreds(mod)
    word << scale unless i.zero?

    words.insert(0, word.join(SPACE))

    n /= 1000

    break if n.zero?
  end

  words
end