activesupport 4.0.13 → 4.2.11.3

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 (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +406 -418
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -2
  5. data/lib/active_support/backtrace_cleaner.rb +8 -8
  6. data/lib/active_support/benchmarkable.rb +0 -10
  7. data/lib/active_support/cache/file_store.rb +32 -22
  8. data/lib/active_support/cache/mem_cache_store.rb +5 -7
  9. data/lib/active_support/cache/memory_store.rb +1 -0
  10. data/lib/active_support/cache/strategy/local_cache.rb +11 -30
  11. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  12. data/lib/active_support/cache.rb +75 -41
  13. data/lib/active_support/callbacks.rb +482 -261
  14. data/lib/active_support/concern.rb +23 -7
  15. data/lib/active_support/configurable.rb +1 -1
  16. data/lib/active_support/core_ext/array/access.rb +11 -1
  17. data/lib/active_support/core_ext/array/conversions.rb +2 -17
  18. data/lib/active_support/core_ext/array/grouping.rb +29 -12
  19. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
  20. data/lib/active_support/core_ext/array.rb +0 -1
  21. data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -15
  22. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +16 -0
  23. data/lib/active_support/core_ext/class/attribute.rb +1 -2
  24. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -170
  25. data/lib/active_support/core_ext/class/delegating_attributes.rb +13 -8
  26. data/lib/active_support/core_ext/class/subclasses.rb +0 -2
  27. data/lib/active_support/core_ext/class.rb +0 -1
  28. data/lib/active_support/core_ext/date/calculations.rb +10 -0
  29. data/lib/active_support/core_ext/date/conversions.rb +9 -1
  30. data/lib/active_support/core_ext/date/zones.rb +2 -33
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +41 -11
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  34. data/lib/active_support/core_ext/date_time/calculations.rb +45 -22
  35. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  36. data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
  37. data/lib/active_support/core_ext/date_time/zones.rb +3 -21
  38. data/lib/active_support/core_ext/date_time.rb +1 -0
  39. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  40. data/lib/active_support/core_ext/enumerable.rb +17 -1
  41. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  42. data/lib/active_support/core_ext/hash/compact.rb +24 -0
  43. data/lib/active_support/core_ext/hash/conversions.rb +9 -8
  44. data/lib/active_support/core_ext/hash/except.rb +8 -2
  45. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
  46. data/lib/active_support/core_ext/hash/keys.rb +25 -19
  47. data/lib/active_support/core_ext/hash/slice.rb +8 -2
  48. data/lib/active_support/core_ext/hash/transform_values.rb +23 -0
  49. data/lib/active_support/core_ext/hash.rb +2 -1
  50. data/lib/active_support/core_ext/integer/time.rb +0 -15
  51. data/lib/active_support/core_ext/kernel/concern.rb +10 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +1 -1
  53. data/lib/active_support/core_ext/kernel/reporting.rb +13 -2
  54. data/lib/active_support/core_ext/kernel.rb +3 -2
  55. data/lib/active_support/core_ext/load_error.rb +4 -1
  56. data/lib/active_support/core_ext/marshal.rb +8 -5
  57. data/lib/active_support/core_ext/module/aliasing.rb +2 -2
  58. data/lib/active_support/core_ext/module/attr_internal.rb +2 -1
  59. data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
  60. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  61. data/lib/active_support/core_ext/module/delegation.rb +53 -25
  62. data/lib/active_support/core_ext/module/deprecation.rb +0 -2
  63. data/lib/active_support/core_ext/module/introspection.rb +0 -16
  64. data/lib/active_support/core_ext/module/method_transplanting.rb +13 -0
  65. data/lib/active_support/core_ext/module.rb +1 -0
  66. data/lib/active_support/core_ext/numeric/conversions.rb +11 -3
  67. data/lib/active_support/core_ext/numeric/time.rb +4 -29
  68. data/lib/active_support/core_ext/object/blank.rb +44 -18
  69. data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
  70. data/lib/active_support/core_ext/object/duplicable.rb +72 -33
  71. data/lib/active_support/core_ext/object/inclusion.rb +16 -15
  72. data/lib/active_support/core_ext/object/itself.rb +15 -0
  73. data/lib/active_support/core_ext/object/json.rb +197 -0
  74. data/lib/active_support/core_ext/object/to_query.rb +14 -6
  75. data/lib/active_support/core_ext/object/try.rb +36 -14
  76. data/lib/active_support/core_ext/object/with_options.rb +30 -3
  77. data/lib/active_support/core_ext/object.rb +2 -1
  78. data/lib/active_support/core_ext/string/access.rb +35 -35
  79. data/lib/active_support/core_ext/string/conversions.rb +10 -9
  80. data/lib/active_support/core_ext/string/exclude.rb +3 -3
  81. data/lib/active_support/core_ext/string/filters.rb +51 -3
  82. data/lib/active_support/core_ext/string/inflections.rb +15 -10
  83. data/lib/active_support/core_ext/string/output_safety.rb +97 -33
  84. data/lib/active_support/core_ext/string/zones.rb +1 -0
  85. data/lib/active_support/core_ext/thread.rb +12 -5
  86. data/lib/active_support/core_ext/time/calculations.rb +47 -68
  87. data/lib/active_support/core_ext/time/compatibility.rb +14 -0
  88. data/lib/active_support/core_ext/time/conversions.rb +4 -2
  89. data/lib/active_support/core_ext/time/zones.rb +2 -20
  90. data/lib/active_support/core_ext/time.rb +1 -0
  91. data/lib/active_support/core_ext.rb +0 -1
  92. data/lib/active_support/dependencies/autoload.rb +1 -1
  93. data/lib/active_support/dependencies.rb +64 -25
  94. data/lib/active_support/deprecation/behaviors.rb +4 -4
  95. data/lib/active_support/deprecation.rb +4 -4
  96. data/lib/active_support/duration.rb +55 -11
  97. data/lib/active_support/file_update_checker.rb +1 -1
  98. data/lib/active_support/gem_version.rb +15 -0
  99. data/lib/active_support/hash_with_indifferent_access.rb +39 -11
  100. data/lib/active_support/i18n.rb +4 -4
  101. data/lib/active_support/i18n_railtie.rb +1 -7
  102. data/lib/active_support/inflections.rb +6 -1
  103. data/lib/active_support/inflector/inflections.rb +19 -19
  104. data/lib/active_support/inflector/methods.rb +66 -25
  105. data/lib/active_support/json/decoding.rb +15 -22
  106. data/lib/active_support/json/encoding.rb +125 -286
  107. data/lib/active_support/key_generator.rb +8 -10
  108. data/lib/active_support/lazy_load_hooks.rb +1 -1
  109. data/lib/active_support/log_subscriber/test_helper.rb +1 -1
  110. data/lib/active_support/logger.rb +51 -1
  111. data/lib/active_support/logger_silence.rb +7 -4
  112. data/lib/active_support/logger_thread_safe_level.rb +32 -0
  113. data/lib/active_support/message_encryptor.rb +14 -6
  114. data/lib/active_support/message_verifier.rb +16 -12
  115. data/lib/active_support/multibyte/chars.rb +2 -3
  116. data/lib/active_support/multibyte/unicode.rb +46 -58
  117. data/lib/active_support/notifications/fanout.rb +12 -7
  118. data/lib/active_support/notifications/instrumenter.rb +2 -1
  119. data/lib/active_support/notifications.rb +11 -6
  120. data/lib/active_support/number_helper/number_converter.rb +182 -0
  121. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  122. data/lib/active_support/number_helper/number_to_delimited_converter.rb +23 -0
  123. data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
  124. data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
  125. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  126. data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
  127. data/lib/active_support/number_helper/number_to_rounded_converter.rb +87 -0
  128. data/lib/active_support/number_helper.rb +32 -324
  129. data/lib/active_support/ordered_options.rb +8 -0
  130. data/lib/active_support/per_thread_registry.rb +13 -10
  131. data/lib/active_support/security_utils.rb +27 -0
  132. data/lib/active_support/subscriber.rb +35 -3
  133. data/lib/active_support/test_case.rb +52 -19
  134. data/lib/active_support/testing/assertions.rb +1 -31
  135. data/lib/active_support/testing/autorun.rb +2 -2
  136. data/lib/active_support/testing/constant_lookup.rb +1 -5
  137. data/lib/active_support/testing/declarative.rb +7 -21
  138. data/lib/active_support/testing/isolation.rb +29 -69
  139. data/lib/active_support/testing/setup_and_teardown.rb +17 -2
  140. data/lib/active_support/testing/tagged_logging.rb +2 -2
  141. data/lib/active_support/testing/time_helpers.rb +134 -0
  142. data/lib/active_support/time.rb +0 -2
  143. data/lib/active_support/time_with_zone.rb +60 -40
  144. data/lib/active_support/values/time_zone.rb +101 -101
  145. data/lib/active_support/values/unicode_tables.dat +0 -0
  146. data/lib/active_support/version.rb +4 -7
  147. data/lib/active_support/xml_mini/jdom.rb +6 -5
  148. data/lib/active_support/xml_mini/libxml.rb +1 -3
  149. data/lib/active_support/xml_mini/libxmlsax.rb +1 -4
  150. data/lib/active_support/xml_mini/nokogiri.rb +1 -3
  151. data/lib/active_support/xml_mini/nokogirisax.rb +1 -3
  152. data/lib/active_support/xml_mini/rexml.rb +7 -8
  153. data/lib/active_support/xml_mini.rb +33 -15
  154. data/lib/active_support.rb +27 -2
  155. metadata +43 -43
  156. data/lib/active_support/basic_object.rb +0 -11
  157. data/lib/active_support/buffered_logger.rb +0 -21
  158. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  159. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  160. data/lib/active_support/core_ext/logger.rb +0 -67
  161. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  162. data/lib/active_support/core_ext/proc.rb +0 -17
  163. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  164. data/lib/active_support/file_watcher.rb +0 -36
  165. data/lib/active_support/json/variable.rb +0 -18
  166. data/lib/active_support/testing/pending.rb +0 -14
@@ -12,12 +12,14 @@ module ActiveSupport
12
12
  # This can be used in situations similar to the <tt>MessageVerifier</tt>, but
13
13
  # where you don't want users to be able to determine the value of the payload.
14
14
  #
15
- # salt = SecureRandom.random_bytes(64)
15
+ # salt = SecureRandom.random_bytes(64)
16
16
  # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..."
17
17
  # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
18
18
  # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
19
19
  # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
20
20
  class MessageEncryptor
21
+ DEFAULT_CIPHER = "aes-256-cbc"
22
+
21
23
  module NullSerializer #:nodoc:
22
24
  def self.load(value)
23
25
  value
@@ -40,6 +42,7 @@ module ActiveSupport
40
42
  # Options:
41
43
  # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
42
44
  # <tt>OpenSSL::Cipher.ciphers</tt>. Default is 'aes-256-cbc'.
45
+ # * <tt>:digest</tt> - String of digest to use for signing. Default is +SHA1+.
43
46
  # * <tt>:serializer</tt> - Object serializer to use. Default is +Marshal+.
44
47
  def initialize(secret, *signature_key_or_options)
45
48
  options = signature_key_or_options.extract_options!
@@ -47,7 +50,7 @@ module ActiveSupport
47
50
  @secret = secret
48
51
  @sign_secret = sign_secret
49
52
  @cipher = options[:cipher] || 'aes-256-cbc'
50
- @verifier = MessageVerifier.new(@sign_secret || @secret, :serializer => NullSerializer)
53
+ @verifier = MessageVerifier.new(@sign_secret || @secret, digest: options[:digest] || 'SHA1', serializer: NullSerializer)
51
54
  @serializer = options[:serializer] || Marshal
52
55
  end
53
56
 
@@ -63,6 +66,11 @@ module ActiveSupport
63
66
  _decrypt(verifier.verify(value))
64
67
  end
65
68
 
69
+ # Given a cipher, returns the key length of the cipher to help generate the key of desired size
70
+ def self.key_len(cipher = DEFAULT_CIPHER)
71
+ OpenSSL::Cipher.new(cipher).key_len
72
+ end
73
+
66
74
  private
67
75
 
68
76
  def _encrypt(value)
@@ -76,12 +84,12 @@ module ActiveSupport
76
84
  encrypted_data = cipher.update(@serializer.dump(value))
77
85
  encrypted_data << cipher.final
78
86
 
79
- [encrypted_data, iv].map {|v| ::Base64.strict_encode64(v)}.join("--")
87
+ "#{::Base64.strict_encode64 encrypted_data}--#{::Base64.strict_encode64 iv}"
80
88
  end
81
89
 
82
90
  def _decrypt(encrypted_message)
83
91
  cipher = new_cipher
84
- encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.decode64(v)}
92
+ encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
85
93
 
86
94
  cipher.decrypt
87
95
  cipher.key = @secret
@@ -91,12 +99,12 @@ module ActiveSupport
91
99
  decrypted_data << cipher.final
92
100
 
93
101
  @serializer.load(decrypted_data)
94
- rescue OpenSSLCipherError, TypeError
102
+ rescue OpenSSLCipherError, TypeError, ArgumentError
95
103
  raise InvalidMessage
96
104
  end
97
105
 
98
106
  def new_cipher
99
- OpenSSL::Cipher::Cipher.new(@cipher)
107
+ OpenSSL::Cipher.new(@cipher)
100
108
  end
101
109
 
102
110
  def verifier
@@ -1,5 +1,6 @@
1
1
  require 'base64'
2
2
  require 'active_support/core_ext/object/blank'
3
+ require 'active_support/security_utils'
3
4
 
4
5
  module ActiveSupport
5
6
  # +MessageVerifier+ makes it easy to generate and verify messages which are
@@ -27,37 +28,40 @@ module ActiveSupport
27
28
  class InvalidSignature < StandardError; end
28
29
 
29
30
  def initialize(secret, options = {})
31
+ raise ArgumentError, 'Secret should not be nil.' unless secret
30
32
  @secret = secret
31
33
  @digest = options[:digest] || 'SHA1'
32
34
  @serializer = options[:serializer] || Marshal
33
35
  end
34
36
 
35
37
  def verify(signed_message)
36
- raise InvalidSignature if signed_message.blank?
38
+ raise InvalidSignature if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
37
39
 
38
40
  data, digest = signed_message.split("--")
39
- if data.present? && digest.present? && secure_compare(digest, generate_digest(data))
40
- @serializer.load(::Base64.decode64(data))
41
+ if data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
42
+ begin
43
+ @serializer.load(decode(data))
44
+ rescue ArgumentError => argument_error
45
+ raise InvalidSignature if argument_error.message =~ %r{invalid base64}
46
+ raise
47
+ end
41
48
  else
42
49
  raise InvalidSignature
43
50
  end
44
51
  end
45
52
 
46
53
  def generate(value)
47
- data = ::Base64.strict_encode64(@serializer.dump(value))
54
+ data = encode(@serializer.dump(value))
48
55
  "#{data}--#{generate_digest(data)}"
49
56
  end
50
57
 
51
58
  private
52
- # constant-time comparison algorithm to prevent timing attacks
53
- def secure_compare(a, b)
54
- return false unless a.bytesize == b.bytesize
55
-
56
- l = a.unpack "C#{a.bytesize}"
59
+ def encode(data)
60
+ ::Base64.strict_encode64(data)
61
+ end
57
62
 
58
- res = 0
59
- b.each_byte { |byte| res |= byte ^ l.shift }
60
- res == 0
63
+ def decode(data)
64
+ ::Base64.strict_decode64(data)
61
65
  end
62
66
 
63
67
  def generate_digest(data)
@@ -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
@@ -87,7 +86,7 @@ module ActiveSupport #:nodoc:
87
86
  @wrapped_string.split(*args).map { |i| self.class.new(i) }
88
87
  end
89
88
 
90
- # Works like like <tt>String#slice!</tt>, but returns an instance of
89
+ # Works like <tt>String#slice!</tt>, but returns an instance of
91
90
  # Chars, or nil if the string was not modified.
92
91
  def slice!(*args)
93
92
  chars(@wrapped_string.slice!(*args))
@@ -11,7 +11,7 @@ module ActiveSupport
11
11
  NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
12
12
 
13
13
  # The Unicode version that is supported by the implementation
14
- UNICODE_VERSION = '6.2.0'
14
+ UNICODE_VERSION = '7.0.0'
15
15
 
16
16
  # The default normalization used for operations that require
17
17
  # normalization. It can be set to any of the normalizations
@@ -211,57 +211,44 @@ module ActiveSupport
211
211
  codepoints
212
212
  end
213
213
 
214
- # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
215
- # resulting in a valid UTF-8 string.
216
- #
217
- # Passing +true+ will forcibly tidy all bytes, assuming that the string's
218
- # encoding is entirely CP1252 or ISO-8859-1.
219
- def tidy_bytes(string, force = false)
220
- if force
221
- return string.unpack("C*").map do |b|
222
- tidy_byte(b)
223
- end.flatten.compact.pack("C*").unpack("U*").pack("U*")
214
+ # Ruby >= 2.1 has String#scrub, which is faster than the workaround used for < 2.1.
215
+ # Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
216
+ if '<3'.respond_to?(:scrub) && !defined?(Rubinius)
217
+ # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
218
+ # resulting in a valid UTF-8 string.
219
+ #
220
+ # Passing +true+ will forcibly tidy all bytes, assuming that the string's
221
+ # encoding is entirely CP1252 or ISO-8859-1.
222
+ def tidy_bytes(string, force = false)
223
+ return string if string.empty?
224
+ return recode_windows1252_chars(string) if force
225
+ string.scrub { |bad| recode_windows1252_chars(bad) }
224
226
  end
227
+ else
228
+ def tidy_bytes(string, force = false)
229
+ return string if string.empty?
230
+ return recode_windows1252_chars(string) if force
231
+
232
+ # We can't transcode to the same format, so we choose a nearly-identical encoding.
233
+ # We're going to 'transcode' bytes from UTF-8 when possible, then fall back to
234
+ # CP1252 when we get errors. The final string will be 'converted' back to UTF-8
235
+ # before returning.
236
+ reader = Encoding::Converter.new(Encoding::UTF_8, Encoding::UTF_16LE)
237
+
238
+ source = string.dup
239
+ out = ''.force_encoding(Encoding::UTF_16LE)
240
+
241
+ loop do
242
+ reader.primitive_convert(source, out)
243
+ _, _, _, error_bytes, _ = reader.primitive_errinfo
244
+ break if error_bytes.nil?
245
+ out << error_bytes.encode(Encoding::UTF_16LE, Encoding::Windows_1252, invalid: :replace, undef: :replace)
246
+ end
225
247
 
226
- bytes = string.unpack("C*")
227
- conts_expected = 0
228
- last_lead = 0
229
-
230
- bytes.each_index do |i|
231
-
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
248
+ reader.finish
237
249
 
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
250
+ out.encode!(Encoding::UTF_8)
263
251
  end
264
- bytes.empty? ? "" : bytes.flatten.compact.pack("C*").unpack("U*").pack("U*")
265
252
  end
266
253
 
267
254
  # Returns the KC normalization of the string by default. NFKC is
@@ -306,6 +293,13 @@ module ActiveSupport
306
293
  class Codepoint
307
294
  attr_accessor :code, :combining_class, :decomp_type, :decomp_mapping, :uppercase_mapping, :lowercase_mapping
308
295
 
296
+ # Initializing Codepoint object with default values
297
+ def initialize
298
+ @combining_class = 0
299
+ @uppercase_mapping = 0
300
+ @lowercase_mapping = 0
301
+ end
302
+
309
303
  def swapcase_mapping
310
304
  uppercase_mapping > 0 ? uppercase_mapping : lowercase_mapping
311
305
  end
@@ -341,7 +335,7 @@ module ActiveSupport
341
335
  begin
342
336
  @codepoints, @composition_exclusion, @composition_map, @boundary, @cp1252 = File.open(self.class.filename, 'rb') { |f| Marshal.load f.read }
343
337
  rescue => e
344
- raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
338
+ raise IOError.new("Couldn't load the Unicode tables for UTF8Handler (#{e.message}), ActiveSupport::Multibyte is unusable")
345
339
  end
346
340
 
347
341
  # Redefine the === method so we can write shorter rules for grapheme cluster breaks
@@ -373,6 +367,7 @@ module ActiveSupport
373
367
  private
374
368
 
375
369
  def apply_mapping(string, mapping) #:nodoc:
370
+ database.codepoints
376
371
  string.each_codepoint.map do |codepoint|
377
372
  cp = database.codepoints[codepoint]
378
373
  if cp and (ncp = cp.send(mapping)) and ncp > 0
@@ -383,20 +378,13 @@ module ActiveSupport
383
378
  end.pack('U*')
384
379
  end
385
380
 
386
- def tidy_byte(byte)
387
- if byte < 160
388
- [database.cp1252[byte] || byte].pack("U").unpack("C*")
389
- elsif byte < 192
390
- [194, byte]
391
- else
392
- [195, byte - 64]
393
- end
381
+ def recode_windows1252_chars(string)
382
+ string.encode(Encoding::UTF_8, Encoding::Windows_1252, invalid: :replace, undef: :replace)
394
383
  end
395
384
 
396
385
  def database
397
386
  @database ||= UnicodeDatabase.new
398
387
  end
399
-
400
388
  end
401
389
  end
402
390
  end
@@ -25,9 +25,15 @@ module ActiveSupport
25
25
  subscriber
26
26
  end
27
27
 
28
- def unsubscribe(subscriber)
28
+ def unsubscribe(subscriber_or_name)
29
29
  synchronize do
30
- @subscribers.reject! { |s| s.matches?(subscriber) }
30
+ case subscriber_or_name
31
+ when String
32
+ @subscribers.reject! { |s| s.matches?(subscriber_or_name) }
33
+ else
34
+ @subscribers.delete(subscriber_or_name)
35
+ end
36
+
31
37
  @listeners_for.clear
32
38
  end
33
39
  end
@@ -97,16 +103,15 @@ module ActiveSupport
97
103
  end
98
104
 
99
105
  def subscribed_to?(name)
100
- @pattern === name.to_s
106
+ @pattern === name
101
107
  end
102
108
 
103
- def matches?(subscriber_or_name)
104
- self === subscriber_or_name ||
105
- @pattern && @pattern === subscriber_or_name
109
+ def matches?(name)
110
+ @pattern && @pattern === name
106
111
  end
107
112
  end
108
113
 
109
- class Timed < Evented
114
+ class Timed < Evented # :nodoc:
110
115
  def publish(name, *args)
111
116
  @delegate.call name, *args
112
117
  end
@@ -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)
@@ -16,7 +16,7 @@ module ActiveSupport
16
16
  # render text: 'Foo'
17
17
  # end
18
18
  #
19
- # That executes the block first and notifies all subscribers once done.
19
+ # That first executes the block and then notifies all subscribers once done.
20
20
  #
21
21
  # In the example above +render+ is the name of the event, and the rest is called
22
22
  # the _payload_. The payload is a mechanism that allows instrumenters to pass
@@ -141,10 +141,15 @@ module ActiveSupport
141
141
  #
142
142
  # ActiveSupport::Notifications.unsubscribe(subscriber)
143
143
  #
144
+ # You can also unsubscribe by passing the name of the subscriber object. Note
145
+ # that this will unsubscribe all subscriptions with the given name:
146
+ #
147
+ # ActiveSupport::Notifications.unsubscribe("render")
148
+ #
144
149
  # == Default Queue
145
150
  #
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.
151
+ # Notifications ships with a queue implementation that consumes and publishes events
152
+ # to all log subscribers. You can use any queue implementation you want.
148
153
  #
149
154
  module Notifications
150
155
  class << self
@@ -173,12 +178,12 @@ module ActiveSupport
173
178
  unsubscribe(subscriber)
174
179
  end
175
180
 
176
- def unsubscribe(args)
177
- notifier.unsubscribe(args)
181
+ def unsubscribe(subscriber_or_name)
182
+ notifier.unsubscribe(subscriber_or_name)
178
183
  end
179
184
 
180
185
  def instrumenter
181
- InstrumentationRegistry.instrumenter_for(notifier)
186
+ InstrumentationRegistry.instance.instrumenter_for(notifier)
182
187
  end
183
188
  end
184
189
 
@@ -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,23 @@
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) do |digit_to_delimit|
17
+ "#{digit_to_delimit}#{options[:delimiter]}"
18
+ end
19
+ [left, right].compact
20
+ end
21
+ end
22
+ end
23
+ end