aboutsummaryrefslogtreecommitdiffstats
path: root/activesupport/lib/active_support/number_helper/number_to_human_converter.rb
blob: 040343b5dd150d7350194b9359b3692a1f6d177a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
module ActiveSupport
  module NumberHelper
    class NumberToHumanConverter < NumberConverter # :nodoc:
      DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
        -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
      INVERTED_DECIMAL_UNITS = DECIMAL_UNITS.invert

      self.namespace      = :human
      self.validate_float = true

      def convert # :nodoc:
        @number = RoundingHelper.new(options).round(number)
        @number = Float(number)

        # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
        unless options.key?(:strip_insignificant_zeros)
          options[:strip_insignificant_zeros] = true
        end

        units = opts[:units]
        exponent = calculate_exponent(units)
        @number = number / (10**exponent)

        rounded_number = NumberToRoundedConverter.convert(number, options)
        unit = determine_unit(units, exponent)
        format.gsub("%n".freeze, rounded_number).gsub("%u".freeze, unit).strip
      end

      private

        def format
          options[:format] || translate_in_locale("human.decimal_units.format")
        end

        def determine_unit(units, exponent)
          exp = DECIMAL_UNITS[exponent]
          case units
          when Hash
            units[exp] || ""
          when String, Symbol
            I18n.translate("#{units}.#{exp}", locale: options[:locale], count: number.to_i)
          else
            translate_in_locale("human.decimal_units.units.#{exp}", count: number.to_i)
          end
        end

        def calculate_exponent(units)
          exponent = number != 0 ? Math.log10(number.abs).floor : 0
          unit_exponents(units).find { |e| exponent >= e } || 0
        end

        def unit_exponents(units)
          case units
          when Hash
            units
          when String, Symbol
            I18n.translate(units.to_s, locale: options[:locale], raise: true)
          when nil
            translate_in_locale("human.decimal_units.units", raise: true)
          else
            raise ArgumentError, ":units must be a Hash or String translation scope."
          end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by(&:-@)
        end
    end
  end
end