activesupport 6.1.4.1 → 7.0.8.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +325 -395
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_support/actionable_error.rb +1 -1
  6. data/lib/active_support/array_inquirer.rb +0 -2
  7. data/lib/active_support/backtrace_cleaner.rb +2 -2
  8. data/lib/active_support/benchmarkable.rb +2 -2
  9. data/lib/active_support/cache/file_store.rb +15 -9
  10. data/lib/active_support/cache/mem_cache_store.rb +148 -37
  11. data/lib/active_support/cache/memory_store.rb +24 -16
  12. data/lib/active_support/cache/null_store.rb +10 -2
  13. data/lib/active_support/cache/redis_cache_store.rb +68 -85
  14. data/lib/active_support/cache/strategy/local_cache.rb +38 -61
  15. data/lib/active_support/cache.rb +299 -147
  16. data/lib/active_support/callbacks.rb +184 -85
  17. data/lib/active_support/code_generator.rb +65 -0
  18. data/lib/active_support/concern.rb +5 -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 +8 -5
  22. data/lib/active_support/configuration_file.rb +1 -1
  23. data/lib/active_support/core_ext/array/access.rb +1 -5
  24. data/lib/active_support/core_ext/array/conversions.rb +13 -12
  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/inquiry.rb +2 -2
  28. data/lib/active_support/core_ext/array.rb +1 -0
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +1 -1
  30. data/lib/active_support/core_ext/class/subclasses.rb +25 -17
  31. data/lib/active_support/core_ext/date/blank.rb +1 -1
  32. data/lib/active_support/core_ext/date/calculations.rb +24 -9
  33. data/lib/active_support/core_ext/date/conversions.rb +14 -14
  34. data/lib/active_support/core_ext/date/deprecated_conversions.rb +40 -0
  35. data/lib/active_support/core_ext/date.rb +1 -0
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +4 -4
  37. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  38. data/lib/active_support/core_ext/date_time/blank.rb +1 -1
  39. data/lib/active_support/core_ext/date_time/calculations.rb +4 -0
  40. data/lib/active_support/core_ext/date_time/conversions.rb +13 -13
  41. data/lib/active_support/core_ext/date_time/deprecated_conversions.rb +36 -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 +112 -38
  45. data/lib/active_support/core_ext/file/atomic.rb +3 -1
  46. data/lib/active_support/core_ext/hash/conversions.rb +0 -1
  47. data/lib/active_support/core_ext/hash/deep_transform_values.rb +3 -3
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +3 -3
  49. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  50. data/lib/active_support/core_ext/integer/inflections.rb +12 -12
  51. data/lib/active_support/core_ext/kernel/reporting.rb +4 -4
  52. data/lib/active_support/core_ext/kernel/singleton_class.rb +1 -1
  53. data/lib/active_support/core_ext/module/attribute_accessors.rb +2 -0
  54. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +19 -10
  55. data/lib/active_support/core_ext/module/delegation.rb +2 -8
  56. data/lib/active_support/core_ext/name_error.rb +2 -8
  57. data/lib/active_support/core_ext/numeric/conversions.rb +80 -77
  58. data/lib/active_support/core_ext/numeric/deprecated_conversions.rb +60 -0
  59. data/lib/active_support/core_ext/numeric.rb +1 -0
  60. data/lib/active_support/core_ext/object/acts_like.rb +29 -5
  61. data/lib/active_support/core_ext/object/blank.rb +2 -2
  62. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  63. data/lib/active_support/core_ext/object/duplicable.rb +15 -4
  64. data/lib/active_support/core_ext/object/json.rb +30 -25
  65. data/lib/active_support/core_ext/object/to_query.rb +2 -4
  66. data/lib/active_support/core_ext/object/try.rb +20 -20
  67. data/lib/active_support/core_ext/object/with_options.rb +21 -2
  68. data/lib/active_support/core_ext/pathname/existence.rb +21 -0
  69. data/lib/active_support/core_ext/pathname.rb +3 -0
  70. data/lib/active_support/core_ext/range/compare_range.rb +0 -25
  71. data/lib/active_support/core_ext/range/conversions.rb +8 -8
  72. data/lib/active_support/core_ext/range/deprecated_conversions.rb +36 -0
  73. data/lib/active_support/core_ext/range/each.rb +1 -1
  74. data/lib/active_support/core_ext/range/include_time_with_zone.rb +3 -26
  75. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  76. data/lib/active_support/core_ext/range.rb +1 -1
  77. data/lib/active_support/core_ext/securerandom.rb +1 -1
  78. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  79. data/lib/active_support/core_ext/string/filters.rb +1 -1
  80. data/lib/active_support/core_ext/string/inflections.rb +1 -5
  81. data/lib/active_support/core_ext/string/inquiry.rb +1 -1
  82. data/lib/active_support/core_ext/string/output_safety.rb +94 -38
  83. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +0 -8
  84. data/lib/active_support/core_ext/time/calculations.rb +13 -8
  85. data/lib/active_support/core_ext/time/conversions.rb +13 -12
  86. data/lib/active_support/core_ext/time/deprecated_conversions.rb +73 -0
  87. data/lib/active_support/core_ext/time/zones.rb +10 -26
  88. data/lib/active_support/core_ext/time.rb +1 -0
  89. data/lib/active_support/core_ext/uri.rb +3 -27
  90. data/lib/active_support/core_ext.rb +1 -0
  91. data/lib/active_support/current_attributes.rb +31 -14
  92. data/lib/active_support/dependencies/interlock.rb +10 -18
  93. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  94. data/lib/active_support/dependencies.rb +58 -788
  95. data/lib/active_support/deprecation/behaviors.rb +8 -5
  96. data/lib/active_support/deprecation/disallowed.rb +3 -3
  97. data/lib/active_support/deprecation/method_wrappers.rb +3 -3
  98. data/lib/active_support/deprecation/proxy_wrappers.rb +2 -2
  99. data/lib/active_support/deprecation.rb +2 -2
  100. data/lib/active_support/descendants_tracker.rb +174 -68
  101. data/lib/active_support/digest.rb +5 -3
  102. data/lib/active_support/duration/iso8601_parser.rb +3 -3
  103. data/lib/active_support/duration/iso8601_serializer.rb +9 -1
  104. data/lib/active_support/duration.rb +81 -51
  105. data/lib/active_support/encrypted_configuration.rb +45 -3
  106. data/lib/active_support/encrypted_file.rb +21 -10
  107. data/lib/active_support/environment_inquirer.rb +1 -1
  108. data/lib/active_support/error_reporter.rb +117 -0
  109. data/lib/active_support/evented_file_update_checker.rb +20 -7
  110. data/lib/active_support/execution_context/test_helper.rb +13 -0
  111. data/lib/active_support/execution_context.rb +53 -0
  112. data/lib/active_support/execution_wrapper.rb +43 -21
  113. data/lib/active_support/executor/test_helper.rb +7 -0
  114. data/lib/active_support/fork_tracker.rb +19 -12
  115. data/lib/active_support/gem_version.rb +5 -5
  116. data/lib/active_support/hash_with_indifferent_access.rb +3 -1
  117. data/lib/active_support/html_safe_translation.rb +43 -0
  118. data/lib/active_support/i18n.rb +1 -0
  119. data/lib/active_support/i18n_railtie.rb +1 -1
  120. data/lib/active_support/inflector/inflections.rb +23 -7
  121. data/lib/active_support/inflector/methods.rb +29 -55
  122. data/lib/active_support/inflector/transliterate.rb +1 -1
  123. data/lib/active_support/isolated_execution_state.rb +72 -0
  124. data/lib/active_support/json/encoding.rb +3 -3
  125. data/lib/active_support/key_generator.rb +22 -5
  126. data/lib/active_support/lazy_load_hooks.rb +28 -4
  127. data/lib/active_support/locale/en.yml +1 -1
  128. data/lib/active_support/log_subscriber/test_helper.rb +2 -2
  129. data/lib/active_support/log_subscriber.rb +15 -5
  130. data/lib/active_support/logger_thread_safe_level.rb +4 -13
  131. data/lib/active_support/message_encryptor.rb +12 -6
  132. data/lib/active_support/message_verifier.rb +46 -14
  133. data/lib/active_support/messages/metadata.rb +2 -2
  134. data/lib/active_support/multibyte/chars.rb +10 -11
  135. data/lib/active_support/multibyte/unicode.rb +0 -12
  136. data/lib/active_support/multibyte.rb +1 -1
  137. data/lib/active_support/notifications/fanout.rb +91 -65
  138. data/lib/active_support/notifications/instrumenter.rb +32 -15
  139. data/lib/active_support/notifications.rb +23 -23
  140. data/lib/active_support/number_helper/number_converter.rb +1 -3
  141. data/lib/active_support/number_helper/number_to_currency_converter.rb +11 -6
  142. data/lib/active_support/number_helper/number_to_delimited_converter.rb +1 -1
  143. data/lib/active_support/number_helper/number_to_human_size_converter.rb +1 -1
  144. data/lib/active_support/number_helper/number_to_phone_converter.rb +1 -1
  145. data/lib/active_support/number_helper/rounding_helper.rb +1 -5
  146. data/lib/active_support/number_helper.rb +4 -5
  147. data/lib/active_support/option_merger.rb +10 -18
  148. data/lib/active_support/ordered_hash.rb +1 -1
  149. data/lib/active_support/ordered_options.rb +1 -1
  150. data/lib/active_support/parameter_filter.rb +20 -11
  151. data/lib/active_support/per_thread_registry.rb +5 -0
  152. data/lib/active_support/railtie.rb +69 -19
  153. data/lib/active_support/reloader.rb +1 -1
  154. data/lib/active_support/rescuable.rb +12 -12
  155. data/lib/active_support/ruby_features.rb +7 -0
  156. data/lib/active_support/secure_compare_rotator.rb +2 -2
  157. data/lib/active_support/string_inquirer.rb +0 -2
  158. data/lib/active_support/subscriber.rb +7 -18
  159. data/lib/active_support/tagged_logging.rb +2 -2
  160. data/lib/active_support/test_case.rb +13 -21
  161. data/lib/active_support/testing/assertions.rb +36 -6
  162. data/lib/active_support/testing/deprecation.rb +52 -1
  163. data/lib/active_support/testing/isolation.rb +30 -29
  164. data/lib/active_support/testing/method_call_assertions.rb +5 -5
  165. data/lib/active_support/testing/parallelization/server.rb +4 -0
  166. data/lib/active_support/testing/parallelization/worker.rb +3 -0
  167. data/lib/active_support/testing/parallelization.rb +4 -0
  168. data/lib/active_support/testing/parallelize_executor.rb +76 -0
  169. data/lib/active_support/testing/stream.rb +3 -5
  170. data/lib/active_support/testing/tagged_logging.rb +1 -1
  171. data/lib/active_support/testing/time_helpers.rb +13 -2
  172. data/lib/active_support/time_with_zone.rb +43 -22
  173. data/lib/active_support/values/time_zone.rb +35 -14
  174. data/lib/active_support/version.rb +1 -1
  175. data/lib/active_support/xml_mini/jdom.rb +1 -1
  176. data/lib/active_support/xml_mini/libxml.rb +5 -5
  177. data/lib/active_support/xml_mini/libxmlsax.rb +1 -1
  178. data/lib/active_support/xml_mini/nokogiri.rb +4 -4
  179. data/lib/active_support/xml_mini/nokogirisax.rb +1 -1
  180. data/lib/active_support/xml_mini/rexml.rb +1 -1
  181. data/lib/active_support/xml_mini.rb +5 -4
  182. data/lib/active_support.rb +17 -1
  183. metadata +26 -23
  184. data/lib/active_support/core_ext/marshal.rb +0 -26
  185. data/lib/active_support/dependencies/zeitwerk_integration.rb +0 -117
@@ -1,828 +1,98 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
- require "thread"
5
- require "concurrent/map"
6
- require "pathname"
7
- require "active_support/core_ext/module/aliasing"
8
- require "active_support/core_ext/module/attribute_accessors"
9
- require "active_support/core_ext/module/introspection"
10
- require "active_support/core_ext/module/anonymous"
11
- require "active_support/core_ext/object/blank"
12
- require "active_support/core_ext/kernel/reporting"
13
- require "active_support/core_ext/load_error"
14
- require "active_support/core_ext/name_error"
15
4
  require "active_support/dependencies/interlock"
16
- require "active_support/inflector"
17
5
 
18
- module ActiveSupport #:nodoc:
19
- module Dependencies #:nodoc:
20
- extend self
6
+ module ActiveSupport # :nodoc:
7
+ module Dependencies # :nodoc:
8
+ require_relative "dependencies/require_dependency"
21
9
 
22
- UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
23
- private_constant :UNBOUND_METHOD_MODULE_NAME
24
-
25
- mattr_accessor :interlock, default: Interlock.new
10
+ singleton_class.attr_accessor :interlock
11
+ @interlock = Interlock.new
26
12
 
27
13
  # :doc:
28
14
 
29
15
  # Execute the supplied block without interference from any
30
16
  # concurrent loads.
31
- def self.run_interlock
32
- Dependencies.interlock.running { yield }
17
+ def self.run_interlock(&block)
18
+ interlock.running(&block)
33
19
  end
34
20
 
35
21
  # Execute the supplied block while holding an exclusive lock,
36
22
  # preventing any other thread from being inside a #run_interlock
37
23
  # block at the same time.
38
- def self.load_interlock
39
- Dependencies.interlock.loading { yield }
24
+ def self.load_interlock(&block)
25
+ interlock.loading(&block)
40
26
  end
41
27
 
42
28
  # Execute the supplied block while holding an exclusive lock,
43
29
  # preventing any other thread from being inside a #run_interlock
44
30
  # block at the same time.
45
- def self.unload_interlock
46
- Dependencies.interlock.unloading { yield }
31
+ def self.unload_interlock(&block)
32
+ interlock.unloading(&block)
47
33
  end
48
34
 
49
35
  # :nodoc:
50
36
 
51
- # Should we turn on Ruby warnings on the first load of dependent files?
52
- mattr_accessor :warnings_on_first_load, default: false
53
-
54
- # All files ever loaded.
55
- mattr_accessor :history, default: Set.new
56
-
57
- # All files currently loaded.
58
- mattr_accessor :loaded, default: Set.new
59
-
60
- # Stack of files being loaded.
61
- mattr_accessor :loading, default: []
62
-
63
- # Should we load files or require them?
64
- mattr_accessor :mechanism, default: ENV["NO_RELOAD"] ? :require : :load
65
-
66
- # The set of directories from which we may automatically load files. Files
67
- # under these directories will be reloaded on each request in development mode,
68
- # unless the directory also appears in autoload_once_paths.
69
- mattr_accessor :autoload_paths, default: []
70
-
71
- # The set of directories from which automatically loaded constants are loaded
72
- # only once. All directories in this set must also be present in +autoload_paths+.
73
- mattr_accessor :autoload_once_paths, default: []
74
-
75
- # This is a private set that collects all eager load paths during bootstrap.
76
- # Useful for Zeitwerk integration. Its public interface is the config.* path
77
- # accessors of each engine.
78
- mattr_accessor :_eager_load_paths, default: Set.new
79
-
80
- # An array of qualified constant names that have been loaded. Adding a name
81
- # to this array will cause it to be unloaded the next time Dependencies are
82
- # cleared.
83
- mattr_accessor :autoloaded_constants, default: []
84
-
85
- # An array of constant names that need to be unloaded on every request. Used
86
- # to allow arbitrary constants to be marked for unloading.
87
- mattr_accessor :explicitly_unloadable_constants, default: []
88
-
89
- # The logger used when tracing autoloads.
90
- mattr_accessor :logger
91
-
92
- # If true, trace autoloads with +logger.debug+.
93
- mattr_accessor :verbose, default: false
94
-
95
- # The WatchStack keeps a stack of the modules being watched as files are
96
- # loaded. If a file in the process of being loaded (parent.rb) triggers the
97
- # load of another file (child.rb) the stack will ensure that child.rb
98
- # handles the new constants.
99
- #
100
- # If child.rb is being autoloaded, its constants will be added to
101
- # autoloaded_constants. If it was being required, they will be discarded.
37
+ # The array of directories from which we autoload and reload, if reloading
38
+ # is enabled. The public interface to push directories to this collection
39
+ # from applications or engines is config.autoload_paths.
102
40
  #
103
- # This is handled by walking back up the watch stack and adding the constants
104
- # found by child.rb to the list of original constants in parent.rb.
105
- class WatchStack
106
- include Enumerable
107
-
108
- # @watching is a stack of lists of constants being watched. For instance,
109
- # if parent.rb is autoloaded, the stack will look like [[Object]]. If
110
- # parent.rb then requires namespace/child.rb, the stack will look like
111
- # [[Object], [Namespace]].
112
-
113
- attr_reader :watching
114
-
115
- def initialize
116
- @watching = []
117
- @stack = Hash.new { |h, k| h[k] = [] }
118
- end
119
-
120
- def each(&block)
121
- @stack.each(&block)
122
- end
123
-
124
- def watching?
125
- !@watching.empty?
126
- end
127
-
128
- # Returns a list of new constants found since the last call to
129
- # <tt>watch_namespaces</tt>.
130
- def new_constants
131
- constants = []
132
-
133
- # Grab the list of namespaces that we're looking for new constants under
134
- @watching.last.each do |namespace|
135
- # Retrieve the constants that were present under the namespace when watch_namespaces
136
- # was originally called
137
- original_constants = @stack[namespace].last
138
-
139
- mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
140
- next unless mod.is_a?(Module)
141
-
142
- # Get a list of the constants that were added
143
- new_constants = mod.constants(false) - original_constants
144
-
145
- # @stack[namespace] returns an Array of the constants that are being evaluated
146
- # for that namespace. For instance, if parent.rb requires child.rb, the first
147
- # element of @stack[Object] will be an Array of the constants that were present
148
- # before parent.rb was required. The second element will be an Array of the
149
- # constants that were present before child.rb was required.
150
- @stack[namespace].each do |namespace_constants|
151
- namespace_constants.concat(new_constants)
152
- end
153
-
154
- # Normalize the list of new constants, and add them to the list we will return
155
- new_constants.each do |suffix|
156
- constants << ([namespace, suffix] - ["Object"]).join("::")
157
- end
158
- end
159
- constants
160
- ensure
161
- # A call to new_constants is always called after a call to watch_namespaces
162
- pop_modules(@watching.pop)
163
- end
164
-
165
- # Add a set of modules to the watch stack, remembering the initial
166
- # constants.
167
- def watch_namespaces(namespaces)
168
- @watching << namespaces.map do |namespace|
169
- module_name = Dependencies.to_constant_name(namespace)
170
- original_constants = Dependencies.qualified_const_defined?(module_name) ?
171
- Inflector.constantize(module_name).constants(false) : []
172
-
173
- @stack[module_name] << original_constants
174
- module_name
175
- end
176
- end
177
-
178
- private
179
- def pop_modules(modules)
180
- modules.each { |mod| @stack[mod].pop }
181
- end
182
- end
183
-
184
- # An internal stack used to record which constants are loaded by any block.
185
- mattr_accessor :constant_watch_stack, default: WatchStack.new
186
-
187
- # Module includes this module.
188
- module ModuleConstMissing #:nodoc:
189
- def self.append_features(base)
190
- base.class_eval do
191
- # Emulate #exclude via an ivar
192
- return if defined?(@_const_missing) && @_const_missing
193
- @_const_missing = instance_method(:const_missing)
194
- remove_method(:const_missing)
195
- end
196
- super
197
- end
198
-
199
- def self.exclude_from(base)
200
- base.class_eval do
201
- define_method :const_missing, @_const_missing
202
- @_const_missing = nil
203
- end
204
- end
205
-
206
- def self.include_into(base)
207
- base.include(self)
208
- append_features(base)
209
- end
210
-
211
- def const_missing(const_name)
212
- from_mod = anonymous? ? guess_for_anonymous(const_name) : self
213
- Dependencies.load_missing_constant(from_mod, const_name)
214
- end
215
-
216
- # We assume that the name of the module reflects the nesting
217
- # (unless it can be proven that is not the case) and the path to the file
218
- # that defines the constant. Anonymous modules cannot follow these
219
- # conventions and therefore we assume that the user wants to refer to a
220
- # top-level constant.
221
- def guess_for_anonymous(const_name)
222
- if Object.const_defined?(const_name)
223
- raise NameError.new "#{const_name} cannot be autoloaded from an anonymous class or module", const_name
224
- else
225
- Object
226
- end
227
- end
228
-
229
- def unloadable(const_desc = self)
230
- super(const_desc)
231
- end
232
- end
233
-
234
- # Object includes this module.
235
- module Loadable #:nodoc:
236
- def self.exclude_from(base)
237
- base.class_eval do
238
- define_method(:load, Kernel.instance_method(:load))
239
- private :load
240
-
241
- define_method(:require, Kernel.instance_method(:require))
242
- private :require
243
- end
244
- end
245
-
246
- def self.include_into(base)
247
- base.include(self)
248
-
249
- if base.instance_method(:load).owner == base
250
- base.remove_method(:load)
251
- end
252
-
253
- if base.instance_method(:require).owner == base
254
- base.remove_method(:require)
255
- end
256
- end
257
-
258
- def require_or_load(file_name)
259
- Dependencies.require_or_load(file_name)
260
- end
261
-
262
- # :doc:
263
-
264
- # <b>Warning:</b> This method is obsolete in +:zeitwerk+ mode. In
265
- # +:zeitwerk+ mode semantics match Ruby's and you do not need to be
266
- # defensive with load order. Just refer to classes and modules normally.
267
- # If the constant name is dynamic, camelize if needed, and constantize.
268
- #
269
- # In +:classic+ mode, interprets a file using +mechanism+ and marks its
270
- # defined constants as autoloaded. +file_name+ can be either a string or
271
- # respond to <tt>to_path</tt>.
272
- #
273
- # In +:classic+ mode, use this method in code that absolutely needs a
274
- # certain constant to be defined at that point. A typical use case is to
275
- # make constant name resolution deterministic for constants with the same
276
- # relative name in different namespaces whose evaluation would depend on
277
- # load order otherwise.
278
- #
279
- # Engines that do not control the mode in which their parent application
280
- # runs should call +require_dependency+ where needed in case the runtime
281
- # mode is +:classic+.
282
- def require_dependency(file_name, message = "No such file to load -- %s.rb")
283
- file_name = file_name.to_path if file_name.respond_to?(:to_path)
284
- unless file_name.is_a?(String)
285
- raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}"
286
- end
287
-
288
- Dependencies.depend_on(file_name, message)
289
- end
290
-
291
- # :nodoc:
292
-
293
- def load_dependency(file)
294
- if Dependencies.load? && Dependencies.constant_watch_stack.watching?
295
- descs = Dependencies.constant_watch_stack.watching.flatten.uniq
296
-
297
- Dependencies.new_constants_in(*descs) { yield }
298
- else
299
- yield
300
- end
301
- rescue Exception => exception # errors from loading file
302
- exception.blame_file! file if exception.respond_to? :blame_file!
303
- raise
304
- end
305
-
306
- # Mark the given constant as unloadable. Unloadable constants are removed
307
- # each time dependencies are cleared.
308
- #
309
- # Note that marking a constant for unloading need only be done once. Setup
310
- # or init scripts may list each unloadable constant that may need unloading;
311
- # each constant will be removed for every subsequent clear, as opposed to
312
- # for the first clear.
313
- #
314
- # The provided constant descriptor may be a (non-anonymous) module or class,
315
- # or a qualified constant name as a string or symbol.
316
- #
317
- # Returns +true+ if the constant was not previously marked for unloading,
318
- # +false+ otherwise.
319
- def unloadable(const_desc)
320
- Dependencies.mark_for_unload const_desc
321
- end
322
-
323
- private
324
- def load(file, wrap = false)
325
- result = false
326
- load_dependency(file) { result = super }
327
- result
328
- end
329
-
330
- def require(file)
331
- result = false
332
- load_dependency(file) { result = super }
333
- result
334
- end
335
- end
336
-
337
- # Exception file-blaming.
338
- module Blamable #:nodoc:
339
- def blame_file!(file)
340
- (@blamed_files ||= []).unshift file
341
- end
342
-
343
- def blamed_files
344
- @blamed_files ||= []
345
- end
346
-
347
- def describe_blame
348
- return nil if blamed_files.empty?
349
- "This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
350
- end
351
-
352
- def copy_blame!(exc)
353
- @blamed_files = exc.blamed_files.clone
354
- self
355
- end
356
- end
357
-
358
- def hook!
359
- Loadable.include_into(Object)
360
- ModuleConstMissing.include_into(Module)
361
- Exception.include(Blamable)
362
- end
363
-
364
- def unhook!
365
- ModuleConstMissing.exclude_from(Module)
366
- Loadable.exclude_from(Object)
367
- end
368
-
369
- def load?
370
- mechanism == :load
371
- end
372
-
373
- def depend_on(file_name, message = "No such file to load -- %s.rb")
374
- path = search_for_file(file_name)
375
- require_or_load(path || file_name)
376
- rescue LoadError => load_error
377
- if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
378
- load_error_message = if load_error.respond_to?(:original_message)
379
- load_error.original_message
380
- else
381
- load_error.message
382
- end
383
- load_error_message.replace(message % file_name)
384
- load_error.copy_blame!(load_error)
385
- end
386
- raise
387
- end
388
-
389
- def clear
390
- Dependencies.unload_interlock do
391
- loaded.clear
392
- loading.clear
393
- remove_unloadable_constants!
394
- end
395
- end
396
-
397
- def require_or_load(file_name, const_path = nil)
398
- file_name = file_name.chomp(".rb")
399
- expanded = File.expand_path(file_name)
400
- return if loaded.include?(expanded)
401
-
402
- Dependencies.load_interlock do
403
- # Maybe it got loaded while we were waiting for our lock:
404
- return if loaded.include?(expanded)
405
-
406
- # Record that we've seen this file *before* loading it to avoid an
407
- # infinite loop with mutual dependencies.
408
- loaded << expanded
409
- loading << expanded
41
+ # This collection is allowed to have intersection with autoload_once_paths.
42
+ # Common directories are not reloaded.
43
+ singleton_class.attr_accessor :autoload_paths
44
+ self.autoload_paths = []
410
45
 
411
- begin
412
- if load?
413
- # Enable warnings if this file has not been loaded before and
414
- # warnings_on_first_load is set.
415
- load_args = ["#{file_name}.rb"]
416
- load_args << const_path unless const_path.nil?
46
+ # The array of directories from which we autoload and never reload, even if
47
+ # reloading is enabled. The public interface to push directories to this
48
+ # collection from applications or engines is config.autoload_once_paths.
49
+ singleton_class.attr_accessor :autoload_once_paths
50
+ self.autoload_once_paths = []
417
51
 
418
- if !warnings_on_first_load || history.include?(expanded)
419
- result = load_file(*load_args)
420
- else
421
- enable_warnings { result = load_file(*load_args) }
422
- end
423
- else
424
- result = require file_name
425
- end
426
- rescue Exception
427
- loaded.delete expanded
428
- raise
429
- ensure
430
- loading.pop
431
- end
432
-
433
- # Record history *after* loading so first load gets warnings.
434
- history << expanded
435
- result
436
- end
437
- end
438
-
439
- # Is the provided constant path defined?
440
- def qualified_const_defined?(path)
441
- Object.const_defined?(path, false)
442
- end
443
-
444
- # Given +path+, a filesystem path to a ruby file, return an array of
445
- # constant paths which would cause Dependencies to attempt to load this
446
- # file.
447
- def loadable_constants_for_path(path, bases = autoload_paths)
448
- path = path.chomp(".rb")
449
- expanded_path = File.expand_path(path)
450
- paths = []
451
-
452
- bases.each do |root|
453
- expanded_root = File.expand_path(root)
454
- next unless expanded_path.start_with?(expanded_root)
455
-
456
- root_size = expanded_root.size
457
- next if expanded_path[root_size] != ?/
458
-
459
- nesting = expanded_path[(root_size + 1)..-1]
460
- paths << nesting.camelize unless nesting.blank?
461
- end
462
-
463
- paths.uniq!
464
- paths
465
- end
466
-
467
- # Search for a file in autoload_paths matching the provided suffix.
468
- def search_for_file(path_suffix)
469
- path_suffix += ".rb" unless path_suffix.end_with?(".rb")
470
-
471
- autoload_paths.each do |root|
472
- path = File.join(root, path_suffix)
473
- return path if File.file? path
474
- end
475
- nil # Gee, I sure wish we had first_match ;-)
476
- end
477
-
478
- # Does the provided path_suffix correspond to an autoloadable module?
479
- # Instead of returning a boolean, the autoload base for this module is
480
- # returned.
481
- def autoloadable_module?(path_suffix)
482
- autoload_paths.each do |load_path|
483
- return load_path if File.directory? File.join(load_path, path_suffix)
484
- end
485
- nil
486
- end
487
-
488
- def load_once_path?(path)
489
- # to_s works around a ruby issue where String#start_with?(Pathname)
490
- # will raise a TypeError: no implicit conversion of Pathname into String
491
- autoload_once_paths.any? { |base| path.start_with?(base.to_s) }
492
- end
493
-
494
- # Attempt to autoload the provided module name by searching for a directory
495
- # matching the expected path suffix. If found, the module is created and
496
- # assigned to +into+'s constants with the name +const_name+. Provided that
497
- # the directory was loaded from a reloadable base path, it is added to the
498
- # set of constants that are to be unloaded.
499
- def autoload_module!(into, const_name, qualified_name, path_suffix)
500
- return nil unless base_path = autoloadable_module?(path_suffix)
501
- mod = Module.new
502
- into.const_set const_name, mod
503
- log("constant #{qualified_name} autoloaded (module autovivified from #{File.join(base_path, path_suffix)})")
504
- autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
505
- autoloaded_constants.uniq!
506
- mod
507
- end
508
-
509
- # Load the file at the provided path. +const_paths+ is a set of qualified
510
- # constant names. When loading the file, Dependencies will watch for the
511
- # addition of these constants. Each that is defined will be marked as
512
- # autoloaded, and will be removed when Dependencies.clear is next called.
513
- #
514
- # If the second parameter is left off, then Dependencies will construct a
515
- # set of names that the file at +path+ may define. See
516
- # +loadable_constants_for_path+ for more details.
517
- def load_file(path, const_paths = loadable_constants_for_path(path))
518
- const_paths = [const_paths].compact unless const_paths.is_a? Array
519
- parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object }
520
-
521
- result = nil
522
- newly_defined_paths = new_constants_in(*parent_paths) do
523
- result = Kernel.load path
524
- end
525
-
526
- autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
527
- autoloaded_constants.uniq!
528
- result
529
- end
530
-
531
- # Returns the constant path for the provided parent and constant name.
532
- def qualified_name_for(mod, name)
533
- mod_name = to_constant_name mod
534
- mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
535
- end
536
-
537
- # Load the constant named +const_name+ which is missing from +from_mod+. If
538
- # it is not possible to load the constant into from_mod, try its parent
539
- # module using +const_missing+.
540
- def load_missing_constant(from_mod, const_name)
541
- from_mod_name = real_mod_name(from_mod)
542
- unless qualified_const_defined?(from_mod_name) && Inflector.constantize(from_mod_name).equal?(from_mod)
543
- raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
544
- end
545
-
546
- qualified_name = qualified_name_for(from_mod, const_name)
547
- path_suffix = qualified_name.underscore
548
-
549
- file_path = search_for_file(path_suffix)
550
-
551
- if file_path
552
- expanded = File.expand_path(file_path)
553
- expanded.delete_suffix!(".rb")
554
-
555
- if loading.include?(expanded)
556
- raise "Circular dependency detected while autoloading constant #{qualified_name}"
557
- else
558
- require_or_load(expanded, qualified_name)
559
-
560
- if from_mod.const_defined?(const_name, false)
561
- log("constant #{qualified_name} autoloaded from #{expanded}.rb")
562
- return from_mod.const_get(const_name)
563
- else
564
- raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it"
565
- end
566
- end
567
- elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
568
- return mod
569
- elsif (parent = from_mod.module_parent) && parent != from_mod &&
570
- ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
571
- # If our parents do not have a constant named +const_name+ then we are free
572
- # to attempt to load upwards. If they do have such a constant, then this
573
- # const_missing must be due to from_mod::const_name, which should not
574
- # return constants from from_mod's parents.
575
- begin
576
- # Since Ruby does not pass the nesting at the point the unknown
577
- # constant triggered the callback we cannot fully emulate constant
578
- # name lookup and need to make a trade-off: we are going to assume
579
- # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
580
- # though it might not be. Counterexamples are
581
- #
582
- # class Foo::Bar
583
- # Module.nesting # => [Foo::Bar]
584
- # end
585
- #
586
- # or
587
- #
588
- # module M::N
589
- # module S::T
590
- # Module.nesting # => [S::T, M::N]
591
- # end
592
- # end
593
- #
594
- # for example.
595
- return parent.const_missing(const_name)
596
- rescue NameError => e
597
- raise unless e.missing_name? qualified_name_for(parent, const_name)
598
- end
599
- end
600
-
601
- name_error = uninitialized_constant(qualified_name, const_name, receiver: from_mod)
602
- name_error.set_backtrace(caller.reject { |l| l.start_with? __FILE__ })
603
- raise name_error
604
- end
605
-
606
- # Remove the constants that have been autoloaded, and those that have been
607
- # marked for unloading. Before each constant is removed a callback is sent
608
- # to its class/module if it implements +before_remove_const+.
52
+ # This is a private set that collects all eager load paths during bootstrap.
53
+ # Useful for Zeitwerk integration. The public interface to push custom
54
+ # directories to this collection from applications or engines is
55
+ # config.eager_load_paths.
56
+ singleton_class.attr_accessor :_eager_load_paths
57
+ self._eager_load_paths = Set.new
58
+
59
+ # If reloading is enabled, this private set holds autoloaded classes tracked
60
+ # by the descendants tracker. It is populated by an on_load callback in the
61
+ # main autoloader. Used to clear state.
62
+ singleton_class.attr_accessor :_autoloaded_tracked_classes
63
+ self._autoloaded_tracked_classes = Set.new
64
+
65
+ # If reloading is enabled, this private attribute stores the main autoloader
66
+ # of a Rails application. It is `nil` otherwise.
609
67
  #
610
- # The callback implementation should be restricted to cleaning up caches, etc.
611
- # as the environment will be in an inconsistent state, e.g. other constants
612
- # may have already been unloaded and not accessible.
613
- def remove_unloadable_constants!
614
- log("removing unloadable constants")
615
- autoloaded_constants.each { |const| remove_constant const }
616
- autoloaded_constants.clear
617
- Reference.clear!
618
- explicitly_unloadable_constants.each { |const| remove_constant const }
619
- end
620
-
621
- class ClassCache
622
- def initialize
623
- @store = Concurrent::Map.new
624
- end
68
+ # The public interface for this autoloader is `Rails.autoloaders.main`.
69
+ singleton_class.attr_accessor :autoloader
625
70
 
626
- def empty?
627
- @store.empty?
628
- end
629
-
630
- def key?(key)
631
- @store.key?(key)
632
- end
633
-
634
- def get(key)
635
- key = key.name if key.respond_to?(:name)
636
- @store[key] ||= Inflector.constantize(key)
637
- end
638
- alias :[] :get
639
-
640
- def safe_get(key)
641
- key = key.name if key.respond_to?(:name)
642
- @store[key] ||= Inflector.safe_constantize(key)
643
- end
644
-
645
- def store(klass)
646
- return self unless klass.respond_to?(:name)
647
- raise(ArgumentError, "anonymous classes cannot be cached") if klass.name.empty?
648
- @store[klass.name] = klass
649
- self
650
- end
651
-
652
- def clear!
653
- @store.clear
654
- end
655
- end
656
-
657
- Reference = ClassCache.new
658
-
659
- # Store a reference to a class +klass+.
660
- def reference(klass)
661
- Reference.store klass
662
- end
663
-
664
- # Get the reference for class named +name+.
665
- # Raises an exception if referenced class does not exist.
666
- def constantize(name)
667
- Reference.get(name)
668
- end
669
-
670
- # Get the reference for class named +name+ if one exists.
671
- # Otherwise returns +nil+.
672
- def safe_constantize(name)
673
- Reference.safe_get(name)
674
- end
675
-
676
- # Determine if the given constant has been automatically loaded.
677
- def autoloaded?(desc)
678
- return false if desc.is_a?(Module) && real_mod_name(desc).nil?
679
- name = to_constant_name desc
680
- return false unless qualified_const_defined?(name)
681
- autoloaded_constants.include?(name)
682
- end
683
-
684
- # Will the provided constant descriptor be unloaded?
685
- def will_unload?(const_desc)
686
- autoloaded?(const_desc) ||
687
- explicitly_unloadable_constants.include?(to_constant_name(const_desc))
688
- end
689
-
690
- # Mark the provided constant name for unloading. This constant will be
691
- # unloaded on each request, not just the next one.
692
- def mark_for_unload(const_desc)
693
- name = to_constant_name const_desc
694
- if explicitly_unloadable_constants.include? name
695
- false
696
- else
697
- explicitly_unloadable_constants << name
698
- true
699
- end
700
- end
701
-
702
- # Run the provided block and detect the new constants that were loaded during
703
- # its execution. Constants may only be regarded as 'new' once -- so if the
704
- # block calls +new_constants_in+ again, then the constants defined within the
705
- # inner call will not be reported in this one.
71
+ # Private method that reloads constants autoloaded by the main autoloader.
706
72
  #
707
- # If the provided block does not run to completion, and instead raises an
708
- # exception, any new constants are regarded as being only partially defined
709
- # and will be removed immediately.
710
- def new_constants_in(*descs)
711
- constant_watch_stack.watch_namespaces(descs)
712
- success = false
713
-
714
- begin
715
- yield # Now yield to the code that is to define new constants.
716
- success = true
717
- ensure
718
- new_constants = constant_watch_stack.new_constants
719
-
720
- return new_constants if success
721
-
722
- # Remove partially loaded constants.
723
- new_constants.each { |c| remove_constant(c) }
73
+ # Rails.application.reloader.reload! is the public interface for application
74
+ # reload. That involves more things, like deleting unloaded classes from the
75
+ # internal state of the descendants tracker, or reloading routes.
76
+ def self.clear
77
+ unload_interlock do
78
+ _autoloaded_tracked_classes.clear
79
+ autoloader.reload
724
80
  end
725
81
  end
726
82
 
727
- # Convert the provided const desc to a qualified constant name (as a string).
728
- # A module, class, symbol, or string may be provided.
729
- def to_constant_name(desc) #:nodoc:
730
- case desc
731
- when String then desc.delete_prefix("::")
732
- when Symbol then desc.to_s
733
- when Module
734
- real_mod_name(desc) ||
735
- raise(ArgumentError, "Anonymous modules have no name to be referenced by")
736
- else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
737
- end
738
- end
739
-
740
- def remove_constant(const) #:nodoc:
741
- # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
742
- normalized = const.to_s.delete_prefix("::")
743
- normalized.sub!(/\A(Object::)+/, "")
744
-
745
- constants = normalized.split("::")
746
- to_remove = constants.pop
747
-
748
- # Remove the file path from the loaded list.
749
- file_path = search_for_file(const.underscore)
750
- if file_path
751
- expanded = File.expand_path(file_path)
752
- expanded.delete_suffix!(".rb")
753
- loaded.delete(expanded)
754
- end
755
-
756
- if constants.empty?
757
- parent = Object
758
- else
759
- # This method is robust to non-reachable constants.
760
- #
761
- # Non-reachable constants may be passed if some of the parents were
762
- # autoloaded and already removed. It is easier to do a sanity check
763
- # here than require the caller to be clever. We check the parent
764
- # rather than the very const argument because we do not want to
765
- # trigger Kernel#autoloads, see the comment below.
766
- parent_name = constants.join("::")
767
- return unless qualified_const_defined?(parent_name)
768
- parent = constantize(parent_name)
769
- end
770
-
771
- # In an autoloaded user.rb like this
772
- #
773
- # autoload :Foo, 'foo'
774
- #
775
- # class User < ActiveRecord::Base
776
- # end
777
- #
778
- # we correctly register "Foo" as being autoloaded. But if the app does
779
- # not use the "Foo" constant we need to be careful not to trigger
780
- # loading "foo.rb" ourselves. While #const_defined? and #const_get? do
781
- # require the file, #autoload? and #remove_const don't.
782
- #
783
- # We are going to remove the constant nonetheless ---which exists as
784
- # far as Ruby is concerned--- because if the user removes the macro
785
- # call from a class or module that were not autoloaded, as in the
786
- # example above with Object, accessing to that constant must err.
787
- unless parent.autoload?(to_remove)
788
- begin
789
- constantized = parent.const_get(to_remove, false)
790
- rescue NameError
791
- # The constant is no longer reachable, just skip it.
792
- return
793
- else
794
- constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
795
- end
796
- end
797
-
798
- begin
799
- parent.instance_eval { remove_const to_remove }
800
- rescue NameError
801
- # The constant is no longer reachable, just skip it.
83
+ # Private method used by require_dependency.
84
+ def self.search_for_file(relpath)
85
+ relpath += ".rb" unless relpath.end_with?(".rb")
86
+ autoload_paths.each do |autoload_path|
87
+ abspath = File.join(autoload_path, relpath)
88
+ return abspath if File.file?(abspath)
802
89
  end
90
+ nil
803
91
  end
804
92
 
805
- def log(message)
806
- logger.debug("autoloading: #{message}") if logger && verbose
93
+ # Private method that helps configuring the autoloaders.
94
+ def self.eager_load?(path)
95
+ _eager_load_paths.member?(path)
807
96
  end
808
-
809
- private
810
- if RUBY_VERSION < "2.6"
811
- def uninitialized_constant(qualified_name, const_name, receiver:)
812
- NameError.new("uninitialized constant #{qualified_name}", const_name)
813
- end
814
- else
815
- def uninitialized_constant(qualified_name, const_name, receiver:)
816
- NameError.new("uninitialized constant #{qualified_name}", const_name, receiver: receiver)
817
- end
818
- end
819
-
820
- # Returns the original name of a class or module even if `name` has been
821
- # overridden.
822
- def real_mod_name(mod)
823
- UNBOUND_METHOD_MODULE_NAME.bind(mod).call
824
- end
825
97
  end
826
98
  end
827
-
828
- ActiveSupport::Dependencies.hook!