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
# File lib/strings/numeral.rb, line 211 def cardinalize(num, **options) instance.cardinalize(num, **options) end
# File lib/strings/numeral.rb, line 201 def self.instance @instance ||= Numeral.new end
# File lib/strings/numeral.rb, line 225 def monetize(num, **options) instance.monetize(num, **options) end
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
# File lib/strings/numeral.rb, line 206 def numeralize(num, **options) instance.numeralize(num, **options) end
# File lib/strings/numeral.rb, line 216 def ordinalize(num, **options) instance.ordinalize(num, **options) end
# File lib/strings/numeral.rb, line 221 def ordinalize_short(num) instance.ordinalize_short(num) end
# File lib/strings/numeral.rb, line 230 def romanize(num) instance.romanize(num) end
Public Instance Methods
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
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
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
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
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
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 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 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 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 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 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