activesupport 6.0.6 → 7.0.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 (204) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +224 -608
  3. data/MIT-LICENSE +1 -1
  4. data/lib/active_support/actionable_error.rb +1 -1
  5. data/lib/active_support/array_inquirer.rb +2 -2
  6. data/lib/active_support/backtrace_cleaner.rb +3 -3
  7. data/lib/active_support/benchmarkable.rb +3 -3
  8. data/lib/active_support/cache/file_store.rb +16 -10
  9. data/lib/active_support/cache/mem_cache_store.rb +143 -38
  10. data/lib/active_support/cache/memory_store.rb +56 -28
  11. data/lib/active_support/cache/null_store.rb +10 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +62 -87
  13. data/lib/active_support/cache/strategy/local_cache.rb +46 -57
  14. data/lib/active_support/cache.rb +268 -77
  15. data/lib/active_support/callbacks.rb +226 -118
  16. data/lib/active_support/code_generator.rb +65 -0
  17. data/lib/active_support/concern.rb +49 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  19. data/lib/active_support/concurrency/share_lock.rb +2 -2
  20. data/lib/active_support/configurable.rb +9 -6
  21. data/lib/active_support/configuration_file.rb +51 -0
  22. data/lib/active_support/core_ext/array/access.rb +1 -5
  23. data/lib/active_support/core_ext/array/conversions.rb +9 -7
  24. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  25. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  26. data/lib/active_support/core_ext/array.rb +1 -0
  27. data/lib/active_support/core_ext/benchmark.rb +2 -2
  28. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  29. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  30. data/lib/active_support/core_ext/class/subclasses.rb +11 -24
  31. data/lib/active_support/core_ext/date/blank.rb +1 -1
  32. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  33. data/lib/active_support/core_ext/date/conversions.rb +5 -4
  34. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  35. data/lib/active_support/core_ext/date.rb +1 -0
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  37. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  38. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  39. data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
  40. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  41. data/lib/active_support/core_ext/date_time.rb +1 -0
  42. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  43. data/lib/active_support/core_ext/enumerable.rb +139 -15
  44. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  45. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  46. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  47. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  48. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  49. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  50. data/lib/active_support/core_ext/load_error.rb +1 -1
  51. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  52. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  53. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  54. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  55. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  56. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  57. data/lib/active_support/core_ext/name_error.rb +23 -2
  58. data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
  59. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  60. data/lib/active_support/core_ext/numeric.rb +1 -0
  61. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  62. data/lib/active_support/core_ext/object/blank.rb +2 -2
  63. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  64. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  65. data/lib/active_support/core_ext/object/json.rb +41 -25
  66. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  67. data/lib/active_support/core_ext/object/try.rb +20 -20
  68. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  69. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  70. data/lib/active_support/core_ext/pathname.rb +3 -0
  71. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  72. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  73. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  74. data/lib/active_support/core_ext/range/each.rb +1 -1
  75. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  76. data/lib/active_support/core_ext/range.rb +1 -1
  77. data/lib/active_support/core_ext/regexp.rb +8 -1
  78. data/lib/active_support/core_ext/string/access.rb +5 -24
  79. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  80. data/lib/active_support/core_ext/string/filters.rb +1 -1
  81. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  82. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  83. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  84. data/lib/active_support/core_ext/string/output_safety.rb +62 -67
  85. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  86. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  87. data/lib/active_support/core_ext/symbol.rb +3 -0
  88. data/lib/active_support/core_ext/time/calculations.rb +23 -5
  89. data/lib/active_support/core_ext/time/conversions.rb +6 -3
  90. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  91. data/lib/active_support/core_ext/time/zones.rb +4 -19
  92. data/lib/active_support/core_ext/time.rb +1 -0
  93. data/lib/active_support/core_ext/uri.rb +3 -23
  94. data/lib/active_support/core_ext.rb +2 -1
  95. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  96. data/lib/active_support/current_attributes.rb +39 -16
  97. data/lib/active_support/dependencies/interlock.rb +10 -18
  98. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  99. data/lib/active_support/dependencies.rb +58 -769
  100. data/lib/active_support/deprecation/behaviors.rb +19 -3
  101. data/lib/active_support/deprecation/disallowed.rb +56 -0
  102. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  103. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  104. data/lib/active_support/deprecation/proxy_wrappers.rb +3 -3
  105. data/lib/active_support/deprecation/reporting.rb +50 -7
  106. data/lib/active_support/deprecation.rb +6 -1
  107. data/lib/active_support/descendants_tracker.rb +174 -64
  108. data/lib/active_support/digest.rb +5 -3
  109. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  110. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  111. data/lib/active_support/duration.rb +134 -55
  112. data/lib/active_support/encrypted_configuration.rb +11 -1
  113. data/lib/active_support/encrypted_file.rb +20 -3
  114. data/lib/active_support/environment_inquirer.rb +20 -0
  115. data/lib/active_support/error_reporter.rb +117 -0
  116. data/lib/active_support/evented_file_update_checker.rb +70 -134
  117. data/lib/active_support/execution_context/test_helper.rb +13 -0
  118. data/lib/active_support/execution_context.rb +53 -0
  119. data/lib/active_support/execution_wrapper.rb +41 -18
  120. data/lib/active_support/executor/test_helper.rb +7 -0
  121. data/lib/active_support/fork_tracker.rb +71 -0
  122. data/lib/active_support/gem_version.rb +2 -2
  123. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  124. data/lib/active_support/html_safe_translation.rb +43 -0
  125. data/lib/active_support/i18n.rb +1 -0
  126. data/lib/active_support/i18n_railtie.rb +14 -19
  127. data/lib/active_support/inflector/inflections.rb +24 -9
  128. data/lib/active_support/inflector/methods.rb +29 -49
  129. data/lib/active_support/inflector/transliterate.rb +4 -4
  130. data/lib/active_support/isolated_execution_state.rb +56 -0
  131. data/lib/active_support/json/decoding.rb +4 -4
  132. data/lib/active_support/json/encoding.rb +8 -4
  133. data/lib/active_support/key_generator.rb +19 -2
  134. data/lib/active_support/locale/en.yml +8 -4
  135. data/lib/active_support/log_subscriber.rb +21 -3
  136. data/lib/active_support/logger.rb +1 -1
  137. data/lib/active_support/logger_silence.rb +2 -26
  138. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  139. data/lib/active_support/message_encryptor.rb +12 -10
  140. data/lib/active_support/message_verifier.rb +50 -18
  141. data/lib/active_support/messages/metadata.rb +2 -2
  142. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  143. data/lib/active_support/messages/rotator.rb +6 -5
  144. data/lib/active_support/multibyte/chars.rb +13 -52
  145. data/lib/active_support/multibyte/unicode.rb +1 -87
  146. data/lib/active_support/multibyte.rb +1 -1
  147. data/lib/active_support/notifications/fanout.rb +110 -69
  148. data/lib/active_support/notifications/instrumenter.rb +37 -29
  149. data/lib/active_support/notifications.rb +47 -26
  150. data/lib/active_support/number_helper/number_converter.rb +2 -4
  151. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  152. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  153. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  154. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  155. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  156. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  157. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  158. data/lib/active_support/number_helper.rb +29 -16
  159. data/lib/active_support/option_merger.rb +9 -16
  160. data/lib/active_support/ordered_hash.rb +1 -1
  161. data/lib/active_support/ordered_options.rb +8 -2
  162. data/lib/active_support/parameter_filter.rb +21 -11
  163. data/lib/active_support/per_thread_registry.rb +6 -1
  164. data/lib/active_support/rails.rb +1 -4
  165. data/lib/active_support/railtie.rb +77 -5
  166. data/lib/active_support/reloader.rb +1 -1
  167. data/lib/active_support/rescuable.rb +6 -6
  168. data/lib/active_support/ruby_features.rb +7 -0
  169. data/lib/active_support/secure_compare_rotator.rb +51 -0
  170. data/lib/active_support/security_utils.rb +19 -12
  171. data/lib/active_support/string_inquirer.rb +2 -2
  172. data/lib/active_support/subscriber.rb +19 -25
  173. data/lib/active_support/tagged_logging.rb +31 -6
  174. data/lib/active_support/test_case.rb +9 -21
  175. data/lib/active_support/testing/assertions.rb +49 -12
  176. data/lib/active_support/testing/deprecation.rb +52 -1
  177. data/lib/active_support/testing/isolation.rb +2 -2
  178. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  179. data/lib/active_support/testing/parallelization/server.rb +82 -0
  180. data/lib/active_support/testing/parallelization/worker.rb +103 -0
  181. data/lib/active_support/testing/parallelization.rb +16 -95
  182. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  183. data/lib/active_support/testing/stream.rb +3 -5
  184. data/lib/active_support/testing/tagged_logging.rb +1 -1
  185. data/lib/active_support/testing/time_helpers.rb +53 -5
  186. data/lib/active_support/time_with_zone.rb +120 -55
  187. data/lib/active_support/values/time_zone.rb +49 -18
  188. data/lib/active_support/xml_mini/jdom.rb +1 -1
  189. data/lib/active_support/xml_mini/libxml.rb +5 -5
  190. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  191. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  192. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  193. data/lib/active_support/xml_mini/rexml.rb +9 -2
  194. data/lib/active_support/xml_mini.rb +5 -4
  195. data/lib/active_support.rb +29 -1
  196. metadata +45 -45
  197. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -5
  198. data/lib/active_support/core_ext/hash/compact.rb +0 -5
  199. data/lib/active_support/core_ext/hash/transform_values.rb +0 -5
  200. data/lib/active_support/core_ext/marshal.rb +0 -24
  201. data/lib/active_support/core_ext/module/reachable.rb +0 -6
  202. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -5
  203. data/lib/active_support/core_ext/range/include_range.rb +0 -9
  204. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -3,6 +3,8 @@
3
3
  require "set"
4
4
  require "pathname"
5
5
  require "concurrent/atomic/atomic_boolean"
6
+ require "listen"
7
+ require "active_support/fork_tracker"
6
8
 
7
9
  module ActiveSupport
8
10
  # Allows you to "listen" to changes in a file system.
@@ -32,68 +34,28 @@ module ActiveSupport
32
34
  # checker.execute_if_updated
33
35
  # # => "changed"
34
36
  #
35
- class EventedFileUpdateChecker #:nodoc: all
37
+ class EventedFileUpdateChecker # :nodoc: all
36
38
  def initialize(files, dirs = {}, &block)
37
39
  unless block
38
40
  raise ArgumentError, "A block is required to initialize an EventedFileUpdateChecker"
39
41
  end
40
42
 
41
- @ph = PathHelper.new
42
- @files = files.map { |f| @ph.xpath(f) }.to_set
43
-
44
- @dirs = {}
45
- dirs.each do |dir, exts|
46
- @dirs[@ph.xpath(dir)] = Array(exts).map { |ext| @ph.normalize_extension(ext) }
47
- end
48
-
49
- @block = block
50
- @updated = Concurrent::AtomicBoolean.new(false)
51
- @lcsp = @ph.longest_common_subpath(@dirs.keys)
52
- @pid = Process.pid
53
- @boot_mutex = Mutex.new
54
-
55
- dtw = directories_to_watch
56
- @dtw, @missing = dtw.partition(&:exist?)
57
-
58
- if @dtw.any?
59
- # Loading listen triggers warnings. These are originated by a legit
60
- # usage of attr_* macros for private attributes, but adds a lot of noise
61
- # to our test suite. Thus, we lazy load it and disable warnings locally.
62
- silence_warnings do
63
- require "listen"
64
- rescue LoadError => e
65
- raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
66
- end
67
- end
68
- boot!
43
+ @block = block
44
+ @core = Core.new(files, dirs)
45
+ ObjectSpace.define_finalizer(self, @core.finalizer)
69
46
  end
70
47
 
71
48
  def updated?
72
- @boot_mutex.synchronize do
73
- if @pid != Process.pid
74
- boot!
75
- @pid = Process.pid
76
- @updated.make_true
77
- end
78
- end
79
-
80
- if @missing.any?(&:exist?)
81
- @boot_mutex.synchronize do
82
- appeared, @missing = @missing.partition(&:exist?)
83
- shutdown!
84
-
85
- @dtw += appeared
86
- boot!
87
-
88
- @updated.make_true
89
- end
49
+ if @core.restart?
50
+ @core.thread_safely(&:restart)
51
+ @core.updated.make_true
90
52
  end
91
53
 
92
- @updated.true?
54
+ @core.updated.true?
93
55
  end
94
56
 
95
57
  def execute
96
- @updated.make_false
58
+ @core.updated.make_false
97
59
  @block.call
98
60
  end
99
61
 
@@ -105,17 +67,59 @@ module ActiveSupport
105
67
  end
106
68
  end
107
69
 
108
- private
109
- def boot!
110
- normalize_dirs!
70
+ class Core
71
+ attr_reader :updated
72
+
73
+ def initialize(files, dirs)
74
+ @files = files.map { |file| Pathname(file).expand_path }.to_set
75
+
76
+ @dirs = dirs.each_with_object({}) do |(dir, exts), hash|
77
+ hash[Pathname(dir).expand_path] = Array(exts).map { |ext| ext.to_s.sub(/\A\.?/, ".") }.to_set
78
+ end
79
+
80
+ @common_path = common_path(@dirs.keys)
81
+
82
+ @dtw = directories_to_watch
83
+ @missing = []
84
+
85
+ @updated = Concurrent::AtomicBoolean.new(false)
86
+ @mutex = Mutex.new
87
+
88
+ start
89
+ @after_fork = ActiveSupport::ForkTracker.after_fork { start }
90
+ end
91
+
92
+ def finalizer
93
+ proc do
94
+ stop
95
+ ActiveSupport::ForkTracker.unregister(@after_fork)
96
+ end
97
+ end
111
98
 
112
- unless @dtw.empty?
113
- Listen.to(*@dtw, &method(:changed)).start
99
+ def thread_safely
100
+ @mutex.synchronize do
101
+ yield self
114
102
  end
115
103
  end
116
104
 
117
- def shutdown!
118
- Listen.stop
105
+ def start
106
+ normalize_dirs!
107
+ @dtw, @missing = [*@dtw, *@missing].partition(&:exist?)
108
+ @listener = @dtw.any? ? Listen.to(*@dtw, &method(:changed)) : nil
109
+ @listener&.start
110
+ end
111
+
112
+ def stop
113
+ @listener&.stop
114
+ end
115
+
116
+ def restart
117
+ stop
118
+ start
119
+ end
120
+
121
+ def restart?
122
+ @missing.any?(&:exist?)
119
123
  end
120
124
 
121
125
  def normalize_dirs!
@@ -125,27 +129,27 @@ module ActiveSupport
125
129
  end
126
130
 
127
131
  def changed(modified, added, removed)
128
- unless updated?
132
+ unless @updated.true?
129
133
  @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
130
134
  end
131
135
  end
132
136
 
133
137
  def watching?(file)
134
- file = @ph.xpath(file)
138
+ file = Pathname(file)
135
139
 
136
140
  if @files.member?(file)
137
141
  true
138
142
  elsif file.directory?
139
143
  false
140
144
  else
141
- ext = @ph.normalize_extension(file.extname)
145
+ ext = file.extname
142
146
 
143
147
  file.dirname.ascend do |dir|
144
148
  matching = @dirs[dir]
145
149
 
146
150
  if matching && (matching.empty? || matching.include?(ext))
147
151
  break true
148
- elsif dir == @lcsp || dir.root?
152
+ elsif dir == @common_path || dir.root?
149
153
  break false
150
154
  end
151
155
  end
@@ -153,82 +157,14 @@ module ActiveSupport
153
157
  end
154
158
 
155
159
  def directories_to_watch
156
- dtw = @files.map(&:dirname) + @dirs.keys
157
- dtw.compact!
158
- dtw.uniq!
159
-
160
- normalized_gem_paths = Gem.path.map { |path| File.join path, "" }
161
- dtw = dtw.reject do |path|
162
- normalized_gem_paths.any? { |gem_path| path.to_s.start_with?(gem_path) }
163
- end
164
-
165
- @ph.filter_out_descendants(dtw)
160
+ dtw = @dirs.keys | @files.map(&:dirname)
161
+ accounted_for = dtw.to_set + Gem.path.map { |path| Pathname(path) }
162
+ dtw.reject { |dir| dir.ascend.drop(1).any? { |parent| accounted_for.include?(parent) } }
166
163
  end
167
164
 
168
- class PathHelper
169
- def xpath(path)
170
- Pathname.new(path).expand_path
171
- end
172
-
173
- def normalize_extension(ext)
174
- ext.to_s.sub(/\A\./, "")
175
- end
176
-
177
- # Given a collection of Pathname objects returns the longest subpath
178
- # common to all of them, or +nil+ if there is none.
179
- def longest_common_subpath(paths)
180
- return if paths.empty?
181
-
182
- lcsp = Pathname.new(paths[0])
183
-
184
- paths[1..-1].each do |path|
185
- until ascendant_of?(lcsp, path)
186
- if lcsp.root?
187
- # If we get here a root directory is not an ascendant of path.
188
- # This may happen if there are paths in different drives on
189
- # Windows.
190
- return
191
- else
192
- lcsp = lcsp.parent
193
- end
194
- end
195
- end
196
-
197
- lcsp
198
- end
199
-
200
- # Returns the deepest existing ascendant, which could be the argument itself.
201
- def existing_parent(dir)
202
- dir.ascend do |ascendant|
203
- break ascendant if ascendant.directory?
204
- end
205
- end
206
-
207
- # Filters out directories which are descendants of others in the collection (stable).
208
- def filter_out_descendants(dirs)
209
- return dirs if dirs.length < 2
210
-
211
- dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length }
212
- descendants = []
213
-
214
- until dirs_sorted_by_nparts.empty?
215
- dir = dirs_sorted_by_nparts.shift
216
-
217
- dirs_sorted_by_nparts.reject! do |possible_descendant|
218
- ascendant_of?(dir, possible_descendant) && descendants << possible_descendant
219
- end
220
- end
221
-
222
- # Array#- preserves order.
223
- dirs - descendants
224
- end
225
-
226
- private
227
- def ascendant_of?(base, other)
228
- base != other && other.ascend do |ascendant|
229
- break true if base == ascendant
230
- end
231
- end
165
+ def common_path(paths)
166
+ paths.map { |path| path.ascend.to_a }.reduce(&:&)&.first
232
167
  end
168
+ end
233
169
  end
234
170
  end
@@ -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,21 +64,18 @@ module ActiveSupport
63
64
  # after the work has been performed.
64
65
  #
65
66
  # Where possible, prefer +wrap+.
66
- def self.run!(reset: false)
67
- if reset
68
- lost_instance = active.delete(Thread.current)
69
- lost_instance&.complete!
67
+ def self.run!
68
+ if active?
69
+ Null
70
70
  else
71
- return Null if active?
72
- end
73
-
74
- new.tap do |instance|
75
- success = nil
76
- begin
77
- instance.run!
78
- success = true
79
- ensure
80
- instance.complete! unless success
71
+ new.tap do |instance|
72
+ success = nil
73
+ begin
74
+ instance.run!
75
+ success = true
76
+ ensure
77
+ instance.complete! unless success
78
+ end
81
79
  end
82
80
  end
83
81
  end
@@ -89,15 +87,32 @@ module ActiveSupport
89
87
  instance = run!
90
88
  begin
91
89
  yield
90
+ rescue => error
91
+ error_reporter.report(error, handled: false)
92
+ raise
92
93
  ensure
93
94
  instance.complete!
94
95
  end
95
96
  end
96
97
 
98
+ def self.perform # :nodoc:
99
+ instance = new
100
+ instance.run
101
+ begin
102
+ yield
103
+ ensure
104
+ instance.complete
105
+ end
106
+ end
107
+
97
108
  class << self # :nodoc:
98
109
  attr_accessor :active
99
110
  end
100
111
 
112
+ def self.error_reporter
113
+ @error_reporter ||= ActiveSupport::ErrorReporter.new
114
+ end
115
+
101
116
  def self.inherited(other) # :nodoc:
102
117
  super
103
118
  other.active = Concurrent::Hash.new
@@ -106,11 +121,15 @@ module ActiveSupport
106
121
  self.active = Concurrent::Hash.new
107
122
 
108
123
  def self.active? # :nodoc:
109
- @active.key?(Thread.current)
124
+ @active[IsolatedExecutionState.unique_id]
110
125
  end
111
126
 
112
127
  def run! # :nodoc:
113
- self.class.active[Thread.current] = self
128
+ self.class.active[IsolatedExecutionState.unique_id] = true
129
+ run
130
+ end
131
+
132
+ def run # :nodoc:
114
133
  run_callbacks(:run)
115
134
  end
116
135
 
@@ -119,9 +138,13 @@ module ActiveSupport
119
138
  #
120
139
  # Where possible, prefer +wrap+.
121
140
  def complete!
122
- run_callbacks(:complete)
141
+ complete
123
142
  ensure
124
- self.class.active.delete Thread.current
143
+ self.class.active.delete(IsolatedExecutionState.unique_id)
144
+ end
145
+
146
+ def complete # :nodoc:
147
+ run_callbacks(:complete)
125
148
  end
126
149
 
127
150
  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
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module ForkTracker # :nodoc:
5
+ module ModernCoreExt
6
+ def _fork
7
+ pid = super
8
+ if pid == 0
9
+ ForkTracker.check!
10
+ end
11
+ pid
12
+ end
13
+ end
14
+
15
+ module CoreExt
16
+ def fork(...)
17
+ if block_given?
18
+ super do
19
+ ForkTracker.check!
20
+ yield
21
+ end
22
+ else
23
+ unless pid = super
24
+ ForkTracker.check!
25
+ end
26
+ pid
27
+ end
28
+ end
29
+ end
30
+
31
+ module CoreExtPrivate
32
+ include CoreExt
33
+ private :fork
34
+ end
35
+
36
+ @pid = Process.pid
37
+ @callbacks = []
38
+
39
+ class << self
40
+ def check!
41
+ new_pid = Process.pid
42
+ if @pid != new_pid
43
+ @callbacks.each(&:call)
44
+ @pid = new_pid
45
+ end
46
+ end
47
+
48
+ def hook!
49
+ if Process.respond_to?(:_fork) # Ruby 3.1+
50
+ ::Process.singleton_class.prepend(ModernCoreExt)
51
+ elsif Process.respond_to?(:fork)
52
+ ::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
53
+ ::Kernel.prepend(CoreExtPrivate)
54
+ ::Kernel.singleton_class.prepend(CoreExt)
55
+ ::Process.singleton_class.prepend(CoreExt)
56
+ end
57
+ end
58
+
59
+ def after_fork(&block)
60
+ @callbacks << block
61
+ block
62
+ end
63
+
64
+ def unregister(callback)
65
+ @callbacks.delete(callback)
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ ActiveSupport::ForkTracker.hook!
@@ -7,9 +7,9 @@ module ActiveSupport
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 6
10
+ MAJOR = 7
11
11
  MINOR = 0
12
- TINY = 6
12
+ TINY = 1
13
13
  PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")