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
@@ -0,0 +1,203 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # = Active Support \Error Reporter
5
+ #
6
+ # +ActiveSupport::ErrorReporter+ is a common interface for error reporting services.
7
+ #
8
+ # To rescue and report any unhandled error, you can use the #handle method:
9
+ #
10
+ # Rails.error.handle do
11
+ # do_something!
12
+ # end
13
+ #
14
+ # If an error is raised, it will be reported and swallowed.
15
+ #
16
+ # Alternatively, if you want to report the error but not swallow it, you can use #record:
17
+ #
18
+ # Rails.error.record do
19
+ # do_something!
20
+ # end
21
+ #
22
+ # Both methods can be restricted to handle only a specific error class:
23
+ #
24
+ # maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
25
+ #
26
+ class ErrorReporter
27
+ SEVERITIES = %i(error warning info)
28
+ DEFAULT_SOURCE = "application"
29
+
30
+ attr_accessor :logger
31
+
32
+ def initialize(*subscribers, logger: nil)
33
+ @subscribers = subscribers.flatten
34
+ @logger = logger
35
+ end
36
+
37
+ # Evaluates the given block, reporting and swallowing any unhandled error.
38
+ # If no error is raised, returns the return value of the block. Otherwise,
39
+ # returns the result of +fallback.call+, or +nil+ if +fallback+ is not
40
+ # specified.
41
+ #
42
+ # # Will report a TypeError to all subscribers and return nil.
43
+ # Rails.error.handle do
44
+ # 1 + '1'
45
+ # end
46
+ #
47
+ # Can be restricted to handle only specific error classes:
48
+ #
49
+ # maybe_tags = Rails.error.handle(Redis::BaseError) { redis.get("tags") }
50
+ #
51
+ # ==== Options
52
+ #
53
+ # * +:severity+ - This value is passed along to subscribers to indicate how
54
+ # important the error report is. Can be +:error+, +:warning+, or +:info+.
55
+ # Defaults to +:warning+.
56
+ #
57
+ # * +:context+ - Extra information that is passed along to subscribers. For
58
+ # example:
59
+ #
60
+ # Rails.error.handle(context: { section: "admin" }) do
61
+ # # ...
62
+ # end
63
+ #
64
+ # * +:fallback+ - A callable that provides +handle+'s return value when an
65
+ # unhandled error is raised. For example:
66
+ #
67
+ # user = Rails.error.handle(fallback: -> { User.anonymous }) do
68
+ # User.find_by(params)
69
+ # end
70
+ #
71
+ # * +:source+ - This value is passed along to subscribers to indicate the
72
+ # source of the error. Subscribers can use this value to ignore certain
73
+ # errors. Defaults to <tt>"application"</tt>.
74
+ def handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
75
+ error_classes = [StandardError] if error_classes.blank?
76
+ yield
77
+ rescue *error_classes => error
78
+ report(error, handled: true, severity: severity, context: context, source: source)
79
+ fallback.call if fallback
80
+ end
81
+
82
+ # Evaluates the given block, reporting and re-raising any unhandled error.
83
+ # If no error is raised, returns the return value of the block.
84
+ #
85
+ # # Will report a TypeError to all subscribers and re-raise it.
86
+ # Rails.error.record do
87
+ # 1 + '1'
88
+ # end
89
+ #
90
+ # Can be restricted to handle only specific error classes:
91
+ #
92
+ # tags = Rails.error.record(Redis::BaseError) { redis.get("tags") }
93
+ #
94
+ # ==== Options
95
+ #
96
+ # * +:severity+ - This value is passed along to subscribers to indicate how
97
+ # important the error report is. Can be +:error+, +:warning+, or +:info+.
98
+ # Defaults to +:error+.
99
+ #
100
+ # * +:context+ - Extra information that is passed along to subscribers. For
101
+ # example:
102
+ #
103
+ # Rails.error.record(context: { section: "admin" }) do
104
+ # # ...
105
+ # end
106
+ #
107
+ # * +:source+ - This value is passed along to subscribers to indicate the
108
+ # source of the error. Subscribers can use this value to ignore certain
109
+ # errors. Defaults to <tt>"application"</tt>.
110
+ def record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)
111
+ error_classes = [StandardError] if error_classes.blank?
112
+ yield
113
+ rescue *error_classes => error
114
+ report(error, handled: false, severity: severity, context: context, source: source)
115
+ raise
116
+ end
117
+
118
+ # Register a new error subscriber. The subscriber must respond to
119
+ #
120
+ # report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String)
121
+ #
122
+ # The +report+ method <b>should never</b> raise an error.
123
+ def subscribe(subscriber)
124
+ unless subscriber.respond_to?(:report)
125
+ raise ArgumentError, "Error subscribers must respond to #report"
126
+ end
127
+ @subscribers << subscriber
128
+ end
129
+
130
+ # Unregister an error subscriber. Accepts either a subscriber or a class.
131
+ #
132
+ # subscriber = MyErrorSubscriber.new
133
+ # Rails.error.subscribe(subscriber)
134
+ #
135
+ # Rails.error.unsubscribe(subscriber)
136
+ # # or
137
+ # Rails.error.unsubscribe(MyErrorSubscriber)
138
+ def unsubscribe(subscriber)
139
+ @subscribers.delete_if { |s| subscriber === s }
140
+ end
141
+
142
+ # Prevent a subscriber from being notified of errors for the
143
+ # duration of the block. You may pass in the subscriber itself, or its class.
144
+ #
145
+ # This can be helpful for error reporting service integrations, when they wish
146
+ # to handle any errors higher in the stack.
147
+ def disable(subscriber)
148
+ disabled_subscribers = (ActiveSupport::IsolatedExecutionState[self] ||= [])
149
+ disabled_subscribers << subscriber
150
+ begin
151
+ yield
152
+ ensure
153
+ disabled_subscribers.delete(subscriber)
154
+ end
155
+ end
156
+
157
+ # Update the execution context that is accessible to error subscribers. Any
158
+ # context passed to #handle, #record, or #report will be merged with the
159
+ # context set here.
160
+ #
161
+ # Rails.error.set_context(section: "checkout", user_id: @user.id)
162
+ #
163
+ def set_context(...)
164
+ ActiveSupport::ExecutionContext.set(...)
165
+ end
166
+
167
+ # Report an error directly to subscribers. You can use this method when the
168
+ # block-based #handle and #record methods are not suitable.
169
+ #
170
+ # Rails.error.report(error)
171
+ #
172
+ def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
173
+ return if error.instance_variable_defined?(:@__rails_error_reported)
174
+
175
+ unless SEVERITIES.include?(severity)
176
+ raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
177
+ end
178
+
179
+ full_context = ActiveSupport::ExecutionContext.to_h.merge(context)
180
+ disabled_subscribers = ActiveSupport::IsolatedExecutionState[self]
181
+ @subscribers.each do |subscriber|
182
+ unless disabled_subscribers&.any? { |s| s === subscriber }
183
+ subscriber.report(error, handled: handled, severity: severity, context: full_context, source: source)
184
+ end
185
+ rescue => subscriber_error
186
+ if logger
187
+ logger.fatal(
188
+ "Error subscriber raised an error: #{subscriber_error.message} (#{subscriber_error.class})\n" +
189
+ subscriber_error.backtrace.join("\n")
190
+ )
191
+ else
192
+ raise
193
+ end
194
+ end
195
+
196
+ unless error.frozen?
197
+ error.instance_variable_set(:@__rails_error_reported, true)
198
+ end
199
+
200
+ nil
201
+ end
202
+ end
203
+ end
@@ -1,15 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ gem "listen", "~> 3.5"
4
+ require "listen"
5
+
3
6
  require "set"
4
7
  require "pathname"
5
8
  require "concurrent/atomic/atomic_boolean"
6
- require "listen"
7
9
  require "active_support/fork_tracker"
8
10
 
9
11
  module ActiveSupport
10
12
  # Allows you to "listen" to changes in a file system.
11
- # The evented file updater does not hit disk when checking for updates
12
- # instead it uses platform specific file system events to trigger a change
13
+ # The evented file updater does not hit disk when checking for updates.
14
+ # Instead, it uses platform-specific file system events to trigger a change
13
15
  # in state.
14
16
  #
15
17
  # The file checker takes an array of files to watch or a hash specifying directories
@@ -17,8 +19,6 @@ module ActiveSupport
17
19
  # EventedFileUpdateChecker#execute is run or when EventedFileUpdateChecker#execute_if_updated
18
20
  # is run and there have been changes to the file system.
19
21
  #
20
- # Note: Forking will cause the first call to `updated?` to return `true`.
21
- #
22
22
  # Example:
23
23
  #
24
24
  # checker = ActiveSupport::EventedFileUpdateChecker.new(["/tmp/foo"]) { puts "changed" }
@@ -34,7 +34,7 @@ module ActiveSupport
34
34
  # checker.execute_if_updated
35
35
  # # => "changed"
36
36
  #
37
- class EventedFileUpdateChecker #:nodoc: all
37
+ class EventedFileUpdateChecker # :nodoc: all
38
38
  def initialize(files, dirs = {}, &block)
39
39
  unless block
40
40
  raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
@@ -45,6 +45,10 @@ module ActiveSupport
45
45
  ObjectSpace.define_finalizer(self, @core.finalizer)
46
46
  end
47
47
 
48
+ def inspect
49
+ "#<ActiveSupport::EventedFileUpdateChecker:#{object_id} @files=#{@core.files.to_a.inspect}"
50
+ end
51
+
48
52
  def updated?
49
53
  if @core.restart?
50
54
  @core.thread_safely(&:restart)
@@ -68,7 +72,7 @@ module ActiveSupport
68
72
  end
69
73
 
70
74
  class Core
71
- attr_reader :updated
75
+ attr_reader :updated, :files
72
76
 
73
77
  def initialize(files, dirs)
74
78
  @files = files.map { |file| Pathname(file).expand_path }.to_set
@@ -86,6 +90,10 @@ module ActiveSupport
86
90
  @mutex = Mutex.new
87
91
 
88
92
  start
93
+ # inotify / FSEvents file descriptors are inherited on fork, so
94
+ # we need to reopen them otherwise only the parent or the child
95
+ # will be notified.
96
+ # FIXME: this callback is keeping a reference on the instance
89
97
  @after_fork = ActiveSupport::ForkTracker.after_fork { start }
90
98
  end
91
99
 
@@ -107,6 +115,11 @@ module ActiveSupport
107
115
  @dtw, @missing = [*@dtw, *@missing].partition(&:exist?)
108
116
  @listener = @dtw.any? ? Listen.to(*@dtw, &method(:changed)) : nil
109
117
  @listener&.start
118
+
119
+ # Wait for the listener to be ready to avoid race conditions
120
+ # Unfortunately this isn't quite enough on macOS because the Darwin backend
121
+ # has an extra private thread we can't wait on.
122
+ @listener&.wait_for_state(:processing_events)
110
123
  end
111
124
 
112
125
  def stop
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport::ExecutionContext::TestHelper # :nodoc:
4
+ def before_setup
5
+ ActiveSupport::ExecutionContext.clear
6
+ super
7
+ end
8
+
9
+ def after_teardown
10
+ super
11
+ ActiveSupport::ExecutionContext.clear
12
+ end
13
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module ExecutionContext # :nodoc:
5
+ @after_change_callbacks = []
6
+ class << self
7
+ def after_change(&block)
8
+ @after_change_callbacks << block
9
+ end
10
+
11
+ # Updates the execution context. If a block is given, it resets the provided keys to their
12
+ # previous value once the block exits.
13
+ def set(**options)
14
+ options.symbolize_keys!
15
+ keys = options.keys
16
+
17
+ store = self.store
18
+
19
+ previous_context = keys.zip(store.values_at(*keys)).to_h
20
+
21
+ store.merge!(options)
22
+ @after_change_callbacks.each(&:call)
23
+
24
+ if block_given?
25
+ begin
26
+ yield
27
+ ensure
28
+ store.merge!(previous_context)
29
+ @after_change_callbacks.each(&:call)
30
+ end
31
+ end
32
+ end
33
+
34
+ def []=(key, value)
35
+ store[key.to_sym] = value
36
+ @after_change_callbacks.each(&:call)
37
+ end
38
+
39
+ def to_h
40
+ store.dup
41
+ end
42
+
43
+ def clear
44
+ store.clear
45
+ end
46
+
47
+ private
48
+ def store
49
+ IsolatedExecutionState[:active_support_execution_context] ||= {}
50
+ end
51
+ end
52
+ end
53
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/error_reporter"
3
4
  require "active_support/callbacks"
4
5
  require "concurrent/hash"
5
6
 
@@ -63,51 +64,68 @@ module ActiveSupport
63
64
  # after the work has been performed.
64
65
  #
65
66
  # Where possible, prefer +wrap+.
66
- def self.run!
67
- if active?
68
- Null
67
+ def self.run!(reset: false)
68
+ if reset
69
+ lost_instance = IsolatedExecutionState.delete(active_key)
70
+ lost_instance&.complete!
69
71
  else
70
- new.tap do |instance|
71
- success = nil
72
- begin
73
- instance.run!
74
- success = true
75
- ensure
76
- instance.complete! unless success
77
- end
72
+ return Null if active?
73
+ end
74
+
75
+ new.tap do |instance|
76
+ success = nil
77
+ begin
78
+ instance.run!
79
+ success = true
80
+ ensure
81
+ instance.complete! unless success
78
82
  end
79
83
  end
80
84
  end
81
85
 
82
86
  # Perform the work in the supplied block as an execution.
83
- def self.wrap
87
+ def self.wrap(source: "application.active_support")
84
88
  return yield if active?
85
89
 
86
90
  instance = run!
87
91
  begin
88
92
  yield
93
+ rescue => error
94
+ error_reporter&.report(error, handled: false, source: source)
95
+ raise
89
96
  ensure
90
97
  instance.complete!
91
98
  end
92
99
  end
93
100
 
94
- class << self # :nodoc:
95
- attr_accessor :active
101
+ def self.perform # :nodoc:
102
+ instance = new
103
+ instance.run
104
+ begin
105
+ yield
106
+ ensure
107
+ instance.complete
108
+ end
96
109
  end
97
110
 
98
- def self.inherited(other) # :nodoc:
99
- super
100
- other.active = Concurrent::Hash.new
111
+ def self.error_reporter # :nodoc:
112
+ ActiveSupport.error_reporter
101
113
  end
102
114
 
103
- self.active = Concurrent::Hash.new
115
+ def self.active_key # :nodoc:
116
+ @active_key ||= :"active_execution_wrapper_#{object_id}"
117
+ end
104
118
 
105
119
  def self.active? # :nodoc:
106
- @active[Thread.current]
120
+ IsolatedExecutionState.key?(active_key)
107
121
  end
108
122
 
109
123
  def run! # :nodoc:
110
- self.class.active[Thread.current] = true
124
+ IsolatedExecutionState[self.class.active_key] = self
125
+ run
126
+ end
127
+
128
+ def run # :nodoc:
111
129
  run_callbacks(:run)
112
130
  end
113
131
 
@@ -116,9 +134,13 @@ module ActiveSupport
116
134
  #
117
135
  # Where possible, prefer +wrap+.
118
136
  def complete!
119
- run_callbacks(:complete)
137
+ complete
120
138
  ensure
121
- self.class.active.delete Thread.current
139
+ IsolatedExecutionState.delete(self.class.active_key)
140
+ end
141
+
142
+ def complete # :nodoc:
143
+ run_callbacks(:complete)
122
144
  end
123
145
 
124
146
  private
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport::Executor::TestHelper # :nodoc:
4
+ def run(...)
5
+ Rails.application.executor.perform { super }
6
+ end
7
+ end
@@ -3,7 +3,9 @@
3
3
  require "active_support/core_ext/time/calculations"
4
4
 
5
5
  module ActiveSupport
6
- # FileUpdateChecker specifies the API used by Rails to watch files
6
+ # = \File Update Checker
7
+ #
8
+ # FileUpdateChecker specifies the API used by \Rails to watch files
7
9
  # and control reloading. The API depends on four methods:
8
10
  #
9
11
  # * +initialize+ which expects two parameters and one block as
@@ -20,7 +22,7 @@ module ActiveSupport
20
22
  # After initialization, a call to +execute_if_updated+ must execute
21
23
  # the block only if there was really a change in the filesystem.
22
24
  #
23
- # This class is used by Rails to reload the I18n framework whenever
25
+ # This class is used by \Rails to reload the I18n framework whenever
24
26
  # they are changed upon a new request.
25
27
  #
26
28
  # i18n_reloader = ActiveSupport::FileUpdateChecker.new(paths) do
@@ -2,8 +2,18 @@
2
2
 
3
3
  module ActiveSupport
4
4
  module ForkTracker # :nodoc:
5
+ module ModernCoreExt
6
+ def _fork
7
+ pid = super
8
+ if pid == 0
9
+ ForkTracker.after_fork_callback
10
+ end
11
+ pid
12
+ end
13
+ end
14
+
5
15
  module CoreExt
6
- def fork(*)
16
+ def fork(...)
7
17
  if block_given?
8
18
  super do
9
19
  ForkTracker.check!
@@ -20,27 +30,34 @@ module ActiveSupport
20
30
 
21
31
  module CoreExtPrivate
22
32
  include CoreExt
23
-
24
- private
25
- def fork(*)
26
- super
27
- end
33
+ private :fork
28
34
  end
29
35
 
30
36
  @pid = Process.pid
31
37
  @callbacks = []
32
38
 
33
39
  class << self
34
- def check!
35
- if @pid != Process.pid
40
+ def after_fork_callback
41
+ new_pid = Process.pid
42
+ if @pid != new_pid
36
43
  @callbacks.each(&:call)
37
- @pid = Process.pid
44
+ @pid = new_pid
45
+ end
46
+ end
47
+
48
+ if Process.respond_to?(:_fork) # Ruby 3.1+
49
+ def check!
50
+ # We trust the `_fork` callback
38
51
  end
52
+ else
53
+ alias_method :check!, :after_fork_callback
39
54
  end
40
55
 
41
56
  def hook!
42
- if Process.respond_to?(:fork)
43
- ::Object.prepend(CoreExtPrivate)
57
+ if Process.respond_to?(:_fork) # Ruby 3.1+
58
+ ::Process.singleton_class.prepend(ModernCoreExt)
59
+ elsif Process.respond_to?(:fork)
60
+ ::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
44
61
  ::Kernel.prepend(CoreExtPrivate)
45
62
  ::Kernel.singleton_class.prepend(CoreExt)
46
63
  ::Process.singleton_class.prepend(CoreExt)
@@ -1,16 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveSupport
4
- # Returns the version of the currently loaded Active Support as a <tt>Gem::Version</tt>.
4
+ # Returns the currently loaded version of Active Support as a +Gem::Version+.
5
5
  def self.gem_version
6
6
  Gem::Version.new VERSION::STRING
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 6
10
+ MAJOR = 7
11
11
  MINOR = 1
12
- TINY = 0
13
- PRE = nil
12
+ TINY = 5
13
+ PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -4,6 +4,8 @@ require "zlib"
4
4
  require "stringio"
5
5
 
6
6
  module ActiveSupport
7
+ # = Active Support \Gzip
8
+ #
7
9
  # A convenient wrapper for the zlib standard library that allows
8
10
  # compression/decompression of strings with gzip.
9
11
  #