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