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
@@ -4,7 +4,7 @@ require 'openssl'
4
4
  module ActiveSupport
5
5
  # KeyGenerator is a simple wrapper around OpenSSL's implementation of PBKDF2
6
6
  # It can be used to derive a number of keys for various purposes from a given secret.
7
- # This lets rails applications have a single secure secret, but avoid reusing that
7
+ # This lets Rails applications have a single secure secret, but avoid reusing that
8
8
  # key in multiple incompatible contexts.
9
9
  class KeyGenerator
10
10
  def initialize(secret, options = {})
@@ -1,5 +1,5 @@
1
1
  module ActiveSupport
2
- # lazy_load_hooks allows rails to lazily load a lot of components and thus
2
+ # lazy_load_hooks allows Rails to lazily load a lot of components and thus
3
3
  # making the app boot faster. Because of this feature now there is no need to
4
4
  # require <tt>ActiveRecord::Base</tt> at boot time purely to apply
5
5
  # configuration. Instead a hook is registered that applies configuration once
@@ -1,5 +1,5 @@
1
1
  require 'active_support/log_subscriber'
2
- require 'active_support/buffered_logger'
2
+ require 'active_support/logger'
3
3
  require 'active_support/notifications'
4
4
 
5
5
  module ActiveSupport
@@ -1,4 +1,4 @@
1
- require 'active_support/core_ext/class/attribute_accessors'
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
2
  require 'active_support/logger_silence'
3
3
  require 'logger'
4
4
 
@@ -76,12 +76,12 @@ module ActiveSupport
76
76
  encrypted_data = cipher.update(@serializer.dump(value))
77
77
  encrypted_data << cipher.final
78
78
 
79
- [encrypted_data, iv].map {|v| ::Base64.strict_encode64(v)}.join("--")
79
+ "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
80
80
  end
81
81
 
82
82
  def _decrypt(encrypted_message)
83
83
  cipher = new_cipher
84
- encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.decode64(v)}
84
+ encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
85
85
 
86
86
  cipher.decrypt
87
87
  cipher.key = @secret
@@ -91,7 +91,7 @@ module ActiveSupport
91
91
  decrypted_data << cipher.final
92
92
 
93
93
  @serializer.load(decrypted_data)
94
- rescue OpenSSLCipherError, TypeError
94
+ rescue OpenSSLCipherError, TypeError, ArgumentError
95
95
  raise InvalidMessage
96
96
  end
97
97
 
@@ -37,7 +37,12 @@ module ActiveSupport
37
37
 
38
38
  data, digest = signed_message.split("--")
39
39
  if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
40
- @serializer.load(::Base64.decode64(data))
40
+ begin
41
+ @serializer.load(::Base64.strict_decode64(data))
42
+ rescue ArgumentError => argument_error
43
+ raise InvalidSignature if argument_error.message =~ %r{invalid base64}
44
+ raise
45
+ end
41
46
  else
42
47
  raise InvalidSignature
43
48
  end
@@ -56,11 +56,10 @@ module ActiveSupport #:nodoc:
56
56
 
57
57
  # Forward all undefined methods to the wrapped string.
58
58
  def method_missing(method, *args, &block)
59
+ result = @wrapped_string.__send__(method, *args, &block)
59
60
  if method.to_s =~ /!$/
60
- result = @wrapped_string.__send__(method, *args, &block)
61
61
  self if result
62
62
  else
63
- result = @wrapped_string.__send__(method, *args, &block)
64
63
  result.kind_of?(String) ? chars(result) : result
65
64
  end
66
65
  end
@@ -42,6 +42,7 @@ module ActiveSupport
42
42
  0x0085, # White_Space # Cc <control-0085>
43
43
  0x00A0, # White_Space # Zs NO-BREAK SPACE
44
44
  0x1680, # White_Space # Zs OGHAM SPACE MARK
45
+ 0x180E, # White_Space # Zs MONGOLIAN VOWEL SEPARATOR
45
46
  (0x2000..0x200A).to_a, # White_Space # Zs [11] EN QUAD..HAIR SPACE
46
47
  0x2028, # White_Space # Zl LINE SEPARATOR
47
48
  0x2029, # White_Space # Zp PARAGRAPH SEPARATOR
@@ -217,51 +218,31 @@ module ActiveSupport
217
218
  # Passing +true+ will forcibly tidy all bytes, assuming that the string's
218
219
  # encoding is entirely CP1252 or ISO-8859-1.
219
220
  def tidy_bytes(string, force = false)
221
+ return string if string.empty?
222
+
220
223
  if force
221
- return string.unpack("C*").map do |b|
222
- tidy_byte(b)
223
- end.flatten.compact.pack("C*").unpack("U*").pack("U*")
224
+ return string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
224
225
  end
225
226
 
226
- bytes = string.unpack("C*")
227
- conts_expected = 0
228
- last_lead = 0
229
-
230
- bytes.each_index do |i|
227
+ # We can't transcode to the same format, so we choose a nearly-identical encoding.
228
+ # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
229
+ # CP1252 when we get errors. The final string will be 'converted' back to UTF-8
230
+ # before returning.
231
+ reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_8_MAC)
231
232
 
232
- byte = bytes[i]
233
- is_cont = byte > 127 && byte < 192
234
- is_lead = byte > 191 && byte < 245
235
- is_unused = byte > 240
236
- is_restricted = byte > 244
233
+ source = string.dup
234
+ out = ''.force_encoding(Encoding::UTF_8_MAC)
237
235
 
238
- # Impossible or highly unlikely byte? Clean it.
239
- if is_unused || is_restricted
240
- bytes[i] = tidy_byte(byte)
241
- elsif is_cont
242
- # Not expecting continuation byte? Clean up. Otherwise, now expect one less.
243
- conts_expected == 0 ? bytes[i] = tidy_byte(byte) : conts_expected -= 1
244
- else
245
- if conts_expected > 0
246
- # Expected continuation, but got ASCII or leading? Clean backwards up to
247
- # the leading byte.
248
- (1..(i - last_lead)).each {|j| bytes[i - j] = tidy_byte(bytes[i - j])}
249
- conts_expected = 0
250
- end
251
- if is_lead
252
- # Final byte is leading? Clean it.
253
- if i == bytes.length - 1
254
- bytes[i] = tidy_byte(bytes.last)
255
- else
256
- # Valid leading byte? Expect continuations determined by position of
257
- # first zero bit, with max of 3.
258
- conts_expected = byte < 224 ? 1 : byte < 240 ? 2 : 3
259
- last_lead = i
260
- end
261
- end
262
- end
236
+ loop do
237
+ reader.primitive_convert(source, out)
238
+ _, _, _, error_bytes, _ = reader.primitive_errinfo
239
+ break if error_bytes.nil?
240
+ out << error_bytes.encode(Encoding::UTF_8_MAC, Encoding::Windows_1252, invalid: :replace, undef: :replace)
263
241
  end
264
- bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
242
+
243
+ reader.finish
244
+
245
+ out.encode!(Encoding::UTF_8)
265
246
  end
266
247
 
267
248
  # Returns the KC normalization of the string by default. NFKC is
@@ -306,6 +287,13 @@ module ActiveSupport
306
287
  class Codepoint
307
288
  attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
308
289
 
290
+ # Initializing Codepoint object with default values
291
+ def initialize
292
+ @combining_class = 0
293
+ @uppercase_mapping = 0
294
+ @lowercase_mapping = 0
295
+ end
296
+
309
297
  def swapcase_mapping
310
298
  uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
311
299
  end
@@ -143,8 +143,8 @@ module ActiveSupport
143
143
  #
144
144
  # == Default Queue
145
145
  #
146
- # Notifications ships with a queue implementation that consumes and publish events
147
- # to log subscribers in a thread. You can use any queue implementation you want.
146
+ # Notifications ships with a queue implementation that consumes and publishes events
147
+ # to all log subscribers. You can use any queue implementation you want.
148
148
  #
149
149
  module Notifications
150
150
  class << self
@@ -178,7 +178,7 @@ module ActiveSupport
178
178
  end
179
179
 
180
180
  def instrumenter
181
- InstrumentationRegistry.instrumenter_for(notifier)
181
+ InstrumentationRegistry.instance.instrumenter_for(notifier)
182
182
  end
183
183
  end
184
184
 
@@ -54,10 +54,11 @@ module ActiveSupport
54
54
  @transaction_id = transaction_id
55
55
  @end = ending
56
56
  @children = []
57
+ @duration = nil
57
58
  end
58
59
 
59
60
  def duration
60
- 1000.0 * (self.end - time)
61
+ @duration ||= 1000.0 * (self.end - time)
61
62
  end
62
63
 
63
64
  def <<(event)
@@ -1,115 +1,19 @@
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
-
6
1
  module ActiveSupport
7
2
  module NumberHelper
8
- extend self
9
-
10
- DEFAULTS = {
11
- # Used in number_to_delimited
12
- # These are also the defaults for 'currency', 'percentage', 'precision', and 'human'
13
- format: {
14
- # Sets the separator between the units, for more precision (e.g. 1.0 / 2.0 == 0.5)
15
- separator: ".",
16
- # Delimits thousands (e.g. 1,000,000 is a million) (always in groups of three)
17
- delimiter: ",",
18
- # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
19
- precision: 3,
20
- # If set to true, precision will mean the number of significant digits instead
21
- # of the number of decimal digits (1234 with precision 2 becomes 1200, 1.23543 becomes 1.2)
22
- significant: false,
23
- # If set, the zeros after the decimal separator will always be stripped (eg.: 1.200 will be 1.2)
24
- strip_insignificant_zeros: false
25
- },
26
-
27
- # Used in number_to_currency
28
- currency: {
29
- format: {
30
- format: "%u%n",
31
- negative_format: "-%u%n",
32
- unit: "$",
33
- # These five are to override number.format and are optional
34
- separator: ".",
35
- delimiter: ",",
36
- precision: 2,
37
- significant: false,
38
- strip_insignificant_zeros: false
39
- }
40
- },
41
-
42
- # Used in number_to_percentage
43
- percentage: {
44
- format: {
45
- delimiter: "",
46
- format: "%n%"
47
- }
48
- },
49
-
50
- # Used in number_to_rounded
51
- precision: {
52
- format: {
53
- delimiter: ""
54
- }
55
- },
56
-
57
- # Used in number_to_human_size and number_to_human
58
- human: {
59
- format: {
60
- # These five are to override number.format and are optional
61
- delimiter: "",
62
- precision: 3,
63
- significant: true,
64
- strip_insignificant_zeros: true
65
- },
66
- # Used in number_to_human_size
67
- storage_units: {
68
- # Storage units output formatting.
69
- # %u is the storage unit, %n is the number (default: 2 MB)
70
- format: "%n %u",
71
- units: {
72
- byte: "Bytes",
73
- kb: "KB",
74
- mb: "MB",
75
- gb: "GB",
76
- tb: "TB"
77
- }
78
- },
79
- # Used in number_to_human
80
- decimal_units: {
81
- format: "%n %u",
82
- # Decimal units output formatting
83
- # By default we will only quantify some of the exponents
84
- # but the commented ones might be defined or overridden
85
- # by the user.
86
- units: {
87
- # femto: Quadrillionth
88
- # pico: Trillionth
89
- # nano: Billionth
90
- # micro: Millionth
91
- # mili: Thousandth
92
- # centi: Hundredth
93
- # deci: Tenth
94
- unit: "",
95
- # ten:
96
- # one: Ten
97
- # other: Tens
98
- # hundred: Hundred
99
- thousand: "Thousand",
100
- million: "Million",
101
- billion: "Billion",
102
- trillion: "Trillion",
103
- quadrillion: "Quadrillion"
104
- }
105
- }
106
- }
107
- }
108
-
109
- DECIMAL_UNITS = { 0 => :unit, 1 => :ten, 2 => :hundred, 3 => :thousand, 6 => :million, 9 => :billion, 12 => :trillion, 15 => :quadrillion,
110
- -1 => :deci, -2 => :centi, -3 => :mili, -6 => :micro, -9 => :nano, -12 => :pico, -15 => :femto }
3
+ extend ActiveSupport::Autoload
4
+
5
+ eager_autoload do
6
+ autoload :NumberConverter
7
+ autoload :NumberToRoundedConverter
8
+ autoload :NumberToDelimitedConverter
9
+ autoload :NumberToHumanConverter
10
+ autoload :NumberToHumanSizeConverter
11
+ autoload :NumberToPhoneConverter
12
+ autoload :NumberToCurrencyConverter
13
+ autoload :NumberToPercentageConverter
14
+ end
111
15
 
112
- STORAGE_UNITS = [:byte, :kb, :mb, :gb, :tb]
16
+ extend self
113
17
 
114
18
  # Formats a +number+ into a US phone number (e.g., (555)
115
19
  # 123-9876). You can customize the format in the +options+ hash.
@@ -137,27 +41,7 @@ module ActiveSupport
137
41
  # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: '.')
138
42
  # # => +1.123.555.1234 x 1343
139
43
  def number_to_phone(number, options = {})
140
- return unless number
141
- options = options.symbolize_keys
142
-
143
- number = number.to_s.strip
144
- area_code = options[:area_code]
145
- delimiter = options[:delimiter] || "-"
146
- extension = options[:extension]
147
- country_code = options[:country_code]
148
-
149
- if area_code
150
- number.gsub!(/(\d{1,3})(\d{3})(\d{4}$)/,"(\\1) \\2#{delimiter}\\3")
151
- else
152
- number.gsub!(/(\d{0,3})(\d{3})(\d{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
153
- number.slice!(0, 1) if number.start_with?(delimiter) && !delimiter.blank?
154
- end
155
-
156
- str = ''
157
- str << "+#{country_code}#{delimiter}" unless country_code.blank?
158
- str << number
159
- str << " x #{extension}" unless extension.blank?
160
- str
44
+ NumberToPhoneConverter.convert(number, options)
161
45
  end
162
46
 
163
47
  # Formats a +number+ into a currency string (e.g., $13.65). You
@@ -199,25 +83,7 @@ module ActiveSupport
199
83
  # number_to_currency(1234567890.50, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
200
84
  # # => 1234567890,50 &pound;
201
85
  def number_to_currency(number, options = {})
202
- return unless number
203
- options = options.symbolize_keys
204
-
205
- currency = i18n_format_options(options[:locale], :currency)
206
- currency[:negative_format] ||= "-" + currency[:format] if currency[:format]
207
-
208
- defaults = default_format_options(:currency).merge!(currency)
209
- defaults[:negative_format] = "-" + options[:format] if options[:format]
210
- options = defaults.merge!(options)
211
-
212
- unit = options.delete(:unit)
213
- format = options.delete(:format)
214
-
215
- if number.to_f.phase != 0
216
- format = options.delete(:negative_format)
217
- number = number.respond_to?("abs") ? number.abs : number.sub(/^-/, '')
218
- end
219
-
220
- format.gsub('%n', self.number_to_rounded(number, options)).gsub('%u', unit)
86
+ NumberToCurrencyConverter.convert(number, options)
221
87
  end
222
88
 
223
89
  # Formats a +number+ as a percentage string (e.g., 65%). You can
@@ -253,14 +119,7 @@ module ActiveSupport
253
119
  # number_to_percentage('98a') # => 98a%
254
120
  # number_to_percentage(100, format: '%n %') # => 100 %
255
121
  def number_to_percentage(number, options = {})
256
- return unless number
257
- options = options.symbolize_keys
258
-
259
- defaults = format_options(options[:locale], :percentage)
260
- options = defaults.merge!(options)
261
-
262
- format = options[:format] || "%n%"
263
- format.gsub('%n', self.number_to_rounded(number, options))
122
+ NumberToPercentageConverter.convert(number, options)
264
123
  end
265
124
 
266
125
  # Formats a +number+ with grouped thousands using +delimiter+
@@ -289,15 +148,7 @@ module ActiveSupport
289
148
  # number_to_delimited(98765432.98, delimiter: ' ', separator: ',')
290
149
  # # => 98 765 432,98
291
150
  def number_to_delimited(number, options = {})
292
- options = options.symbolize_keys
293
-
294
- return number unless valid_float?(number)
295
-
296
- options = format_options(options[:locale]).merge!(options)
297
-
298
- parts = number.to_s.split('.')
299
- parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{options[:delimiter]}")
300
- parts.join(options[:separator])
151
+ NumberToDelimitedConverter.convert(number, options)
301
152
  end
302
153
 
303
154
  # Formats a +number+ with the specified level of
@@ -340,39 +191,7 @@ module ActiveSupport
340
191
  # number_to_rounded(1111.2345, precision: 2, separator: ',', delimiter: '.')
341
192
  # # => 1.111,23
342
193
  def number_to_rounded(number, options = {})
343
- return number unless valid_float?(number)
344
- number = Float(number)
345
- options = options.symbolize_keys
346
-
347
- defaults = format_options(options[:locale], :precision)
348
- options = defaults.merge!(options)
349
-
350
- precision = options.delete :precision
351
- significant = options.delete :significant
352
- strip_insignificant_zeros = options.delete :strip_insignificant_zeros
353
-
354
- if significant && precision > 0
355
- if number == 0
356
- digits, rounded_number = 1, 0
357
- else
358
- digits = (Math.log10(number.abs) + 1).floor
359
- multiplier = 10 ** (digits - precision)
360
- rounded_number = (BigDecimal.new(number.to_s) / BigDecimal.new(multiplier.to_f.to_s)).round.to_f * multiplier
361
- digits = (Math.log10(rounded_number.abs) + 1).floor # After rounding, the number of digits may have changed
362
- end
363
- precision -= digits
364
- precision = 0 if precision < 0 # don't let it be negative
365
- else
366
- rounded_number = BigDecimal.new(number.to_s).round(precision).to_f
367
- rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
368
- end
369
- formatted_number = self.number_to_delimited("%01.#{precision}f" % rounded_number, options)
370
- if strip_insignificant_zeros
371
- escaped_separator = Regexp.escape(options[:separator])
372
- formatted_number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
373
- else
374
- formatted_number
375
- end
194
+ NumberToRoundedConverter.convert(number, options)
376
195
  end
377
196
 
378
197
  # Formats the bytes in +number+ into a more understandable
@@ -420,36 +239,7 @@ module ActiveSupport
420
239
  # number_to_human_size(1234567890123, precision: 5) # => "1.1229 TB"
421
240
  # number_to_human_size(524288000, precision: 5) # => "500 MB"
422
241
  def number_to_human_size(number, options = {})
423
- options = options.symbolize_keys
424
-
425
- return number unless valid_float?(number)
426
- number = Float(number)
427
-
428
- defaults = format_options(options[:locale], :human)
429
- options = defaults.merge!(options)
430
-
431
- #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
432
- options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
433
-
434
- storage_units_format = translate_number_value_with_default('human.storage_units.format', :locale => options[:locale], :raise => true)
435
-
436
- base = options[:prefix] == :si ? 1000 : 1024
437
-
438
- if number.to_i < base
439
- unit = translate_number_value_with_default('human.storage_units.units.byte', :locale => options[:locale], :count => number.to_i, :raise => true)
440
- storage_units_format.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
441
- else
442
- max_exp = STORAGE_UNITS.size - 1
443
- exponent = (Math.log(number) / Math.log(base)).to_i # Convert to base
444
- exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
445
- number /= base ** exponent
446
-
447
- unit_key = STORAGE_UNITS[exponent]
448
- unit = translate_number_value_with_default("human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
449
-
450
- formatted_number = self.number_to_rounded(number, options)
451
- storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
452
- end
242
+ NumberToHumanSizeConverter.convert(number, options)
453
243
  end
454
244
 
455
245
  # Pretty prints (formats and approximates) a number in a way it
@@ -550,88 +340,7 @@ module ActiveSupport
550
340
  # number_to_human(1, units: :distance) # => "1 meter"
551
341
  # number_to_human(0.34, units: :distance) # => "34 centimeters"
552
342
  def number_to_human(number, options = {})
553
- options = options.symbolize_keys
554
-
555
- return number unless valid_float?(number)
556
- number = Float(number)
557
-
558
- defaults = format_options(options[:locale], :human)
559
- options = defaults.merge!(options)
560
-
561
- #for backwards compatibility with those that didn't add strip_insignificant_zeros to their locale files
562
- options[:strip_insignificant_zeros] = true if not options.key?(:strip_insignificant_zeros)
563
-
564
- inverted_du = DECIMAL_UNITS.invert
565
-
566
- units = options.delete :units
567
- unit_exponents = case units
568
- when Hash
569
- units
570
- when String, Symbol
571
- I18n.translate(:"#{units}", :locale => options[:locale], :raise => true)
572
- when nil
573
- translate_number_value_with_default("human.decimal_units.units", :locale => options[:locale], :raise => true)
574
- else
575
- raise ArgumentError, ":units must be a Hash or String translation scope."
576
- end.keys.map{|e_name| inverted_du[e_name] }.sort_by{|e| -e}
577
-
578
- number_exponent = number != 0 ? Math.log10(number.abs).floor : 0
579
- display_exponent = unit_exponents.find{ |e| number_exponent >= e } || 0
580
- number /= 10 ** display_exponent
581
-
582
- unit = case units
583
- when Hash
584
- units[DECIMAL_UNITS[display_exponent]] || ''
585
- when String, Symbol
586
- I18n.translate(:"#{units}.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
587
- else
588
- translate_number_value_with_default("human.decimal_units.units.#{DECIMAL_UNITS[display_exponent]}", :locale => options[:locale], :count => number.to_i)
589
- end
590
-
591
- decimal_format = options[:format] || translate_number_value_with_default('human.decimal_units.format', :locale => options[:locale])
592
- formatted_number = self.number_to_rounded(number, options)
593
- decimal_format.gsub(/%n/, formatted_number).gsub(/%u/, unit).strip
594
- end
595
-
596
- def self.private_module_and_instance_method(method_name) #:nodoc:
597
- private method_name
598
- private_class_method method_name
599
- end
600
- private_class_method :private_module_and_instance_method
601
-
602
- def format_options(locale, namespace = nil) #:nodoc:
603
- default_format_options(namespace).merge!(i18n_format_options(locale, namespace))
604
- end
605
- private_module_and_instance_method :format_options
606
-
607
- def default_format_options(namespace = nil) #:nodoc:
608
- options = DEFAULTS[:format].dup
609
- options.merge!(DEFAULTS[namespace][:format]) if namespace
610
- options
611
- end
612
- private_module_and_instance_method :default_format_options
613
-
614
- def i18n_format_options(locale, namespace = nil) #:nodoc:
615
- options = I18n.translate(:'number.format', locale: locale, default: {}).dup
616
- if namespace
617
- options.merge!(I18n.translate(:"number.#{namespace}.format", locale: locale, default: {}))
618
- end
619
- options
620
- end
621
- private_module_and_instance_method :i18n_format_options
622
-
623
- def translate_number_value_with_default(key, i18n_options = {}) #:nodoc:
624
- default = key.split('.').reduce(DEFAULTS) { |defaults, k| defaults[k.to_sym] }
625
-
626
- I18n.translate(key, { default: default, scope: :number }.merge!(i18n_options))
627
- end
628
- private_module_and_instance_method :translate_number_value_with_default
629
-
630
- def valid_float?(number) #:nodoc:
631
- Float(number)
632
- rescue ArgumentError, TypeError
633
- false
343
+ NumberToHumanConverter.convert(number, options)
634
344
  end
635
- private_module_and_instance_method :valid_float?
636
345
  end
637
346
  end