-
Notifications
You must be signed in to change notification settings - Fork 84
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Separate core_ext #88
Changes from all commits
a84b371
9acb896
21c48e2
c23bb1e
3a817e6
500e185
0abcfc9
68cdc35
a347bbb
9339246
32bd066
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,133 +1,4 @@ | ||
require 'bigdecimal' | ||
require_relative 'humanize/locales' | ||
# frozen_string_literal: true | ||
|
||
module Humanize | ||
SPACE = ' '.freeze | ||
EMPTY = ''.freeze | ||
# Big numbers are big: http://wiki.answers.com/Q/What_number_is_after_vigintillion&src=ansTT | ||
|
||
def humanize(locale: Humanize.config.default_locale, | ||
decimals_as: Humanize.config.decimals_as) | ||
locale_class, spacer = Humanize.for_locale(locale) | ||
|
||
return locale_class::SUB_ONE_GROUPING[0] if zero? | ||
|
||
infinity = to_f.infinite? | ||
if infinity | ||
infinity_word = locale_class::INFINITY | ||
return infinity == 1 ? infinity_word : "#{locale_class::NEGATIVE}#{spacer}#{infinity_word}" | ||
elsif is_a?(Float) && nan? | ||
return locale_class::UNDEFINED | ||
end | ||
|
||
sign = locale_class::NEGATIVE if negative? | ||
|
||
parts = locale_class.new.humanize(abs) | ||
process_decimals(locale_class, locale, parts, decimals_as, spacer) | ||
Humanize.stringify(parts, sign, spacer) | ||
end | ||
|
||
def self.for_locale(locale) | ||
case locale.to_sym | ||
# NOTE: add locales here in ealphabetical order | ||
when :az, :de, :en, :es, :fr, :id, :ms, :pt, :ru, :vi | ||
[Object.const_get("Humanize::#{locale.capitalize}"), SPACE] | ||
when :th | ||
[Humanize::Th, EMPTY] | ||
when :tr | ||
[Humanize::Tr, SPACE] | ||
when :jp | ||
[Humanize::Jp, EMPTY] | ||
when :'zh-tw' | ||
[Humanize::ZhTw, EMPTY] | ||
when :'fr-CH' | ||
[Humanize::FrCh, SPACE] | ||
else | ||
raise "Unsupported humanize locale: #{locale}" | ||
end | ||
end | ||
|
||
def self.stringify(parts, sign, spacer) | ||
output = parts.reverse.join(spacer).squeeze(spacer) | ||
if locale_is?(:es) && sign | ||
"#{output}#{spacer}#{sign}" | ||
elsif sign | ||
"#{sign}#{spacer}#{output}" | ||
else | ||
output | ||
end | ||
end | ||
|
||
def self.locale_is?(locale) | ||
Humanize.config.default_locale == locale | ||
end | ||
|
||
def process_decimals(locale_class, locale, parts, decimals_as, spacer) | ||
return unless is_a?(Float) || is_a?(BigDecimal) | ||
|
||
# Why 15? | ||
# (byebug) BigDecimal.new(number, 15) | ||
# 0.8000015e1 | ||
# (byebug) BigDecimal.new(number, 16) | ||
# 0.8000014999999999e1 | ||
decimal = BigDecimal(self, 15) - BigDecimal(to_i) | ||
|
||
_sign, significant_digits, _base, exponent = decimal.split | ||
return if significant_digits == "0" | ||
|
||
grouping = locale_class::SUB_ONE_GROUPING | ||
leading_zeroes = [grouping[0]] * exponent.abs | ||
decimals_as = :digits if leading_zeroes.any? | ||
|
||
decimals_as_words = | ||
case decimals_as | ||
when :digits | ||
digits = significant_digits.chars.map do |num| | ||
grouping[num.to_i] | ||
end | ||
|
||
(leading_zeroes + digits).join(spacer) | ||
when :number | ||
significant_digits.to_i.humanize(locale:) | ||
end | ||
|
||
parts.insert(0, decimals_as_words, locale_class::POINT) | ||
end | ||
|
||
class << self | ||
attr_writer :config | ||
end | ||
|
||
def self.config | ||
@config ||= Configuration.new | ||
end | ||
|
||
def self.reset_config | ||
@config = Configuration.new | ||
end | ||
|
||
def self.configure | ||
yield(config) | ||
end | ||
|
||
class Configuration | ||
attr_accessor :default_locale, :decimals_as | ||
|
||
def initialize | ||
@default_locale = :en | ||
@decimals_as = :digits | ||
end | ||
end | ||
end | ||
|
||
class Integer | ||
include Humanize | ||
end | ||
|
||
class Float | ||
include Humanize | ||
end | ||
|
||
class BigDecimal | ||
include Humanize | ||
end | ||
require "humanize/module" # require just this if you don't need the core_ext extensions to Integer, Float, BigDecimal | ||
require "humanize/core_ext" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
module Humanize | ||
def humanize(locale: Humanize.config.default_locale, | ||
decimals_as: Humanize.config.decimals_as) | ||
Humanize.format(self, | ||
locale:, | ||
decimals_as:) | ||
end | ||
end | ||
|
||
class Integer | ||
include Humanize | ||
end | ||
|
||
class Float | ||
include Humanize | ||
end | ||
|
||
class BigDecimal | ||
include Humanize | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
require 'bigdecimal' | ||
require_relative 'locales' | ||
|
||
module Humanize | ||
# Big numbers are big: http://wiki.answers.com/Q/What_number_is_after_vigintillion&src=ansTT | ||
|
||
class << self | ||
def format(number, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this changed from |
||
locale: Humanize.config.default_locale, | ||
decimals_as: Humanize.config.decimals_as) | ||
locale_class, spacer = Humanize.for_locale(locale) | ||
|
||
return locale_class::SUB_ONE_GROUPING[0] if number.zero? | ||
|
||
infinity = number.to_f.infinite? | ||
if infinity | ||
infinity_word = locale_class::INFINITY | ||
return infinity == 1 ? infinity_word : "#{locale_class::NEGATIVE}#{spacer}#{infinity_word}" | ||
elsif number.is_a?(Float) && number.nan? | ||
return locale_class::UNDEFINED | ||
end | ||
|
||
sign = locale_class::NEGATIVE if number.negative? | ||
|
||
parts = locale_class.new.humanize(number.abs) | ||
process_decimals(number, locale_class, locale, parts, decimals_as, spacer) | ||
Humanize.stringify(parts, sign, spacer) | ||
end | ||
|
||
def for_locale(locale) | ||
case locale.to_sym | ||
# NOTE: add locales here in alphabetical order | ||
when :az, :de, :en, :es, :fr, :id, :ms, :pt, :ru, :vi | ||
[Object.const_get("Humanize::#{locale.capitalize}"), ' '] | ||
when :th | ||
[Humanize::Th, ''] | ||
when :tr | ||
[Humanize::Tr, ' '] | ||
when :jp | ||
[Humanize::Jp, ''] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is missing the recent zh-TW addition. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @radar Oops, I didn't mean to make any changes to this locale list. I just looked over in And I'm looking at the build failures. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The build failures were rubocop suggestions. All addressed here: 0abcfc9 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, merged #87. So can we now get that in here? I think it's just the two lines for the when + the config. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, I've merged latest |
||
when :'zh-tw' | ||
[Humanize::ZhTw, ''] | ||
when :'fr-CH' | ||
[Humanize::FrCh, ' '] | ||
else | ||
raise "Unsupported humanize locale: #{locale}" | ||
end | ||
end | ||
|
||
def stringify(parts, sign, spacer) | ||
output = parts.reverse.join(spacer).squeeze(spacer) | ||
if locale_is?(:es) && sign | ||
"#{output}#{spacer}#{sign}" | ||
elsif sign | ||
"#{sign}#{spacer}#{output}" | ||
else | ||
output | ||
end | ||
end | ||
|
||
def locale_is?(locale) | ||
Humanize.config.default_locale == locale | ||
end | ||
|
||
# rubocop:disable Metrics/ParameterLists | ||
def process_decimals(number, locale_class, locale, parts, decimals_as, spacer) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note that this previously polluted the mixin namespace (it became a public method of |
||
# rubocop:enable Metrics/ParameterLists | ||
return unless number.is_a?(Float) || number.is_a?(BigDecimal) | ||
|
||
# Why 15? | ||
# (byebug) BigDecimal.new(number, 15) | ||
# 0.8000015e1 | ||
# (byebug) BigDecimal.new(number, 16) | ||
# 0.8000014999999999e1 | ||
decimal = BigDecimal(number, 15) - BigDecimal(number.to_i) | ||
|
||
_sign, significant_digits, _base, exponent = decimal.split | ||
return if significant_digits == "0" | ||
|
||
grouping = locale_class::SUB_ONE_GROUPING | ||
leading_zeroes = [grouping[0]] * exponent.abs | ||
decimals_as = :digits if leading_zeroes.any? | ||
|
||
decimals_as_words = | ||
case decimals_as | ||
when :digits | ||
digits = significant_digits.chars.map do |num| | ||
grouping[num.to_i] | ||
end | ||
|
||
(leading_zeroes + digits).join(spacer) | ||
when :number | ||
Humanize.format(significant_digits.to_i, locale:) | ||
end | ||
|
||
parts.insert(0, decimals_as_words, locale_class::POINT) | ||
end | ||
|
||
attr_writer :config | ||
|
||
def config | ||
@config ||= Configuration.new | ||
end | ||
|
||
def reset_config | ||
@config = Configuration.new | ||
end | ||
|
||
def configure | ||
yield(config) | ||
end | ||
end | ||
|
||
class Configuration | ||
attr_accessor :default_locale, :decimals_as | ||
|
||
def initialize | ||
@default_locale = :en | ||
@decimals_as = :digits | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is the glue code that converts from implicit
self
arg to explicitself
passed as thenumber
arg.