提交 fc73ebf3 编写于 作者: R Rafael Mendonça França

Merge pull request #10996 from mattdbridges/number-helper-refactor

Refactor and clean up number helpers

Conflicts:
	activesupport/lib/active_support/number_helper.rb
require 'active_support/core_ext/big_decimal/conversions'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/hash/keys'
require 'active_support/i18n'
module ActiveSupport
module NumberHelper
extend self
DEFAULTS = {
# Used in number_to_delimited
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format: {
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: ".",
# Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ",",
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3,
# If set to true, precision will mean the number of significant digits instead
# of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
significant: false,
# If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
strip_insignificant_zeros: false
},
# Used in number_to_currency
currency: {
format: {
format: "%u%n",
negative_format: "-%u%n",
unit: "$",
# These five are to override number.format and are optional
separator: ".",
delimiter: ",",
precision: 2,
significant: false,
strip_insignificant_zeros: false
}
},
# Used in number_to_percentage
percentage: {
format: {
delimiter: "",
format: "%n%"
}
},
# Used in number_to_rounded
precision: {
format: {
delimiter: ""
}
},
autoload :NumberToRoundedConverter, "active_support/number_helper/number_to_rounded"
autoload :NumberToDelimitedConverter, "active_support/number_helper/number_to_delimited"
autoload :NumberToHumanConverter, "active_support/number_helper/number_to_human"
autoload :NumberToHumanSizeConverter, "active_support/number_helper/number_to_human_size"
autoload :NumberToPhoneConverter, "active_support/number_helper/number_to_phone"
autoload :NumberToCurrencyConverter, "active_support/number_helper/number_to_currency"
autoload :NumberToPercentageConverter, "active_support/number_helper/number_to_percentage"
# Used in number_to_human_size and number_to_human
human: {
format: {
# These five are to override number.format and are optional
delimiter: "",
precision: 3,
significant: true,
strip_insignificant_zeros: true
},
# Used in number_to_human_size
storage_units: {
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u",
units: {
byte: "Bytes",
kb: "KB",
mb: "MB",
gb: "GB",
tb: "TB"
}
},
# Used in number_to_human
decimal_units: {
format: "%n %u",
# Decimal units output formatting
# By default we will only quantify some of the exponents
# but the commented ones might be defined or overridden
# by the user.
units: {
# femto: Quadrillionth
# pico: Trillionth
# nano: Billionth
# micro: Millionth
# mili: Thousandth
# centi: Hundredth
# deci: Tenth
unit: "",
# ten:
# one: Ten
# other: Tens
# hundred: Hundred
thousand: "Thousand",
million: "Million",
billion: "Billion",
trillion: "Trillion",
quadrillion: "Quadrillion"
}
}
}
}
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
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
extend self
# Formats a +number+ into a US phone number (e.g., (555)
# 123-9876). You can customize the format in the +options+ hash.
......@@ -137,27 +38,7 @@ module NumberHelper
# number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
# # => +1.123.555.1234 x 1343
def number_to_phone(number, options = {})
return unless number
options = options.symbolize_keys
number = number.to_s.strip
area_code = options[:area_code]
delimiter = options[:delimiter] || "-"
extension = options[:extension]
country_code = options[:country_code]
if area_code
number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
else
number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
number.slice!(0, 1) if number.start_with?(delimiter) && !delimiter.blank?
end
str = ''
str << "+#{country_code}#{delimiter}" unless country_code.blank?
str << number
str << " x #{extension}" unless extension.blank?
str
NumberToPhoneConverter.new(number, options).execute
end
# Formats a +number+ into a currency string (e.g., $13.65). You
......@@ -199,25 +80,7 @@ def number_to_phone(number, options = {})
# number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
# # => 1234567890,50 &pound;
def number_to_currency(number, options = {})
return unless number
options = options.symbolize_keys
currency = i18n_format_options(options[:locale], :currency)
currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
defaults = default_format_options(:currency).merge!(currency)
defaults[:negative_format] = "-" + options[:format] if options[:format]
options = defaults.merge!(options)
unit = options.delete(:unit)
format = options.delete(:format)
if number.to_f.phase != 0
format = options.delete(:negative_format)
number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
end
format.gsub('%n', self.number_to_rounded(number, options)).gsub('%u', unit)
NumberToCurrencyConverter.new(number, options).execute
end
# Formats a +number+ as a percentage string (e.g., 65%). You can
......@@ -253,14 +116,7 @@ def number_to_currency(number, options = {})
# number_to_percentage('98a') # => 98a%
# number_to_percentage(100, format: '%n %') # => 100 %
def number_to_percentage(number, options = {})
return unless number
options = options.symbolize_keys
defaults = format_options(options[:locale], :percentage)
options = defaults.merge!(options)
format = options[:format] || "%n%"
format.gsub('%n', self.number_to_rounded(number, options))
NumberToPercentageConverter.new(number, options).execute
end
# Formats a +number+ with grouped thousands using +delimiter+
......@@ -289,15 +145,7 @@ def number_to_percentage(number, options = {})
# number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
# # => 98 765 432,98
def number_to_delimited(number, options = {})
options = options.symbolize_keys
return number unless valid_float?(number)
options = format_options(options[:locale]).merge!(options)
parts = number.to_s.split('.')
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
parts.join(options[:separator])
NumberToDelimitedConverter.new(number, options).execute
end
# Formats a +number+ with the specified level of
......@@ -340,39 +188,7 @@ def number_to_delimited(number, options = {})
# number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
# # => 1.111,23
def number_to_rounded(number, options = {})
return number unless valid_float?(number)
number = Float(number)
options = options.symbolize_keys
defaults = format_options(options[:locale], :precision)
options = defaults.merge!(options)
precision = options.delete :precision
significant = options.delete :significant
strip_insignificant_zeros = options.delete :strip_insignificant_zeros
if significant && precision > 0
if number == 0
digits, rounded_number = 1, 0
else
digits = (Math.log10(number.abs) + 1).floor
multiplier = 10 ** (digits - precision)
rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier
digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
end
precision -= digits
precision = 0 if precision < 0 # don't let it be negative
else
rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
end
formatted_number = self.number_to_delimited("%01.#{precision}f" % rounded_number, options)
if strip_insignificant_zeros
escaped_separator = Regexp.escape(options[:separator])
formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
else
formatted_number
end
NumberToRoundedConverter.new(number, options).execute
end
# Formats the bytes in +number+ into a more understandable
......@@ -420,36 +236,7 @@ def number_to_rounded(number, options = {})
# number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
# number_to_human_size(524288000, precision: 5) # => "500 MB"
def number_to_human_size(number, options = {})
options = options.symbolize_keys
return number unless valid_float?(number)
number = Float(number)
defaults = format_options(options[:locale], :human)
options = defaults.merge!(options)
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
storage_units_format = translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
base = options[:prefix] == :si ? 1000 : 1024
if number.to_i < base
unit = translate_number_value_with_default('human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
else
max_exp = STORAGE_UNITS.size - 1
exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
number /= base ** exponent
unit_key = STORAGE_UNITS[exponent]
unit = translate_number_value_with_default("human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
formatted_number = self.number_to_rounded(number, options)
storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
end
NumberToHumanSizeConverter.new(number, options).execute
end
# Pretty prints (formats and approximates) a number in a way it
......@@ -550,86 +337,8 @@ def number_to_human_size(number, options = {})
# number_to_human(1, units: :distance) # => "1 meter"
# number_to_human(0.34, units: :distance) # => "34 centimeters"
def number_to_human(number, options = {})
options = options.symbolize_keys
return number unless valid_float?(number)
number = Float(number)
defaults = format_options(options[:locale], :human)
options = defaults.merge!(options)
#for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
units = options.delete :units
unit_exponents = case units
when Hash
units
when String, Symbol
I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
when nil
translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :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!{|e| -e}
number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
number /= 10 ** display_exponent
unit = case units
when Hash
units[DECIMAL_UNITS[display_exponent]] || ''
when String, Symbol
I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
else
translate_number_value_with_default("human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
end
decimal_format = options[:format] || translate_number_value_with_default('human.decimal_units.format', :locale => options[:locale])
formatted_number = self.number_to_rounded(number, options)
decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip
NumberToHumanConverter.new(number, options).execute
end
def self.private_module_and_instance_method(method_name) #:nodoc:
private method_name
private_class_method method_name
end
private_class_method :private_module_and_instance_method
def format_options(locale, namespace = nil) #:nodoc:
default_format_options(namespace).merge!(i18n_format_options(locale, namespace))
end
private_module_and_instance_method :format_options
def default_format_options(namespace = nil) #:nodoc:
options = DEFAULTS[:format].dup
options.merge!(DEFAULTS[namespace][:format]) if namespace
options
end
private_module_and_instance_method :default_format_options
def i18n_format_options(locale, namespace = nil) #:nodoc:
options = I18n.translate(:'number.format', locale: locale, default: {}).dup
if namespace
options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
end
options
end
private_module_and_instance_method :i18n_format_options
def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
default = key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
I18n.translate(key, { default: default, scope: :number }.merge!(i18n_options))
end
private_module_and_instance_method :translate_number_value_with_default
def valid_float?(number) #:nodoc:
Float(number)
rescue ArgumentError, TypeError
false
end
private_module_and_instance_method :valid_float?
end
end
require 'active_support/core_ext/big_decimal/conversions'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/hash/keys'
require 'active_support/i18n'
require 'active_support/core_ext/class/attribute'
module ActiveSupport
module NumberHelper
class NumberConverter # :nodoc:
# Default and i18n option namespace per class
class_attribute :namespace
# Does the object need a number that is a valid float?
class_attribute :need_valid_float
attr_reader :number, :opts
DEFAULTS = {
# Used in number_to_delimited
# These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
format: {
# Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
separator: ".",
# Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
delimiter: ",",
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3,
# If set to true, precision will mean the number of significant digits instead
# of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
significant: false,
# If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
strip_insignificant_zeros: false
},
# Used in number_to_currency
currency: {
format: {
format: "%u%n",
negative_format: "-%u%n",
unit: "$",
# These five are to override number.format and are optional
separator: ".",
delimiter: ",",
precision: 2,
significant: false,
strip_insignificant_zeros: false
}
},
# Used in number_to_percentage
percentage: {
format: {
delimiter: "",
format: "%n%"
}
},
# Used in number_to_rounded
precision: {
format: {
delimiter: ""
}
},
# Used in number_to_human_size and number_to_human
human: {
format: {
# These five are to override number.format and are optional
delimiter: "",
precision: 3,
significant: true,
strip_insignificant_zeros: true
},
# Used in number_to_human_size
storage_units: {
# Storage units output formatting.
# %u is the storage unit, %n is the number (default: 2 MB)
format: "%n %u",
units: {
byte: "Bytes",
kb: "KB",
mb: "MB",
gb: "GB",
tb: "TB"
}
},
# Used in number_to_human
decimal_units: {
format: "%n %u",
# Decimal units output formatting
# By default we will only quantify some of the exponents
# but the commented ones might be defined or overridden
# by the user.
units: {
# femto: Quadrillionth
# pico: Trillionth
# nano: Billionth
# micro: Millionth
# mili: Thousandth
# centi: Hundredth
# deci: Tenth
unit: "",
# ten:
# one: Ten
# other: Tens
# hundred: Hundred
thousand: "Thousand",
million: "Million",
billion: "Billion",
trillion: "Trillion",
quadrillion: "Quadrillion"
}
}
}
}
def initialize(number, opts = {})
@number = number
@opts = opts.symbolize_keys
end
def execute
return unless @number
return @number if need_valid_float? && !valid_float?
convert
end
private
def options
@options ||= format_options.merge(opts)
end
def format_options #:nodoc:
default_format_options.merge!(i18n_format_options)
end
def default_format_options #:nodoc:
options = DEFAULTS[:format].dup
options.merge!(DEFAULTS[namespace][:format]) if namespace
options
end
def i18n_format_options #:nodoc:
locale = opts[:locale]
options = I18n.translate(:'number.format', locale: locale, default: {}).dup
if namespace
options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
end
options
end
def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
end
def translate_in_locale(key, i18n_options = {})
translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
end
def default_value(key)
key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
end
def valid_float? #:nodoc:
Float(@number)
rescue ArgumentError, TypeError
false
end
end
end
end
require 'active_support/number_helper/number_converter'
require 'active_support/number_helper/number_to_rounded'
module ActiveSupport
module NumberHelper
class NumberToCurrencyConverter < NumberConverter # :nodoc:
self.namespace = :currency
def convert
number = @number.to_s.strip
format = options[:format]
if is_negative?(number)
format = options[:negative_format]
number = absolute_value(number)
end
rounded_number = NumberToRoundedConverter.new(number, options).execute
format.gsub('%n', rounded_number).gsub('%u', options[:unit])
end
private
def is_negative?(number)
number.to_f.phase != 0
end
def absolute_value(number)
number.respond_to?("abs") ? number.abs : number.sub(/\A-/, '')
end
def options
@options ||= begin
defaults = default_format_options.merge(i18n_opts)
# Override negative format if format options is given
defaults[:negative_format] = "-#{opts[:format]}" if opts[:format]
defaults.merge(opts)
end
end
def i18n_opts
# Set International negative format if not exists
i18n = i18n_format_options
i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format]
i18n
end
end
end
end
require 'active_support/number_helper/number_converter'
module ActiveSupport
module NumberHelper
class NumberToDelimitedConverter < NumberConverter #:nodoc:
self.need_valid_float = true
DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
def convert
parts.join(options[:separator])
end
private
def parts
left, right = number.to_s.split('.')
left.gsub!(DELIMITED_REGEX) { "#{$1}#{options[:delimiter]}" }
[left, right].compact
end
end
end
end
require 'active_support/number_helper/number_converter'
require 'active_support/number_helper/number_to_rounded'
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.need_valid_float = true
def convert # :nodoc:
@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)
unit = determine_unit(units, exponent)
rounded_number = NumberToRoundedConverter.new(@number, options).execute
format.gsub(/%n/, rounded_number).gsub(/%u/, 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 { |e| -e }
end
end
end
end
require 'active_support/number_helper/number_converter'
require 'active_support/number_helper/number_to_rounded'
module ActiveSupport
module NumberHelper
class NumberToHumanSizeConverter < NumberConverter
STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
self.namespace = :human
self.need_valid_float = true
def convert
@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
if smaller_than_base?
number_to_format = @number.to_i.to_s
else
human_size = @number / (base ** exponent)
number_to_format = NumberToRoundedConverter.new(human_size, options).execute
end
conversion_format.gsub(/%n/, number_to_format).gsub(/%u/, unit)
end
private
def conversion_format
translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
end
def unit
translate_number_value_with_default(storage_unit_key, :locale => options[:locale], :count => @number.to_i, :raise => true)
end
def storage_unit_key
key_end = smaller_than_base? ? 'byte' : STORAGE_UNITS[exponent]
"human.storage_units.units.#{key_end}"
end
def exponent
max = STORAGE_UNITS.size - 1
exp = (Math.log(@number) / Math.log(base)).to_i
exp = max if exp > max # avoid overflow for the highest unit
exp
end
def smaller_than_base?
@number.to_i < base
end
def base
opts[:prefix] == :si ? 1000 : 1024
end
end
end
end
require 'active_support/number_helper/number_converter'
require 'active_support/number_helper/number_to_rounded'
module ActiveSupport
module NumberHelper
class NumberToPercentageConverter < NumberConverter # :nodoc:
self.namespace = :percentage
def convert
rounded_number = NumberToRoundedConverter.new(number, options).execute
options[:format].gsub('%n', rounded_number)
end
end
end
end
require 'active_support/number_helper/number_converter'
require 'active_support/number_helper/number_to_rounded'
module ActiveSupport
module NumberHelper
class NumberToPhoneConverter < NumberConverter
def convert
str = ''
str << country_code(opts[:country_code])
str << convert_to_phone_number(@number.to_s.strip)
str << phone_ext(opts[:extension])
end
private
def convert_to_phone_number(number)
if opts[:area_code]
convert_with_area_code(number)
else
convert_without_area_code(number)
end
end
def convert_with_area_code(number)
number.gsub(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
end
def convert_without_area_code(number)
number.tap { |n|
n.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
n.slice!(0, 1) if begins_with_delimiter?(n)
}
end
def begins_with_delimiter?(number)
number.start_with?(delimiter) && !delimiter.blank?
end
def delimiter
opts[:delimiter] || "-"
end
def country_code(code)
code.blank? ? "" : "+#{code}#{delimiter}"
end
def phone_ext(ext)
ext.blank? ? "" : " x #{ext}"
end
end
end
end
require 'active_support/number_helper/number_converter'
module ActiveSupport
module NumberHelper
class NumberToRoundedConverter < NumberConverter # :nodoc:
self.namespace = :precision
self.need_valid_float = true
def convert
@number = Float(@number)
precision = options.delete :precision
significant = options.delete :significant
if significant && precision > 0
digits, rounded_number = digits_and_rounded_number(precision)
precision -= digits
precision = 0 if precision < 0 # don't let it be negative
else
rounded_number = BigDecimal.new(@number.to_s).round(precision).to_f
rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
end
delimited_number = NumberToDelimitedConverter.new("%01.#{precision}f" % rounded_number, options).execute
format_number(delimited_number)
end
private
def digits_and_rounded_number(precision)
return [1,0] if @number.zero?
digits = digit_count(@number)
multiplier = 10 ** (digits - precision)
rounded_number = calculate_rounded_number(multiplier)
digits = digit_count(rounded_number) # After rounding, the number of digits may have changed
[digits, rounded_number]
end
def calculate_rounded_number(multiplier)
(BigDecimal.new(@number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier
end
def digit_count(number)
(Math.log10(number.abs) + 1).floor
end
def strip_insignificant_zeros
options[:strip_insignificant_zeros]
end
def format_number(number)
if strip_insignificant_zeros
escaped_separator = Regexp.escape(options[:separator])
number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
else
number
end
end
end
end
end
......@@ -370,12 +370,6 @@ def test_number_helpers_should_return_non_numeric_param_unchanged
end
end
def test_extending_or_including_number_helper_correctly_hides_private_methods
[@instance_with_helpers, TestClassWithClassNumberHelpers, ActiveSupport::NumberHelper].each do |number_helper|
assert !number_helper.respond_to?(:valid_float?)
assert number_helper.respond_to?(:valid_float?, true)
end
end
end
end
end
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册