activesupport 6.1.0 → 7.1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1075 -325
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/lib/active_support/actionable_error.rb +4 -2
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +32 -7
  8. data/lib/active_support/benchmarkable.rb +3 -2
  9. data/lib/active_support/broadcast_logger.rb +251 -0
  10. data/lib/active_support/builder.rb +1 -1
  11. data/lib/active_support/cache/coder.rb +153 -0
  12. data/lib/active_support/cache/entry.rb +134 -0
  13. data/lib/active_support/cache/file_store.rb +53 -20
  14. data/lib/active_support/cache/mem_cache_store.rb +201 -62
  15. data/lib/active_support/cache/memory_store.rb +86 -24
  16. data/lib/active_support/cache/null_store.rb +16 -2
  17. data/lib/active_support/cache/redis_cache_store.rb +186 -193
  18. data/lib/active_support/cache/serializer_with_fallback.rb +175 -0
  19. data/lib/active_support/cache/strategy/local_cache.rb +63 -71
  20. data/lib/active_support/cache.rb +487 -249
  21. data/lib/active_support/callbacks.rb +227 -105
  22. data/lib/active_support/code_generator.rb +70 -0
  23. data/lib/active_support/concern.rb +9 -7
  24. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +44 -7
  25. data/lib/active_support/concurrency/null_lock.rb +13 -0
  26. data/lib/active_support/concurrency/share_lock.rb +2 -2
  27. data/lib/active_support/configurable.rb +18 -5
  28. data/lib/active_support/configuration_file.rb +7 -2
  29. data/lib/active_support/core_ext/array/access.rb +1 -5
  30. data/lib/active_support/core_ext/array/conversions.rb +15 -13
  31. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +2 -2
  33. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  34. data/lib/active_support/core_ext/class/subclasses.rb +37 -26
  35. data/lib/active_support/core_ext/date/blank.rb +1 -1
  36. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  37. data/lib/active_support/core_ext/date/conversions.rb +16 -15
  38. data/lib/active_support/core_ext/date_and_time/calculations.rb +14 -4
  39. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  41. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  42. data/lib/active_support/core_ext/date_time/conversions.rb +19 -15
  43. data/lib/active_support/core_ext/digest/uuid.rb +30 -13
  44. data/lib/active_support/core_ext/enumerable.rb +85 -83
  45. data/lib/active_support/core_ext/erb/util.rb +196 -0
  46. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +1 -2
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +22 -14
  49. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  50. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  51. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  52. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  53. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  54. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  55. data/lib/active_support/core_ext/module/attribute_accessors.rb +8 -0
  56. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +49 -22
  57. data/lib/active_support/core_ext/module/concerning.rb +6 -6
  58. data/lib/active_support/core_ext/module/delegation.rb +81 -43
  59. data/lib/active_support/core_ext/module/deprecation.rb +15 -12
  60. data/lib/active_support/core_ext/module/introspection.rb +0 -1
  61. data/lib/active_support/core_ext/name_error.rb +2 -8
  62. data/lib/active_support/core_ext/numeric/bytes.rb +9 -0
  63. data/lib/active_support/core_ext/numeric/conversions.rb +82 -77
  64. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  65. data/lib/active_support/core_ext/object/blank.rb +2 -2
  66. data/lib/active_support/core_ext/object/deep_dup.rb +17 -1
  67. data/lib/active_support/core_ext/object/duplicable.rb +31 -11
  68. data/lib/active_support/core_ext/object/inclusion.rb +13 -5
  69. data/lib/active_support/core_ext/object/instance_variables.rb +22 -12
  70. data/lib/active_support/core_ext/object/json.rb +49 -27
  71. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  72. data/lib/active_support/core_ext/object/try.rb +20 -20
  73. data/lib/active_support/core_ext/object/with.rb +44 -0
  74. data/lib/active_support/core_ext/object/with_options.rb +25 -6
  75. data/lib/active_support/core_ext/object.rb +1 -0
  76. data/lib/active_support/core_ext/pathname/blank.rb +16 -0
  77. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  78. data/lib/active_support/core_ext/pathname.rb +4 -0
  79. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  80. data/lib/active_support/core_ext/range/conversions.rb +34 -13
  81. data/lib/active_support/core_ext/range/each.rb +1 -1
  82. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  83. data/lib/active_support/core_ext/range.rb +1 -2
  84. data/lib/active_support/core_ext/securerandom.rb +25 -13
  85. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  86. data/lib/active_support/core_ext/string/filters.rb +21 -15
  87. data/lib/active_support/core_ext/string/indent.rb +1 -1
  88. data/lib/active_support/core_ext/string/inflections.rb +17 -10
  89. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  90. data/lib/active_support/core_ext/string/output_safety.rb +85 -165
  91. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  92. data/lib/active_support/core_ext/thread/backtrace/location.rb +12 -0
  93. data/lib/active_support/core_ext/time/calculations.rb +30 -8
  94. data/lib/active_support/core_ext/time/conversions.rb +15 -13
  95. data/lib/active_support/core_ext/time/zones.rb +12 -28
  96. data/lib/active_support/core_ext.rb +2 -1
  97. data/lib/active_support/current_attributes.rb +47 -20
  98. data/lib/active_support/deep_mergeable.rb +53 -0
  99. data/lib/active_support/dependencies/autoload.rb +17 -12
  100. data/lib/active_support/dependencies/interlock.rb +10 -18
  101. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  102. data/lib/active_support/dependencies.rb +58 -788
  103. data/lib/active_support/deprecation/behaviors.rb +66 -40
  104. data/lib/active_support/deprecation/constant_accessor.rb +5 -4
  105. data/lib/active_support/deprecation/deprecators.rb +104 -0
  106. data/lib/active_support/deprecation/disallowed.rb +6 -8
  107. data/lib/active_support/deprecation/instance_delegator.rb +31 -4
  108. data/lib/active_support/deprecation/method_wrappers.rb +9 -26
  109. data/lib/active_support/deprecation/proxy_wrappers.rb +38 -23
  110. data/lib/active_support/deprecation/reporting.rb +43 -26
  111. data/lib/active_support/deprecation.rb +32 -5
  112. data/lib/active_support/deprecator.rb +7 -0
  113. data/lib/active_support/descendants_tracker.rb +150 -72
  114. data/lib/active_support/digest.rb +5 -3
  115. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  116. data/lib/active_support/duration/iso8601_serializer.rb +9 -3
  117. data/lib/active_support/duration.rb +83 -52
  118. data/lib/active_support/encrypted_configuration.rb +72 -9
  119. data/lib/active_support/encrypted_file.rb +29 -13
  120. data/lib/active_support/environment_inquirer.rb +23 -3
  121. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  122. data/lib/active_support/error_reporter.rb +203 -0
  123. data/lib/active_support/evented_file_update_checker.rb +20 -7
  124. data/lib/active_support/execution_context/test_helper.rb +13 -0
  125. data/lib/active_support/execution_context.rb +53 -0
  126. data/lib/active_support/execution_wrapper.rb +44 -22
  127. data/lib/active_support/executor/test_helper.rb +7 -0
  128. data/lib/active_support/file_update_checker.rb +4 -2
  129. data/lib/active_support/fork_tracker.rb +28 -11
  130. data/lib/active_support/gem_version.rb +4 -4
  131. data/lib/active_support/gzip.rb +2 -0
  132. data/lib/active_support/hash_with_indifferent_access.rb +44 -19
  133. data/lib/active_support/html_safe_translation.rb +53 -0
  134. data/lib/active_support/i18n.rb +2 -1
  135. data/lib/active_support/i18n_railtie.rb +21 -14
  136. data/lib/active_support/inflector/inflections.rb +25 -7
  137. data/lib/active_support/inflector/methods.rb +50 -64
  138. data/lib/active_support/inflector/transliterate.rb +4 -2
  139. data/lib/active_support/isolated_execution_state.rb +76 -0
  140. data/lib/active_support/json/decoding.rb +2 -1
  141. data/lib/active_support/json/encoding.rb +27 -45
  142. data/lib/active_support/key_generator.rb +31 -6
  143. data/lib/active_support/lazy_load_hooks.rb +33 -7
  144. data/lib/active_support/locale/en.yml +4 -2
  145. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  146. data/lib/active_support/log_subscriber.rb +97 -35
  147. data/lib/active_support/logger.rb +9 -60
  148. data/lib/active_support/logger_thread_safe_level.rb +11 -34
  149. data/lib/active_support/message_encryptor.rb +206 -56
  150. data/lib/active_support/message_encryptors.rb +141 -0
  151. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  152. data/lib/active_support/message_pack/extensions.rb +292 -0
  153. data/lib/active_support/message_pack/serializer.rb +63 -0
  154. data/lib/active_support/message_pack.rb +50 -0
  155. data/lib/active_support/message_verifier.rb +235 -84
  156. data/lib/active_support/message_verifiers.rb +135 -0
  157. data/lib/active_support/messages/codec.rb +65 -0
  158. data/lib/active_support/messages/metadata.rb +112 -46
  159. data/lib/active_support/messages/rotation_coordinator.rb +93 -0
  160. data/lib/active_support/messages/rotator.rb +34 -32
  161. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  162. data/lib/active_support/multibyte/chars.rb +12 -11
  163. data/lib/active_support/multibyte/unicode.rb +9 -49
  164. data/lib/active_support/multibyte.rb +1 -1
  165. data/lib/active_support/notifications/fanout.rb +304 -114
  166. data/lib/active_support/notifications/instrumenter.rb +117 -35
  167. data/lib/active_support/notifications.rb +25 -25
  168. data/lib/active_support/number_helper/number_converter.rb +14 -7
  169. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  170. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  171. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -4
  172. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  173. data/lib/active_support/number_helper/number_to_rounded_converter.rb +10 -6
  174. data/lib/active_support/number_helper/rounding_helper.rb +2 -6
  175. data/lib/active_support/number_helper.rb +379 -319
  176. data/lib/active_support/option_merger.rb +10 -18
  177. data/lib/active_support/ordered_hash.rb +4 -4
  178. data/lib/active_support/ordered_options.rb +15 -1
  179. data/lib/active_support/parameter_filter.rb +105 -81
  180. data/lib/active_support/proxy_object.rb +2 -0
  181. data/lib/active_support/railtie.rb +83 -21
  182. data/lib/active_support/reloader.rb +13 -5
  183. data/lib/active_support/rescuable.rb +18 -16
  184. data/lib/active_support/ruby_features.rb +7 -0
  185. data/lib/active_support/secure_compare_rotator.rb +18 -11
  186. data/lib/active_support/security_utils.rb +1 -1
  187. data/lib/active_support/string_inquirer.rb +3 -3
  188. data/lib/active_support/subscriber.rb +11 -40
  189. data/lib/active_support/syntax_error_proxy.rb +60 -0
  190. data/lib/active_support/tagged_logging.rb +65 -25
  191. data/lib/active_support/test_case.rb +166 -27
  192. data/lib/active_support/testing/assertions.rb +61 -15
  193. data/lib/active_support/testing/autorun.rb +0 -2
  194. data/lib/active_support/testing/constant_stubbing.rb +32 -0
  195. data/lib/active_support/testing/deprecation.rb +53 -2
  196. data/lib/active_support/testing/error_reporter_assertions.rb +107 -0
  197. data/lib/active_support/testing/isolation.rb +30 -29
  198. data/lib/active_support/testing/method_call_assertions.rb +24 -11
  199. data/lib/active_support/testing/parallelization/server.rb +4 -0
  200. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  201. data/lib/active_support/testing/parallelization.rb +4 -0
  202. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  203. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  204. data/lib/active_support/testing/stream.rb +4 -6
  205. data/lib/active_support/testing/strict_warnings.rb +39 -0
  206. data/lib/active_support/testing/tagged_logging.rb +1 -1
  207. data/lib/active_support/testing/time_helpers.rb +49 -16
  208. data/lib/active_support/time_with_zone.rb +39 -28
  209. data/lib/active_support/values/time_zone.rb +50 -18
  210. data/lib/active_support/version.rb +1 -1
  211. data/lib/active_support/xml_mini/jdom.rb +4 -11
  212. data/lib/active_support/xml_mini/libxml.rb +5 -5
  213. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  214. data/lib/active_support/xml_mini/nokogiri.rb +5 -5
  215. data/lib/active_support/xml_mini/nokogirisax.rb +2 -2
  216. data/lib/active_support/xml_mini/rexml.rb +2 -2
  217. data/lib/active_support/xml_mini.rb +7 -6
  218. data/lib/active_support.rb +28 -1
  219. metadata +150 -18
  220. data/lib/active_support/core_ext/marshal.rb +0 -26
  221. data/lib/active_support/core_ext/range/include_time_with_zone.rb +0 -28
  222. data/lib/active_support/core_ext/range/overlaps.rb +0 -10
  223. data/lib/active_support/core_ext/uri.rb +0 -29
  224. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
  225. data/lib/active_support/per_thread_registry.rb +0 -60
@@ -4,7 +4,9 @@ require "active_support/security_utils"
4
4
  require "active_support/messages/rotator"
5
5
 
6
6
  module ActiveSupport
7
- # The ActiveSupport::SecureCompareRotator is a wrapper around +ActiveSupport::SecurityUtils.secure_compare+
7
+ # = Secure Compare Rotator
8
+ #
9
+ # The ActiveSupport::SecureCompareRotator is a wrapper around ActiveSupport::SecurityUtils.secure_compare
8
10
  # and allows you to rotate a previously defined value to a new one.
9
11
  #
10
12
  # It can be used as follow:
@@ -17,7 +19,7 @@ module ActiveSupport
17
19
  #
18
20
  # class MyController < ApplicationController
19
21
  # def authenticate_request
20
- # rotator = ActiveSupport::SecureComparerotator.new('new_password')
22
+ # rotator = ActiveSupport::SecureCompareRotator.new('new_password')
21
23
  # rotator.rotate('old_password')
22
24
  #
23
25
  # authenticate_or_request_with_http_basic do |username, password|
@@ -29,23 +31,28 @@ module ActiveSupport
29
31
  # end
30
32
  class SecureCompareRotator
31
33
  include SecurityUtils
32
- prepend Messages::Rotator
33
34
 
34
35
  InvalidMatch = Class.new(StandardError)
35
36
 
36
- def initialize(value, **_options)
37
+ def initialize(value, on_rotation: nil)
37
38
  @value = value
39
+ @rotate_values = []
40
+ @on_rotation = on_rotation
38
41
  end
39
42
 
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)
43
+ def rotate(previous_value)
44
+ @rotate_values << previous_value
44
45
  end
45
46
 
46
- private
47
- def build_rotation(previous_value, _options)
48
- self.class.new(previous_value)
47
+ def secure_compare!(other_value, on_rotation: @on_rotation)
48
+ if secure_compare(@value, other_value)
49
+ true
50
+ elsif @rotate_values.any? { |value| secure_compare(value, other_value) }
51
+ on_rotation&.call
52
+ true
53
+ else
54
+ raise InvalidMatch
49
55
  end
56
+ end
50
57
  end
51
58
  end
@@ -31,7 +31,7 @@ module ActiveSupport
31
31
  # the secret length. This should be considered when using secure_compare
32
32
  # to compare weak, short secrets to user input.
33
33
  def secure_compare(a, b)
34
- a.length == b.length && fixed_length_secure_compare(a, b)
34
+ a.bytesize == b.bytesize && fixed_length_secure_compare(a, b)
35
35
  end
36
36
  module_function :secure_compare
37
37
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/symbol/starts_ends_with"
4
-
5
3
  module ActiveSupport
4
+ # = \String Inquirer
5
+ #
6
6
  # Wrapping a string in this class gives you a prettier way to test
7
7
  # for equality. The value returned by <tt>Rails.env</tt> is wrapped
8
8
  # in a StringInquirer object, so instead of calling this:
@@ -13,7 +13,7 @@ module ActiveSupport
13
13
  #
14
14
  # Rails.env.production?
15
15
  #
16
- # == Instantiating a new StringInquirer
16
+ # == Instantiating a new \StringInquirer
17
17
  #
18
18
  # vehicle = ActiveSupport::StringInquirer.new('car')
19
19
  # vehicle.car? # => true
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/per_thread_registry"
4
3
  require "active_support/notifications"
5
4
 
6
5
  module ActiveSupport
7
- # ActiveSupport::Subscriber is an object set to consume
6
+ # = Active Support \Subscriber
7
+ #
8
+ # +ActiveSupport::Subscriber+ is an object set to consume
8
9
  # ActiveSupport::Notifications. The subscriber dispatches notifications to
9
10
  # a registered object based on its given namespace.
10
11
  #
@@ -21,9 +22,9 @@ module ActiveSupport
21
22
  # end
22
23
  # end
23
24
  #
24
- # After configured, whenever a "sql.active_record" notification is published,
25
- # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
26
- # the +sql+ method.
25
+ # After configured, whenever a <tt>"sql.active_record"</tt> notification is
26
+ # published, it will properly dispatch the event
27
+ # (ActiveSupport::Notifications::Event) to the +sql+ method.
27
28
  #
28
29
  # We can detach a subscriber as well:
29
30
  #
@@ -127,48 +128,18 @@ module ActiveSupport
127
128
  attr_reader :patterns # :nodoc:
128
129
 
129
130
  def initialize
130
- @queue_key = [self.class.name, object_id].join "-"
131
131
  @patterns = {}
132
132
  super
133
133
  end
134
134
 
135
- def start(name, id, payload)
136
- event = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload)
137
- event.start!
138
- parent = event_stack.last
139
- parent << event if parent
140
-
141
- event_stack.push event
142
- end
143
-
144
- def finish(name, id, payload)
145
- event = event_stack.pop
146
- event.finish!
147
- event.payload.merge!(payload)
148
-
149
- method = name.split(".").first
135
+ def call(event)
136
+ method = event.name[0, event.name.index(".")]
150
137
  send(method, event)
151
138
  end
152
139
 
153
- private
154
- def event_stack
155
- SubscriberQueueRegistry.instance.get_queue(@queue_key)
156
- end
157
- end
158
-
159
- # This is a registry for all the event stacks kept for subscribers.
160
- #
161
- # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
162
- # for further details.
163
- class SubscriberQueueRegistry # :nodoc:
164
- extend PerThreadRegistry
165
-
166
- def initialize
167
- @registry = {}
168
- end
169
-
170
- def get_queue(queue_key)
171
- @registry[queue_key] ||= []
140
+ def publish_event(event) # :nodoc:
141
+ method = event.name[0, event.name.index(".")]
142
+ send(method, event)
172
143
  end
173
144
  end
174
145
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+
5
+ module ActiveSupport
6
+ # This is a class for wrapping syntax errors. The purpose of this class
7
+ # is to enhance the backtraces on SyntaxError exceptions to include the
8
+ # source location of the syntax error. That way we can display the error
9
+ # source on error pages in development.
10
+ class SyntaxErrorProxy < DelegateClass(SyntaxError) # :nodoc:
11
+ def backtrace
12
+ parse_message_for_trace + super
13
+ end
14
+
15
+ class BacktraceLocation < Struct.new(:path, :lineno, :to_s) # :nodoc:
16
+ def spot(_)
17
+ end
18
+
19
+ def label
20
+ end
21
+ end
22
+
23
+ class BacktraceLocationProxy < DelegateClass(Thread::Backtrace::Location) # :nodoc:
24
+ def initialize(loc, ex)
25
+ super(loc)
26
+ @ex = ex
27
+ end
28
+
29
+ def spot(_)
30
+ super(@ex.__getobj__)
31
+ end
32
+ end
33
+
34
+ def backtrace_locations
35
+ return nil if super.nil?
36
+
37
+ parse_message_for_trace.map { |trace|
38
+ file, line = trace.match(/^(.+?):(\d+).*$/, &:captures) || trace
39
+ BacktraceLocation.new(file, line.to_i, trace)
40
+ # We have to wrap these backtrace locations because we need the
41
+ # spot information to come from the originating exception, not the
42
+ # proxy object that's generating these
43
+ } + super.map { |loc| BacktraceLocationProxy.new(loc, self) }
44
+ end
45
+
46
+ private
47
+ def parse_message_for_trace
48
+ if __getobj__.to_s.start_with?("(eval")
49
+ # If the exception is coming from a call to eval, we need to keep
50
+ # the path of the file in which eval was called to ensure we can
51
+ # return the right source fragment to show the location of the
52
+ # error
53
+ location = __getobj__.backtrace_locations[0]
54
+ ["#{location.path}:#{location.lineno}: #{__getobj__}"]
55
+ else
56
+ __getobj__.to_s.split("\n")
57
+ end
58
+ end
59
+ end
60
+ end
@@ -6,14 +6,16 @@ require "logger"
6
6
  require "active_support/logger"
7
7
 
8
8
  module ActiveSupport
9
+ # = Active Support Tagged Logging
10
+ #
9
11
  # Wraps any standard Logger object to provide tagging capabilities.
10
12
  #
11
13
  # May be called with a block:
12
14
  #
13
15
  # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
14
- # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
15
- # logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
16
- # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
16
+ # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
17
+ # logger.tagged('BCX', "Jason") { |tagged_logger| tagged_logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
18
+ # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
17
19
  #
18
20
  # If called without a block, a new logger will be returned with applied tags:
19
21
  #
@@ -29,60 +31,98 @@ module ActiveSupport
29
31
  module Formatter # :nodoc:
30
32
  # This method is invoked when a log event occurs.
31
33
  def call(severity, timestamp, progname, msg)
32
- super(severity, timestamp, progname, "#{tags_text}#{msg}")
34
+ super(severity, timestamp, progname, tag_stack.format_message(msg))
33
35
  end
34
36
 
35
37
  def tagged(*tags)
36
- new_tags = push_tags(*tags)
38
+ pushed_count = tag_stack.push_tags(tags).size
37
39
  yield self
38
40
  ensure
39
- pop_tags(new_tags.size)
41
+ pop_tags(pushed_count)
40
42
  end
41
43
 
42
44
  def push_tags(*tags)
43
- tags.flatten!
44
- tags.reject!(&:blank?)
45
- current_tags.concat tags
46
- tags
45
+ tag_stack.push_tags(tags)
47
46
  end
48
47
 
49
- def pop_tags(size = 1)
50
- current_tags.pop size
48
+ def pop_tags(count = 1)
49
+ tag_stack.pop_tags(count)
51
50
  end
52
51
 
53
52
  def clear_tags!
54
- current_tags.clear
53
+ tag_stack.clear
55
54
  end
56
55
 
57
- def current_tags
56
+ def tag_stack
58
57
  # We use our object ID here to avoid conflicting with other instances
59
- thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
60
- Thread.current[thread_key] ||= []
58
+ @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
59
+ IsolatedExecutionState[@thread_key] ||= TagStack.new
60
+ end
61
+
62
+ def current_tags
63
+ tag_stack.tags
61
64
  end
62
65
 
63
66
  def tags_text
64
- tags = current_tags
65
- if tags.one?
66
- "[#{tags[0]}] "
67
- elsif tags.any?
68
- tags.collect { |tag| "[#{tag}] " }.join
67
+ tag_stack.format_message("")
68
+ end
69
+ end
70
+
71
+ class TagStack # :nodoc:
72
+ attr_reader :tags
73
+
74
+ def initialize
75
+ @tags = []
76
+ @tags_string = nil
77
+ end
78
+
79
+ def push_tags(tags)
80
+ @tags_string = nil
81
+ tags.flatten!
82
+ tags.reject!(&:blank?)
83
+ @tags.concat(tags)
84
+ tags
85
+ end
86
+
87
+ def pop_tags(count)
88
+ @tags_string = nil
89
+ @tags.pop(count)
90
+ end
91
+
92
+ def clear
93
+ @tags_string = nil
94
+ @tags.clear
95
+ end
96
+
97
+ def format_message(message)
98
+ if @tags.empty?
99
+ message
100
+ elsif @tags.size == 1
101
+ "[#{@tags[0]}] #{message}"
102
+ else
103
+ @tags_string ||= "[#{@tags.join("] [")}] "
104
+ "#{@tags_string}#{message}"
69
105
  end
70
106
  end
71
107
  end
72
108
 
73
109
  module LocalTagStorage # :nodoc:
74
- attr_accessor :current_tags
110
+ attr_accessor :tag_stack
75
111
 
76
112
  def self.extended(base)
77
- base.current_tags = []
113
+ base.tag_stack = TagStack.new
78
114
  end
79
115
  end
80
116
 
81
117
  def self.new(logger)
82
- logger = logger.dup
118
+ logger = logger.clone
83
119
 
84
120
  if logger.formatter
85
- logger.formatter = logger.formatter.dup
121
+ logger.formatter = logger.formatter.clone
122
+
123
+ # Workaround for https://bugs.ruby-lang.org/issues/20250
124
+ # Can be removed when Ruby 3.4 is the least supported version.
125
+ logger.formatter.object_id if logger.formatter.is_a?(Proc)
86
126
  else
87
127
  # Ensure we set a default formatter so we aren't extending nil!
88
128
  logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
@@ -1,17 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- gem "minitest" # make sure we get the gem, not stdlib
4
3
  require "minitest"
5
4
  require "active_support/testing/tagged_logging"
6
5
  require "active_support/testing/setup_and_teardown"
7
6
  require "active_support/testing/assertions"
7
+ require "active_support/testing/error_reporter_assertions"
8
8
  require "active_support/testing/deprecation"
9
9
  require "active_support/testing/declarative"
10
10
  require "active_support/testing/isolation"
11
11
  require "active_support/testing/constant_lookup"
12
12
  require "active_support/testing/time_helpers"
13
+ require "active_support/testing/constant_stubbing"
13
14
  require "active_support/testing/file_fixtures"
14
15
  require "active_support/testing/parallelization"
16
+ require "active_support/testing/parallelize_executor"
15
17
  require "concurrent/utility/processor_counter"
16
18
 
17
19
  module ActiveSupport
@@ -65,32 +67,21 @@ module ActiveSupport
65
67
  # The default parallelization method is to fork processes. If you'd like to
66
68
  # use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+
67
69
  # method. Note the threaded parallelization does not create multiple
68
- # database and will not work with system tests at this time.
70
+ # databases and will not work with system tests.
69
71
  #
70
72
  # parallelize(workers: :number_of_processors, with: :threads)
71
73
  #
72
74
  # The threaded parallelization uses minitest's parallel executor directly.
73
75
  # The processes parallelization uses a Ruby DRb server.
74
- def parallelize(workers: :number_of_processors, with: :processes)
76
+ #
77
+ # Because parallelization presents an overhead, it is only enabled when the
78
+ # number of tests to run is above the +threshold+ param. The default value is
79
+ # 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
80
+ def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold)
75
81
  workers = Concurrent.physical_processor_count if workers == :number_of_processors
76
82
  workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
77
83
 
78
- return if workers <= 1
79
-
80
- executor = case with
81
- when :processes
82
- Testing::Parallelization.new(workers)
83
- when :threads
84
- Minitest::Parallel::Executor.new(workers)
85
- else
86
- raise ArgumentError, "#{with} is not a supported parallelization executor."
87
- end
88
-
89
- self.lock_threads = false if defined?(self.lock_threads) && with == :threads
90
-
91
- Minitest.parallel_executor = executor
92
-
93
- parallelize_me!
84
+ Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
94
85
  end
95
86
 
96
87
  # Set up hook for parallel testing. This can be used if you have multiple
@@ -107,9 +98,7 @@ module ActiveSupport
107
98
  # end
108
99
  # end
109
100
  def parallelize_setup(&block)
110
- ActiveSupport::Testing::Parallelization.after_fork_hook do |worker|
111
- yield worker
112
- end
101
+ ActiveSupport::Testing::Parallelization.after_fork_hook(&block)
113
102
  end
114
103
 
115
104
  # Clean up hook for parallel testing. This can be used to drop databases
@@ -126,10 +115,27 @@ module ActiveSupport
126
115
  # end
127
116
  # end
128
117
  def parallelize_teardown(&block)
129
- ActiveSupport::Testing::Parallelization.run_cleanup_hook do |worker|
130
- yield worker
131
- end
118
+ ActiveSupport::Testing::Parallelization.run_cleanup_hook(&block)
132
119
  end
120
+
121
+ # :singleton-method: fixture_paths
122
+ #
123
+ # Returns the ActiveRecord::FixtureSet collection.
124
+ #
125
+ # In your +test_helper.rb+ you must have <tt>require "rails/test_help"</tt>.
126
+
127
+ # :singleton-method: fixture_paths=
128
+ #
129
+ # :call-seq:
130
+ # fixture_paths=(fixture_paths)
131
+ #
132
+ # Sets the given path to the fixture set.
133
+ #
134
+ # Can also append multiple paths.
135
+ #
136
+ # ActiveSupport::TestCase.fixture_paths << "component1/test/fixtures"
137
+ #
138
+ # In your +test_helper.rb+ you must have <tt>require "rails/test_help"</tt>.
133
139
  end
134
140
 
135
141
  alias_method :method_name, :name
@@ -137,27 +143,160 @@ module ActiveSupport
137
143
  include ActiveSupport::Testing::TaggedLogging
138
144
  prepend ActiveSupport::Testing::SetupAndTeardown
139
145
  include ActiveSupport::Testing::Assertions
146
+ include ActiveSupport::Testing::ErrorReporterAssertions
140
147
  include ActiveSupport::Testing::Deprecation
148
+ include ActiveSupport::Testing::ConstantStubbing
141
149
  include ActiveSupport::Testing::TimeHelpers
142
150
  include ActiveSupport::Testing::FileFixtures
143
151
  extend ActiveSupport::Testing::Declarative
144
152
 
145
- # test/unit backwards compatibility methods
146
- alias :assert_raise :assert_raises
153
+ ##
154
+ # :method: assert_not_empty
155
+ #
156
+ # :call-seq:
157
+ # assert_not_empty(obj, msg = nil)
158
+ #
159
+ # Alias for: refute_empty[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_empty]
160
+
161
+ #
147
162
  alias :assert_not_empty :refute_empty
163
+
164
+ ##
165
+ # :method: assert_not_equal
166
+ #
167
+ # :call-seq:
168
+ # assert_not_equal(exp, act, msg = nil)
169
+ #
170
+ # Alias for: refute_equal[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_equal]
171
+
172
+ #
148
173
  alias :assert_not_equal :refute_equal
174
+
175
+ ##
176
+ # :method: assert_not_in_delta
177
+ #
178
+ # :call-seq:
179
+ # assert_not_in_delta(exp, act, delta = 0.001, msg = nil)
180
+ #
181
+ # Alias for: refute_in_delta[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_in_delta]
182
+
183
+ #
149
184
  alias :assert_not_in_delta :refute_in_delta
185
+
186
+ ##
187
+ # :method: assert_not_in_epsilon
188
+ #
189
+ # :call-seq:
190
+ # assert_not_in_epsilon(a, b, epsilon = 0.001, msg = nil)
191
+ #
192
+ # Alias for: refute_in_epsilon[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_in_epsilon]
193
+
194
+ #
150
195
  alias :assert_not_in_epsilon :refute_in_epsilon
196
+
197
+ ##
198
+ # :method: assert_not_includes
199
+ #
200
+ # :call-seq:
201
+ # assert_not_includes(collection, obj, msg = nil)
202
+ #
203
+ # Alias for: refute_includes[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_includes]
204
+
205
+ #
151
206
  alias :assert_not_includes :refute_includes
207
+
208
+ ##
209
+ # :method: assert_not_instance_of
210
+ #
211
+ # :call-seq:
212
+ # assert_not_instance_of(cls, obj, msg = nil)
213
+ #
214
+ # Alias for: refute_instance_of[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_instance_of]
215
+
216
+ #
152
217
  alias :assert_not_instance_of :refute_instance_of
218
+
219
+ ##
220
+ # :method: assert_not_kind_of
221
+ #
222
+ # :call-seq:
223
+ # assert_not_kind_of(cls, obj, msg = nil)
224
+ #
225
+ # Alias for: refute_kind_of[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_kind_of]
226
+
227
+ #
153
228
  alias :assert_not_kind_of :refute_kind_of
229
+
230
+ ##
231
+ # :method: assert_no_match
232
+ #
233
+ # :call-seq:
234
+ # assert_no_match(matcher, obj, msg = nil)
235
+ #
236
+ # Alias for: refute_match[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_match]
237
+
238
+ #
154
239
  alias :assert_no_match :refute_match
240
+
241
+ ##
242
+ # :method: assert_not_nil
243
+ #
244
+ # :call-seq:
245
+ # assert_not_nil(obj, msg = nil)
246
+ #
247
+ # Alias for: refute_nil[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_nil]
248
+
249
+ #
155
250
  alias :assert_not_nil :refute_nil
251
+
252
+ ##
253
+ # :method: assert_not_operator
254
+ #
255
+ # :call-seq:
256
+ # assert_not_operator(o1, op, o2 = UNDEFINED, msg = nil)
257
+ #
258
+ # Alias for: refute_operator[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_operator]
259
+
260
+ #
156
261
  alias :assert_not_operator :refute_operator
262
+
263
+ ##
264
+ # :method: assert_not_predicate
265
+ #
266
+ # :call-seq:
267
+ # assert_not_predicate(o1, op, msg = nil)
268
+ #
269
+ # Alias for: refute_predicate[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_predicate]
270
+
271
+ #
157
272
  alias :assert_not_predicate :refute_predicate
273
+
274
+ ##
275
+ # :method: assert_not_respond_to
276
+ #
277
+ # :call-seq:
278
+ # assert_not_respond_to(obj, meth, msg = nil)
279
+ #
280
+ # Alias for: refute_respond_to[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_respond_to]
281
+
282
+ #
158
283
  alias :assert_not_respond_to :refute_respond_to
284
+
285
+ ##
286
+ # :method: assert_not_same
287
+ #
288
+ # :call-seq:
289
+ # assert_not_same(exp, act, msg = nil)
290
+ #
291
+ # Alias for: refute_same[https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-refute_same]
292
+
293
+ #
159
294
  alias :assert_not_same :refute_same
160
295
 
161
296
  ActiveSupport.run_load_hooks(:active_support_test_case, self)
297
+
298
+ def inspect # :nodoc:
299
+ Object.instance_method(:to_s).bind_call(self)
300
+ end
162
301
  end
163
302
  end