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
@@ -1,804 +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.replace(message % file_name)
371
- load_error.copy_blame!(load_error)
372
- end
373
- raise
374
- end
375
-
376
- def clear
377
- Dependencies.unload_interlock do
378
- loaded.clear
379
- loading.clear
380
- remove_unloadable_constants!
381
- end
382
- end
383
-
384
- def require_or_load(file_name, const_path = nil)
385
- file_name = file_name.chomp(".rb")
386
- expanded = File.expand_path(file_name)
387
- return if loaded.include?(expanded)
388
-
389
- Dependencies.load_interlock do
390
- # Maybe it got loaded while we were waiting for our lock:
391
- 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 = []
392
45
 
393
- # Record that we've seen this file *before* loading it to avoid an
394
- # infinite loop with mutual dependencies.
395
- loaded << expanded
396
- 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 = []
397
51
 
398
- begin
399
- if load?
400
- # Enable warnings if this file has not been loaded before and
401
- # warnings_on_first_load is set.
402
- load_args = ["#{file_name}.rb"]
403
- load_args << const_path unless const_path.nil?
404
-
405
- if !warnings_on_first_load || history.include?(expanded)
406
- result = load_file(*load_args)
407
- else
408
- enable_warnings { result = load_file(*load_args) }
409
- end
410
- else
411
- result = require file_name
412
- end
413
- rescue Exception
414
- loaded.delete expanded
415
- raise
416
- ensure
417
- loading.pop
418
- end
419
-
420
- # Record history *after* loading so first load gets warnings.
421
- history << expanded
422
- result
423
- end
424
- end
425
-
426
- # Is the provided constant path defined?
427
- def qualified_const_defined?(path)
428
- Object.const_defined?(path, false)
429
- end
430
-
431
- # Given +path+, a filesystem path to a ruby file, return an array of
432
- # constant paths which would cause Dependencies to attempt to load this
433
- # file.
434
- def loadable_constants_for_path(path, bases = autoload_paths)
435
- path = path.chomp(".rb")
436
- expanded_path = File.expand_path(path)
437
- paths = []
438
-
439
- bases.each do |root|
440
- expanded_root = File.expand_path(root)
441
- next unless expanded_path.start_with?(expanded_root)
442
-
443
- root_size = expanded_root.size
444
- next if expanded_path[root_size] != ?/
445
-
446
- nesting = expanded_path[(root_size + 1)..-1]
447
- paths << nesting.camelize unless nesting.blank?
448
- end
449
-
450
- paths.uniq!
451
- paths
452
- end
453
-
454
- # Search for a file in autoload_paths matching the provided suffix.
455
- def search_for_file(path_suffix)
456
- path_suffix += ".rb" unless path_suffix.ends_with?(".rb")
457
-
458
- autoload_paths.each do |root|
459
- path = File.join(root, path_suffix)
460
- return path if File.file? path
461
- end
462
- nil # Gee, I sure wish we had first_match ;-)
463
- end
464
-
465
- # Does the provided path_suffix correspond to an autoloadable module?
466
- # Instead of returning a boolean, the autoload base for this module is
467
- # returned.
468
- def autoloadable_module?(path_suffix)
469
- autoload_paths.each do |load_path|
470
- return load_path if File.directory? File.join(load_path, path_suffix)
471
- end
472
- nil
473
- end
474
-
475
- def load_once_path?(path)
476
- # to_s works around a ruby issue where String#starts_with?(Pathname)
477
- # will raise a TypeError: no implicit conversion of Pathname into String
478
- autoload_once_paths.any? { |base| path.starts_with? base.to_s }
479
- end
480
-
481
- # Attempt to autoload the provided module name by searching for a directory
482
- # matching the expected path suffix. If found, the module is created and
483
- # assigned to +into+'s constants with the name +const_name+. Provided that
484
- # the directory was loaded from a reloadable base path, it is added to the
485
- # set of constants that are to be unloaded.
486
- def autoload_module!(into, const_name, qualified_name, path_suffix)
487
- return nil unless base_path = autoloadable_module?(path_suffix)
488
- mod = Module.new
489
- into.const_set const_name, mod
490
- log("constant #{qualified_name} autoloaded (module autovivified from #{File.join(base_path, path_suffix)})")
491
- autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
492
- autoloaded_constants.uniq!
493
- mod
494
- end
495
-
496
- # Load the file at the provided path. +const_paths+ is a set of qualified
497
- # constant names. When loading the file, Dependencies will watch for the
498
- # addition of these constants. Each that is defined will be marked as
499
- # 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.
500
67
  #
501
- # If the second parameter is left off, then Dependencies will construct a
502
- # set of names that the file at +path+ may define. See
503
- # +loadable_constants_for_path+ for more details.
504
- def load_file(path, const_paths = loadable_constants_for_path(path))
505
- const_paths = [const_paths].compact unless const_paths.is_a? Array
506
- parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object }
507
-
508
- result = nil
509
- newly_defined_paths = new_constants_in(*parent_paths) do
510
- result = Kernel.load path
511
- end
512
-
513
- autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
514
- autoloaded_constants.uniq!
515
- result
516
- end
517
-
518
- # Returns the constant path for the provided parent and constant name.
519
- def qualified_name_for(mod, name)
520
- mod_name = to_constant_name mod
521
- mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
522
- end
523
-
524
- # Load the constant named +const_name+ which is missing from +from_mod+. If
525
- # it is not possible to load the constant into from_mod, try its parent
526
- # module using +const_missing+.
527
- def load_missing_constant(from_mod, const_name)
528
- unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
529
- raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
530
- end
531
-
532
- qualified_name = qualified_name_for(from_mod, const_name)
533
- path_suffix = qualified_name.underscore
68
+ # The public interface for this autoloader is `Rails.autoloaders.main`.
69
+ singleton_class.attr_accessor :autoloader
534
70
 
535
- file_path = search_for_file(path_suffix)
536
-
537
- if file_path
538
- expanded = File.expand_path(file_path)
539
- expanded.sub!(/\.rb\z/, "")
540
-
541
- if loading.include?(expanded)
542
- raise "Circular dependency detected while autoloading constant #{qualified_name}"
543
- else
544
- require_or_load(expanded, qualified_name)
545
-
546
- if from_mod.const_defined?(const_name, false)
547
- log("constant #{qualified_name} autoloaded from #{expanded}.rb")
548
- return from_mod.const_get(const_name)
549
- else
550
- raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it"
551
- end
552
- end
553
- elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
554
- return mod
555
- elsif (parent = from_mod.module_parent) && parent != from_mod &&
556
- ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
557
- # If our parents do not have a constant named +const_name+ then we are free
558
- # to attempt to load upwards. If they do have such a constant, then this
559
- # const_missing must be due to from_mod::const_name, which should not
560
- # return constants from from_mod's parents.
561
- begin
562
- # Since Ruby does not pass the nesting at the point the unknown
563
- # constant triggered the callback we cannot fully emulate constant
564
- # name lookup and need to make a trade-off: we are going to assume
565
- # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
566
- # though it might not be. Counterexamples are
567
- #
568
- # class Foo::Bar
569
- # Module.nesting # => [Foo::Bar]
570
- # end
571
- #
572
- # or
573
- #
574
- # module M::N
575
- # module S::T
576
- # Module.nesting # => [S::T, M::N]
577
- # end
578
- # end
579
- #
580
- # for example.
581
- return parent.const_missing(const_name)
582
- rescue NameError => e
583
- raise unless e.missing_name? qualified_name_for(parent, const_name)
584
- end
585
- end
586
-
587
- name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
588
- name_error.set_backtrace(caller.reject { |l| l.starts_with? __FILE__ })
589
- raise name_error
590
- end
591
-
592
- # Remove the constants that have been autoloaded, and those that have been
593
- # marked for unloading. Before each constant is removed a callback is sent
594
- # to its class/module if it implements +before_remove_const+.
71
+ # Private method that reloads constants autoloaded by the main autoloader.
595
72
  #
596
- # The callback implementation should be restricted to cleaning up caches, etc.
597
- # as the environment will be in an inconsistent state, e.g. other constants
598
- # may have already been unloaded and not accessible.
599
- def remove_unloadable_constants!
600
- log("removing unloadable constants")
601
- autoloaded_constants.each { |const| remove_constant const }
602
- autoloaded_constants.clear
603
- Reference.clear!
604
- explicitly_unloadable_constants.each { |const| remove_constant const }
605
- end
606
-
607
- class ClassCache
608
- def initialize
609
- @store = Concurrent::Map.new
610
- end
611
-
612
- def empty?
613
- @store.empty?
614
- end
615
-
616
- def key?(key)
617
- @store.key?(key)
618
- end
619
-
620
- def get(key)
621
- key = key.name if key.respond_to?(:name)
622
- @store[key] ||= Inflector.constantize(key)
623
- end
624
- alias :[] :get
625
-
626
- def safe_get(key)
627
- key = key.name if key.respond_to?(:name)
628
- @store[key] ||= Inflector.safe_constantize(key)
629
- end
630
-
631
- def store(klass)
632
- return self unless klass.respond_to?(:name)
633
- raise(ArgumentError, "anonymous classes cannot be cached") if klass.name.empty?
634
- @store[klass.name] = klass
635
- self
636
- end
637
-
638
- def clear!
639
- @store.clear
640
- end
641
- end
642
-
643
- Reference = ClassCache.new
644
-
645
- # Store a reference to a class +klass+.
646
- def reference(klass)
647
- Reference.store klass
648
- end
649
-
650
- # Get the reference for class named +name+.
651
- # Raises an exception if referenced class does not exist.
652
- def constantize(name)
653
- Reference.get(name)
654
- end
655
-
656
- # Get the reference for class named +name+ if one exists.
657
- # Otherwise returns +nil+.
658
- def safe_constantize(name)
659
- Reference.safe_get(name)
660
- end
661
-
662
- # Determine if the given constant has been automatically loaded.
663
- def autoloaded?(desc)
664
- return false if desc.is_a?(Module) && real_mod_name(desc).nil?
665
- name = to_constant_name desc
666
- return false unless qualified_const_defined?(name)
667
- autoloaded_constants.include?(name)
668
- end
669
-
670
- # Will the provided constant descriptor be unloaded?
671
- def will_unload?(const_desc)
672
- autoloaded?(const_desc) ||
673
- explicitly_unloadable_constants.include?(to_constant_name(const_desc))
674
- end
675
-
676
- # Mark the provided constant name for unloading. This constant will be
677
- # unloaded on each request, not just the next one.
678
- def mark_for_unload(const_desc)
679
- name = to_constant_name const_desc
680
- if explicitly_unloadable_constants.include? name
681
- false
682
- else
683
- explicitly_unloadable_constants << name
684
- true
685
- end
686
- end
687
-
688
- # Run the provided block and detect the new constants that were loaded during
689
- # its execution. Constants may only be regarded as 'new' once -- so if the
690
- # block calls +new_constants_in+ again, then the constants defined within the
691
- # inner call will not be reported in this one.
692
- #
693
- # If the provided block does not run to completion, and instead raises an
694
- # exception, any new constants are regarded as being only partially defined
695
- # and will be removed immediately.
696
- def new_constants_in(*descs)
697
- constant_watch_stack.watch_namespaces(descs)
698
- success = false
699
-
700
- begin
701
- yield # Now yield to the code that is to define new constants.
702
- success = true
703
- ensure
704
- new_constants = constant_watch_stack.new_constants
705
-
706
- return new_constants if success
707
-
708
- # Remove partially loaded constants.
709
- 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
710
80
  end
711
81
  end
712
82
 
713
- # Convert the provided const desc to a qualified constant name (as a string).
714
- # A module, class, symbol, or string may be provided.
715
- def to_constant_name(desc) #:nodoc:
716
- case desc
717
- when String then desc.sub(/^::/, "")
718
- when Symbol then desc.to_s
719
- when Module
720
- real_mod_name(desc) ||
721
- raise(ArgumentError, "Anonymous modules have no name to be referenced by")
722
- else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
723
- end
724
- end
725
-
726
- def remove_constant(const) #:nodoc:
727
- # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
728
- normalized = const.to_s.sub(/\A::/, "")
729
- normalized.sub!(/\A(Object::)+/, "")
730
-
731
- constants = normalized.split("::")
732
- to_remove = constants.pop
733
-
734
- # Remove the file path from the loaded list.
735
- file_path = search_for_file(const.underscore)
736
- if file_path
737
- expanded = File.expand_path(file_path)
738
- expanded.sub!(/\.rb\z/, "")
739
- loaded.delete(expanded)
740
- end
741
-
742
- if constants.empty?
743
- parent = Object
744
- else
745
- # This method is robust to non-reachable constants.
746
- #
747
- # Non-reachable constants may be passed if some of the parents were
748
- # autoloaded and already removed. It is easier to do a sanity check
749
- # here than require the caller to be clever. We check the parent
750
- # rather than the very const argument because we do not want to
751
- # trigger Kernel#autoloads, see the comment below.
752
- parent_name = constants.join("::")
753
- return unless qualified_const_defined?(parent_name)
754
- parent = constantize(parent_name)
755
- end
756
-
757
- # In an autoloaded user.rb like this
758
- #
759
- # autoload :Foo, 'foo'
760
- #
761
- # class User < ActiveRecord::Base
762
- # end
763
- #
764
- # we correctly register "Foo" as being autoloaded. But if the app does
765
- # not use the "Foo" constant we need to be careful not to trigger
766
- # loading "foo.rb" ourselves. While #const_defined? and #const_get? do
767
- # require the file, #autoload? and #remove_const don't.
768
- #
769
- # We are going to remove the constant nonetheless ---which exists as
770
- # far as Ruby is concerned--- because if the user removes the macro
771
- # call from a class or module that were not autoloaded, as in the
772
- # example above with Object, accessing to that constant must err.
773
- unless parent.autoload?(to_remove)
774
- begin
775
- constantized = parent.const_get(to_remove, false)
776
- rescue NameError
777
- # The constant is no longer reachable, just skip it.
778
- return
779
- else
780
- constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
781
- end
782
- end
783
-
784
- begin
785
- parent.instance_eval { remove_const to_remove }
786
- rescue NameError
787
- # 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)
788
89
  end
90
+ nil
789
91
  end
790
92
 
791
- def log(message)
792
- 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)
793
96
  end
794
-
795
- private
796
- # Returns the original name of a class or module even if `name` has been
797
- # overridden.
798
- def real_mod_name(mod)
799
- UNBOUND_METHOD_MODULE_NAME.bind(mod).call
800
- end
801
97
  end
802
98
  end
803
-
804
- ActiveSupport::Dependencies.hook!