activesupport 5.1.7 → 7.0.4.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 (279) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +259 -585
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -5
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/all.rb +2 -0
  7. data/lib/active_support/array_inquirer.rb +4 -2
  8. data/lib/active_support/backtrace_cleaner.rb +33 -5
  9. data/lib/active_support/benchmarkable.rb +5 -3
  10. data/lib/active_support/builder.rb +2 -0
  11. data/lib/active_support/cache/file_store.rb +50 -43
  12. data/lib/active_support/cache/mem_cache_store.rb +194 -67
  13. data/lib/active_support/cache/memory_store.rb +70 -34
  14. data/lib/active_support/cache/null_store.rb +18 -3
  15. data/lib/active_support/cache/redis_cache_store.rb +474 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +73 -50
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +2 -0
  18. data/lib/active_support/cache.rb +556 -220
  19. data/lib/active_support/callbacks.rb +264 -159
  20. data/lib/active_support/code_generator.rb +65 -0
  21. data/lib/active_support/concern.rb +81 -8
  22. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +16 -0
  23. data/lib/active_support/concurrency/share_lock.rb +4 -3
  24. data/lib/active_support/configurable.rb +17 -16
  25. data/lib/active_support/configuration_file.rb +51 -0
  26. data/lib/active_support/core_ext/array/access.rb +18 -8
  27. data/lib/active_support/core_ext/array/conversions.rb +20 -17
  28. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  29. data/lib/active_support/core_ext/array/extract.rb +21 -0
  30. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  31. data/lib/active_support/core_ext/array/grouping.rb +8 -6
  32. data/lib/active_support/core_ext/array/inquiry.rb +4 -2
  33. data/lib/active_support/core_ext/array/wrap.rb +2 -0
  34. data/lib/active_support/core_ext/array.rb +4 -1
  35. data/lib/active_support/core_ext/benchmark.rb +4 -2
  36. data/lib/active_support/core_ext/big_decimal/conversions.rb +3 -1
  37. data/lib/active_support/core_ext/big_decimal.rb +2 -0
  38. data/lib/active_support/core_ext/class/attribute.rb +50 -47
  39. data/lib/active_support/core_ext/class/attribute_accessors.rb +2 -0
  40. data/lib/active_support/core_ext/class/subclasses.rb +10 -24
  41. data/lib/active_support/core_ext/class.rb +2 -0
  42. data/lib/active_support/core_ext/date/acts_like.rb +2 -0
  43. data/lib/active_support/core_ext/date/blank.rb +3 -1
  44. data/lib/active_support/core_ext/date/calculations.rb +17 -14
  45. data/lib/active_support/core_ext/date/conversions.rb +24 -22
  46. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  47. data/lib/active_support/core_ext/date/zones.rb +2 -0
  48. data/lib/active_support/core_ext/date.rb +3 -0
  49. data/lib/active_support/core_ext/date_and_time/calculations.rb +65 -41
  50. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -1
  51. data/lib/active_support/core_ext/date_and_time/zones.rb +2 -1
  52. data/lib/active_support/core_ext/date_time/acts_like.rb +2 -0
  53. data/lib/active_support/core_ext/date_time/blank.rb +3 -1
  54. data/lib/active_support/core_ext/date_time/calculations.rb +3 -1
  55. data/lib/active_support/core_ext/date_time/compatibility.rb +7 -5
  56. data/lib/active_support/core_ext/date_time/conversions.rb +15 -14
  57. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  58. data/lib/active_support/core_ext/date_time.rb +3 -0
  59. data/lib/active_support/core_ext/digest/uuid.rb +42 -14
  60. data/lib/active_support/core_ext/digest.rb +3 -0
  61. data/lib/active_support/core_ext/enumerable.rb +244 -72
  62. data/lib/active_support/core_ext/file/atomic.rb +6 -2
  63. data/lib/active_support/core_ext/file.rb +2 -0
  64. data/lib/active_support/core_ext/hash/conversions.rb +7 -6
  65. data/lib/active_support/core_ext/hash/deep_merge.rb +8 -12
  66. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  67. data/lib/active_support/core_ext/hash/except.rb +4 -2
  68. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  69. data/lib/active_support/core_ext/hash/keys.rb +4 -31
  70. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  71. data/lib/active_support/core_ext/hash/slice.rb +8 -29
  72. data/lib/active_support/core_ext/hash.rb +3 -2
  73. data/lib/active_support/core_ext/integer/inflections.rb +2 -0
  74. data/lib/active_support/core_ext/integer/multiple.rb +3 -1
  75. data/lib/active_support/core_ext/integer/time.rb +7 -14
  76. data/lib/active_support/core_ext/integer.rb +2 -0
  77. data/lib/active_support/core_ext/kernel/concern.rb +2 -0
  78. data/lib/active_support/core_ext/kernel/reporting.rb +6 -4
  79. data/lib/active_support/core_ext/kernel/singleton_class.rb +3 -1
  80. data/lib/active_support/core_ext/kernel.rb +2 -1
  81. data/lib/active_support/core_ext/load_error.rb +3 -8
  82. data/lib/active_support/core_ext/module/aliasing.rb +2 -0
  83. data/lib/active_support/core_ext/module/anonymous.rb +2 -0
  84. data/lib/active_support/core_ext/module/attr_internal.rb +4 -2
  85. data/lib/active_support/core_ext/module/attribute_accessors.rb +46 -56
  86. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +36 -27
  87. data/lib/active_support/core_ext/module/concerning.rb +15 -10
  88. data/lib/active_support/core_ext/module/delegation.rb +97 -58
  89. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  90. data/lib/active_support/core_ext/module/introspection.rb +18 -15
  91. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +5 -23
  93. data/lib/active_support/core_ext/module.rb +3 -1
  94. data/lib/active_support/core_ext/name_error.rb +30 -2
  95. data/lib/active_support/core_ext/numeric/bytes.rb +2 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +134 -129
  97. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +7 -15
  99. data/lib/active_support/core_ext/numeric.rb +3 -1
  100. data/lib/active_support/core_ext/object/acts_like.rb +41 -6
  101. data/lib/active_support/core_ext/object/blank.rb +15 -5
  102. data/lib/active_support/core_ext/object/conversions.rb +2 -0
  103. data/lib/active_support/core_ext/object/deep_dup.rb +3 -1
  104. data/lib/active_support/core_ext/object/duplicable.rb +16 -110
  105. data/lib/active_support/core_ext/object/inclusion.rb +2 -0
  106. data/lib/active_support/core_ext/object/instance_variables.rb +2 -0
  107. data/lib/active_support/core_ext/object/json.rb +51 -26
  108. data/lib/active_support/core_ext/object/to_param.rb +2 -0
  109. data/lib/active_support/core_ext/object/to_query.rb +4 -2
  110. data/lib/active_support/core_ext/object/try.rb +26 -14
  111. data/lib/active_support/core_ext/object/with_options.rb +24 -3
  112. data/lib/active_support/core_ext/object.rb +2 -0
  113. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  114. data/lib/active_support/core_ext/pathname.rb +3 -0
  115. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  116. data/lib/active_support/core_ext/range/conversions.rb +35 -25
  117. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  118. data/lib/active_support/core_ext/range/each.rb +6 -3
  119. data/lib/active_support/core_ext/range/include_time_with_zone.rb +7 -0
  120. data/lib/active_support/core_ext/range/overlaps.rb +3 -1
  121. data/lib/active_support/core_ext/range.rb +4 -1
  122. data/lib/active_support/core_ext/regexp.rb +10 -5
  123. data/lib/active_support/core_ext/securerandom.rb +25 -3
  124. data/lib/active_support/core_ext/string/access.rb +7 -16
  125. data/lib/active_support/core_ext/string/behavior.rb +2 -0
  126. data/lib/active_support/core_ext/string/conversions.rb +5 -2
  127. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  128. data/lib/active_support/core_ext/string/filters.rb +44 -1
  129. data/lib/active_support/core_ext/string/indent.rb +2 -0
  130. data/lib/active_support/core_ext/string/inflections.rb +69 -16
  131. data/lib/active_support/core_ext/string/inquiry.rb +4 -1
  132. data/lib/active_support/core_ext/string/multibyte.rb +9 -4
  133. data/lib/active_support/core_ext/string/output_safety.rb +135 -27
  134. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -2
  135. data/lib/active_support/core_ext/string/strip.rb +5 -1
  136. data/lib/active_support/core_ext/string/zones.rb +2 -0
  137. data/lib/active_support/core_ext/string.rb +2 -0
  138. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  139. data/lib/active_support/core_ext/symbol.rb +3 -0
  140. data/lib/active_support/core_ext/time/acts_like.rb +2 -0
  141. data/lib/active_support/core_ext/time/calculations.rb +81 -24
  142. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  143. data/lib/active_support/core_ext/time/conversions.rb +17 -12
  144. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  145. data/lib/active_support/core_ext/time/zones.rb +12 -25
  146. data/lib/active_support/core_ext/time.rb +3 -0
  147. data/lib/active_support/core_ext/uri.rb +4 -23
  148. data/lib/active_support/core_ext.rb +4 -1
  149. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  150. data/lib/active_support/current_attributes.rb +226 -0
  151. data/lib/active_support/dependencies/autoload.rb +2 -0
  152. data/lib/active_support/dependencies/interlock.rb +12 -18
  153. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  154. data/lib/active_support/dependencies.rb +59 -715
  155. data/lib/active_support/deprecation/behaviors.rb +48 -13
  156. data/lib/active_support/deprecation/constant_accessor.rb +4 -2
  157. data/lib/active_support/deprecation/disallowed.rb +56 -0
  158. data/lib/active_support/deprecation/instance_delegator.rb +2 -1
  159. data/lib/active_support/deprecation/method_wrappers.rb +29 -21
  160. data/lib/active_support/deprecation/proxy_wrappers.rb +34 -8
  161. data/lib/active_support/deprecation/reporting.rb +54 -9
  162. data/lib/active_support/deprecation.rb +10 -3
  163. data/lib/active_support/descendants_tracker.rb +192 -34
  164. data/lib/active_support/digest.rb +22 -0
  165. data/lib/active_support/duration/iso8601_parser.rb +9 -9
  166. data/lib/active_support/duration/iso8601_serializer.rb +29 -15
  167. data/lib/active_support/duration.rb +158 -72
  168. data/lib/active_support/encrypted_configuration.rb +56 -0
  169. data/lib/active_support/encrypted_file.rb +129 -0
  170. data/lib/active_support/environment_inquirer.rb +20 -0
  171. data/lib/active_support/error_reporter.rb +117 -0
  172. data/lib/active_support/evented_file_update_checker.rb +87 -122
  173. data/lib/active_support/execution_context/test_helper.rb +13 -0
  174. data/lib/active_support/execution_context.rb +53 -0
  175. data/lib/active_support/execution_wrapper.rb +46 -21
  176. data/lib/active_support/executor/test_helper.rb +7 -0
  177. data/lib/active_support/executor.rb +2 -0
  178. data/lib/active_support/file_update_checker.rb +2 -1
  179. data/lib/active_support/fork_tracker.rb +71 -0
  180. data/lib/active_support/gem_version.rb +7 -5
  181. data/lib/active_support/gzip.rb +2 -0
  182. data/lib/active_support/hash_with_indifferent_access.rb +126 -42
  183. data/lib/active_support/html_safe_translation.rb +43 -0
  184. data/lib/active_support/i18n.rb +5 -1
  185. data/lib/active_support/i18n_railtie.rb +19 -14
  186. data/lib/active_support/inflections.rb +2 -0
  187. data/lib/active_support/inflector/inflections.rb +41 -14
  188. data/lib/active_support/inflector/methods.rb +73 -87
  189. data/lib/active_support/inflector/transliterate.rb +56 -18
  190. data/lib/active_support/inflector.rb +2 -0
  191. data/lib/active_support/isolated_execution_state.rb +72 -0
  192. data/lib/active_support/json/decoding.rb +27 -26
  193. data/lib/active_support/json/encoding.rb +16 -6
  194. data/lib/active_support/json.rb +2 -0
  195. data/lib/active_support/key_generator.rb +25 -38
  196. data/lib/active_support/lazy_load_hooks.rb +35 -6
  197. data/lib/active_support/locale/en.rb +33 -0
  198. data/lib/active_support/locale/en.yml +8 -4
  199. data/lib/active_support/log_subscriber/test_helper.rb +4 -2
  200. data/lib/active_support/log_subscriber.rb +54 -13
  201. data/lib/active_support/logger.rb +4 -17
  202. data/lib/active_support/logger_silence.rb +13 -20
  203. data/lib/active_support/logger_thread_safe_level.rb +48 -10
  204. data/lib/active_support/message_encryptor.rb +111 -37
  205. data/lib/active_support/message_verifier.rb +124 -21
  206. data/lib/active_support/messages/metadata.rb +80 -0
  207. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  208. data/lib/active_support/messages/rotator.rb +57 -0
  209. data/lib/active_support/multibyte/chars.rb +19 -76
  210. data/lib/active_support/multibyte/unicode.rb +9 -331
  211. data/lib/active_support/multibyte.rb +3 -1
  212. data/lib/active_support/notifications/fanout.rb +165 -37
  213. data/lib/active_support/notifications/instrumenter.rb +92 -11
  214. data/lib/active_support/notifications.rb +96 -30
  215. data/lib/active_support/number_helper/number_converter.rb +8 -9
  216. data/lib/active_support/number_helper/number_to_currency_converter.rb +14 -12
  217. data/lib/active_support/number_helper/number_to_delimited_converter.rb +6 -3
  218. data/lib/active_support/number_helper/number_to_human_converter.rb +6 -3
  219. data/lib/active_support/number_helper/number_to_human_size_converter.rb +7 -4
  220. data/lib/active_support/number_helper/number_to_percentage_converter.rb +5 -1
  221. data/lib/active_support/number_helper/number_to_phone_converter.rb +6 -3
  222. data/lib/active_support/number_helper/number_to_rounded_converter.rb +14 -27
  223. data/lib/active_support/number_helper/rounding_helper.rb +16 -34
  224. data/lib/active_support/number_helper.rb +38 -12
  225. data/lib/active_support/option_merger.rb +19 -6
  226. data/lib/active_support/ordered_hash.rb +4 -2
  227. data/lib/active_support/ordered_options.rb +18 -6
  228. data/lib/active_support/parameter_filter.rb +138 -0
  229. data/lib/active_support/per_thread_registry.rb +8 -1
  230. data/lib/active_support/proxy_object.rb +2 -0
  231. data/lib/active_support/rails.rb +3 -10
  232. data/lib/active_support/railtie.rb +112 -11
  233. data/lib/active_support/reloader.rb +12 -11
  234. data/lib/active_support/rescuable.rb +19 -18
  235. data/lib/active_support/ruby_features.rb +7 -0
  236. data/lib/active_support/secure_compare_rotator.rb +51 -0
  237. data/lib/active_support/security_utils.rb +26 -15
  238. data/lib/active_support/string_inquirer.rb +4 -3
  239. data/lib/active_support/subscriber.rb +81 -42
  240. data/lib/active_support/tagged_logging.rb +45 -9
  241. data/lib/active_support/test_case.rb +86 -2
  242. data/lib/active_support/testing/assertions.rb +89 -21
  243. data/lib/active_support/testing/autorun.rb +2 -0
  244. data/lib/active_support/testing/constant_lookup.rb +2 -0
  245. data/lib/active_support/testing/declarative.rb +2 -0
  246. data/lib/active_support/testing/deprecation.rb +54 -2
  247. data/lib/active_support/testing/file_fixtures.rb +4 -0
  248. data/lib/active_support/testing/isolation.rb +6 -4
  249. data/lib/active_support/testing/method_call_assertions.rb +34 -5
  250. data/lib/active_support/testing/parallelization/server.rb +82 -0
  251. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  252. data/lib/active_support/testing/parallelization.rb +55 -0
  253. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  254. data/lib/active_support/testing/setup_and_teardown.rb +12 -7
  255. data/lib/active_support/testing/stream.rb +6 -7
  256. data/lib/active_support/testing/tagged_logging.rb +3 -1
  257. data/lib/active_support/testing/time_helpers.rb +91 -15
  258. data/lib/active_support/time.rb +2 -0
  259. data/lib/active_support/time_with_zone.rb +168 -56
  260. data/lib/active_support/values/time_zone.rb +85 -37
  261. data/lib/active_support/version.rb +3 -1
  262. data/lib/active_support/xml_mini/jdom.rb +6 -5
  263. data/lib/active_support/xml_mini/libxml.rb +9 -7
  264. data/lib/active_support/xml_mini/libxmlsax.rb +7 -5
  265. data/lib/active_support/xml_mini/nokogiri.rb +8 -6
  266. data/lib/active_support/xml_mini/nokogirisax.rb +6 -4
  267. data/lib/active_support/xml_mini/rexml.rb +13 -4
  268. data/lib/active_support/xml_mini.rb +10 -15
  269. data/lib/active_support.rb +30 -9
  270. metadata +76 -35
  271. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -7
  272. data/lib/active_support/core_ext/hash/compact.rb +0 -27
  273. data/lib/active_support/core_ext/hash/transform_values.rb +0 -30
  274. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -11
  275. data/lib/active_support/core_ext/marshal.rb +0 -22
  276. data/lib/active_support/core_ext/module/reachable.rb +0 -8
  277. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -26
  278. data/lib/active_support/core_ext/range/include_range.rb +0 -23
  279. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -1,4 +1,5 @@
1
- require "active_support/per_thread_registry"
1
+ # frozen_string_literal: true
2
+
2
3
  require "active_support/notifications"
3
4
 
4
5
  module ActiveSupport
@@ -22,22 +23,46 @@ module ActiveSupport
22
23
  # After configured, whenever a "sql.active_record" notification is published,
23
24
  # it will properly dispatch the event (ActiveSupport::Notifications::Event) to
24
25
  # the +sql+ method.
26
+ #
27
+ # We can detach a subscriber as well:
28
+ #
29
+ # ActiveRecord::StatsSubscriber.detach_from(:active_record)
25
30
  class Subscriber
26
31
  class << self
27
32
  # Attach the subscriber to a namespace.
28
- def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications)
33
+ def attach_to(namespace, subscriber = new, notifier = ActiveSupport::Notifications, inherit_all: false)
29
34
  @namespace = namespace
30
35
  @subscriber = subscriber
31
36
  @notifier = notifier
37
+ @inherit_all = inherit_all
32
38
 
33
39
  subscribers << subscriber
34
40
 
35
41
  # Add event subscribers for all existing methods on the class.
36
- subscriber.public_methods(false).each do |event|
42
+ fetch_public_methods(subscriber, inherit_all).each do |event|
37
43
  add_event_subscriber(event)
38
44
  end
39
45
  end
40
46
 
47
+ # Detach the subscriber from a namespace.
48
+ def detach_from(namespace, notifier = ActiveSupport::Notifications)
49
+ @namespace = namespace
50
+ @subscriber = find_attached_subscriber
51
+ @notifier = notifier
52
+
53
+ return unless subscriber
54
+
55
+ subscribers.delete(subscriber)
56
+
57
+ # Remove event subscribers of all existing methods on the class.
58
+ fetch_public_methods(subscriber, true).each do |event|
59
+ remove_event_subscriber(event)
60
+ end
61
+
62
+ # Reset notifier so that event subscribers will not add for new methods added to the class.
63
+ @notifier = nil
64
+ end
65
+
41
66
  # Adds event subscribers for all new methods added to the class.
42
67
  def method_added(event)
43
68
  # Only public methods are added as subscribers, and only if a notifier
@@ -52,73 +77,87 @@ module ActiveSupport
52
77
  @@subscribers ||= []
53
78
  end
54
79
 
55
- # TODO Change this to private once we've dropped Ruby 2.2 support.
56
- # Workaround for Ruby 2.2 "private attribute?" warning.
57
- protected
80
+ private
81
+ attr_reader :subscriber, :notifier, :namespace
58
82
 
59
- attr_reader :subscriber, :notifier, :namespace
83
+ def add_event_subscriber(event) # :doc:
84
+ return if invalid_event?(event)
60
85
 
61
- private
86
+ pattern = prepare_pattern(event)
62
87
 
63
- def add_event_subscriber(event) # :doc:
64
- return if %w{ start finish }.include?(event.to_s)
88
+ # Don't add multiple subscribers (e.g. if methods are redefined).
89
+ return if pattern_subscribed?(pattern)
65
90
 
66
- pattern = "#{event}.#{namespace}"
91
+ subscriber.patterns[pattern] = notifier.subscribe(pattern, subscriber)
92
+ end
67
93
 
68
- # Don't add multiple subscribers (eg. if methods are redefined).
69
- return if subscriber.patterns.include?(pattern)
94
+ def remove_event_subscriber(event) # :doc:
95
+ return if invalid_event?(event)
70
96
 
71
- subscriber.patterns << pattern
72
- notifier.subscribe(pattern, subscriber)
73
- end
97
+ pattern = prepare_pattern(event)
98
+
99
+ return unless pattern_subscribed?(pattern)
100
+
101
+ notifier.unsubscribe(subscriber.patterns[pattern])
102
+ subscriber.patterns.delete(pattern)
103
+ end
104
+
105
+ def find_attached_subscriber
106
+ subscribers.find { |attached_subscriber| attached_subscriber.instance_of?(self) }
107
+ end
108
+
109
+ def invalid_event?(event)
110
+ %i{ start finish }.include?(event.to_sym)
111
+ end
112
+
113
+ def prepare_pattern(event)
114
+ "#{event}.#{namespace}"
115
+ end
116
+
117
+ def pattern_subscribed?(pattern)
118
+ subscriber.patterns.key?(pattern)
119
+ end
120
+
121
+ def fetch_public_methods(subscriber, inherit_all)
122
+ subscriber.public_methods(inherit_all) - Subscriber.public_instance_methods(true)
123
+ end
74
124
  end
75
125
 
76
126
  attr_reader :patterns # :nodoc:
77
127
 
78
128
  def initialize
79
129
  @queue_key = [self.class.name, object_id].join "-"
80
- @patterns = []
130
+ @patterns = {}
81
131
  super
82
132
  end
83
133
 
84
134
  def start(name, id, payload)
85
- e = ActiveSupport::Notifications::Event.new(name, Time.now, nil, id, payload)
135
+ event = ActiveSupport::Notifications::Event.new(name, nil, nil, id, payload)
136
+ event.start!
86
137
  parent = event_stack.last
87
- parent << e if parent
138
+ parent << event if parent
88
139
 
89
- event_stack.push e
140
+ event_stack.push event
90
141
  end
91
142
 
92
143
  def finish(name, id, payload)
93
- finished = Time.now
94
- event = event_stack.pop
95
- event.end = finished
144
+ event = event_stack.pop
145
+ event.finish!
96
146
  event.payload.merge!(payload)
97
147
 
98
- method = name.split(".".freeze).first
148
+ method = name.split(".").first
99
149
  send(method, event)
100
150
  end
101
151
 
102
- private
152
+ def publish_event(event) # :nodoc:
153
+ method = event.name.split(".").first
154
+ send(method, event)
155
+ end
103
156
 
157
+ private
104
158
  def event_stack
105
- SubscriberQueueRegistry.instance.get_queue(@queue_key)
159
+ registry = ActiveSupport::IsolatedExecutionState[:active_support_subscriber_queue_registry] ||= {}
160
+ registry[@queue_key] ||= []
106
161
  end
107
162
  end
108
-
109
- # This is a registry for all the event stacks kept for subscribers.
110
- #
111
- # See the documentation of <tt>ActiveSupport::PerThreadRegistry</tt>
112
- # for further details.
113
- class SubscriberQueueRegistry # :nodoc:
114
- extend PerThreadRegistry
115
-
116
- def initialize
117
- @registry = {}
118
- end
119
-
120
- def get_queue(queue_key)
121
- @registry[queue_key] ||= []
122
- end
123
- end
124
163
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/delegation"
2
4
  require "active_support/core_ext/object/blank"
3
5
  require "logger"
@@ -6,11 +8,20 @@ require "active_support/logger"
6
8
  module ActiveSupport
7
9
  # Wraps any standard Logger object to provide tagging capabilities.
8
10
  #
11
+ # May be called with a block:
12
+ #
9
13
  # logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
10
14
  # logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
11
15
  # logger.tagged('BCX', "Jason") { logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
12
16
  # logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
13
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
+ #
14
25
  # This is used by the default Rails.logger as configured by Railties to make
15
26
  # it easy to stamp log lines with subdomains, request ids, and anything else
16
27
  # to aid debugging of multi-user production applications.
@@ -29,9 +40,10 @@ module ActiveSupport
29
40
  end
30
41
 
31
42
  def push_tags(*tags)
32
- tags.flatten.reject(&:blank?).tap do |new_tags|
33
- current_tags.concat new_tags
34
- end
43
+ tags.flatten!
44
+ tags.reject!(&:blank?)
45
+ current_tags.concat tags
46
+ tags
35
47
  end
36
48
 
37
49
  def pop_tags(size = 1)
@@ -44,21 +56,38 @@ module ActiveSupport
44
56
 
45
57
  def current_tags
46
58
  # We use our object ID here to avoid conflicting with other instances
47
- thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}".freeze
48
- Thread.current[thread_key] ||= []
59
+ thread_key = @thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
60
+ IsolatedExecutionState[thread_key] ||= []
49
61
  end
50
62
 
51
63
  def tags_text
52
64
  tags = current_tags
53
- if tags.any?
65
+ if tags.one?
66
+ "[#{tags[0]}] "
67
+ elsif tags.any?
54
68
  tags.collect { |tag| "[#{tag}] " }.join
55
69
  end
56
70
  end
57
71
  end
58
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
+
59
81
  def self.new(logger)
60
- # Ensure we set a default formatter so we aren't extending nil!
61
- logger.formatter ||= ActiveSupport::Logger::SimpleFormatter.new
82
+ logger = logger.clone
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
+
62
91
  logger.formatter.extend Formatter
63
92
  logger.extend(self)
64
93
  end
@@ -66,7 +95,14 @@ module ActiveSupport
66
95
  delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
67
96
 
68
97
  def tagged(*tags)
69
- 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
70
106
  end
71
107
 
72
108
  def flush
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  gem "minitest" # make sure we get the gem, not stdlib
2
4
  require "minitest"
3
5
  require "active_support/testing/tagged_logging"
@@ -9,7 +11,9 @@ require "active_support/testing/isolation"
9
11
  require "active_support/testing/constant_lookup"
10
12
  require "active_support/testing/time_helpers"
11
13
  require "active_support/testing/file_fixtures"
12
- require "active_support/core_ext/kernel/reporting"
14
+ require "active_support/testing/parallelization"
15
+ require "active_support/testing/parallelize_executor"
16
+ require "concurrent/utility/processor_counter"
13
17
 
14
18
  module ActiveSupport
15
19
  class TestCase < ::Minitest::Test
@@ -38,12 +42,88 @@ module ActiveSupport
38
42
  def test_order
39
43
  ActiveSupport.test_order ||= :random
40
44
  end
45
+
46
+ # Parallelizes the test suite.
47
+ #
48
+ # Takes a +workers+ argument that controls how many times the process
49
+ # is forked. For each process a new database will be created suffixed
50
+ # with the worker number.
51
+ #
52
+ # test-database-0
53
+ # test-database-1
54
+ #
55
+ # If <tt>ENV["PARALLEL_WORKERS"]</tt> is set the workers argument will be ignored
56
+ # and the environment variable will be used instead. This is useful for CI
57
+ # environments, or other environments where you may need more workers than
58
+ # you do for local testing.
59
+ #
60
+ # If the number of workers is set to +1+ or fewer, the tests will not be
61
+ # parallelized.
62
+ #
63
+ # If +workers+ is set to +:number_of_processors+, the number of workers will be
64
+ # set to the actual core count on the machine you are on.
65
+ #
66
+ # The default parallelization method is to fork processes. If you'd like to
67
+ # use threads instead you can pass <tt>with: :threads</tt> to the +parallelize+
68
+ # method. Note the threaded parallelization does not create multiple
69
+ # database and will not work with system tests at this time.
70
+ #
71
+ # parallelize(workers: :number_of_processors, with: :threads)
72
+ #
73
+ # The threaded parallelization uses minitest's parallel executor directly.
74
+ # The processes parallelization uses a Ruby DRb server.
75
+ #
76
+ # Because parallelization presents an overhead, it is only enabled when the
77
+ # number of tests to run is above the +threshold+ param. The default value is
78
+ # 50, and it's configurable via +config.active_support.test_parallelization_threshold+.
79
+ def parallelize(workers: :number_of_processors, with: :processes, threshold: ActiveSupport.test_parallelization_threshold)
80
+ workers = Concurrent.physical_processor_count if workers == :number_of_processors
81
+ workers = ENV["PARALLEL_WORKERS"].to_i if ENV["PARALLEL_WORKERS"]
82
+
83
+ return if workers <= 1
84
+
85
+ Minitest.parallel_executor = ActiveSupport::Testing::ParallelizeExecutor.new(size: workers, with: with, threshold: threshold)
86
+ end
87
+
88
+ # Set up hook for parallel testing. This can be used if you have multiple
89
+ # databases or any behavior that needs to be run after the process is forked
90
+ # but before the tests run.
91
+ #
92
+ # Note: this feature is not available with the threaded parallelization.
93
+ #
94
+ # In your +test_helper.rb+ add the following:
95
+ #
96
+ # class ActiveSupport::TestCase
97
+ # parallelize_setup do
98
+ # # create databases
99
+ # end
100
+ # end
101
+ def parallelize_setup(&block)
102
+ ActiveSupport::Testing::Parallelization.after_fork_hook(&block)
103
+ end
104
+
105
+ # Clean up hook for parallel testing. This can be used to drop databases
106
+ # if your app uses multiple write/read databases or other clean up before
107
+ # the tests finish. This runs before the forked process is closed.
108
+ #
109
+ # Note: this feature is not available with the threaded parallelization.
110
+ #
111
+ # In your +test_helper.rb+ add the following:
112
+ #
113
+ # class ActiveSupport::TestCase
114
+ # parallelize_teardown do
115
+ # # drop databases
116
+ # end
117
+ # end
118
+ def parallelize_teardown(&block)
119
+ ActiveSupport::Testing::Parallelization.run_cleanup_hook(&block)
120
+ end
41
121
  end
42
122
 
43
123
  alias_method :method_name, :name
44
124
 
45
125
  include ActiveSupport::Testing::TaggedLogging
46
- include ActiveSupport::Testing::SetupAndTeardown
126
+ prepend ActiveSupport::Testing::SetupAndTeardown
47
127
  include ActiveSupport::Testing::Assertions
48
128
  include ActiveSupport::Testing::Deprecation
49
129
  include ActiveSupport::Testing::TimeHelpers
@@ -67,5 +147,9 @@ module ActiveSupport
67
147
  alias :assert_not_same :refute_same
68
148
 
69
149
  ActiveSupport.run_load_hooks(:active_support_test_case, self)
150
+
151
+ def inspect # :nodoc:
152
+ Object.instance_method(:to_s).bind_call(self)
153
+ end
70
154
  end
71
155
  end
@@ -1,3 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/enumerable"
4
+
1
5
  module ActiveSupport
2
6
  module Testing
3
7
  module Assertions
@@ -28,6 +32,8 @@ module ActiveSupport
28
32
  # end
29
33
  def assert_nothing_raised
30
34
  yield
35
+ rescue => error
36
+ raise Minitest::UnexpectedError.new(error)
31
37
  end
32
38
 
33
39
  # Test numeric difference between the return value of an expression as a
@@ -56,6 +62,12 @@ module ActiveSupport
56
62
  # post :create, params: { article: {...} }
57
63
  # end
58
64
  #
65
+ # A hash of expressions/numeric differences can also be passed in and evaluated.
66
+ #
67
+ # assert_difference ->{ Article.count } => 1, ->{ Notification.count } => 2 do
68
+ # post :create, params: { article: {...} }
69
+ # end
70
+ #
59
71
  # A lambda or a list of lambdas can be passed in and evaluated:
60
72
  #
61
73
  # assert_difference ->{ Article.count }, 2 do
@@ -71,20 +83,28 @@ module ActiveSupport
71
83
  # assert_difference 'Article.count', -1, 'An Article should be destroyed' do
72
84
  # post :delete, params: { id: ... }
73
85
  # end
74
- def assert_difference(expression, difference = 1, message = nil, &block)
75
- expressions = Array(expression)
86
+ def assert_difference(expression, *args, &block)
87
+ expressions =
88
+ if expression.is_a?(Hash)
89
+ message = args[0]
90
+ expression
91
+ else
92
+ difference = args[0] || 1
93
+ message = args[1]
94
+ Array(expression).index_with(difference)
95
+ end
76
96
 
77
- exps = expressions.map { |e|
97
+ exps = expressions.keys.map { |e|
78
98
  e.respond_to?(:call) ? e : lambda { eval(e, block.binding) }
79
99
  }
80
100
  before = exps.map(&:call)
81
101
 
82
- retval = yield
102
+ retval = _assert_nothing_raised_or_warn("assert_difference", &block)
83
103
 
84
- expressions.zip(exps).each_with_index do |(code, e), i|
85
- error = "#{code.inspect} didn't change by #{difference}"
104
+ expressions.zip(exps, before) do |(code, diff), exp, before_value|
105
+ error = "#{code.inspect} didn't change by #{diff}"
86
106
  error = "#{message}.\n#{error}" if message
87
- assert_equal(before[i] + difference, e.call, error)
107
+ assert_equal(before_value + diff, exp.call, error)
88
108
  end
89
109
 
90
110
  retval
@@ -97,11 +117,23 @@ module ActiveSupport
97
117
  # post :create, params: { article: invalid_attributes }
98
118
  # end
99
119
  #
120
+ # A lambda can be passed in and evaluated.
121
+ #
122
+ # assert_no_difference -> { Article.count } do
123
+ # post :create, params: { article: invalid_attributes }
124
+ # end
125
+ #
100
126
  # An error message can be specified.
101
127
  #
102
128
  # assert_no_difference 'Article.count', 'An Article should not be created' do
103
129
  # post :create, params: { article: invalid_attributes }
104
130
  # end
131
+ #
132
+ # An array of expressions can also be passed in and evaluated.
133
+ #
134
+ # assert_no_difference [ 'Article.count', -> { Post.count } ] do
135
+ # post :create, params: { article: invalid_attributes }
136
+ # end
105
137
  def assert_no_difference(expression, message = nil, &block)
106
138
  assert_difference expression, 0, message, &block
107
139
  end
@@ -127,7 +159,7 @@ module ActiveSupport
127
159
  # @object = 42
128
160
  # end
129
161
  #
130
- # The keyword arguments :from and :to can be given to specify the
162
+ # The keyword arguments +:from+ and +:to+ can be given to specify the
131
163
  # expected initial value and the expected value after the block was
132
164
  # executed.
133
165
  #
@@ -144,22 +176,23 @@ module ActiveSupport
144
176
  exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
145
177
 
146
178
  before = exp.call
147
- retval = yield
179
+ retval = _assert_nothing_raised_or_warn("assert_changes", &block)
148
180
 
149
181
  unless from == UNTRACKED
150
- error = "#{expression.inspect} isn't #{from.inspect}"
182
+ error = "Expected change from #{from.inspect}"
151
183
  error = "#{message}.\n#{error}" if message
152
184
  assert from === before, error
153
185
  end
154
186
 
155
187
  after = exp.call
156
188
 
157
- if to == UNTRACKED
158
- error = "#{expression.inspect} didn't changed"
159
- error = "#{message}.\n#{error}" if message
160
- assert_not_equal before, after, error
161
- else
162
- error = "#{expression.inspect} didn't change to #{to}"
189
+ error = "#{expression.inspect} didn't change"
190
+ error = "#{error}. It was already #{to}" if before == to
191
+ error = "#{message}.\n#{error}" if message
192
+ refute_equal before, after, error
193
+
194
+ unless to == UNTRACKED
195
+ error = "Expected change to #{to}\n"
163
196
  error = "#{message}.\n#{error}" if message
164
197
  assert to === after, error
165
198
  end
@@ -167,31 +200,66 @@ module ActiveSupport
167
200
  retval
168
201
  end
169
202
 
170
- # Assertion that the result of evaluating an expression is changed before
203
+ # Assertion that the result of evaluating an expression is not changed before
171
204
  # and after invoking the passed in block.
172
205
  #
173
206
  # assert_no_changes 'Status.all_good?' do
174
207
  # post :create, params: { status: { ok: true } }
175
208
  # end
176
209
  #
210
+ # Provide the optional keyword argument :from to specify the expected
211
+ # initial value.
212
+ #
213
+ # assert_no_changes -> { Status.all_good? }, from: true do
214
+ # post :create, params: { status: { ok: true } }
215
+ # end
216
+ #
177
217
  # An error message can be specified.
178
218
  #
179
219
  # assert_no_changes -> { Status.all_good? }, 'Expected the status to be good' do
180
220
  # post :create, params: { status: { ok: false } }
181
221
  # end
182
- def assert_no_changes(expression, message = nil, &block)
222
+ def assert_no_changes(expression, message = nil, from: UNTRACKED, &block)
183
223
  exp = expression.respond_to?(:call) ? expression : -> { eval(expression.to_s, block.binding) }
184
224
 
185
225
  before = exp.call
186
- retval = yield
226
+ retval = _assert_nothing_raised_or_warn("assert_no_changes", &block)
227
+
228
+ unless from == UNTRACKED
229
+ error = "Expected initial value of #{from.inspect}"
230
+ error = "#{message}.\n#{error}" if message
231
+ assert from === before, error
232
+ end
233
+
187
234
  after = exp.call
188
235
 
189
- error = "#{expression.inspect} did change to #{after}"
236
+ error = "#{expression.inspect} changed"
190
237
  error = "#{message}.\n#{error}" if message
191
- assert_equal before, after, error
238
+
239
+ if before.nil?
240
+ assert_nil after, error
241
+ else
242
+ assert_equal before, after, error
243
+ end
192
244
 
193
245
  retval
194
246
  end
247
+
248
+ private
249
+ def _assert_nothing_raised_or_warn(assertion, &block)
250
+ assert_nothing_raised(&block)
251
+ rescue Minitest::UnexpectedError => e
252
+ if tagged_logger && tagged_logger.warn?
253
+ warning = <<~MSG
254
+ #{self.class} - #{name}: #{e.error.class} raised.
255
+ If you expected this exception, use `assert_raises` as near to the code that raises as possible.
256
+ Other block based assertions (e.g. `#{assertion}`) can be used, as long as `assert_raises` is inside their block.
257
+ MSG
258
+ tagged_logger.warn warning
259
+ end
260
+
261
+ raise
262
+ end
195
263
  end
196
264
  end
197
265
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  gem "minitest"
2
4
 
3
5
  require "minitest"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/concern"
2
4
  require "active_support/inflector"
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveSupport
2
4
  module Testing
3
5
  module Declarative