activesupport 4.0.13 → 4.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +283 -508
  3. data/README.rdoc +1 -1
  4. data/lib/active_support.rb +7 -1
  5. data/lib/active_support/backtrace_cleaner.rb +5 -5
  6. data/lib/active_support/benchmarkable.rb +0 -10
  7. data/lib/active_support/cache.rb +62 -26
  8. data/lib/active_support/cache/file_store.rb +27 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +2 -2
  10. data/lib/active_support/cache/memory_store.rb +1 -0
  11. data/lib/active_support/cache/strategy/local_cache.rb +3 -0
  12. data/lib/active_support/callbacks.rb +416 -245
  13. data/lib/active_support/concern.rb +13 -5
  14. data/lib/active_support/core_ext.rb +0 -1
  15. data/lib/active_support/core_ext/array.rb +0 -1
  16. data/lib/active_support/core_ext/array/access.rb +2 -0
  17. data/lib/active_support/core_ext/array/conversions.rb +2 -17
  18. data/lib/active_support/core_ext/array/grouping.rb +24 -12
  19. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
  20. data/lib/active_support/core_ext/class.rb +0 -1
  21. data/lib/active_support/core_ext/class/attribute.rb +1 -2
  22. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  23. data/lib/active_support/core_ext/date/calculations.rb +10 -0
  24. data/lib/active_support/core_ext/date/conversions.rb +5 -6
  25. data/lib/active_support/core_ext/date/zones.rb +2 -33
  26. data/lib/active_support/core_ext/date_and_time/calculations.rb +30 -11
  27. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  28. data/lib/active_support/core_ext/date_time/calculations.rb +12 -25
  29. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  30. data/lib/active_support/core_ext/date_time/zones.rb +3 -21
  31. data/lib/active_support/core_ext/hash.rb +0 -1
  32. data/lib/active_support/core_ext/hash/conversions.rb +6 -3
  33. data/lib/active_support/core_ext/hash/deep_merge.rb +11 -22
  34. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
  35. data/lib/active_support/core_ext/hash/keys.rb +27 -47
  36. data/lib/active_support/core_ext/kernel/reporting.rb +2 -6
  37. data/lib/active_support/core_ext/module.rb +1 -0
  38. data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
  39. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  40. data/lib/active_support/core_ext/module/delegation.rb +14 -4
  41. data/lib/active_support/core_ext/module/deprecation.rb +0 -2
  42. data/lib/active_support/core_ext/module/introspection.rb +0 -16
  43. data/lib/active_support/core_ext/module/method_transplanting.rb +11 -0
  44. data/lib/active_support/core_ext/numeric/time.rb +8 -0
  45. data/lib/active_support/core_ext/object.rb +1 -1
  46. data/lib/active_support/core_ext/object/blank.rb +1 -1
  47. data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
  48. data/lib/active_support/core_ext/object/inclusion.rb +4 -15
  49. data/lib/active_support/core_ext/object/json.rb +197 -0
  50. data/lib/active_support/core_ext/object/to_json.rb +4 -26
  51. data/lib/active_support/core_ext/object/to_param.rb +58 -1
  52. data/lib/active_support/core_ext/object/to_query.rb +7 -56
  53. data/lib/active_support/core_ext/object/try.rb +1 -1
  54. data/lib/active_support/core_ext/range/each.rb +2 -1
  55. data/lib/active_support/core_ext/string/access.rb +31 -31
  56. data/lib/active_support/core_ext/string/conversions.rb +9 -8
  57. data/lib/active_support/core_ext/string/exclude.rb +3 -3
  58. data/lib/active_support/core_ext/string/filters.rb +14 -4
  59. data/lib/active_support/core_ext/string/inflections.rb +11 -9
  60. data/lib/active_support/core_ext/string/output_safety.rb +65 -24
  61. data/lib/active_support/core_ext/string/zones.rb +1 -0
  62. data/lib/active_support/core_ext/thread.rb +4 -4
  63. data/lib/active_support/core_ext/time/calculations.rb +10 -57
  64. data/lib/active_support/core_ext/time/conversions.rb +3 -1
  65. data/lib/active_support/core_ext/time/zones.rb +2 -21
  66. data/lib/active_support/dependencies.rb +29 -13
  67. data/lib/active_support/deprecation.rb +4 -4
  68. data/lib/active_support/deprecation/behaviors.rb +3 -3
  69. data/lib/active_support/duration.rb +5 -7
  70. data/lib/active_support/file_update_checker.rb +1 -1
  71. data/lib/active_support/hash_with_indifferent_access.rb +4 -9
  72. data/lib/active_support/i18n.rb +4 -4
  73. data/lib/active_support/i18n_railtie.rb +2 -6
  74. data/lib/active_support/inflections.rb +0 -1
  75. data/lib/active_support/inflector/inflections.rb +17 -17
  76. data/lib/active_support/inflector/methods.rb +34 -17
  77. data/lib/active_support/json/decoding.rb +14 -21
  78. data/lib/active_support/json/encoding.rb +113 -285
  79. data/lib/active_support/key_generator.rb +1 -1
  80. data/lib/active_support/lazy_load_hooks.rb +1 -1
  81. data/lib/active_support/log_subscriber/test_helper.rb +1 -1
  82. data/lib/active_support/logger.rb +1 -1
  83. data/lib/active_support/message_encryptor.rb +3 -3
  84. data/lib/active_support/message_verifier.rb +6 -1
  85. data/lib/active_support/multibyte/chars.rb +1 -2
  86. data/lib/active_support/multibyte/unicode.rb +27 -39
  87. data/lib/active_support/notifications.rb +3 -3
  88. data/lib/active_support/notifications/instrumenter.rb +2 -1
  89. data/lib/active_support/number_helper.rb +20 -311
  90. data/lib/active_support/number_helper/number_converter.rb +182 -0
  91. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  92. data/lib/active_support/number_helper/number_to_delimited_converter.rb +21 -0
  93. data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
  94. data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
  95. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  96. data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
  97. data/lib/active_support/number_helper/number_to_rounded_converter.rb +62 -0
  98. data/lib/active_support/option_merger.rb +1 -1
  99. data/lib/active_support/ordered_hash.rb +0 -8
  100. data/lib/active_support/ordered_options.rb +8 -0
  101. data/lib/active_support/per_thread_registry.rb +9 -8
  102. data/lib/active_support/subscriber.rb +26 -3
  103. data/lib/active_support/test_case.rb +9 -10
  104. data/lib/active_support/testing/assertions.rb +0 -30
  105. data/lib/active_support/testing/autorun.rb +2 -2
  106. data/lib/active_support/testing/declarative.rb +18 -8
  107. data/lib/active_support/testing/isolation.rb +13 -65
  108. data/lib/active_support/testing/setup_and_teardown.rb +17 -2
  109. data/lib/active_support/testing/tagged_logging.rb +1 -1
  110. data/lib/active_support/testing/time_helpers.rb +55 -0
  111. data/lib/active_support/time_with_zone.rb +4 -4
  112. data/lib/active_support/values/time_zone.rb +18 -15
  113. data/lib/active_support/version.rb +1 -1
  114. data/lib/active_support/xml_mini.rb +2 -4
  115. metadata +71 -61
  116. data/lib/active_support/basic_object.rb +0 -11
  117. data/lib/active_support/buffered_logger.rb +0 -21
  118. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  119. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  120. data/lib/active_support/core_ext/logger.rb +0 -67
  121. data/lib/active_support/core_ext/proc.rb +0 -17
  122. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  123. data/lib/active_support/json/variable.rb +0 -18
  124. data/lib/active_support/testing/pending.rb +0 -14
@@ -0,0 +1,182 @@
1
+ require 'active_support/core_ext/big_decimal/conversions'
2
+ require 'active_support/core_ext/object/blank'
3
+ require 'active_support/core_ext/hash/keys'
4
+ require 'active_support/i18n'
5
+ require 'active_support/core_ext/class/attribute'
6
+
7
+ module ActiveSupport
8
+ module NumberHelper
9
+ class NumberConverter # :nodoc:
10
+ # Default and i18n option namespace per class
11
+ class_attribute :namespace
12
+
13
+ # Does the object need a number that is a valid float?
14
+ class_attribute :validate_float
15
+
16
+ attr_reader :number, :opts
17
+
18
+ DEFAULTS = {
19
+ # Used in number_to_delimited
20
+ # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
21
+ format: {
22
+ # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
23
+ separator: ".",
24
+ # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
25
+ delimiter: ",",
26
+ # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
27
+ precision: 3,
28
+ # If set to true, precision will mean the number of significant digits instead
29
+ # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
30
+ significant: false,
31
+ # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
32
+ strip_insignificant_zeros: false
33
+ },
34
+
35
+ # Used in number_to_currency
36
+ currency: {
37
+ format: {
38
+ format: "%u%n",
39
+ negative_format: "-%u%n",
40
+ unit: "$",
41
+ # These five are to override number.format and are optional
42
+ separator: ".",
43
+ delimiter: ",",
44
+ precision: 2,
45
+ significant: false,
46
+ strip_insignificant_zeros: false
47
+ }
48
+ },
49
+
50
+ # Used in number_to_percentage
51
+ percentage: {
52
+ format: {
53
+ delimiter: "",
54
+ format: "%n%"
55
+ }
56
+ },
57
+
58
+ # Used in number_to_rounded
59
+ precision: {
60
+ format: {
61
+ delimiter: ""
62
+ }
63
+ },
64
+
65
+ # Used in number_to_human_size and number_to_human
66
+ human: {
67
+ format: {
68
+ # These five are to override number.format and are optional
69
+ delimiter: "",
70
+ precision: 3,
71
+ significant: true,
72
+ strip_insignificant_zeros: true
73
+ },
74
+ # Used in number_to_human_size
75
+ storage_units: {
76
+ # Storage units output formatting.
77
+ # %u is the storage unit, %n is the number (default: 2 MB)
78
+ format: "%n %u",
79
+ units: {
80
+ byte: "Bytes",
81
+ kb: "KB",
82
+ mb: "MB",
83
+ gb: "GB",
84
+ tb: "TB"
85
+ }
86
+ },
87
+ # Used in number_to_human
88
+ decimal_units: {
89
+ format: "%n %u",
90
+ # Decimal units output formatting
91
+ # By default we will only quantify some of the exponents
92
+ # but the commented ones might be defined or overridden
93
+ # by the user.
94
+ units: {
95
+ # femto: Quadrillionth
96
+ # pico: Trillionth
97
+ # nano: Billionth
98
+ # micro: Millionth
99
+ # mili: Thousandth
100
+ # centi: Hundredth
101
+ # deci: Tenth
102
+ unit: "",
103
+ # ten:
104
+ # one: Ten
105
+ # other: Tens
106
+ # hundred: Hundred
107
+ thousand: "Thousand",
108
+ million: "Million",
109
+ billion: "Billion",
110
+ trillion: "Trillion",
111
+ quadrillion: "Quadrillion"
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ def self.convert(number, options)
118
+ new(number, options).execute
119
+ end
120
+
121
+ def initialize(number, options)
122
+ @number = number
123
+ @opts = options.symbolize_keys
124
+ end
125
+
126
+ def execute
127
+ if !number
128
+ nil
129
+ elsif validate_float? && !valid_float?
130
+ number
131
+ else
132
+ convert
133
+ end
134
+ end
135
+
136
+ private
137
+
138
+ def options
139
+ @options ||= format_options.merge(opts)
140
+ end
141
+
142
+ def format_options #:nodoc:
143
+ default_format_options.merge!(i18n_format_options)
144
+ end
145
+
146
+ def default_format_options #:nodoc:
147
+ options = DEFAULTS[:format].dup
148
+ options.merge!(DEFAULTS[namespace][:format]) if namespace
149
+ options
150
+ end
151
+
152
+ def i18n_format_options #:nodoc:
153
+ locale = opts[:locale]
154
+ options = I18n.translate(:'number.format', locale: locale, default: {}).dup
155
+
156
+ if namespace
157
+ options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
158
+ end
159
+
160
+ options
161
+ end
162
+
163
+ def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
164
+ I18n.translate(key, { default: default_value(key), scope: :number }.merge!(i18n_options))
165
+ end
166
+
167
+ def translate_in_locale(key, i18n_options = {})
168
+ translate_number_value_with_default(key, { locale: options[:locale] }.merge(i18n_options))
169
+ end
170
+
171
+ def default_value(key)
172
+ key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
173
+ end
174
+
175
+ def valid_float? #:nodoc:
176
+ Float(number)
177
+ rescue ArgumentError, TypeError
178
+ false
179
+ end
180
+ end
181
+ end
182
+ end
@@ -0,0 +1,46 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToCurrencyConverter < NumberConverter # :nodoc:
4
+ self.namespace = :currency
5
+
6
+ def convert
7
+ number = self.number.to_s.strip
8
+ format = options[:format]
9
+
10
+ if is_negative?(number)
11
+ format = options[:negative_format]
12
+ number = absolute_value(number)
13
+ end
14
+
15
+ rounded_number = NumberToRoundedConverter.convert(number, options)
16
+ format.gsub('%n', rounded_number).gsub('%u', options[:unit])
17
+ end
18
+
19
+ private
20
+
21
+ def is_negative?(number)
22
+ number.to_f.phase != 0
23
+ end
24
+
25
+ def absolute_value(number)
26
+ number.respond_to?("abs") ? number.abs : number.sub(/\A-/, '')
27
+ end
28
+
29
+ def options
30
+ @options ||= begin
31
+ defaults = default_format_options.merge(i18n_opts)
32
+ # Override negative format if format options is given
33
+ defaults[:negative_format] = "-#{opts[:format]}" if opts[:format]
34
+ defaults.merge!(opts)
35
+ end
36
+ end
37
+
38
+ def i18n_opts
39
+ # Set International negative format if not exists
40
+ i18n = i18n_format_options
41
+ i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format]
42
+ i18n
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,21 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToDelimitedConverter < NumberConverter #:nodoc:
4
+ self.validate_float = true
5
+
6
+ DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
7
+
8
+ def convert
9
+ parts.join(options[:separator])
10
+ end
11
+
12
+ private
13
+
14
+ def parts
15
+ left, right = number.to_s.split('.')
16
+ left.gsub!(DELIMITED_REGEX) { "#{$1}#{options[:delimiter]}" }
17
+ [left, right].compact
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,66 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToHumanConverter < NumberConverter # :nodoc:
4
+ DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
5
+ -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
6
+ INVERTED_DECIMAL_UNITS = DECIMAL_UNITS.invert
7
+
8
+ self.namespace = :human
9
+ self.validate_float = true
10
+
11
+ def convert # :nodoc:
12
+ @number = Float(number)
13
+
14
+ # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
15
+ unless options.key?(:strip_insignificant_zeros)
16
+ options[:strip_insignificant_zeros] = true
17
+ end
18
+
19
+ units = opts[:units]
20
+ exponent = calculate_exponent(units)
21
+ @number = number / (10 ** exponent)
22
+
23
+ unit = determine_unit(units, exponent)
24
+
25
+ rounded_number = NumberToRoundedConverter.convert(number, options)
26
+ format.gsub(/%n/, rounded_number).gsub(/%u/, unit).strip
27
+ end
28
+
29
+ private
30
+
31
+ def format
32
+ options[:format] || translate_in_locale('human.decimal_units.format')
33
+ end
34
+
35
+ def determine_unit(units, exponent)
36
+ exp = DECIMAL_UNITS[exponent]
37
+ case units
38
+ when Hash
39
+ units[exp] || ''
40
+ when String, Symbol
41
+ I18n.translate("#{units}.#{exp}", :locale => options[:locale], :count => number.to_i)
42
+ else
43
+ translate_in_locale("human.decimal_units.units.#{exp}", count: number.to_i)
44
+ end
45
+ end
46
+
47
+ def calculate_exponent(units)
48
+ exponent = number != 0 ? Math.log10(number.abs).floor : 0
49
+ unit_exponents(units).find { |e| exponent >= e } || 0
50
+ end
51
+
52
+ def unit_exponents(units)
53
+ case units
54
+ when Hash
55
+ units
56
+ when String, Symbol
57
+ I18n.translate(units.to_s, :locale => options[:locale], :raise => true)
58
+ when nil
59
+ translate_in_locale("human.decimal_units.units", raise: true)
60
+ else
61
+ raise ArgumentError, ":units must be a Hash or String translation scope."
62
+ end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by { |e| -e }
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,58 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToHumanSizeConverter < NumberConverter #:nodoc:
4
+ STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
5
+
6
+ self.namespace = :human
7
+ self.validate_float = true
8
+
9
+ def convert
10
+ @number = Float(number)
11
+
12
+ # for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
13
+ unless options.key?(:strip_insignificant_zeros)
14
+ options[:strip_insignificant_zeros] = true
15
+ end
16
+
17
+ if smaller_than_base?
18
+ number_to_format = number.to_i.to_s
19
+ else
20
+ human_size = number / (base ** exponent)
21
+ number_to_format = NumberToRoundedConverter.convert(human_size, options)
22
+ end
23
+ conversion_format.gsub(/%n/, number_to_format).gsub(/%u/, unit)
24
+ end
25
+
26
+ private
27
+
28
+ def conversion_format
29
+ translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
30
+ end
31
+
32
+ def unit
33
+ translate_number_value_with_default(storage_unit_key, :locale => options[:locale], :count => number.to_i, :raise => true)
34
+ end
35
+
36
+ def storage_unit_key
37
+ key_end = smaller_than_base? ? 'byte' : STORAGE_UNITS[exponent]
38
+ "human.storage_units.units.#{key_end}"
39
+ end
40
+
41
+ def exponent
42
+ max = STORAGE_UNITS.size - 1
43
+ exp = (Math.log(number) / Math.log(base)).to_i
44
+ exp = max if exp > max # avoid overflow for the highest unit
45
+ exp
46
+ end
47
+
48
+ def smaller_than_base?
49
+ number.to_i < base
50
+ end
51
+
52
+ def base
53
+ opts[:prefix] == :si ? 1000 : 1024
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,12 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToPercentageConverter < NumberConverter # :nodoc:
4
+ self.namespace = :percentage
5
+
6
+ def convert
7
+ rounded_number = NumberToRoundedConverter.convert(number, options)
8
+ options[:format].gsub('%n', rounded_number)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,49 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToPhoneConverter < NumberConverter #:nodoc:
4
+ def convert
5
+ str = country_code(opts[:country_code])
6
+ str << convert_to_phone_number(number.to_s.strip)
7
+ str << phone_ext(opts[:extension])
8
+ end
9
+
10
+ private
11
+
12
+ def convert_to_phone_number(number)
13
+ if opts[:area_code]
14
+ convert_with_area_code(number)
15
+ else
16
+ convert_without_area_code(number)
17
+ end
18
+ end
19
+
20
+ def convert_with_area_code(number)
21
+ number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
22
+ number
23
+ end
24
+
25
+ def convert_without_area_code(number)
26
+ number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
27
+ number.slice!(0, 1) if start_with_delimiter?(number)
28
+ number
29
+ end
30
+
31
+ def start_with_delimiter?(number)
32
+ delimiter.present? && number.start_with?(delimiter)
33
+ end
34
+
35
+ def delimiter
36
+ opts[:delimiter] || "-"
37
+ end
38
+
39
+ def country_code(code)
40
+ code.blank? ? "" : "+#{code}#{delimiter}"
41
+ end
42
+
43
+ def phone_ext(ext)
44
+ ext.blank? ? "" : " x #{ext}"
45
+ end
46
+ end
47
+ end
48
+ end
49
+
@@ -0,0 +1,62 @@
1
+ module ActiveSupport
2
+ module NumberHelper
3
+ class NumberToRoundedConverter < NumberConverter # :nodoc:
4
+ self.namespace = :precision
5
+ self.validate_float = true
6
+
7
+ def convert
8
+ @number = Float(number)
9
+
10
+ precision = options.delete :precision
11
+ significant = options.delete :significant
12
+
13
+ if significant && precision > 0
14
+ digits, rounded_number = digits_and_rounded_number(precision)
15
+ precision -= digits
16
+ precision = 0 if precision < 0 # don't let it be negative
17
+ else
18
+ rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
19
+ rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
20
+ end
21
+
22
+ delimited_number = NumberToDelimitedConverter.convert("%01.#{precision}f" % rounded_number, options)
23
+ format_number(delimited_number)
24
+ end
25
+
26
+ private
27
+
28
+ def digits_and_rounded_number(precision)
29
+ if number.zero?
30
+ [1, 0]
31
+ else
32
+ digits = digit_count(number)
33
+ multiplier = 10 ** (digits - precision)
34
+ rounded_number = calculate_rounded_number(multiplier)
35
+ digits = digit_count(rounded_number) # After rounding, the number of digits may have changed
36
+ [digits, rounded_number]
37
+ end
38
+ end
39
+
40
+ def calculate_rounded_number(multiplier)
41
+ (BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier
42
+ end
43
+
44
+ def digit_count(number)
45
+ (Math.log10(number.abs) + 1).floor
46
+ end
47
+
48
+ def strip_insignificant_zeros
49
+ options[:strip_insignificant_zeros]
50
+ end
51
+
52
+ def format_number(number)
53
+ if strip_insignificant_zeros
54
+ escaped_separator = Regexp.escape(options[:separator])
55
+ number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
56
+ else
57
+ number
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end