activesupport 4.2.11.3 → 5.0.7.2

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 (182) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +678 -348
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +2 -3
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +1 -1
  7. data/lib/active_support/benchmarkable.rb +1 -1
  8. data/lib/active_support/cache/file_store.rb +36 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +63 -54
  10. data/lib/active_support/cache/memory_store.rb +16 -21
  11. data/lib/active_support/cache/null_store.rb +1 -4
  12. data/lib/active_support/cache/strategy/local_cache.rb +31 -20
  13. data/lib/active_support/cache/strategy/local_cache_middleware.rb +4 -4
  14. data/lib/active_support/cache.rb +71 -87
  15. data/lib/active_support/callbacks.rb +109 -113
  16. data/lib/active_support/concern.rb +1 -1
  17. data/lib/active_support/concurrency/latch.rb +11 -12
  18. data/lib/active_support/concurrency/share_lock.rb +226 -0
  19. data/lib/active_support/configurable.rb +1 -0
  20. data/lib/active_support/core_ext/array/access.rb +27 -1
  21. data/lib/active_support/core_ext/array/conversions.rb +6 -4
  22. data/lib/active_support/core_ext/array/grouping.rb +9 -18
  23. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  24. data/lib/active_support/core_ext/array/wrap.rb +5 -4
  25. data/lib/active_support/core_ext/array.rb +1 -0
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -10
  27. data/lib/active_support/core_ext/class/attribute.rb +10 -9
  28. data/lib/active_support/core_ext/class/subclasses.rb +3 -2
  29. data/lib/active_support/core_ext/class.rb +0 -1
  30. data/lib/active_support/core_ext/date/blank.rb +12 -0
  31. data/lib/active_support/core_ext/date/calculations.rb +1 -1
  32. data/lib/active_support/core_ext/date/conversions.rb +7 -6
  33. data/lib/active_support/core_ext/date.rb +1 -1
  34. data/lib/active_support/core_ext/date_and_time/calculations.rb +100 -27
  35. data/lib/active_support/core_ext/date_and_time/compatibility.rb +0 -1
  36. data/lib/active_support/core_ext/date_and_time/zones.rb +3 -4
  37. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  38. data/lib/active_support/core_ext/date_time/calculations.rb +14 -8
  39. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  40. data/lib/active_support/core_ext/date_time.rb +1 -1
  41. data/lib/active_support/core_ext/enumerable.rb +75 -25
  42. data/lib/active_support/core_ext/file/atomic.rb +30 -25
  43. data/lib/active_support/core_ext/hash/conversions.rb +22 -2
  44. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -1
  45. data/lib/active_support/core_ext/hash/except.rb +9 -8
  46. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +25 -21
  48. data/lib/active_support/core_ext/hash/slice.rb +1 -1
  49. data/lib/active_support/core_ext/hash/transform_values.rb +11 -5
  50. data/lib/active_support/core_ext/integer/time.rb +2 -2
  51. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  52. data/lib/active_support/core_ext/kernel/debugger.rb +3 -10
  53. data/lib/active_support/core_ext/kernel/reporting.rb +2 -84
  54. data/lib/active_support/core_ext/kernel.rb +0 -1
  55. data/lib/active_support/core_ext/load_error.rb +5 -2
  56. data/lib/active_support/core_ext/marshal.rb +7 -9
  57. data/lib/active_support/core_ext/module/aliasing.rb +6 -1
  58. data/lib/active_support/core_ext/module/anonymous.rb +10 -1
  59. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  60. data/lib/active_support/core_ext/module/attribute_accessors.rb +15 -15
  61. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  62. data/lib/active_support/core_ext/module/concerning.rb +4 -4
  63. data/lib/active_support/core_ext/module/delegation.rb +11 -20
  64. data/lib/active_support/core_ext/module/deprecation.rb +2 -2
  65. data/lib/active_support/core_ext/module/introspection.rb +8 -2
  66. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -13
  67. data/lib/active_support/core_ext/module/qualified_const.rb +30 -12
  68. data/lib/active_support/core_ext/module/remove_method.rb +23 -0
  69. data/lib/active_support/core_ext/module.rb +1 -0
  70. data/lib/active_support/core_ext/name_error.rb +15 -2
  71. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  72. data/lib/active_support/core_ext/numeric/conversions.rb +78 -77
  73. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  74. data/lib/active_support/core_ext/numeric/time.rb +26 -6
  75. data/lib/active_support/core_ext/numeric.rb +1 -0
  76. data/lib/active_support/core_ext/object/blank.rb +15 -3
  77. data/lib/active_support/core_ext/object/deep_dup.rb +10 -3
  78. data/lib/active_support/core_ext/object/duplicable.rb +7 -12
  79. data/lib/active_support/core_ext/object/inclusion.rb +2 -2
  80. data/lib/active_support/core_ext/object/instance_variables.rb +1 -1
  81. data/lib/active_support/core_ext/object/json.rb +15 -7
  82. data/lib/active_support/core_ext/object/to_query.rb +1 -1
  83. data/lib/active_support/core_ext/object/try.rb +67 -21
  84. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  85. data/lib/active_support/core_ext/object.rb +0 -1
  86. data/lib/active_support/core_ext/range/conversions.rb +18 -6
  87. data/lib/active_support/core_ext/range/each.rb +16 -18
  88. data/lib/active_support/core_ext/range/include_range.rb +20 -20
  89. data/lib/active_support/core_ext/securerandom.rb +23 -0
  90. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  91. data/lib/active_support/core_ext/string/conversions.rb +3 -2
  92. data/lib/active_support/core_ext/string/filters.rb +1 -2
  93. data/lib/active_support/core_ext/string/inflections.rb +32 -5
  94. data/lib/active_support/core_ext/string/multibyte.rb +11 -7
  95. data/lib/active_support/core_ext/string/output_safety.rb +12 -14
  96. data/lib/active_support/core_ext/string/strip.rb +3 -6
  97. data/lib/active_support/core_ext/struct.rb +3 -6
  98. data/lib/active_support/core_ext/time/calculations.rb +18 -9
  99. data/lib/active_support/core_ext/time/conversions.rb +4 -2
  100. data/lib/active_support/core_ext/time/marshal.rb +2 -29
  101. data/lib/active_support/core_ext/time/zones.rb +36 -4
  102. data/lib/active_support/core_ext/time.rb +0 -1
  103. data/lib/active_support/core_ext/uri.rb +1 -3
  104. data/lib/active_support/core_ext.rb +2 -1
  105. data/lib/active_support/dependencies/interlock.rb +55 -0
  106. data/lib/active_support/dependencies.rb +88 -95
  107. data/lib/active_support/deprecation/behaviors.rb +15 -1
  108. data/lib/active_support/deprecation/instance_delegator.rb +13 -0
  109. data/lib/active_support/deprecation/method_wrappers.rb +42 -16
  110. data/lib/active_support/deprecation/proxy_wrappers.rb +47 -24
  111. data/lib/active_support/deprecation/reporting.rb +23 -5
  112. data/lib/active_support/deprecation.rb +1 -1
  113. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  114. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  115. data/lib/active_support/duration.rb +90 -15
  116. data/lib/active_support/evented_file_update_checker.rb +199 -0
  117. data/lib/active_support/execution_wrapper.rb +126 -0
  118. data/lib/active_support/executor.rb +6 -0
  119. data/lib/active_support/file_update_checker.rb +23 -3
  120. data/lib/active_support/gem_version.rb +5 -5
  121. data/lib/active_support/gzip.rb +1 -1
  122. data/lib/active_support/hash_with_indifferent_access.rb +40 -11
  123. data/lib/active_support/i18n_railtie.rb +25 -4
  124. data/lib/active_support/inflector/inflections.rb +36 -5
  125. data/lib/active_support/inflector/methods.rb +97 -90
  126. data/lib/active_support/inflector/transliterate.rb +36 -21
  127. data/lib/active_support/json/decoding.rb +11 -10
  128. data/lib/active_support/json/encoding.rb +1 -51
  129. data/lib/active_support/key_generator.rb +7 -9
  130. data/lib/active_support/lazy_load_hooks.rb +46 -18
  131. data/lib/active_support/locale/en.yml +2 -0
  132. data/lib/active_support/log_subscriber/test_helper.rb +3 -3
  133. data/lib/active_support/log_subscriber.rb +1 -1
  134. data/lib/active_support/logger.rb +3 -4
  135. data/lib/active_support/logger_silence.rb +2 -1
  136. data/lib/active_support/logger_thread_safe_level.rb +2 -3
  137. data/lib/active_support/message_encryptor.rb +7 -7
  138. data/lib/active_support/message_verifier.rb +70 -8
  139. data/lib/active_support/multibyte/chars.rb +12 -3
  140. data/lib/active_support/multibyte/unicode.rb +44 -21
  141. data/lib/active_support/notifications/fanout.rb +5 -5
  142. data/lib/active_support/notifications/instrumenter.rb +20 -2
  143. data/lib/active_support/notifications.rb +2 -2
  144. data/lib/active_support/number_helper/number_to_currency_converter.rb +7 -9
  145. data/lib/active_support/number_helper/number_to_delimited_converter.rb +8 -3
  146. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -4
  147. data/lib/active_support/number_helper/number_to_human_size_converter.rb +6 -2
  148. data/lib/active_support/number_helper/number_to_percentage_converter.rb +1 -1
  149. data/lib/active_support/number_helper/number_to_phone_converter.rb +11 -2
  150. data/lib/active_support/number_helper/number_to_rounded_converter.rb +30 -25
  151. data/lib/active_support/number_helper.rb +90 -67
  152. data/lib/active_support/ordered_hash.rb +1 -1
  153. data/lib/active_support/ordered_options.rb +15 -1
  154. data/lib/active_support/per_thread_registry.rb +3 -0
  155. data/lib/active_support/rails.rb +2 -2
  156. data/lib/active_support/railtie.rb +6 -1
  157. data/lib/active_support/reloader.rb +129 -0
  158. data/lib/active_support/rescuable.rb +101 -47
  159. data/lib/active_support/string_inquirer.rb +1 -1
  160. data/lib/active_support/subscriber.rb +5 -10
  161. data/lib/active_support/tagged_logging.rb +8 -7
  162. data/lib/active_support/test_case.rb +17 -29
  163. data/lib/active_support/testing/assertions.rb +15 -13
  164. data/lib/active_support/testing/deprecation.rb +9 -8
  165. data/lib/active_support/testing/file_fixtures.rb +34 -0
  166. data/lib/active_support/testing/isolation.rb +22 -8
  167. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  168. data/lib/active_support/testing/stream.rb +42 -0
  169. data/lib/active_support/testing/time_helpers.rb +3 -1
  170. data/lib/active_support/time_with_zone.rb +123 -33
  171. data/lib/active_support/values/time_zone.rb +101 -47
  172. data/lib/active_support/values/unicode_tables.dat +0 -0
  173. data/lib/active_support/xml_mini/jdom.rb +1 -1
  174. data/lib/active_support/xml_mini/libxml.rb +2 -2
  175. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  176. data/lib/active_support.rb +11 -6
  177. metadata +36 -17
  178. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  179. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  180. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  181. data/lib/active_support/core_ext/object/itself.rb +0 -15
  182. data/lib/active_support/core_ext/thread.rb +0 -86
@@ -20,29 +20,57 @@ module ActiveSupport
20
20
  # +activerecord/lib/active_record/base.rb+ is:
21
21
  #
22
22
  # ActiveSupport.run_load_hooks(:active_record, ActiveRecord::Base)
23
- @load_hooks = Hash.new { |h,k| h[k] = [] }
24
- @loaded = Hash.new { |h,k| h[k] = [] }
25
-
26
- def self.on_load(name, options = {}, &block)
27
- @loaded[name].each do |base|
28
- execute_hook(base, options, block)
23
+ module LazyLoadHooks
24
+ def self.extended(base) # :nodoc:
25
+ base.class_eval do
26
+ @load_hooks = Hash.new { |h, k| h[k] = [] }
27
+ @loaded = Hash.new { |h, k| h[k] = [] }
28
+ @run_once = Hash.new { |h, k| h[k] = [] }
29
+ end
29
30
  end
30
31
 
31
- @load_hooks[name] << [block, options]
32
- end
32
+ # Declares a block that will be executed when a Rails component is fully
33
+ # loaded.
34
+ #
35
+ # Options:
36
+ #
37
+ # * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
38
+ # * <tt>:run_once</tt> - Given +block+ will run only once.
39
+ def on_load(name, options = {}, &block)
40
+ @loaded[name].each do |base|
41
+ execute_hook(name, base, options, block)
42
+ end
33
43
 
34
- def self.execute_hook(base, options, block)
35
- if options[:yield]
36
- block.call(base)
37
- else
38
- base.instance_eval(&block)
44
+ @load_hooks[name] << [block, options]
39
45
  end
40
- end
41
46
 
42
- def self.run_load_hooks(name, base = Object)
43
- @loaded[name] << base
44
- @load_hooks[name].each do |hook, options|
45
- execute_hook(base, options, hook)
47
+ def run_load_hooks(name, base = Object)
48
+ @loaded[name] << base
49
+ @load_hooks[name].each do |hook, options|
50
+ execute_hook(name, base, options, hook)
51
+ end
46
52
  end
53
+
54
+ private
55
+
56
+ def with_execution_control(name, block, once)
57
+ unless @run_once[name].include?(block)
58
+ @run_once[name] << block if once
59
+
60
+ yield
61
+ end
62
+ end
63
+
64
+ def execute_hook(name, base, options, block)
65
+ with_execution_control(name, block, options[:run_once]) do
66
+ if options[:yield]
67
+ block.call(base)
68
+ else
69
+ base.instance_eval(&block)
70
+ end
71
+ end
72
+ end
47
73
  end
74
+
75
+ extend LazyLoadHooks
48
76
  end
@@ -106,6 +106,8 @@ en:
106
106
  mb: "MB"
107
107
  gb: "GB"
108
108
  tb: "TB"
109
+ pb: "PB"
110
+ eb: "EB"
109
111
  # Used in NumberHelper.number_to_human()
110
112
  decimal_units:
111
113
  format: "%n %u"
@@ -10,7 +10,7 @@ module ActiveSupport
10
10
  # class SyncLogSubscriberTest < ActiveSupport::TestCase
11
11
  # include ActiveSupport::LogSubscriber::TestHelper
12
12
  #
13
- # def setup
13
+ # setup do
14
14
  # ActiveRecord::LogSubscriber.attach_to(:active_record)
15
15
  # end
16
16
  #
@@ -33,7 +33,7 @@ module ActiveSupport
33
33
  # you can collect them doing @logger.logged(level), where level is the level
34
34
  # used in logging, like info, debug, warn and so on.
35
35
  module TestHelper
36
- def setup
36
+ def setup # :nodoc:
37
37
  @logger = MockLogger.new
38
38
  @notifier = ActiveSupport::Notifications::Fanout.new
39
39
 
@@ -44,7 +44,7 @@ module ActiveSupport
44
44
  ActiveSupport::Notifications.notifier = @notifier
45
45
  end
46
46
 
47
- def teardown
47
+ def teardown # :nodoc:
48
48
  set_logger(nil)
49
49
  ActiveSupport::Notifications.notifier = @old_notifier
50
50
  end
@@ -95,7 +95,7 @@ module ActiveSupport
95
95
  METHOD
96
96
  end
97
97
 
98
- # Set color by using a string or one of the defined constants. If a third
98
+ # Set color by using a symbol or one of the defined constants. If a third
99
99
  # option is set to +true+, it also adds bold to the string. This is based
100
100
  # on the Highline implementation and will automatically append CLEAR to the
101
101
  # end of the returned String.
@@ -1,4 +1,3 @@
1
- require 'active_support/core_ext/module/attribute_accessors'
2
1
  require 'active_support/logger_silence'
3
2
  require 'active_support/logger_thread_safe_level'
4
3
  require 'logger'
@@ -58,16 +57,16 @@ module ActiveSupport
58
57
  end
59
58
 
60
59
  define_method(:silence) do |level = Logger::ERROR, &block|
61
- if logger.respond_to?(:silence) && logger.method(:silence).owner != ::Kernel
60
+ if logger.respond_to?(:silence)
62
61
  logger.silence(level) do
63
- if respond_to?(:silence) && method(:silence).owner != ::Kernel
62
+ if defined?(super)
64
63
  super(level, &block)
65
64
  else
66
65
  block.call(self)
67
66
  end
68
67
  end
69
68
  else
70
- if respond_to?(:silence) && method(:silence).owner != ::Kernel
69
+ if defined?(super)
71
70
  super(level, &block)
72
71
  else
73
72
  block.call(self)
@@ -1,5 +1,6 @@
1
1
  require 'active_support/concern'
2
- require 'thread_safe'
2
+ require 'active_support/core_ext/module/attribute_accessors'
3
+ require 'concurrent'
3
4
 
4
5
  module LoggerSilence
5
6
  extend ActiveSupport::Concern
@@ -1,12 +1,11 @@
1
1
  require 'active_support/concern'
2
- require 'thread_safe'
3
2
 
4
3
  module ActiveSupport
5
- module LoggerThreadSafeLevel
4
+ module LoggerThreadSafeLevel # :nodoc:
6
5
  extend ActiveSupport::Concern
7
6
 
8
7
  def after_initialize
9
- @local_levels = ThreadSafe::Cache.new
8
+ @local_levels = Concurrent::Map.new(initial_capacity: 2)
10
9
  end
11
10
 
12
11
  def local_log_id
@@ -13,10 +13,10 @@ module ActiveSupport
13
13
  # where you don't want users to be able to determine the value of the payload.
14
14
  #
15
15
  # salt = SecureRandom.random_bytes(64)
16
- # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt) # => "\x89\xE0\x156\xAC..."
17
- # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
18
- # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
19
- # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
16
+ # key = ActiveSupport::KeyGenerator.new('password').generate_key(salt, 32) # => "\x89\xE0\x156\xAC..."
17
+ # crypt = ActiveSupport::MessageEncryptor.new(key) # => #<ActiveSupport::MessageEncryptor ...>
18
+ # encrypted_data = crypt.encrypt_and_sign('my secret data') # => "NlFBTTMwOUV5UlA1QlNEN2xkY2d6eThYWWh..."
19
+ # crypt.decrypt_and_verify(encrypted_data) # => "my secret data"
20
20
  class MessageEncryptor
21
21
  DEFAULT_CIPHER = "aes-256-cbc"
22
22
 
@@ -36,8 +36,8 @@ module ActiveSupport
36
36
  # Initialize a new MessageEncryptor. +secret+ must be at least as long as
37
37
  # the cipher key size. For the default 'aes-256-cbc' cipher, this is 256
38
38
  # bits. If you are using a user-entered secret, you can generate a suitable
39
- # key with <tt>OpenSSL::Digest::SHA256.new(user_secret).digest</tt> or
40
- # similar.
39
+ # key by using <tt>ActiveSupport::KeyGenerator</tt> or a similar key
40
+ # derivation function.
41
41
  #
42
42
  # Options:
43
43
  # * <tt>:cipher</tt> - Cipher to use. Can be any cipher returned by
@@ -89,7 +89,7 @@ module ActiveSupport
89
89
 
90
90
  def _decrypt(encrypted_message)
91
91
  cipher = new_cipher
92
- encrypted_data, iv = encrypted_message.split("--").map {|v| ::Base64.strict_decode64(v)}
92
+ encrypted_data, iv = encrypted_message.split("--".freeze).map {|v| ::Base64.strict_decode64(v)}
93
93
 
94
94
  cipher.decrypt
95
95
  cipher.key = @secret
@@ -15,7 +15,7 @@ module ActiveSupport
15
15
  # In the authentication filter:
16
16
  #
17
17
  # id, time = @verifier.verify(cookies[:remember_me])
18
- # if time < Time.now
18
+ # if Time.now < time
19
19
  # self.current_user = User.find(id)
20
20
  # end
21
21
  #
@@ -24,6 +24,12 @@ module ActiveSupport
24
24
  # hash upon initialization:
25
25
  #
26
26
  # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', serializer: YAML)
27
+ #
28
+ # +MessageVerifier+ creates HMAC signatures using SHA1 hash algorithm by default.
29
+ # If you want to use a different hash algorithm, you can change it by providing
30
+ # `:digest` key as an option while initializing the verifier:
31
+ #
32
+ # @verifier = ActiveSupport::MessageVerifier.new('s3Krit', digest: 'SHA256')
27
33
  class MessageVerifier
28
34
  class InvalidSignature < StandardError; end
29
35
 
@@ -34,22 +40,78 @@ module ActiveSupport
34
40
  @serializer = options[:serializer] || Marshal
35
41
  end
36
42
 
37
- def verify(signed_message)
38
- raise InvalidSignature if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
43
+ # Checks if a signed message could have been generated by signing an object
44
+ # with the +MessageVerifier+'s secret.
45
+ #
46
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
47
+ # signed_message = verifier.generate 'a private message'
48
+ # verifier.valid_message?(signed_message) # => true
49
+ #
50
+ # tampered_message = signed_message.chop # editing the message invalidates the signature
51
+ # verifier.valid_message?(tampered_message) # => false
52
+ def valid_message?(signed_message)
53
+ return if signed_message.nil? || !signed_message.valid_encoding? || signed_message.blank?
54
+
55
+ data, digest = signed_message.split("--".freeze)
56
+ data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
57
+ end
39
58
 
40
- data, digest = signed_message.split("--")
41
- if data.present? && digest.present? && ActiveSupport::SecurityUtils.secure_compare(digest, generate_digest(data))
59
+ # Decodes the signed message using the +MessageVerifier+'s secret.
60
+ #
61
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
62
+ #
63
+ # signed_message = verifier.generate 'a private message'
64
+ # verifier.verified(signed_message) # => 'a private message'
65
+ #
66
+ # Returns +nil+ if the message was not signed with the same secret.
67
+ #
68
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
69
+ # other_verifier.verified(signed_message) # => nil
70
+ #
71
+ # Returns +nil+ if the message is not Base64-encoded.
72
+ #
73
+ # invalid_message = "f--46a0120593880c733a53b6dad75b42ddc1c8996d"
74
+ # verifier.verified(invalid_message) # => nil
75
+ #
76
+ # Raises any error raised while decoding the signed message.
77
+ #
78
+ # incompatible_message = "test--dad7b06c94abba8d46a15fafaef56c327665d5ff"
79
+ # verifier.verified(incompatible_message) # => TypeError: incompatible marshal file format
80
+ def verified(signed_message)
81
+ if valid_message?(signed_message)
42
82
  begin
83
+ data = signed_message.split("--".freeze)[0]
43
84
  @serializer.load(decode(data))
44
85
  rescue ArgumentError => argument_error
45
- raise InvalidSignature if argument_error.message =~ %r{invalid base64}
86
+ return if argument_error.message =~ %r{invalid base64}
46
87
  raise
47
88
  end
48
- else
49
- raise InvalidSignature
50
89
  end
51
90
  end
52
91
 
92
+ # Decodes the signed message using the +MessageVerifier+'s secret.
93
+ #
94
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
95
+ # signed_message = verifier.generate 'a private message'
96
+ #
97
+ # verifier.verify(signed_message) # => 'a private message'
98
+ #
99
+ # Raises +InvalidSignature+ if the message was not signed with the same
100
+ # secret or was not Base64-encoded.
101
+ #
102
+ # other_verifier = ActiveSupport::MessageVerifier.new 'd1ff3r3nt-s3Krit'
103
+ # other_verifier.verify(signed_message) # => ActiveSupport::MessageVerifier::InvalidSignature
104
+ def verify(signed_message)
105
+ verified(signed_message) || raise(InvalidSignature)
106
+ end
107
+
108
+ # Generates a signed message for the provided value.
109
+ #
110
+ # The message is signed with the +MessageVerifier+'s secret. Without knowing
111
+ # the secret, the original value cannot be extracted from the message.
112
+ #
113
+ # verifier = ActiveSupport::MessageVerifier.new 's3Krit'
114
+ # verifier.generate 'a private message' # => "BAhJIhRwcml2YXRlLW1lc3NhZ2UGOgZFVA==--e2d724331ebdee96a10fb99b089508d1c72bd772"
53
115
  def generate(value)
54
116
  data = encode(@serializer.dump(value))
55
117
  "#{data}--#{generate_digest(data)}"
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  require 'active_support/json'
3
2
  require 'active_support/core_ext/string/access'
4
3
  require 'active_support/core_ext/string/behavior'
@@ -87,9 +86,19 @@ module ActiveSupport #:nodoc:
87
86
  end
88
87
 
89
88
  # Works like <tt>String#slice!</tt>, but returns an instance of
90
- # Chars, or nil if the string was not modified.
89
+ # Chars, or nil if the string was not modified. The string will not be
90
+ # modified if the range given is out of bounds
91
+ #
92
+ # string = 'Welcome'
93
+ # string.mb_chars.slice!(3) # => #<ActiveSupport::Multibyte::Chars:0x000000038109b8 @wrapped_string="c">
94
+ # string # => 'Welome'
95
+ # string.mb_chars.slice!(0..3) # => #<ActiveSupport::Multibyte::Chars:0x00000002eb80a0 @wrapped_string="Welo">
96
+ # string # => 'me'
91
97
  def slice!(*args)
92
- chars(@wrapped_string.slice!(*args))
98
+ string_sliced = @wrapped_string.slice!(*args)
99
+ if string_sliced
100
+ chars(string_sliced)
101
+ end
93
102
  end
94
103
 
95
104
  # Reverses all characters in the string.
@@ -1,4 +1,3 @@
1
- # encoding: utf-8
2
1
  module ActiveSupport
3
2
  module Multibyte
4
3
  module Unicode
@@ -11,7 +10,7 @@ module ActiveSupport
11
10
  NORMALIZATION_FORMS = [:c, :kc, :d, :kd]
12
11
 
13
12
  # The Unicode version that is supported by the implementation
14
- UNICODE_VERSION = '7.0.0'
13
+ UNICODE_VERSION = '8.0.0'
15
14
 
16
15
  # The default normalization used for operations that require
17
16
  # normalization. It can be set to any of the normalizations
@@ -58,7 +57,7 @@ module ActiveSupport
58
57
  # Returns a regular expression pattern that matches the passed Unicode
59
58
  # codepoints.
60
59
  def self.codepoints_to_pattern(array_of_codepoints) #:nodoc:
61
- array_of_codepoints.collect{ |e| [e].pack 'U*' }.join('|')
60
+ array_of_codepoints.collect{ |e| [e].pack 'U*'.freeze }.join('|'.freeze)
62
61
  end
63
62
  TRAILERS_PAT = /(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+\Z/u
64
63
  LEADERS_PAT = /\A(#{codepoints_to_pattern(LEADERS_AND_TRAILERS)})+/u
@@ -88,19 +87,44 @@ module ActiveSupport
88
87
  pos += 1
89
88
  previous = codepoints[pos-1]
90
89
  current = codepoints[pos]
91
- if (
92
- # CR X LF
93
- ( previous == database.boundary[:cr] and current == database.boundary[:lf] ) or
94
- # L X (L|V|LV|LVT)
95
- ( database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt]) ) or
96
- # (LV|V) X (V|T)
97
- ( in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t]) ) or
98
- # (LVT|T) X (T)
99
- ( in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current ) or
100
- # X Extend
101
- (database.boundary[:extend] === current)
102
- )
103
- else
90
+
91
+ should_break =
92
+ # GB3. CR X LF
93
+ if previous == database.boundary[:cr] and current == database.boundary[:lf]
94
+ false
95
+ # GB4. (Control|CR|LF) ÷
96
+ elsif previous and in_char_class?(previous, [:control,:cr,:lf])
97
+ true
98
+ # GB5. ÷ (Control|CR|LF)
99
+ elsif in_char_class?(current, [:control,:cr,:lf])
100
+ true
101
+ # GB6. L X (L|V|LV|LVT)
102
+ elsif database.boundary[:l] === previous and in_char_class?(current, [:l,:v,:lv,:lvt])
103
+ false
104
+ # GB7. (LV|V) X (V|T)
105
+ elsif in_char_class?(previous, [:lv,:v]) and in_char_class?(current, [:v,:t])
106
+ false
107
+ # GB8. (LVT|T) X (T)
108
+ elsif in_char_class?(previous, [:lvt,:t]) and database.boundary[:t] === current
109
+ false
110
+ # GB8a. Regional_Indicator X Regional_Indicator
111
+ elsif database.boundary[:regional_indicator] === previous and database.boundary[:regional_indicator] === current
112
+ false
113
+ # GB9. X Extend
114
+ elsif database.boundary[:extend] === current
115
+ false
116
+ # GB9a. X SpacingMark
117
+ elsif database.boundary[:spacingmark] === current
118
+ false
119
+ # GB9b. Prepend X
120
+ elsif database.boundary[:prepend] === previous
121
+ false
122
+ # GB10. Any ÷ Any
123
+ else
124
+ true
125
+ end
126
+
127
+ if should_break
104
128
  unpacked << codepoints[marker..pos-1]
105
129
  marker = pos
106
130
  end
@@ -211,9 +235,8 @@ module ActiveSupport
211
235
  codepoints
212
236
  end
213
237
 
214
- # Ruby >= 2.1 has String#scrub, which is faster than the workaround used for < 2.1.
215
238
  # Rubinius' String#scrub, however, doesn't support ASCII-incompatible chars.
216
- if '<3'.respond_to?(:scrub) && !defined?(Rubinius)
239
+ if !defined?(Rubinius)
217
240
  # Replaces all ISO-8859-1 or CP1252 characters by their UTF-8 equivalent
218
241
  # resulting in a valid UTF-8 string.
219
242
  #
@@ -258,7 +281,7 @@ module ActiveSupport
258
281
  # * <tt>string</tt> - The string to perform normalization on.
259
282
  # * <tt>form</tt> - The form you want to normalize in. Should be one of
260
283
  # the following: <tt>:c</tt>, <tt>:kc</tt>, <tt>:d</tt>, or <tt>:kd</tt>.
261
- # Default is ActiveSupport::Multibyte.default_normalization_form.
284
+ # Default is ActiveSupport::Multibyte::Unicode.default_normalization_form.
262
285
  def normalize(string, form=nil)
263
286
  form ||= @default_normalization_form
264
287
  # See http://www.unicode.org/reports/tr15, Table 1
@@ -274,7 +297,7 @@ module ActiveSupport
274
297
  compose(reorder_characters(decompose(:compatibility, codepoints)))
275
298
  else
276
299
  raise ArgumentError, "#{form} is not a valid normalization variant", caller
277
- end.pack('U*')
300
+ end.pack('U*'.freeze)
278
301
  end
279
302
 
280
303
  def downcase(string)
@@ -339,7 +362,7 @@ module ActiveSupport
339
362
  end
340
363
 
341
364
  # Redefine the === method so we can write shorter rules for grapheme cluster breaks
342
- @boundary.each do |k,_|
365
+ @boundary.each_key do |k|
343
366
  @boundary[k].instance_eval do
344
367
  def ===(other)
345
368
  detect { |i| i === other } ? true : false
@@ -1,5 +1,5 @@
1
1
  require 'mutex_m'
2
- require 'thread_safe'
2
+ require 'concurrent/map'
3
3
 
4
4
  module ActiveSupport
5
5
  module Notifications
@@ -12,7 +12,7 @@ module ActiveSupport
12
12
 
13
13
  def initialize
14
14
  @subscribers = []
15
- @listeners_for = ThreadSafe::Cache.new
15
+ @listeners_for = Concurrent::Map.new
16
16
  super
17
17
  end
18
18
 
@@ -42,8 +42,8 @@ module ActiveSupport
42
42
  listeners_for(name).each { |s| s.start(name, id, payload) }
43
43
  end
44
44
 
45
- def finish(name, id, payload)
46
- listeners_for(name).each { |s| s.finish(name, id, payload) }
45
+ def finish(name, id, payload, listeners = listeners_for(name))
46
+ listeners.each { |s| s.finish(name, id, payload) }
47
47
  end
48
48
 
49
49
  def publish(name, *args)
@@ -51,7 +51,7 @@ module ActiveSupport
51
51
  end
52
52
 
53
53
  def listeners_for(name)
54
- # this is correctly done double-checked locking (ThreadSafe::Cache's lookups have volatile semantics)
54
+ # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
55
55
  @listeners_for[name] || synchronize do
56
56
  # use synchronisation when accessing @subscribers
57
57
  @listeners_for[name] ||= @subscribers.select { |s| s.subscribed_to?(name) }
@@ -15,14 +15,16 @@ module ActiveSupport
15
15
  # and publish it. Notice that events get sent even if an error occurs
16
16
  # in the passed-in block.
17
17
  def instrument(name, payload={})
18
- start name, payload
18
+ # some of the listeners might have state
19
+ listeners_state = start name, payload
19
20
  begin
20
21
  yield payload
21
22
  rescue Exception => e
22
23
  payload[:exception] = [e.class.name, e.message]
24
+ payload[:exception_object] = e
23
25
  raise e
24
26
  ensure
25
- finish name, payload
27
+ finish_with_state listeners_state, name, payload
26
28
  end
27
29
  end
28
30
 
@@ -36,6 +38,10 @@ module ActiveSupport
36
38
  @notifier.finish name, @id, payload
37
39
  end
38
40
 
41
+ def finish_with_state(listeners_state, name, payload)
42
+ @notifier.finish name, @id, payload, listeners_state
43
+ end
44
+
39
45
  private
40
46
 
41
47
  def unique_id
@@ -57,6 +63,18 @@ module ActiveSupport
57
63
  @duration = nil
58
64
  end
59
65
 
66
+ # Returns the difference in milliseconds between when the execution of the
67
+ # event started and when it ended.
68
+ #
69
+ # ActiveSupport::Notifications.subscribe('wait') do |*args|
70
+ # @event = ActiveSupport::Notifications::Event.new(*args)
71
+ # end
72
+ #
73
+ # ActiveSupport::Notifications.instrument('wait') do
74
+ # sleep 1
75
+ # end
76
+ #
77
+ # @event.duration # => 1000.138
60
78
  def duration
61
79
  @duration ||= 1000.0 * (self.end - time)
62
80
  end
@@ -69,8 +69,8 @@ module ActiveSupport
69
69
  # is able to take the arguments as they come and provide an object-oriented
70
70
  # interface to that data.
71
71
  #
72
- # It is also possible to pass an object as the second parameter passed to the
73
- # <tt>subscribe</tt> method instead of a block:
72
+ # It is also possible to pass an object which responds to <tt>call</tt> method
73
+ # as the second parameter to the <tt>subscribe</tt> method instead of a block:
74
74
  #
75
75
  # module ActionController
76
76
  # class PageRequest
@@ -1,3 +1,5 @@
1
+ require 'active_support/core_ext/numeric/inquiry'
2
+
1
3
  module ActiveSupport
2
4
  module NumberHelper
3
5
  class NumberToCurrencyConverter < NumberConverter # :nodoc:
@@ -7,36 +9,32 @@ module ActiveSupport
7
9
  number = self.number.to_s.strip
8
10
  format = options[:format]
9
11
 
10
- if is_negative?(number)
12
+ if number.to_f.negative?
11
13
  format = options[:negative_format]
12
14
  number = absolute_value(number)
13
15
  end
14
16
 
15
17
  rounded_number = NumberToRoundedConverter.convert(number, options)
16
- format.gsub(/%n/, rounded_number).gsub(/%u/, options[:unit])
18
+ format.gsub('%n'.freeze, rounded_number).gsub('%u'.freeze, options[:unit])
17
19
  end
18
20
 
19
21
  private
20
22
 
21
- def is_negative?(number)
22
- number.to_f.phase != 0
23
- end
24
-
25
23
  def absolute_value(number)
26
- number.respond_to?("abs") ? number.abs : number.sub(/\A-/, '')
24
+ number.respond_to?(:abs) ? number.abs : number.sub(/\A-/, '')
27
25
  end
28
26
 
29
27
  def options
30
28
  @options ||= begin
31
29
  defaults = default_format_options.merge(i18n_opts)
32
- # Override negative format if format options is given
30
+ # Override negative format if format options are given
33
31
  defaults[:negative_format] = "-#{opts[:format]}" if opts[:format]
34
32
  defaults.merge!(opts)
35
33
  end
36
34
  end
37
35
 
38
36
  def i18n_opts
39
- # Set International negative format if not exists
37
+ # Set International negative format if it does not exist
40
38
  i18n = i18n_format_options
41
39
  i18n[:negative_format] ||= "-#{i18n[:format]}" if i18n[:format]
42
40
  i18n
@@ -3,7 +3,7 @@ module ActiveSupport
3
3
  class NumberToDelimitedConverter < NumberConverter #:nodoc:
4
4
  self.validate_float = true
5
5
 
6
- DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
6
+ DEFAULT_DELIMITER_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
7
7
 
8
8
  def convert
9
9
  parts.join(options[:separator])
@@ -12,12 +12,17 @@ module ActiveSupport
12
12
  private
13
13
 
14
14
  def parts
15
- left, right = number.to_s.split('.')
16
- left.gsub!(DELIMITED_REGEX) do |digit_to_delimit|
15
+ left, right = number.to_s.split('.'.freeze)
16
+ left.gsub!(delimiter_pattern) do |digit_to_delimit|
17
17
  "#{digit_to_delimit}#{options[:delimiter]}"
18
18
  end
19
19
  [left, right].compact
20
20
  end
21
+
22
+ def delimiter_pattern
23
+ options.fetch(:delimiter_pattern, DEFAULT_DELIMITER_REGEX)
24
+ end
25
+
21
26
  end
22
27
  end
23
28
  end
@@ -20,10 +20,12 @@ module ActiveSupport
20
20
  exponent = calculate_exponent(units)
21
21
  @number = number / (10 ** exponent)
22
22
 
23
+ until (rounded_number = NumberToRoundedConverter.convert(number, options)) != NumberToRoundedConverter.convert(1000, options)
24
+ @number = number / 1000.0
25
+ exponent += 3
26
+ end
23
27
  unit = determine_unit(units, exponent)
24
-
25
- rounded_number = NumberToRoundedConverter.convert(number, options)
26
- format.gsub(/%n/, rounded_number).gsub(/%u/, unit).strip
28
+ format.gsub('%n'.freeze, rounded_number).gsub('%u'.freeze, unit).strip
27
29
  end
28
30
 
29
31
  private
@@ -59,7 +61,7 @@ module ActiveSupport
59
61
  translate_in_locale("human.decimal_units.units", raise: true)
60
62
  else
61
63
  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 }
64
+ end.keys.map { |e_name| INVERTED_DECIMAL_UNITS[e_name] }.sort_by(&:-@)
63
65
  end
64
66
  end
65
67
  end