activesupport 5.2.4.4 → 6.1.1

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 (187) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +353 -435
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support.rb +14 -1
  6. data/lib/active_support/actionable_error.rb +48 -0
  7. data/lib/active_support/array_inquirer.rb +4 -2
  8. data/lib/active_support/backtrace_cleaner.rb +29 -3
  9. data/lib/active_support/benchmarkable.rb +1 -1
  10. data/lib/active_support/cache.rb +142 -78
  11. data/lib/active_support/cache/file_store.rb +33 -33
  12. data/lib/active_support/cache/mem_cache_store.rb +32 -20
  13. data/lib/active_support/cache/memory_store.rb +59 -33
  14. data/lib/active_support/cache/null_store.rb +8 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +70 -43
  16. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  17. data/lib/active_support/callbacks.rb +81 -64
  18. data/lib/active_support/concern.rb +70 -3
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  20. data/lib/active_support/concurrency/share_lock.rb +0 -1
  21. data/lib/active_support/configurable.rb +10 -14
  22. data/lib/active_support/configuration_file.rb +46 -0
  23. data/lib/active_support/core_ext.rb +1 -1
  24. data/lib/active_support/core_ext/array.rb +1 -1
  25. data/lib/active_support/core_ext/array/access.rb +18 -6
  26. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  27. data/lib/active_support/core_ext/array/extract.rb +21 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  30. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  31. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  32. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  33. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  34. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  35. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  36. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  37. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  38. data/lib/active_support/core_ext/enumerable.rb +171 -75
  39. data/lib/active_support/core_ext/hash.rb +1 -2
  40. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  41. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  42. data/lib/active_support/core_ext/hash/except.rb +2 -2
  43. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  44. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  45. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  46. data/lib/active_support/core_ext/kernel.rb +0 -1
  47. data/lib/active_support/core_ext/load_error.rb +1 -1
  48. data/lib/active_support/core_ext/marshal.rb +2 -0
  49. data/lib/active_support/core_ext/module.rb +0 -1
  50. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  51. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  52. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  53. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  54. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  55. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  56. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  57. data/lib/active_support/core_ext/name_error.rb +29 -2
  58. data/lib/active_support/core_ext/numeric.rb +0 -1
  59. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  60. data/lib/active_support/core_ext/object/blank.rb +1 -2
  61. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  62. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  63. data/lib/active_support/core_ext/object/json.rb +14 -2
  64. data/lib/active_support/core_ext/object/try.rb +17 -7
  65. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  66. data/lib/active_support/core_ext/range/compare_range.rb +34 -13
  67. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  68. data/lib/active_support/core_ext/range/each.rb +0 -1
  69. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  70. data/lib/active_support/core_ext/regexp.rb +8 -5
  71. data/lib/active_support/core_ext/securerandom.rb +23 -3
  72. data/lib/active_support/core_ext/string/access.rb +5 -16
  73. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  74. data/lib/active_support/core_ext/string/filters.rb +42 -1
  75. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  76. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  77. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  78. data/lib/active_support/core_ext/string/output_safety.rb +70 -13
  79. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  80. data/lib/active_support/core_ext/string/strip.rb +3 -1
  81. data/lib/active_support/core_ext/symbol.rb +3 -0
  82. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  83. data/lib/active_support/core_ext/time/calculations.rb +50 -3
  84. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  85. data/lib/active_support/core_ext/uri.rb +6 -1
  86. data/lib/active_support/current_attributes.rb +15 -2
  87. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  88. data/lib/active_support/dependencies.rb +109 -34
  89. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  90. data/lib/active_support/deprecation.rb +6 -1
  91. data/lib/active_support/deprecation/behaviors.rb +16 -3
  92. data/lib/active_support/deprecation/disallowed.rb +56 -0
  93. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  94. data/lib/active_support/deprecation/method_wrappers.rb +18 -23
  95. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  96. data/lib/active_support/deprecation/reporting.rb +50 -7
  97. data/lib/active_support/descendants_tracker.rb +59 -9
  98. data/lib/active_support/duration.rb +90 -38
  99. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  100. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  101. data/lib/active_support/encrypted_configuration.rb +0 -4
  102. data/lib/active_support/encrypted_file.rb +22 -4
  103. data/lib/active_support/environment_inquirer.rb +20 -0
  104. data/lib/active_support/evented_file_update_checker.rb +82 -117
  105. data/lib/active_support/execution_wrapper.rb +1 -0
  106. data/lib/active_support/file_update_checker.rb +0 -1
  107. data/lib/active_support/fork_tracker.rb +62 -0
  108. data/lib/active_support/gem_version.rb +4 -4
  109. data/lib/active_support/hash_with_indifferent_access.rb +64 -41
  110. data/lib/active_support/i18n.rb +1 -0
  111. data/lib/active_support/i18n_railtie.rb +15 -8
  112. data/lib/active_support/inflector/inflections.rb +2 -7
  113. data/lib/active_support/inflector/methods.rb +49 -58
  114. data/lib/active_support/inflector/transliterate.rb +47 -18
  115. data/lib/active_support/json/decoding.rb +25 -26
  116. data/lib/active_support/json/encoding.rb +11 -3
  117. data/lib/active_support/key_generator.rb +1 -33
  118. data/lib/active_support/lazy_load_hooks.rb +5 -2
  119. data/lib/active_support/locale/en.rb +33 -0
  120. data/lib/active_support/locale/en.yml +7 -3
  121. data/lib/active_support/log_subscriber.rb +39 -9
  122. data/lib/active_support/logger.rb +2 -17
  123. data/lib/active_support/logger_silence.rb +11 -19
  124. data/lib/active_support/logger_thread_safe_level.rb +50 -6
  125. data/lib/active_support/message_encryptor.rb +8 -13
  126. data/lib/active_support/message_verifier.rb +10 -10
  127. data/lib/active_support/messages/metadata.rb +11 -2
  128. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  129. data/lib/active_support/messages/rotator.rb +10 -9
  130. data/lib/active_support/multibyte/chars.rb +10 -68
  131. data/lib/active_support/multibyte/unicode.rb +15 -327
  132. data/lib/active_support/notifications.rb +72 -8
  133. data/lib/active_support/notifications/fanout.rb +116 -16
  134. data/lib/active_support/notifications/instrumenter.rb +71 -9
  135. data/lib/active_support/number_helper.rb +38 -12
  136. data/lib/active_support/number_helper/number_converter.rb +5 -6
  137. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  138. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  139. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  140. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  141. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  142. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  143. data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
  144. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  145. data/lib/active_support/option_merger.rb +22 -3
  146. data/lib/active_support/ordered_hash.rb +1 -1
  147. data/lib/active_support/ordered_options.rb +13 -3
  148. data/lib/active_support/parameter_filter.rb +133 -0
  149. data/lib/active_support/per_thread_registry.rb +1 -1
  150. data/lib/active_support/rails.rb +1 -10
  151. data/lib/active_support/railtie.rb +23 -1
  152. data/lib/active_support/reloader.rb +4 -5
  153. data/lib/active_support/rescuable.rb +4 -4
  154. data/lib/active_support/secure_compare_rotator.rb +51 -0
  155. data/lib/active_support/security_utils.rb +19 -12
  156. data/lib/active_support/string_inquirer.rb +4 -3
  157. data/lib/active_support/subscriber.rb +72 -28
  158. data/lib/active_support/tagged_logging.rb +42 -8
  159. data/lib/active_support/test_case.rb +91 -0
  160. data/lib/active_support/testing/assertions.rb +30 -9
  161. data/lib/active_support/testing/deprecation.rb +0 -1
  162. data/lib/active_support/testing/file_fixtures.rb +2 -0
  163. data/lib/active_support/testing/isolation.rb +2 -2
  164. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  165. data/lib/active_support/testing/parallelization.rb +51 -0
  166. data/lib/active_support/testing/parallelization/server.rb +78 -0
  167. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  168. data/lib/active_support/testing/stream.rb +1 -2
  169. data/lib/active_support/testing/time_helpers.rb +47 -12
  170. data/lib/active_support/time_with_zone.rb +81 -47
  171. data/lib/active_support/values/time_zone.rb +32 -17
  172. data/lib/active_support/xml_mini.rb +2 -10
  173. data/lib/active_support/xml_mini/jdom.rb +2 -3
  174. data/lib/active_support/xml_mini/libxml.rb +2 -2
  175. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  176. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  177. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  178. data/lib/active_support/xml_mini/rexml.rb +10 -3
  179. metadata +58 -32
  180. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  181. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  182. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  183. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  184. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  185. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  186. data/lib/active_support/core_ext/range/include_range.rb +0 -3
  187. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -40,7 +40,7 @@ module ActiveSupport
40
40
  # If the class has an initializer, it must accept no arguments.
41
41
  module PerThreadRegistry
42
42
  def self.extended(object)
43
- object.instance_variable_set "@per_thread_registry_key", object.name.freeze
43
+ object.instance_variable_set :@per_thread_registry_key, object.name.freeze
44
44
  end
45
45
 
46
46
  def instance
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # This is private interface.
3
+ # This is a private interface.
4
4
  #
5
5
  # Rails components cherry pick from Active Support as needed, but there are a
6
6
  # few features that are used for sure in some way or another and it is not worth
@@ -13,9 +13,6 @@
13
13
  # Defines Object#blank? and Object#present?.
14
14
  require "active_support/core_ext/object/blank"
15
15
 
16
- # Rails own autoload, eager_load, etc.
17
- require "active_support/dependencies/autoload"
18
-
19
16
  # Support for ClassMethods and the included macro.
20
17
  require "active_support/concern"
21
18
 
@@ -27,9 +24,3 @@ require "active_support/core_ext/module/delegation"
27
24
 
28
25
  # Defines ActiveSupport::Deprecation.
29
26
  require "active_support/deprecation"
30
-
31
- # Defines Regexp#match?.
32
- #
33
- # This should be removed when Rails needs Ruby 2.4 or later, and the require
34
- # added where other Regexp extensions are being used (easy to grep).
35
- require "active_support/core_ext/regexp"
@@ -22,12 +22,25 @@ module ActiveSupport
22
22
  app.reloader.before_class_unload { ActiveSupport::CurrentAttributes.clear_all }
23
23
  app.executor.to_run { ActiveSupport::CurrentAttributes.reset_all }
24
24
  app.executor.to_complete { ActiveSupport::CurrentAttributes.reset_all }
25
+
26
+ ActiveSupport.on_load(:active_support_test_case) do
27
+ require "active_support/current_attributes/test_helper"
28
+ include ActiveSupport::CurrentAttributes::TestHelper
29
+ end
25
30
  end
26
31
 
27
32
  initializer "active_support.deprecation_behavior" do |app|
28
33
  if deprecation = app.config.active_support.deprecation
29
34
  ActiveSupport::Deprecation.behavior = deprecation
30
35
  end
36
+
37
+ if disallowed_deprecation = app.config.active_support.disallowed_deprecation
38
+ ActiveSupport::Deprecation.disallowed_behavior = disallowed_deprecation
39
+ end
40
+
41
+ if disallowed_warnings = app.config.active_support.disallowed_deprecation_warnings
42
+ ActiveSupport::Deprecation.disallowed_warnings = disallowed_warnings
43
+ end
31
44
  end
32
45
 
33
46
  # Sets the default value for Time.zone
@@ -65,15 +78,24 @@ module ActiveSupport
65
78
  initializer "active_support.set_configs" do |app|
66
79
  app.config.active_support.each do |k, v|
67
80
  k = "#{k}="
68
- ActiveSupport.send(k, v) if ActiveSupport.respond_to? k
81
+ ActiveSupport.public_send(k, v) if ActiveSupport.respond_to? k
69
82
  end
70
83
  end
71
84
 
72
85
  initializer "active_support.set_hash_digest_class" do |app|
73
86
  config.after_initialize do
74
87
  if app.config.active_support.use_sha1_digests
88
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
89
+ config.active_support.use_sha1_digests is deprecated and will
90
+ be removed from Rails 6.2. Use
91
+ config.active_support.hash_digest_class = ::Digest::SHA1 instead.
92
+ MSG
75
93
  ActiveSupport::Digest.hash_digest_class = ::Digest::SHA1
76
94
  end
95
+
96
+ if klass = app.config.active_support.hash_digest_class
97
+ ActiveSupport::Digest.hash_digest_class = klass
98
+ end
77
99
  end
78
100
  end
79
101
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/execution_wrapper"
4
+ require "active_support/executor"
4
5
 
5
6
  module ActiveSupport
6
7
  #--
@@ -49,11 +50,9 @@ module ActiveSupport
49
50
  def self.reload!
50
51
  executor.wrap do
51
52
  new.tap do |instance|
52
- begin
53
- instance.run!
54
- ensure
55
- instance.complete!
56
- end
53
+ instance.run!
54
+ ensure
55
+ instance.complete!
57
56
  end
58
57
  end
59
58
  prepare!
@@ -14,12 +14,12 @@ module ActiveSupport
14
14
  end
15
15
 
16
16
  module ClassMethods
17
- # Rescue exceptions raised in controller actions.
17
+ # Registers exception classes with a handler to be called by <tt>rescue_with_handler</tt>.
18
18
  #
19
19
  # <tt>rescue_from</tt> receives a series of exception classes or class
20
- # names, and a trailing <tt>:with</tt> option with the name of a method
21
- # or a Proc object to be called to handle them. Alternatively a block can
22
- # be given.
20
+ # names, and an exception handler specified by a trailing <tt>:with</tt>
21
+ # option containing the name of a method or a Proc object. Alternatively, a block
22
+ # can be given as the handler.
23
23
  #
24
24
  # Handlers that take one argument will be called with the exception, so
25
25
  # that the exception can be inspected when dealing with it.
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/security_utils"
4
+ require "active_support/messages/rotator"
5
+
6
+ module ActiveSupport
7
+ # The ActiveSupport::SecureCompareRotator is a wrapper around +ActiveSupport::SecurityUtils.secure_compare+
8
+ # and allows you to rotate a previously defined value to a new one.
9
+ #
10
+ # It can be used as follow:
11
+ #
12
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_production_value')
13
+ # rotator.rotate('previous_production_value')
14
+ # rotator.secure_compare!('previous_production_value')
15
+ #
16
+ # One real use case example would be to rotate a basic auth credentials:
17
+ #
18
+ # class MyController < ApplicationController
19
+ # def authenticate_request
20
+ # rotator = ActiveSupport::SecureComparerotator.new('new_password')
21
+ # rotator.rotate('old_password')
22
+ #
23
+ # authenticate_or_request_with_http_basic do |username, password|
24
+ # rotator.secure_compare!(password)
25
+ # rescue ActiveSupport::SecureCompareRotator::InvalidMatch
26
+ # false
27
+ # end
28
+ # end
29
+ # end
30
+ class SecureCompareRotator
31
+ include SecurityUtils
32
+ prepend Messages::Rotator
33
+
34
+ InvalidMatch = Class.new(StandardError)
35
+
36
+ def initialize(value, **_options)
37
+ @value = value
38
+ end
39
+
40
+ def secure_compare!(other_value, on_rotation: @on_rotation)
41
+ secure_compare(@value, other_value) ||
42
+ run_rotations(on_rotation) { |wrapper| wrapper.secure_compare!(other_value) } ||
43
+ raise(InvalidMatch)
44
+ end
45
+
46
+ private
47
+ def build_rotation(previous_value, _options)
48
+ self.class.new(previous_value)
49
+ end
50
+ end
51
+ end
@@ -1,30 +1,37 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "digest/sha2"
4
-
5
3
  module ActiveSupport
6
4
  module SecurityUtils
7
5
  # Constant time string comparison, for fixed length strings.
8
6
  #
9
7
  # The values compared should be of fixed length, such as strings
10
8
  # that have already been processed by HMAC. Raises in case of length mismatch.
11
- def fixed_length_secure_compare(a, b)
12
- raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
13
9
 
14
- l = a.unpack "C#{a.bytesize}"
10
+ if defined?(OpenSSL.fixed_length_secure_compare)
11
+ def fixed_length_secure_compare(a, b)
12
+ OpenSSL.fixed_length_secure_compare(a, b)
13
+ end
14
+ else
15
+ def fixed_length_secure_compare(a, b)
16
+ raise ArgumentError, "string length mismatch." unless a.bytesize == b.bytesize
17
+
18
+ l = a.unpack "C#{a.bytesize}"
15
19
 
16
- res = 0
17
- b.each_byte { |byte| res |= byte ^ l.shift }
18
- res == 0
20
+ res = 0
21
+ b.each_byte { |byte| res |= byte ^ l.shift }
22
+ res == 0
23
+ end
19
24
  end
20
25
  module_function :fixed_length_secure_compare
21
26
 
22
- # Constant time string comparison, for variable length strings.
27
+ # Secure string comparison for strings of variable length.
23
28
  #
24
- # The values are first processed by SHA256, so that we don't leak length info
25
- # via timing attacks.
29
+ # While a timing attack would not be able to discern the content of
30
+ # a secret compared via secure_compare, it is possible to determine
31
+ # the secret length. This should be considered when using secure_compare
32
+ # to compare weak, short secrets to user input.
26
33
  def secure_compare(a, b)
27
- fixed_length_secure_compare(::Digest::SHA256.hexdigest(a), ::Digest::SHA256.hexdigest(b)) && a == b
34
+ a.length == b.length && fixed_length_secure_compare(a, b)
28
35
  end
29
36
  module_function :secure_compare
30
37
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/symbol/starts_ends_with"
4
+
3
5
  module ActiveSupport
4
6
  # Wrapping a string in this class gives you a prettier way to test
5
7
  # for equality. The value returned by <tt>Rails.env</tt> is wrapped
@@ -18,13 +20,12 @@ module ActiveSupport
18
20
  # vehicle.bike? # => false
19
21
  class StringInquirer < String
20
22
  private
21
-
22
23
  def respond_to_missing?(method_name, include_private = false)
23
- (method_name[-1] == "?") || super
24
+ method_name.end_with?("?") || super
24
25
  end
25
26
 
26
27
  def method_missing(method_name, *arguments)
27
- if method_name[-1] == "?"
28
+ if method_name.end_with?("?")
28
29
  self == method_name[0..-2]
29
30
  else
30
31
  super
@@ -24,22 +24,46 @@ module ActiveSupport
24
24
  # After configured, whenever a "sql.active_record" notification is published,
25
25
  # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
26
26
  # the +sql+ method.
27
+ #
28
+ # We can detach a subscriber as well:
29
+ #
30
+ # ActiveRecord::StatsSubscriber.detach_from(:active_record)
27
31
  class Subscriber
28
32
  class << self
29
33
  # Attach the subscriber to a namespace.
30
- def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications)
34
+ def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false)
31
35
  @namespace = namespace
32
36
  @subscriber = subscriber
33
37
  @notifier = notifier
38
+ @inherit_all = inherit_all
34
39
 
35
40
  subscribers << subscriber
36
41
 
37
42
  # Add event subscribers for all existing methods on the class.
38
- subscriber.public_methods(false).each do |event|
43
+ fetch_public_methods(subscriber, inherit_all).each do |event|
39
44
  add_event_subscriber(event)
40
45
  end
41
46
  end
42
47
 
48
+ # Detach the subscriber from a namespace.
49
+ def detach_from(namespace, notifier = ActiveSupport::Notifications)
50
+ @namespace = namespace
51
+ @subscriber = find_attached_subscriber
52
+ @notifier = notifier
53
+
54
+ return unless subscriber
55
+
56
+ subscribers.delete(subscriber)
57
+
58
+ # Remove event subscribers of all existing methods on the class.
59
+ fetch_public_methods(subscriber, true).each do |event|
60
+ remove_event_subscriber(event)
61
+ end
62
+
63
+ # Reset notifier so that event subscribers will not add for new methods added to the class.
64
+ @notifier = nil
65
+ end
66
+
43
67
  # Adds event subscribers for all new methods added to the class.
44
68
  def method_added(event)
45
69
  # Only public methods are added as subscribers, and only if a notifier
@@ -54,62 +78,82 @@ module ActiveSupport
54
78
  @@subscribers ||= []
55
79
  end
56
80
 
57
- # TODO Change this to private once we've dropped Ruby 2.2 support.
58
- # Workaround for Ruby 2.2 "private attribute?" warning.
59
- protected
81
+ private
82
+ attr_reader :subscriber, :notifier, :namespace
83
+
84
+ def add_event_subscriber(event) # :doc:
85
+ return if invalid_event?(event)
60
86
 
61
- attr_reader :subscriber, :notifier, :namespace
87
+ pattern = prepare_pattern(event)
62
88
 
63
- private
89
+ # Don't add multiple subscribers (e.g. if methods are redefined).
90
+ return if pattern_subscribed?(pattern)
64
91
 
65
- def add_event_subscriber(event) # :doc:
66
- return if %w{ start finish }.include?(event.to_s)
92
+ subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
93
+ end
67
94
 
68
- pattern = "#{event}.#{namespace}"
95
+ def remove_event_subscriber(event) # :doc:
96
+ return if invalid_event?(event)
69
97
 
70
- # Don't add multiple subscribers (eg. if methods are redefined).
71
- return if subscriber.patterns.include?(pattern)
98
+ pattern = prepare_pattern(event)
72
99
 
73
- subscriber.patterns << pattern
74
- notifier.subscribe(pattern, subscriber)
75
- end
100
+ return unless pattern_subscribed?(pattern)
101
+
102
+ notifier.unsubscribe(subscriber.patterns[pattern])
103
+ subscriber.patterns.delete(pattern)
104
+ end
105
+
106
+ def find_attached_subscriber
107
+ subscribers.find { |attached_subscriber| attached_subscriber.instance_of?(self) }
108
+ end
109
+
110
+ def invalid_event?(event)
111
+ %i{ start finish }.include?(event.to_sym)
112
+ end
113
+
114
+ def prepare_pattern(event)
115
+ "#{event}.#{namespace}"
116
+ end
117
+
118
+ def pattern_subscribed?(pattern)
119
+ subscriber.patterns.key?(pattern)
120
+ end
121
+
122
+ def fetch_public_methods(subscriber, inherit_all)
123
+ subscriber.public_methods(inherit_all) - Subscriber.public_instance_methods(true)
124
+ end
76
125
  end
77
126
 
78
127
  attr_reader :patterns # :nodoc:
79
128
 
80
129
  def initialize
81
130
  @queue_key = [self.class.name, object_id].join "-"
82
- @patterns = []
131
+ @patterns = {}
83
132
  super
84
133
  end
85
134
 
86
135
  def start(name, id, payload)
87
- e = ActiveSupport::Notifications::Event.new(name, now, nil, id, payload)
136
+ event = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload)
137
+ event.start!
88
138
  parent = event_stack.last
89
- parent << e if parent
139
+ parent << event if parent
90
140
 
91
- event_stack.push e
141
+ event_stack.push event
92
142
  end
93
143
 
94
144
  def finish(name, id, payload)
95
- finished = now
96
- event = event_stack.pop
97
- event.end = finished
145
+ event = event_stack.pop
146
+ event.finish!
98
147
  event.payload.merge!(payload)
99
148
 
100
- method = name.split(".".freeze).first
149
+ method = name.split(".").first
101
150
  send(method, event)
102
151
  end
103
152
 
104
153
  private
105
-
106
154
  def event_stack
107
155
  SubscriberQueueRegistry.instance.get_queue(@queue_key)
108
156
  end
109
-
110
- def now
111
- Process.clock_gettime(Process::CLOCK_MONOTONIC)
112
- end
113
157
  end
114
158
 
115
159
  # This is a registry for all the event stacks kept for subscribers.
@@ -8,11 +8,20 @@ require "active_support/logger"
8
8
  module ActiveSupport
9
9
  # Wraps any standard Logger object to provide tagging capabilities.
10
10
  #
11
+ # May be called with a block:
12
+ #
11
13
  # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
12
14
  # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
13
15
  # logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
14
16
  # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
15
17
  #
18
+ # If called without a block, a new logger will be returned with applied tags:
19
+ #
20
+ # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
21
+ # logger.tagged("BCX").info "Stuff" # Logs "[BCX] Stuff"
22
+ # logger.tagged("BCX", "Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
23
+ # logger.tagged("BCX").tagged("Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
24
+ #
16
25
  # This is used by the default Rails.logger as configured by Railties to make
17
26
  # it easy to stamp log lines with subdomains, request ids, and anything else
18
27
  # to aid debugging of multi-user production applications.
@@ -31,9 +40,10 @@ module ActiveSupport
31
40
  end
32
41
 
33
42
  def push_tags(*tags)
34
- tags.flatten.reject(&:blank?).tap do |new_tags|
35
- current_tags.concat new_tags
36
- end
43
+ tags.flatten!
44
+ tags.reject!(&:blank?)
45
+ current_tags.concat tags
46
+ tags
37
47
  end
38
48
 
39
49
  def pop_tags(size = 1)
@@ -46,21 +56,38 @@ module ActiveSupport
46
56
 
47
57
  def current_tags
48
58
  # We use our object ID here to avoid conflicting with other instances
49
- thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
59
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
50
60
  Thread.current[thread_key] ||= []
51
61
  end
52
62
 
53
63
  def tags_text
54
64
  tags = current_tags
55
- if tags.any?
65
+ if tags.one?
66
+ "[#{tags[0]}] "
67
+ elsif tags.any?
56
68
  tags.collect { |tag| "[#{tag}] " }.join
57
69
  end
58
70
  end
59
71
  end
60
72
 
73
+ module LocalTagStorage # :nodoc:
74
+ attr_accessor :current_tags
75
+
76
+ def self.extended(base)
77
+ base.current_tags = []
78
+ end
79
+ end
80
+
61
81
  def self.new(logger)
62
- # Ensure we set a default formatter so we aren't extending nil!
63
- logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
82
+ logger = logger.dup
83
+
84
+ if logger.formatter
85
+ logger.formatter = logger.formatter.dup
86
+ else
87
+ # Ensure we set a default formatter so we aren't extending nil!
88
+ logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
89
+ end
90
+
64
91
  logger.formatter.extend Formatter
65
92
  logger.extend(self)
66
93
  end
@@ -68,7 +95,14 @@ module ActiveSupport
68
95
  delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
69
96
 
70
97
  def tagged(*tags)
71
- formatter.tagged(*tags) { yield self }
98
+ if block_given?
99
+ formatter.tagged(*tags) { yield self }
100
+ else
101
+ logger = ActiveSupport::TaggedLogging.new(self)
102
+ logger.formatter.extend LocalTagStorage
103
+ logger.push_tags(*formatter.current_tags, *tags)
104
+ logger
105
+ end
72
106
  end
73
107
 
74
108
  def flush