lib/twitter_cldr/formatters/numbers/number_formatter.rb
# encoding: UTF-8
# Copyright 2012 Twitter, Inc
# http://www.apache.org/licenses/LICENSE-2.0
module TwitterCldr
module Formatters
class NumberFormatter < Formatter
attr_reader :data_reader
def initialize(data_reader)
@data_reader = data_reader
end
def format(tokens, number, options = {})
options[:precision] ||= precision_from(number)
options[:type] ||= :decimal
prefix, suffix, integer_format, fraction_format = *partition_tokens(tokens)
number = truncate_number(number, integer_format.format.length)
int, fraction = parse_number(number, options)
result = integer_format.apply(int, options)
result << fraction_format.apply(fraction, options) if fraction
number_system.transliterate(
"#{prefix.to_s}#{result}#{suffix.to_s}"
)
end
def truncate_number(number, decimal_digits)
if abbreviate?(number)
factor = [0, number.to_i.abs.to_s.length - decimal_digits].max
number / (10.0 ** factor)
else
number
end
end
protected
def number_system
@number_system ||= TwitterCldr::Shared::NumberingSystem.for_name(
data_reader.number_system
)
end
def partition_tokens(tokens)
[
token_val_from(tokens[0]),
token_val_from(tokens[2]),
Numbers::Integer.new(
tokens[1],
data_reader.symbols
),
Numbers::Fraction.new(
tokens[1],
data_reader.symbols
)
]
end
def token_val_from(token)
token ? token.value : ""
end
def parse_number(number, options = {})
precision = options[:precision] || precision_from(number)
rounding = options[:rounding] || 0
if number.is_a? BigDecimal
number = precision == 0 ?
round_to(number, precision, rounding).abs.fix.to_s("F") :
round_to(number, precision, rounding).abs.round(precision).to_s("F")
else
number = "%.#{precision}f" % round_to(number, precision, rounding).abs
end
number.split(".")
end
def round_to(number, precision, rounding = 0)
factor = 10 ** precision
result = number.is_a?(BigDecimal) ?
((number * factor).fix / factor) :
((number * factor).round.to_f / factor)
if rounding > 0
rounding = rounding.to_f / factor
result = number.is_a?(BigDecimal) ?
((result * (1.0 / rounding)).fix / (1.0 / rounding)) :
((result * (1.0 / rounding)).round.to_f / (1.0 / rounding))
end
result
end
def precision_from(num)
return 0 if num.is_a?(BigDecimal) && num.fix == num
parts = (num.is_a?(BigDecimal) ? num.to_s("F") : num.to_s ).split(".")
parts.size == 2 ? parts[1].size : 0
end
def abbreviate?(number)
TwitterCldr::DataReaders::NumberDataReader.within_abbreviation_range?(number) && (
data_reader.format == :short ||
data_reader.format == :long
)
end
end
end
end