activesupport 6.0.3.7 → 7.0.0

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 +220 -533
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/active_support/actionable_error.rb +1 -1
  6. data/lib/active_support/array_inquirer.rb +2 -2
  7. data/lib/active_support/backtrace_cleaner.rb +3 -3
  8. data/lib/active_support/benchmarkable.rb +3 -3
  9. data/lib/active_support/cache/file_store.rb +18 -11
  10. data/lib/active_support/cache/mem_cache_store.rb +143 -37
  11. data/lib/active_support/cache/memory_store.rb +56 -28
  12. data/lib/active_support/cache/null_store.rb +10 -2
  13. data/lib/active_support/cache/redis_cache_store.rb +63 -88
  14. data/lib/active_support/cache/strategy/local_cache.rb +46 -57
  15. data/lib/active_support/cache.rb +273 -82
  16. data/lib/active_support/callbacks.rb +226 -118
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +49 -5
  19. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +2 -4
  20. data/lib/active_support/concurrency/share_lock.rb +2 -2
  21. data/lib/active_support/configurable.rb +9 -6
  22. data/lib/active_support/configuration_file.rb +51 -0
  23. data/lib/active_support/core_ext/array/access.rb +1 -5
  24. data/lib/active_support/core_ext/array/conversions.rb +9 -7
  25. data/lib/active_support/core_ext/array/deprecated_conversions.rb +25 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +6 -6
  27. data/lib/active_support/core_ext/array.rb +1 -0
  28. data/lib/active_support/core_ext/benchmark.rb +2 -2
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/attribute.rb +34 -44
  31. data/lib/active_support/core_ext/class/subclasses.rb +21 -40
  32. data/lib/active_support/core_ext/date/blank.rb +1 -1
  33. data/lib/active_support/core_ext/date/calculations.rb +4 -4
  34. data/lib/active_support/core_ext/date/conversions.rb +5 -4
  35. data/lib/active_support/core_ext/date/deprecated_conversions.rb +26 -0
  36. data/lib/active_support/core_ext/date.rb +1 -0
  37. data/lib/active_support/core_ext/date_and_time/calculations.rb +13 -0
  38. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  39. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  40. data/lib/active_support/core_ext/date_time/conversions.rb +5 -5
  41. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +22 -0
  42. data/lib/active_support/core_ext/date_time.rb +1 -0
  43. data/lib/active_support/core_ext/digest/uuid.rb +39 -13
  44. data/lib/active_support/core_ext/enumerable.rb +139 -15
  45. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +2 -2
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +1 -1
  48. data/lib/active_support/core_ext/hash/except.rb +1 -1
  49. data/lib/active_support/core_ext/hash/keys.rb +2 -2
  50. data/lib/active_support/core_ext/hash/slice.rb +3 -2
  51. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  52. data/lib/active_support/core_ext/load_error.rb +1 -1
  53. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  54. data/lib/active_support/core_ext/module/attribute_accessors.rb +25 -29
  55. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +26 -13
  56. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  57. data/lib/active_support/core_ext/module/delegation.rb +40 -36
  58. data/lib/active_support/core_ext/module/introspection.rb +1 -25
  59. data/lib/active_support/core_ext/name_error.rb +23 -2
  60. data/lib/active_support/core_ext/numeric/conversions.rb +79 -72
  61. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  62. data/lib/active_support/core_ext/numeric.rb +1 -0
  63. data/lib/active_support/core_ext/object/blank.rb +2 -2
  64. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  65. data/lib/active_support/core_ext/object/duplicable.rb +11 -0
  66. data/lib/active_support/core_ext/object/json.rb +42 -26
  67. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  68. data/lib/active_support/core_ext/object/try.rb +20 -20
  69. data/lib/active_support/core_ext/object/with_options.rb +20 -1
  70. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  71. data/lib/active_support/core_ext/pathname.rb +3 -0
  72. data/lib/active_support/core_ext/range/compare_range.rb +6 -25
  73. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  74. data/lib/active_support/core_ext/range/deprecated_conversions.rb +26 -0
  75. data/lib/active_support/core_ext/range/each.rb +1 -1
  76. data/lib/active_support/core_ext/range/include_time_with_zone.rb +4 -20
  77. data/lib/active_support/core_ext/range.rb +1 -1
  78. data/lib/active_support/core_ext/regexp.rb +8 -1
  79. data/lib/active_support/core_ext/string/access.rb +5 -24
  80. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  81. data/lib/active_support/core_ext/string/filters.rb +1 -1
  82. data/lib/active_support/core_ext/string/inflections.rb +39 -5
  83. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  84. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  85. data/lib/active_support/core_ext/string/output_safety.rb +69 -45
  86. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  87. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  88. data/lib/active_support/core_ext/symbol.rb +3 -0
  89. data/lib/active_support/core_ext/time/calculations.rb +26 -6
  90. data/lib/active_support/core_ext/time/conversions.rb +6 -3
  91. data/lib/active_support/core_ext/time/deprecated_conversions.rb +22 -0
  92. data/lib/active_support/core_ext/time/zones.rb +4 -19
  93. data/lib/active_support/core_ext/time.rb +1 -0
  94. data/lib/active_support/core_ext/uri.rb +3 -23
  95. data/lib/active_support/core_ext.rb +2 -1
  96. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  97. data/lib/active_support/current_attributes.rb +39 -16
  98. data/lib/active_support/dependencies/interlock.rb +10 -18
  99. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  100. data/lib/active_support/dependencies.rb +58 -764
  101. data/lib/active_support/deprecation/behaviors.rb +19 -3
  102. data/lib/active_support/deprecation/disallowed.rb +56 -0
  103. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  104. data/lib/active_support/deprecation/method_wrappers.rb +6 -5
  105. data/lib/active_support/deprecation/proxy_wrappers.rb +4 -4
  106. data/lib/active_support/deprecation/reporting.rb +50 -7
  107. data/lib/active_support/deprecation.rb +6 -1
  108. data/lib/active_support/descendants_tracker.rb +177 -64
  109. data/lib/active_support/digest.rb +5 -3
  110. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  111. data/lib/active_support/duration/iso8601_serializer.rb +24 -10
  112. data/lib/active_support/duration.rb +134 -55
  113. data/lib/active_support/encrypted_configuration.rb +11 -1
  114. data/lib/active_support/encrypted_file.rb +20 -3
  115. data/lib/active_support/environment_inquirer.rb +20 -0
  116. data/lib/active_support/error_reporter.rb +117 -0
  117. data/lib/active_support/evented_file_update_checker.rb +70 -134
  118. data/lib/active_support/execution_context/test_helper.rb +13 -0
  119. data/lib/active_support/execution_context.rb +53 -0
  120. data/lib/active_support/execution_wrapper.rb +30 -4
  121. data/lib/active_support/executor/test_helper.rb +7 -0
  122. data/lib/active_support/fork_tracker.rb +71 -0
  123. data/lib/active_support/gem_version.rb +3 -3
  124. data/lib/active_support/hash_with_indifferent_access.rb +51 -25
  125. data/lib/active_support/html_safe_translation.rb +43 -0
  126. data/lib/active_support/i18n.rb +1 -0
  127. data/lib/active_support/i18n_railtie.rb +14 -19
  128. data/lib/active_support/inflector/inflections.rb +24 -9
  129. data/lib/active_support/inflector/methods.rb +29 -49
  130. data/lib/active_support/inflector/transliterate.rb +4 -4
  131. data/lib/active_support/isolated_execution_state.rb +56 -0
  132. data/lib/active_support/json/decoding.rb +4 -4
  133. data/lib/active_support/json/encoding.rb +8 -4
  134. data/lib/active_support/key_generator.rb +19 -2
  135. data/lib/active_support/locale/en.yml +8 -4
  136. data/lib/active_support/log_subscriber.rb +21 -3
  137. data/lib/active_support/logger.rb +1 -1
  138. data/lib/active_support/logger_silence.rb +2 -26
  139. data/lib/active_support/logger_thread_safe_level.rb +34 -21
  140. data/lib/active_support/message_encryptor.rb +12 -10
  141. data/lib/active_support/message_verifier.rb +50 -18
  142. data/lib/active_support/messages/metadata.rb +11 -3
  143. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  144. data/lib/active_support/messages/rotator.rb +6 -5
  145. data/lib/active_support/multibyte/chars.rb +13 -52
  146. data/lib/active_support/multibyte/unicode.rb +1 -87
  147. data/lib/active_support/multibyte.rb +1 -1
  148. data/lib/active_support/notifications/fanout.rb +110 -69
  149. data/lib/active_support/notifications/instrumenter.rb +37 -29
  150. data/lib/active_support/notifications.rb +47 -26
  151. data/lib/active_support/number_helper/number_converter.rb +2 -4
  152. data/lib/active_support/number_helper/number_to_currency_converter.rb +10 -9
  153. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  154. data/lib/active_support/number_helper/number_to_human_converter.rb +1 -1
  155. data/lib/active_support/number_helper/number_to_human_size_converter.rb +2 -2
  156. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  157. data/lib/active_support/number_helper/number_to_rounded_converter.rb +9 -5
  158. data/lib/active_support/number_helper/rounding_helper.rb +12 -32
  159. data/lib/active_support/number_helper.rb +29 -16
  160. data/lib/active_support/option_merger.rb +9 -16
  161. data/lib/active_support/ordered_hash.rb +1 -1
  162. data/lib/active_support/ordered_options.rb +8 -2
  163. data/lib/active_support/parameter_filter.rb +21 -11
  164. data/lib/active_support/per_thread_registry.rb +6 -1
  165. data/lib/active_support/rails.rb +1 -4
  166. data/lib/active_support/railtie.rb +77 -5
  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 +46 -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
 
@@ -86,15 +87,32 @@ module ActiveSupport
86
87
  instance = run!
87
88
  begin
88
89
  yield
90
+ rescue => error
91
+ error_reporter.report(error, handled: false)
92
+ raise
89
93
  ensure
90
94
  instance.complete!
91
95
  end
92
96
  end
93
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
+
94
108
  class << self # :nodoc:
95
109
  attr_accessor :active
96
110
  end
97
111
 
112
+ def self.error_reporter
113
+ @error_reporter ||= ActiveSupport::ErrorReporter.new
114
+ end
115
+
98
116
  def self.inherited(other) # :nodoc:
99
117
  super
100
118
  other.active = Concurrent::Hash.new
@@ -103,11 +121,15 @@ module ActiveSupport
103
121
  self.active = Concurrent::Hash.new
104
122
 
105
123
  def self.active? # :nodoc:
106
- @active[Thread.current]
124
+ @active[IsolatedExecutionState.unique_id]
107
125
  end
108
126
 
109
127
  def run! # :nodoc:
110
- self.class.active[Thread.current] = true
128
+ self.class.active[IsolatedExecutionState.unique_id] = true
129
+ run
130
+ end
131
+
132
+ def run # :nodoc:
111
133
  run_callbacks(:run)
112
134
  end
113
135
 
@@ -116,9 +138,13 @@ module ActiveSupport
116
138
  #
117
139
  # Where possible, prefer +wrap+.
118
140
  def complete!
119
- run_callbacks(:complete)
141
+ complete
120
142
  ensure
121
- 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)
122
148
  end
123
149
 
124
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,10 +7,10 @@ module ActiveSupport
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 6
10
+ MAJOR = 7
11
11
  MINOR = 0
12
- TINY = 3
13
- PRE = "7"
12
+ TINY = 0
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -3,6 +3,7 @@
3
3
  require "active_support/core_ext/hash/keys"
4
4
  require "active_support/core_ext/hash/reverse_merge"
5
5
  require "active_support/core_ext/hash/except"
6
+ require "active_support/core_ext/hash/slice"
6
7
 
7
8
  module ActiveSupport
8
9
  # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
@@ -64,14 +65,16 @@ module ActiveSupport
64
65
  self
65
66
  end
66
67
 
67
- def initialize(constructor = {})
68
+ def initialize(constructor = nil)
68
69
  if constructor.respond_to?(:to_hash)
69
70
  super()
70
71
  update(constructor)
71
72
 
72
- hash = constructor.to_hash
73
+ hash = constructor.is_a?(Hash) ? constructor : constructor.to_hash
73
74
  self.default = hash.default if hash.default
74
75
  self.default_proc = hash.default_proc if hash.default_proc
76
+ elsif constructor.nil?
77
+ super()
75
78
  else
76
79
  super(constructor)
77
80
  end
@@ -91,12 +94,12 @@ module ActiveSupport
91
94
  #
92
95
  # This value can be later fetched using either +:key+ or <tt>'key'</tt>.
93
96
  def []=(key, value)
94
- regular_writer(convert_key(key), convert_value(value, for: :assignment))
97
+ regular_writer(convert_key(key), convert_value(value, conversion: :assignment))
95
98
  end
96
99
 
97
100
  alias_method :store, :[]=
98
101
 
99
- # Updates the receiver in-place, merging in the hash passed as argument:
102
+ # Updates the receiver in-place, merging in the hashes passed as arguments:
100
103
  #
101
104
  # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
102
105
  # hash_1[:key] = 'value'
@@ -106,11 +109,14 @@ module ActiveSupport
106
109
  #
107
110
  # hash_1.update(hash_2) # => {"key"=>"New Value!"}
108
111
  #
109
- # The argument can be either an
112
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
113
+ # hash.update({ "a" => 1 }, { "b" => 2 }) # => { "a" => 1, "b" => 2 }
114
+ #
115
+ # The arguments can be either an
110
116
  # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
111
117
  # In either case the merge respects the semantics of indifferent access.
112
118
  #
113
- # If the argument is a regular hash with keys +:key+ and +"key"+ only one
119
+ # If the argument is a regular hash with keys +:key+ and <tt>"key"</tt> only one
114
120
  # of the values end up in the receiver, but which one is unspecified.
115
121
  #
116
122
  # When given a block, the value for duplicated keys will be determined
@@ -121,18 +127,15 @@ module ActiveSupport
121
127
  # hash_1[:key] = 10
122
128
  # hash_2['key'] = 12
123
129
  # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
124
- def update(other_hash)
125
- if other_hash.is_a? HashWithIndifferentAccess
126
- super(other_hash)
130
+ def update(*other_hashes, &block)
131
+ if other_hashes.size == 1
132
+ update_with_single_argument(other_hashes.first, block)
127
133
  else
128
- other_hash.to_hash.each_pair do |key, value|
129
- if block_given? && key?(key)
130
- value = yield(convert_key(key), self[key], value)
131
- end
132
- regular_writer(convert_key(key), convert_value(value))
134
+ other_hashes.each do |other_hash|
135
+ update_with_single_argument(other_hash, block)
133
136
  end
134
- self
135
137
  end
138
+ self
136
139
  end
137
140
 
138
141
  alias_method :merge!, :update
@@ -259,8 +262,8 @@ module ActiveSupport
259
262
  # This method has the same semantics of +update+, except it does not
260
263
  # modify the receiver but rather returns a new hash with indifferent
261
264
  # access with the result of the merge.
262
- def merge(hash, &block)
263
- dup.update(hash, &block)
265
+ def merge(*hashes, &block)
266
+ dup.update(*hashes, &block)
264
267
  end
265
268
 
266
269
  # Like +merge+ but the other way around: Merges the receiver into the
@@ -293,6 +296,10 @@ module ActiveSupport
293
296
  super(convert_key(key))
294
297
  end
295
298
 
299
+ # Returns a hash with indifferent access that includes everything except given keys.
300
+ # hash = { a: "x", b: "y", c: 10 }.with_indifferent_access
301
+ # hash.except(:a, "b") # => {c: 10}.with_indifferent_access
302
+ # hash # => { a: "x", b: "y", c: 10 }.with_indifferent_access
296
303
  def except(*keys)
297
304
  slice(*self.keys - keys.map { |key| convert_key(key) })
298
305
  end
@@ -357,40 +364,59 @@ module ActiveSupport
357
364
  set_defaults(_new_hash)
358
365
 
359
366
  each do |key, value|
360
- _new_hash[key] = convert_value(value, for: :to_hash)
367
+ _new_hash[key] = convert_value(value, conversion: :to_hash)
361
368
  end
362
369
  _new_hash
363
370
  end
364
371
 
365
372
  private
366
- def convert_key(key) # :doc:
367
- key.kind_of?(Symbol) ? key.to_s : key
373
+ if Symbol.method_defined?(:name)
374
+ def convert_key(key)
375
+ key.kind_of?(Symbol) ? key.name : key
376
+ end
377
+ else
378
+ def convert_key(key)
379
+ key.kind_of?(Symbol) ? key.to_s : key
380
+ end
368
381
  end
369
382
 
370
- def convert_value(value, options = {}) # :doc:
383
+ def convert_value(value, conversion: nil)
371
384
  if value.is_a? Hash
372
- if options[:for] == :to_hash
385
+ if conversion == :to_hash
373
386
  value.to_hash
374
387
  else
375
388
  value.nested_under_indifferent_access
376
389
  end
377
390
  elsif value.is_a?(Array)
378
- if options[:for] != :assignment || value.frozen?
391
+ if conversion != :assignment || value.frozen?
379
392
  value = value.dup
380
393
  end
381
- value.map! { |e| convert_value(e, options) }
394
+ value.map! { |e| convert_value(e, conversion: conversion) }
382
395
  else
383
396
  value
384
397
  end
385
398
  end
386
399
 
387
- def set_defaults(target) # :doc:
400
+ def set_defaults(target)
388
401
  if default_proc
389
402
  target.default_proc = default_proc.dup
390
403
  else
391
404
  target.default = default
392
405
  end
393
406
  end
407
+
408
+ def update_with_single_argument(other_hash, block)
409
+ if other_hash.is_a? HashWithIndifferentAccess
410
+ regular_update(other_hash, &block)
411
+ else
412
+ other_hash.to_hash.each_pair do |key, value|
413
+ if block && key?(key)
414
+ value = block.call(convert_key(key), self[key], value)
415
+ end
416
+ regular_writer(convert_key(key), convert_value(value))
417
+ end
418
+ end
419
+ end
394
420
  end
395
421
  end
396
422