module ActionView::Helpers::NumberHelper
def delegate_number_helper_method(method, number, options)
def delegate_number_helper_method(method, number, options) return unless number options = escape_unsafe_options(options.symbolize_keys) wrap_with_output_safety_handling(number, options.delete(:raise)) { ActiveSupport::NumberHelper.public_send(method, number, options) } end
def escape_units(units)
def escape_units(units) units.transform_values do |v| ERB::Util.html_escape(v) end end
def escape_unsafe_options(options)
def escape_unsafe_options(options) options[:format] = ERB::Util.html_escape(options[:format]) if options[:format] options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format] options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator] options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter] options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe? options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units] options end
def number_to_currency(number, options = {})
number_to_currency(1234567890.50, strip_insignificant_zeros: true)
# => 1234567890,50 R$
number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
# => R$1234567890,50
number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
# => ($1,234,567,890.50)
number_to_currency(-1234567890.50, negative_format: "(%u%n)")
# => "$0"
number_to_currency(-0.456789, precision: 0)
number_to_currency("123a456", raise: true) # => InvalidNumberError
number_to_currency("123a456") # => $123a456
number_to_currency(1234567890.506, locale: :fr) # => 1 234 567 890,51 €
number_to_currency(1234567890.506, precision: 3) # => $1,234,567,890.506
number_to_currency(1234567890.506) # => $1,234,567,890.51
number_to_currency(1234567890.50) # => $1,234,567,890.50
==== Examples
+false+).
insignificant zeros after the decimal separator (defaults to
* :strip_insignificant_zeros - If +true+ removes
the argument is invalid.
* :raise - If true, raises +InvalidNumberError+ when
absolute value of the number.
than :format, except %n is here the
number given by :format). Accepts the same fields
numbers (defaults to prepending a hyphen to the formatted
* :negative_format - Sets the format for negative
currency, and %n for the number.
(defaults to "%u%n"). Fields are %u for the
* :format - Sets the format for non-negative numbers
to ",").
* :delimiter - Sets the thousands delimiter (defaults
(defaults to ".").
* :separator - Sets the separator between the units
(defaults to "$").
* :unit - Sets the denomination of the currency
to 2).
* :precision - Sets the level of precision (defaults
(defaults to current locale).
* :locale - Sets the locale to be used for formatting
==== Options
using a library capable of currency conversion.
may want to specify a constant :locale option or consider
this helper. If your application will ever support multiple locales, you
also be able to change the relative value of the currency displayed with
is performed. If the user is given a way to change their locale, they will
unless otherwise specified in the provided options. No currency conversion
The currency unit and number formatting of the current locale will be used
can customize the format in the +options+ hash.
Formats a +number+ into a currency string (e.g., $13.65). You
def number_to_currency(number, options = {}) delegate_number_helper_method(:number_to_currency, number, options) end
def number_to_human(number, options = {})
number_to_human(0.34, units: :distance) # => "34 centimeters"
number_to_human(1, units: :distance) # => "1 meter"
number_to_human(343, units: :distance, precision: 1) # => "300 meters"
number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
number_to_human(54393498, units: :distance) # => "54400 kilometers"
number_to_human(543934, units: :distance) # => "544 kilometers"
Then you could do:
billion: "gazillion-distance"
other: "kilometers"
one: "kilometer"
thousand:
other: "meters"
one: "meter"
unit:
other: "centimeters"
one: "centimeter"
centi:
distance:
If in your I18n locale you have:
number_to_human(500000, units: {unit: "ml", thousand: "lt"}) # => "500 lt"
You can also use your own custom unit quantifiers:
==== Custom Unit Quantifiers
number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
number_to_human(12.00001) # => "12"
+false+ to change that):
out by default (set :strip_insignificant_zeros to
Non-significant zeros after the decimal separator are stripped
number_to_human(12345012345, significant: false) # => "12.345 Billion"
number_to_human(500000000, precision: 5) # => "500 Million"
significant: false) # => "1,2 Million"
separator: ',',
number_to_human(1234567, precision: 1,
significant: false) # => "1.2346 Million"
number_to_human(1234567, precision: 4,
number_to_human(489939, precision: 4) # => "489.9 Thousand"
number_to_human(489939, precision: 2) # => "490 Thousand"
number_to_human(1234567890123456789) # => "1230 Quadrillion"
number_to_human(1234567890123456) # => "1.23 Quadrillion"
number_to_human(1234567890123) # => "1.23 Trillion"
number_to_human(1234567890) # => "1.23 Billion"
number_to_human(1234567) # => "1.23 Million"
number_to_human(12345) # => "12.3 Thousand"
number_to_human(1234) # => "1.23 Thousand"
number_to_human(123) # => "123"
==== Examples
the argument is invalid.
* :raise - If true, raises +InvalidNumberError+ when
* %n - The number
* %u - The quantifier (ex.: 'thousand')
(defaults to "%n %u"). The field types are:
* :format - Sets the format of the output string
:pico, :femto
:mili, :micro, :nano,
* *fractionals*: :deci, :centi,
:quadrillion
:billion, :trillion,
:hundred, :thousand, :million,
* *integers*: :unit, :ten,
might have the following keys:
string containing an i18n scope where to find this hash. It
* :units - A Hash of unit quantifier names. Or a
+true+)
insignificant zeros after the decimal separator (defaults to
* :strip_insignificant_zeros - If +true+ removes
to "").
* :delimiter - Sets the thousands delimiter (defaults
fractional and integer digits (defaults to ".").
* :separator - Sets the separator between the
digits (defaults to +true+)
of significant_digits. If +false+, the number of fractional
* :significant - If +true+, precision will be the number
(defaults to 3).
* :precision - Sets the precision of the number
(defaults to current locale).
* :locale - Sets the locale to be used for formatting
==== Options
(centi, deci, mili, etc).
define a wide range of unit quantifiers, even fractional ones
kilometers", 0.150 becomes "150 milliliters", etc). You may
to use other decimal units (e.g.: 1500 becomes "1.5
You can also define your own unit-quantifier names if you want
size.
See number_to_human_size if you want to print a file
(and too hard to read).
Billion"). This is useful for numbers that can get very large
is more readable by humans (e.g.: 1200000000 becomes "1.2
Pretty prints (formats and approximates) a number in a way it
def number_to_human(number, options = {}) delegate_number_helper_method(:number_to_human, number, options) end
def number_to_human_size(number, options = {})
number_to_human_size(1234567890123, precision: 5) # => "1.1228 TB"
number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
number_to_human_size(483989, precision: 2) # => 470 KB
number_to_human_size(1234567, precision: 2) # => 1.2 MB
number_to_human_size(1234567890123456789) # => 1.07 EB
number_to_human_size(1234567890123456) # => 1.1 PB
number_to_human_size(1234567890123) # => 1.12 TB
number_to_human_size(1234567890) # => 1.15 GB
number_to_human_size(1234567) # => 1.18 MB
number_to_human_size(12345) # => 12.1 KB
number_to_human_size(1234) # => 1.21 KB
number_to_human_size(123) # => 123 Bytes
==== Examples
the argument is invalid.
* :raise - If true, raises +InvalidNumberError+ when
+true+)
insignificant zeros after the decimal separator (defaults to
* :strip_insignificant_zeros - If +true+ removes
to "").
* :delimiter - Sets the thousands delimiter (defaults
fractional and integer digits (defaults to ".").
* :separator - Sets the separator between the
digits (defaults to +true+)
of significant_digits. If +false+, the number of fractional
* :significant - If +true+, precision will be the number
(defaults to 3).
* :precision - Sets the precision of the number
(defaults to current locale).
* :locale - Sets the locale to be used for formatting
==== Options
generic number.
See number_to_human if you want to pretty-print a
customize the format in the +options+ hash.
method is useful for reporting file sizes to users. You can
representation (e.g., giving it 1500 yields 1.46 KB). This
Formats the bytes in +number+ into a more understandable
def number_to_human_size(number, options = {}) delegate_number_helper_method(:number_to_human_size, number, options) end
def number_to_percentage(number, options = {})
number_to_percentage(100, format: "%n %") # => 100.000 %
number_to_percentage("98a") # => 98a%
number_to_percentage(1000, locale: :fr) # => 1 000,000%
number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
number_to_percentage(1000, delimiter: '.', separator: ',') # => 1.000,000%
number_to_percentage(100, precision: 0) # => 100%
number_to_percentage("98") # => 98.000%
number_to_percentage(100) # => 100.000%
==== Examples
the argument is invalid.
* :raise - If true, raises +InvalidNumberError+ when
string The number field is %n (defaults to "%n%").
* :format - Specifies the format of the percentage
+false+).
insignificant zeros after the decimal separator (defaults to
* :strip_insignificant_zeros - If +true+ removes
to "").
* :delimiter - Sets the thousands delimiter (defaults
fractional and integer digits (defaults to ".").
* :separator - Sets the separator between the
digits (defaults to +false+).
of significant_digits. If +false+, the number of fractional
* :significant - If +true+, precision will be the number
(defaults to 3).
* :precision - Sets the precision of the number
(defaults to current locale).
* :locale - Sets the locale to be used for formatting
==== Options
customize the format in the +options+ hash.
Formats a +number+ as a percentage string (e.g., 65%). You can
def number_to_percentage(number, options = {}) delegate_number_helper_method(:number_to_percentage, number, options) end
def number_to_phone(number, options = {})
number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
# => "(755) 6123-4567"
number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
# => +1.123.555.1234 x 1343
number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
number_to_phone("1234a567", raise: true) # => InvalidNumberError
number_to_phone("123a456") # => 123a456
number_to_phone(1235551234, country_code: 1) # => +1-123-555-1234
number_to_phone(1235551234, area_code: true, extension: 555) # => (123) 555-1234 x 555
number_to_phone(1235551234, delimiter: " ") # => 123 555 1234
number_to_phone(1235551234, area_code: true) # => (123) 555-1234
number_to_phone(1235551234) # => 123-555-1234
number_to_phone("5551234") # => 555-1234
number_to_phone(5551234) # => 555-1234
==== Examples
the argument is invalid.
* :raise - If true, raises +InvalidNumberError+ when
groups with the custom regexp to override the default format.
* :pattern - Specifies how the number is divided into three
number.
* :country_code - Sets the country code for the phone
end of the generated number.
* :extension - Specifies an extension to add to the
(defaults to "-").
* :delimiter - Specifies the delimiter to use
* :area_code - Adds parentheses around the area code.
==== Options
123-9876). You can customize the format in the +options+ hash.
Formats a +number+ into a phone number (US by default e.g., (555)
def number_to_phone(number, options = {}) return unless number options = options.symbolize_keys parse_float(number, true) if options.delete(:raise) ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options)) end
def number_with_delimiter(number, options = {})
delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) # => "1,23,456.78"
number_with_delimiter("123456.78",
# => 98 765 432,98
number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
number_with_delimiter("112a") # => 112a
number_with_delimiter(12345678.05, locale: :fr) # => 12 345 678,05
number_with_delimiter(12345678.05, separator: " ") # => 12,345,678 05
number_with_delimiter(12345678, delimiter: ",") # => 12,345,678
number_with_delimiter(12345678, delimiter: ".") # => 12.345.678
number_with_delimiter(12345678.05) # => 12,345,678.05
number_with_delimiter("123456") # => 123,456
number_with_delimiter(12345678) # => 12,345,678
==== Examples
the argument is invalid.
* :raise - If true, raises +InvalidNumberError+ when
like INR.
deriving the placement of delimiter. Helpful when using currency formats
* :delimiter_pattern - Sets a custom regular expression used for
fractional and integer digits (defaults to ".").
* :separator - Sets the separator between the
to ",").
* :delimiter - Sets the thousands delimiter (defaults
(defaults to current locale).
* :locale - Sets the locale to be used for formatting
==== Options
hash.
(e.g., 12,324). You can customize the format in the +options+
Formats a +number+ with grouped thousands using +delimiter+
def number_with_delimiter(number, options = {}) delegate_number_helper_method(:number_to_delimited, number, options) end
def number_with_precision(number, options = {})
number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
number_with_precision(389.32314, precision: 4, significant: true) # => 389.3
# => 13
number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true)
number_with_precision(111.234, locale: :fr) # => 111,234
number_with_precision(13, precision: 5, significant: true) # => 13.000
number_with_precision(111.2345, precision: 1, significant: true) # => 100
number_with_precision(111.2345, significant: true) # => 111
number_with_precision(389.32314, precision: 0) # => 389
number_with_precision(13, precision: 5) # => 13.00000
number_with_precision(111.2345, precision: 2) # => 111.23
number_with_precision(111.2345) # => 111.235
==== Examples
the argument is invalid.
* :raise - If true, raises +InvalidNumberError+ when
+false+).
insignificant zeros after the decimal separator (defaults to
* :strip_insignificant_zeros - If +true+ removes
to "").
* :delimiter - Sets the thousands delimiter (defaults
fractional and integer digits (defaults to ".").
* :separator - Sets the separator between the
digits (defaults to +false+).
of significant_digits. If +false+, the number of fractional
* :significant - If +true+, precision will be the number
(defaults to 3).
* :precision - Sets the precision of the number
(defaults to current locale).
* :locale - Sets the locale to be used for formatting
==== Options
You can customize the format in the +options+ hash.
+:significant+ is +false+, and 5 if +:significant+ is +true+).
:precision (e.g., 112.32 has a precision of 2 if
Formats a +number+ with the specified level of
def number_with_precision(number, options = {}) delegate_number_helper_method(:number_to_rounded, number, options) end
def parse_float(number, raise_error)
def parse_float(number, raise_error) result = Float(number, exception: false) raise InvalidNumberError, number if result.nil? && raise_error result end
def valid_float?(number)
def valid_float?(number) !parse_float(number, false).nil? end
def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
def wrap_with_output_safety_handling(number, raise_on_invalid, &block) valid_float = valid_float?(number) raise InvalidNumberError, number if raise_on_invalid && !valid_float formatted_number = yield if valid_float || number.html_safe? formatted_number.html_safe else formatted_number end end