activesupport 5.2.0 → 6.1.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 (190) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +362 -333
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -3
  5. data/lib/active_support/actionable_error.rb +48 -0
  6. data/lib/active_support/array_inquirer.rb +4 -2
  7. data/lib/active_support/backtrace_cleaner.rb +29 -3
  8. data/lib/active_support/benchmarkable.rb +1 -1
  9. data/lib/active_support/cache/file_store.rb +33 -33
  10. data/lib/active_support/cache/mem_cache_store.rb +31 -29
  11. data/lib/active_support/cache/memory_store.rb +59 -33
  12. data/lib/active_support/cache/null_store.rb +8 -3
  13. data/lib/active_support/cache/redis_cache_store.rb +84 -45
  14. data/lib/active_support/cache/strategy/local_cache.rb +41 -26
  15. data/lib/active_support/cache.rb +174 -113
  16. data/lib/active_support/callbacks.rb +81 -64
  17. data/lib/active_support/concern.rb +76 -5
  18. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  19. data/lib/active_support/concurrency/share_lock.rb +0 -1
  20. data/lib/active_support/configurable.rb +10 -14
  21. data/lib/active_support/configuration_file.rb +46 -0
  22. data/lib/active_support/core_ext/array/access.rb +18 -6
  23. data/lib/active_support/core_ext/array/conversions.rb +5 -5
  24. data/lib/active_support/core_ext/array/extract.rb +21 -0
  25. data/lib/active_support/core_ext/array.rb +1 -1
  26. data/lib/active_support/core_ext/benchmark.rb +2 -2
  27. data/lib/active_support/core_ext/class/attribute.rb +32 -47
  28. data/lib/active_support/core_ext/class/subclasses.rb +17 -38
  29. data/lib/active_support/core_ext/date/calculations.rb +6 -5
  30. data/lib/active_support/core_ext/date/conversions.rb +2 -1
  31. data/lib/active_support/core_ext/date_and_time/calculations.rb +37 -47
  32. data/lib/active_support/core_ext/date_and_time/compatibility.rb +15 -0
  33. data/lib/active_support/core_ext/date_and_time/zones.rb +0 -1
  34. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  35. data/lib/active_support/core_ext/date_time/conversions.rb +0 -1
  36. data/lib/active_support/core_ext/digest.rb +3 -0
  37. data/lib/active_support/core_ext/enumerable.rb +171 -70
  38. data/lib/active_support/core_ext/file/atomic.rb +1 -1
  39. data/lib/active_support/core_ext/hash/conversions.rb +3 -3
  40. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  41. data/lib/active_support/core_ext/hash/except.rb +2 -2
  42. data/lib/active_support/core_ext/hash/keys.rb +1 -30
  43. data/lib/active_support/core_ext/hash/slice.rb +6 -27
  44. data/lib/active_support/core_ext/hash.rb +1 -2
  45. data/lib/active_support/core_ext/integer/multiple.rb +1 -1
  46. data/lib/active_support/core_ext/kernel.rb +0 -1
  47. data/lib/active_support/core_ext/load_error.rb +1 -1
  48. data/lib/active_support/core_ext/marshal.rb +2 -0
  49. data/lib/active_support/core_ext/module/attr_internal.rb +2 -2
  50. data/lib/active_support/core_ext/module/attribute_accessors.rb +30 -39
  51. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +17 -19
  52. data/lib/active_support/core_ext/module/concerning.rb +8 -2
  53. data/lib/active_support/core_ext/module/delegation.rb +76 -33
  54. data/lib/active_support/core_ext/module/introspection.rb +16 -15
  55. data/lib/active_support/core_ext/module/redefine_method.rb +8 -17
  56. data/lib/active_support/core_ext/module.rb +0 -1
  57. data/lib/active_support/core_ext/name_error.rb +29 -2
  58. data/lib/active_support/core_ext/numeric/conversions.rb +129 -129
  59. data/lib/active_support/core_ext/numeric.rb +0 -1
  60. data/lib/active_support/core_ext/object/blank.rb +1 -2
  61. data/lib/active_support/core_ext/object/deep_dup.rb +1 -1
  62. data/lib/active_support/core_ext/object/duplicable.rb +7 -114
  63. data/lib/active_support/core_ext/object/json.rb +7 -2
  64. data/lib/active_support/core_ext/object/to_query.rb +5 -2
  65. data/lib/active_support/core_ext/object/try.rb +17 -7
  66. data/lib/active_support/core_ext/object/with_options.rb +1 -1
  67. data/lib/active_support/core_ext/range/compare_range.rb +82 -0
  68. data/lib/active_support/core_ext/range/conversions.rb +31 -29
  69. data/lib/active_support/core_ext/range/each.rb +0 -1
  70. data/lib/active_support/core_ext/range/include_time_with_zone.rb +8 -3
  71. data/lib/active_support/core_ext/range.rb +1 -1
  72. data/lib/active_support/core_ext/regexp.rb +8 -5
  73. data/lib/active_support/core_ext/securerandom.rb +23 -3
  74. data/lib/active_support/core_ext/string/access.rb +5 -16
  75. data/lib/active_support/core_ext/string/conversions.rb +1 -0
  76. data/lib/active_support/core_ext/string/filters.rb +42 -1
  77. data/lib/active_support/core_ext/string/inflections.rb +45 -6
  78. data/lib/active_support/core_ext/string/inquiry.rb +1 -0
  79. data/lib/active_support/core_ext/string/multibyte.rb +6 -5
  80. data/lib/active_support/core_ext/string/output_safety.rb +69 -12
  81. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -2
  82. data/lib/active_support/core_ext/string/strip.rb +3 -1
  83. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +14 -0
  84. data/lib/active_support/core_ext/symbol.rb +3 -0
  85. data/lib/active_support/core_ext/time/calculations.rb +50 -3
  86. data/lib/active_support/core_ext/time/conversions.rb +1 -0
  87. data/lib/active_support/core_ext/uri.rb +7 -5
  88. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  89. data/lib/active_support/current_attributes.rb +15 -2
  90. data/lib/active_support/dependencies/zeitwerk_integration.rb +117 -0
  91. data/lib/active_support/dependencies.rb +118 -35
  92. data/lib/active_support/deprecation/behaviors.rb +20 -3
  93. data/lib/active_support/deprecation/disallowed.rb +56 -0
  94. data/lib/active_support/deprecation/instance_delegator.rb +0 -1
  95. data/lib/active_support/deprecation/method_wrappers.rb +21 -13
  96. data/lib/active_support/deprecation/proxy_wrappers.rb +29 -6
  97. data/lib/active_support/deprecation/reporting.rb +51 -8
  98. data/lib/active_support/deprecation.rb +6 -1
  99. data/lib/active_support/descendants_tracker.rb +59 -9
  100. data/lib/active_support/duration/iso8601_parser.rb +2 -4
  101. data/lib/active_support/duration/iso8601_serializer.rb +18 -14
  102. data/lib/active_support/duration.rb +90 -38
  103. data/lib/active_support/encrypted_configuration.rb +1 -5
  104. data/lib/active_support/encrypted_file.rb +23 -5
  105. data/lib/active_support/environment_inquirer.rb +20 -0
  106. data/lib/active_support/evented_file_update_checker.rb +82 -117
  107. data/lib/active_support/execution_wrapper.rb +1 -0
  108. data/lib/active_support/file_update_checker.rb +0 -1
  109. data/lib/active_support/fork_tracker.rb +62 -0
  110. data/lib/active_support/gem_version.rb +2 -2
  111. data/lib/active_support/hash_with_indifferent_access.rb +78 -41
  112. data/lib/active_support/i18n.rb +1 -0
  113. data/lib/active_support/i18n_railtie.rb +16 -5
  114. data/lib/active_support/inflector/inflections.rb +2 -7
  115. data/lib/active_support/inflector/methods.rb +50 -57
  116. data/lib/active_support/inflector/transliterate.rb +47 -18
  117. data/lib/active_support/json/decoding.rb +25 -26
  118. data/lib/active_support/json/encoding.rb +11 -3
  119. data/lib/active_support/key_generator.rb +1 -33
  120. data/lib/active_support/lazy_load_hooks.rb +5 -2
  121. data/lib/active_support/locale/en.rb +33 -0
  122. data/lib/active_support/locale/en.yml +7 -3
  123. data/lib/active_support/log_subscriber.rb +39 -9
  124. data/lib/active_support/logger.rb +2 -17
  125. data/lib/active_support/logger_silence.rb +11 -19
  126. data/lib/active_support/logger_thread_safe_level.rb +52 -7
  127. data/lib/active_support/message_encryptor.rb +8 -13
  128. data/lib/active_support/message_verifier.rb +10 -10
  129. data/lib/active_support/messages/metadata.rb +11 -2
  130. data/lib/active_support/messages/rotation_configuration.rb +2 -1
  131. data/lib/active_support/messages/rotator.rb +10 -9
  132. data/lib/active_support/multibyte/chars.rb +10 -68
  133. data/lib/active_support/multibyte/unicode.rb +15 -327
  134. data/lib/active_support/notifications/fanout.rb +116 -16
  135. data/lib/active_support/notifications/instrumenter.rb +71 -9
  136. data/lib/active_support/notifications.rb +72 -8
  137. data/lib/active_support/number_helper/number_converter.rb +5 -6
  138. data/lib/active_support/number_helper/number_to_currency_converter.rb +4 -9
  139. data/lib/active_support/number_helper/number_to_delimited_converter.rb +3 -2
  140. data/lib/active_support/number_helper/number_to_human_converter.rb +4 -3
  141. data/lib/active_support/number_helper/number_to_human_size_converter.rb +4 -3
  142. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  143. data/lib/active_support/number_helper/number_to_phone_converter.rb +2 -1
  144. data/lib/active_support/number_helper/number_to_rounded_converter.rb +8 -7
  145. data/lib/active_support/number_helper/rounding_helper.rb +12 -28
  146. data/lib/active_support/number_helper.rb +38 -12
  147. data/lib/active_support/option_merger.rb +22 -3
  148. data/lib/active_support/ordered_hash.rb +1 -1
  149. data/lib/active_support/ordered_options.rb +13 -3
  150. data/lib/active_support/parameter_filter.rb +133 -0
  151. data/lib/active_support/per_thread_registry.rb +1 -1
  152. data/lib/active_support/rails.rb +1 -10
  153. data/lib/active_support/railtie.rb +23 -1
  154. data/lib/active_support/reloader.rb +4 -5
  155. data/lib/active_support/secure_compare_rotator.rb +51 -0
  156. data/lib/active_support/security_utils.rb +19 -12
  157. data/lib/active_support/string_inquirer.rb +4 -3
  158. data/lib/active_support/subscriber.rb +72 -24
  159. data/lib/active_support/tagged_logging.rb +42 -8
  160. data/lib/active_support/test_case.rb +92 -1
  161. data/lib/active_support/testing/assertions.rb +30 -9
  162. data/lib/active_support/testing/deprecation.rb +0 -1
  163. data/lib/active_support/testing/file_fixtures.rb +2 -0
  164. data/lib/active_support/testing/isolation.rb +2 -2
  165. data/lib/active_support/testing/method_call_assertions.rb +28 -1
  166. data/lib/active_support/testing/parallelization/server.rb +78 -0
  167. data/lib/active_support/testing/parallelization/worker.rb +100 -0
  168. data/lib/active_support/testing/parallelization.rb +51 -0
  169. data/lib/active_support/testing/setup_and_teardown.rb +5 -9
  170. data/lib/active_support/testing/stream.rb +1 -2
  171. data/lib/active_support/testing/time_helpers.rb +47 -12
  172. data/lib/active_support/time_with_zone.rb +81 -47
  173. data/lib/active_support/values/time_zone.rb +34 -18
  174. data/lib/active_support/xml_mini/jdom.rb +2 -3
  175. data/lib/active_support/xml_mini/libxml.rb +2 -2
  176. data/lib/active_support/xml_mini/libxmlsax.rb +4 -4
  177. data/lib/active_support/xml_mini/nokogiri.rb +2 -2
  178. data/lib/active_support/xml_mini/nokogirisax.rb +3 -3
  179. data/lib/active_support/xml_mini/rexml.rb +10 -3
  180. data/lib/active_support/xml_mini.rb +2 -10
  181. data/lib/active_support.rb +14 -1
  182. metadata +57 -30
  183. data/lib/active_support/core_ext/array/prepend_and_append.rb +0 -9
  184. data/lib/active_support/core_ext/hash/compact.rb +0 -29
  185. data/lib/active_support/core_ext/hash/transform_values.rb +0 -32
  186. data/lib/active_support/core_ext/kernel/agnostics.rb +0 -13
  187. data/lib/active_support/core_ext/module/reachable.rb +0 -11
  188. data/lib/active_support/core_ext/numeric/inquiry.rb +0 -28
  189. data/lib/active_support/core_ext/range/include_range.rb +0 -25
  190. data/lib/active_support/values/unicode_tables.dat +0 -0
@@ -6,6 +6,7 @@ require "active_support/time_with_zone"
6
6
  require "active_support/core_ext/time/zones"
7
7
  require "active_support/core_ext/date_and_time/calculations"
8
8
  require "active_support/core_ext/date/calculations"
9
+ require "active_support/core_ext/module/remove_method"
9
10
 
10
11
  class Time
11
12
  include DateAndTime::Calculations
@@ -47,7 +48,9 @@ class Time
47
48
  # Time.at can be called with a time or numerical value
48
49
  time_or_number = args.first
49
50
 
50
- if time_or_number.is_a?(ActiveSupport::TimeWithZone) || time_or_number.is_a?(DateTime)
51
+ if time_or_number.is_a?(ActiveSupport::TimeWithZone)
52
+ at_without_coercion(time_or_number.to_r).getlocal
53
+ elsif time_or_number.is_a?(DateTime)
51
54
  at_without_coercion(time_or_number.to_f).getlocal
52
55
  else
53
56
  at_without_coercion(time_or_number)
@@ -105,6 +108,21 @@ class Time
105
108
  subsec
106
109
  end
107
110
 
111
+ unless Time.method_defined?(:floor)
112
+ def floor(precision = 0)
113
+ change(nsec: 0) + subsec.floor(precision)
114
+ end
115
+ end
116
+
117
+ # Restricted Ruby version due to a bug in `Time#ceil`
118
+ # See https://bugs.ruby-lang.org/issues/17025 for more details
119
+ if RUBY_VERSION <= "2.8"
120
+ remove_possible_method :ceil
121
+ def ceil(precision = 0)
122
+ change(nsec: 0) + subsec.ceil(precision)
123
+ end
124
+ end
125
+
108
126
  # Returns a new Time where one or more of the elements have been changed according
109
127
  # to the +options+ parameter. The time options (<tt>:hour</tt>, <tt>:min</tt>,
110
128
  # <tt>:sec</tt>, <tt>:usec</tt>, <tt>:nsec</tt>) reset cascadingly, so if only
@@ -170,8 +188,7 @@ class Time
170
188
  options[:hours] = options.fetch(:hours, 0) + 24 * partial_days
171
189
  end
172
190
 
173
- d = to_date.advance(options)
174
- d = d.gregorian if d.julian?
191
+ d = to_date.gregorian.advance(options)
175
192
  time_advanced_by_date = change(year: d.year, month: d.month, day: d.day)
176
193
  seconds_to_advance = \
177
194
  options.fetch(:seconds, 0) +
@@ -312,4 +329,34 @@ class Time
312
329
  end
313
330
  alias_method :eql_without_coercion, :eql?
314
331
  alias_method :eql?, :eql_with_coercion
332
+
333
+ # Returns a new time the specified number of days ago.
334
+ def prev_day(days = 1)
335
+ advance(days: -days)
336
+ end
337
+
338
+ # Returns a new time the specified number of days in the future.
339
+ def next_day(days = 1)
340
+ advance(days: days)
341
+ end
342
+
343
+ # Returns a new time the specified number of months ago.
344
+ def prev_month(months = 1)
345
+ advance(months: -months)
346
+ end
347
+
348
+ # Returns a new time the specified number of months in the future.
349
+ def next_month(months = 1)
350
+ advance(months: months)
351
+ end
352
+
353
+ # Returns a new time the specified number of years ago.
354
+ def prev_year(years = 1)
355
+ advance(years: -years)
356
+ end
357
+
358
+ # Returns a new time the specified number of years in the future.
359
+ def next_year(years = 1)
360
+ advance(years: years)
361
+ end
315
362
  end
@@ -6,6 +6,7 @@ require "active_support/values/time_zone"
6
6
  class Time
7
7
  DATE_FORMATS = {
8
8
  db: "%Y-%m-%d %H:%M:%S",
9
+ inspect: "%Y-%m-%d %H:%M:%S.%9N %z",
9
10
  number: "%Y%m%d%H%M%S",
10
11
  nsec: "%Y%m%d%H%M%S%9N",
11
12
  usec: "%Y%m%d%H%M%S%6N",
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "uri"
4
- str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
5
- parser = URI::Parser.new
6
4
 
7
- unless str == parser.unescape(parser.escape(str))
5
+ if RUBY_VERSION < "2.6.0"
8
6
  require "active_support/core_ext/module/redefine_method"
9
7
  URI::Parser.class_eval do
10
8
  silence_redefinition_of_method :unescape
@@ -13,7 +11,7 @@ unless str == parser.unescape(parser.escape(str))
13
11
  # YK: My initial experiments say yes, but let's be sure please
14
12
  enc = str.encoding
15
13
  enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
16
- str.gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
14
+ str.dup.force_encoding(Encoding::ASCII_8BIT).gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
17
15
  end
18
16
  end
19
17
  end
@@ -21,7 +19,11 @@ end
21
19
  module URI
22
20
  class << self
23
21
  def parser
24
- @parser ||= URI::Parser.new
22
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
23
+ URI.parser is deprecated and will be removed in Rails 6.2.
24
+ Use `URI::DEFAULT_PARSER` instead.
25
+ MSG
26
+ URI::DEFAULT_PARSER
25
27
  end
26
28
  end
27
29
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport::CurrentAttributes::TestHelper # :nodoc:
4
+ def before_setup
5
+ ActiveSupport::CurrentAttributes.reset_all
6
+ super
7
+ end
8
+
9
+ def after_teardown
10
+ super
11
+ ActiveSupport::CurrentAttributes.reset_all
12
+ end
13
+ end
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/callbacks"
4
+ require "active_support/core_ext/enumerable"
5
+
3
6
  module ActiveSupport
4
7
  # Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
5
8
  # before and after each request. This allows you to keep all the per-request attributes easily
@@ -89,7 +92,7 @@ module ActiveSupport
89
92
  class << self
90
93
  # Returns singleton instance for this class in this thread. If none exists, one is created.
91
94
  def instance
92
- current_instances[name] ||= new
95
+ current_instances[current_instances_key] ||= new
93
96
  end
94
97
 
95
98
  # Declares one or more attributes that will be given both class and instance accessor methods.
@@ -117,10 +120,16 @@ module ActiveSupport
117
120
  end
118
121
  end
119
122
 
123
+ # Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
124
+ def before_reset(&block)
125
+ set_callback :reset, :before, &block
126
+ end
127
+
120
128
  # Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
121
129
  def resets(&block)
122
130
  set_callback :reset, :after, &block
123
131
  end
132
+ alias_method :after_reset, :resets
124
133
 
125
134
  delegate :set, :reset, to: :instance
126
135
 
@@ -142,6 +151,10 @@ module ActiveSupport
142
151
  Thread.current[:current_attributes_instances] ||= {}
143
152
  end
144
153
 
154
+ def current_instances_key
155
+ @current_instances_key ||= name.to_sym
156
+ end
157
+
145
158
  def method_missing(name, *args, &block)
146
159
  # Caches the method definition as a singleton method of the receiver.
147
160
  #
@@ -189,7 +202,7 @@ module ActiveSupport
189
202
  end
190
203
 
191
204
  def compute_attributes(keys)
192
- keys.collect { |key| [ key, public_send(key) ] }.to_h
205
+ keys.index_with { |key| public_send(key) }
193
206
  end
194
207
  end
195
208
  end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "active_support/core_ext/string/inflections"
5
+
6
+ module ActiveSupport
7
+ module Dependencies
8
+ module ZeitwerkIntegration # :nodoc: all
9
+ module Decorations
10
+ def clear
11
+ Dependencies.unload_interlock do
12
+ Rails.autoloaders.main.reload
13
+ rescue Zeitwerk::ReloadingDisabledError
14
+ raise "reloading is disabled because config.cache_classes is true"
15
+ end
16
+ end
17
+
18
+ def constantize(cpath)
19
+ ActiveSupport::Inflector.constantize(cpath)
20
+ end
21
+
22
+ def safe_constantize(cpath)
23
+ ActiveSupport::Inflector.safe_constantize(cpath)
24
+ end
25
+
26
+ def autoloaded_constants
27
+ Rails.autoloaders.main.unloadable_cpaths
28
+ end
29
+
30
+ def autoloaded?(object)
31
+ cpath = object.is_a?(Module) ? real_mod_name(object) : object.to_s
32
+ Rails.autoloaders.main.unloadable_cpath?(cpath)
33
+ end
34
+
35
+ def verbose=(verbose)
36
+ l = verbose ? logger || Rails.logger : nil
37
+ Rails.autoloaders.each { |autoloader| autoloader.logger = l }
38
+ end
39
+
40
+ def unhook!
41
+ :no_op
42
+ end
43
+ end
44
+
45
+ module RequireDependency
46
+ def require_dependency(filename)
47
+ filename = filename.to_path if filename.respond_to?(:to_path)
48
+ if abspath = ActiveSupport::Dependencies.search_for_file(filename)
49
+ require abspath
50
+ else
51
+ require filename
52
+ end
53
+ end
54
+ end
55
+
56
+ module Inflector
57
+ # Concurrent::Map is not needed. This is a private class, and overrides
58
+ # must be defined while the application boots.
59
+ @overrides = {}
60
+
61
+ def self.camelize(basename, _abspath)
62
+ @overrides[basename] || basename.camelize
63
+ end
64
+
65
+ def self.inflect(overrides)
66
+ @overrides.merge!(overrides)
67
+ end
68
+ end
69
+
70
+ class << self
71
+ def take_over(enable_reloading:)
72
+ setup_autoloaders(enable_reloading)
73
+ freeze_paths
74
+ decorate_dependencies
75
+ end
76
+
77
+ private
78
+ def setup_autoloaders(enable_reloading)
79
+ Dependencies.autoload_paths.each do |autoload_path|
80
+ # Zeitwerk only accepts existing directories in `push_dir` to
81
+ # prevent misconfigurations.
82
+ next unless File.directory?(autoload_path)
83
+
84
+ autoloader = \
85
+ autoload_once?(autoload_path) ? Rails.autoloaders.once : Rails.autoloaders.main
86
+
87
+ autoloader.push_dir(autoload_path)
88
+ autoloader.do_not_eager_load(autoload_path) unless eager_load?(autoload_path)
89
+ end
90
+
91
+ Rails.autoloaders.main.enable_reloading if enable_reloading
92
+ Rails.autoloaders.each(&:setup)
93
+ end
94
+
95
+ def autoload_once?(autoload_path)
96
+ Dependencies.autoload_once_paths.include?(autoload_path)
97
+ end
98
+
99
+ def eager_load?(autoload_path)
100
+ Dependencies._eager_load_paths.member?(autoload_path)
101
+ end
102
+
103
+ def freeze_paths
104
+ Dependencies.autoload_paths.freeze
105
+ Dependencies.autoload_once_paths.freeze
106
+ Dependencies._eager_load_paths.freeze
107
+ end
108
+
109
+ def decorate_dependencies
110
+ Dependencies.unhook!
111
+ Dependencies.singleton_class.prepend(Decorations)
112
+ Object.prepend(RequireDependency)
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -12,7 +12,6 @@ require "active_support/core_ext/object/blank"
12
12
  require "active_support/core_ext/kernel/reporting"
13
13
  require "active_support/core_ext/load_error"
14
14
  require "active_support/core_ext/name_error"
15
- require "active_support/core_ext/string/starts_ends_with"
16
15
  require "active_support/dependencies/interlock"
17
16
  require "active_support/inflector"
18
17
 
@@ -20,6 +19,9 @@ module ActiveSupport #:nodoc:
20
19
  module Dependencies #:nodoc:
21
20
  extend self
22
21
 
22
+ UNBOUND_METHOD_MODULE_NAME = Module.instance_method(:name)
23
+ private_constant :UNBOUND_METHOD_MODULE_NAME
24
+
23
25
  mattr_accessor :interlock, default: Interlock.new
24
26
 
25
27
  # :doc:
@@ -70,6 +72,11 @@ module ActiveSupport #:nodoc:
70
72
  # only once. All directories in this set must also be present in +autoload_paths+.
71
73
  mattr_accessor :autoload_once_paths, default: []
72
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
+
73
80
  # An array of qualified constant names that have been loaded. Adding a name
74
81
  # to this array will cause it to be unloaded the next time Dependencies are
75
82
  # cleared.
@@ -79,6 +86,12 @@ module ActiveSupport #:nodoc:
79
86
  # to allow arbitrary constants to be marked for unloading.
80
87
  mattr_accessor :explicitly_unloadable_constants, default: []
81
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
+
82
95
  # The WatchStack keeps a stack of the modules being watched as files are
83
96
  # loaded. If a file in the process of being loaded (parent.rb) triggers the
84
97
  # load of another file (child.rb) the stack will ensure that child.rb
@@ -97,6 +110,8 @@ module ActiveSupport #:nodoc:
97
110
  # parent.rb then requires namespace/child.rb, the stack will look like
98
111
  # [[Object], [Namespace]].
99
112
 
113
+ attr_reader :watching
114
+
100
115
  def initialize
101
116
  @watching = []
102
117
  @stack = Hash.new { |h, k| h[k] = [] }
@@ -138,7 +153,7 @@ module ActiveSupport #:nodoc:
138
153
 
139
154
  # Normalize the list of new constants, and add them to the list we will return
140
155
  new_constants.each do |suffix|
141
- constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
156
+ constants << ([namespace, suffix] - ["Object"]).join("::")
142
157
  end
143
158
  end
144
159
  constants
@@ -188,6 +203,11 @@ module ActiveSupport #:nodoc:
188
203
  end
189
204
  end
190
205
 
206
+ def self.include_into(base)
207
+ base.include(self)
208
+ append_features(base)
209
+ end
210
+
191
211
  def const_missing(const_name)
192
212
  from_mod = anonymous? ? guess_for_anonymous(const_name) : self
193
213
  Dependencies.load_missing_constant(from_mod, const_name)
@@ -217,6 +237,21 @@ module ActiveSupport #:nodoc:
217
237
  base.class_eval do
218
238
  define_method(:load, Kernel.instance_method(:load))
219
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)
220
255
  end
221
256
  end
222
257
 
@@ -224,15 +259,26 @@ module ActiveSupport #:nodoc:
224
259
  Dependencies.require_or_load(file_name)
225
260
  end
226
261
 
227
- # Interprets a file using <tt>mechanism</tt> and marks its defined
228
- # constants as autoloaded. <tt>file_name</tt> can be either a string or
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
229
271
  # respond to <tt>to_path</tt>.
230
272
  #
231
- # Use this method in code that absolutely needs a certain constant to be
232
- # defined at that point. A typical use case is to make constant name
233
- # resolution deterministic for constants with the same relative name in
234
- # different namespaces whose evaluation would depend on load order
235
- # otherwise.
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+.
236
282
  def require_dependency(file_name, message = "No such file to load -- %s.rb")
237
283
  file_name = file_name.to_path if file_name.respond_to?(:to_path)
238
284
  unless file_name.is_a?(String)
@@ -242,9 +288,13 @@ module ActiveSupport #:nodoc:
242
288
  Dependencies.depend_on(file_name, message)
243
289
  end
244
290
 
291
+ # :nodoc:
292
+
245
293
  def load_dependency(file)
246
294
  if Dependencies.load? && Dependencies.constant_watch_stack.watching?
247
- Dependencies.new_constants_in(Object) { yield }
295
+ descs = Dependencies.constant_watch_stack.watching.flatten.uniq
296
+
297
+ Dependencies.new_constants_in(*descs) { yield }
248
298
  else
249
299
  yield
250
300
  end
@@ -271,7 +321,6 @@ module ActiveSupport #:nodoc:
271
321
  end
272
322
 
273
323
  private
274
-
275
324
  def load(file, wrap = false)
276
325
  result = false
277
326
  load_dependency(file) { result = super }
@@ -307,9 +356,9 @@ module ActiveSupport #:nodoc:
307
356
  end
308
357
 
309
358
  def hook!
310
- Object.class_eval { include Loadable }
311
- Module.class_eval { include ModuleConstMissing }
312
- Exception.class_eval { include Blamable }
359
+ Loadable.include_into(Object)
360
+ ModuleConstMissing.include_into(Module)
361
+ Exception.include(Blamable)
313
362
  end
314
363
 
315
364
  def unhook!
@@ -326,7 +375,12 @@ module ActiveSupport #:nodoc:
326
375
  require_or_load(path || file_name)
327
376
  rescue LoadError => load_error
328
377
  if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
329
- load_error.message.replace(message % file_name)
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)
330
384
  load_error.copy_blame!(load_error)
331
385
  end
332
386
  raise
@@ -341,7 +395,7 @@ module ActiveSupport #:nodoc:
341
395
  end
342
396
 
343
397
  def require_or_load(file_name, const_path = nil)
344
- file_name = $` if file_name =~ /\.rb\z/
398
+ file_name = file_name.chomp(".rb")
345
399
  expanded = File.expand_path(file_name)
346
400
  return if loaded.include?(expanded)
347
401
 
@@ -391,7 +445,7 @@ module ActiveSupport #:nodoc:
391
445
  # constant paths which would cause Dependencies to attempt to load this
392
446
  # file.
393
447
  def loadable_constants_for_path(path, bases = autoload_paths)
394
- path = $` if path =~ /\.rb\z/
448
+ path = path.chomp(".rb")
395
449
  expanded_path = File.expand_path(path)
396
450
  paths = []
397
451
 
@@ -400,7 +454,7 @@ module ActiveSupport #:nodoc:
400
454
  next unless expanded_path.start_with?(expanded_root)
401
455
 
402
456
  root_size = expanded_root.size
403
- next if expanded_path[root_size] != ?/.freeze
457
+ next if expanded_path[root_size] != ?/
404
458
 
405
459
  nesting = expanded_path[(root_size + 1)..-1]
406
460
  paths << nesting.camelize unless nesting.blank?
@@ -412,7 +466,7 @@ module ActiveSupport #:nodoc:
412
466
 
413
467
  # Search for a file in autoload_paths matching the provided suffix.
414
468
  def search_for_file(path_suffix)
415
- path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
469
+ path_suffix += ".rb" unless path_suffix.end_with?(".rb")
416
470
 
417
471
  autoload_paths.each do |root|
418
472
  path = File.join(root, path_suffix)
@@ -432,9 +486,9 @@ module ActiveSupport #:nodoc:
432
486
  end
433
487
 
434
488
  def load_once_path?(path)
435
- # to_s works around a ruby issue where String#starts_with?(Pathname)
489
+ # to_s works around a ruby issue where String#start_with?(Pathname)
436
490
  # will raise a TypeError: no implicit conversion of Pathname into String
437
- autoload_once_paths.any? { |base| path.starts_with? base.to_s }
491
+ autoload_once_paths.any? { |base| path.start_with?(base.to_s) }
438
492
  end
439
493
 
440
494
  # Attempt to autoload the provided module name by searching for a directory
@@ -446,6 +500,7 @@ module ActiveSupport #:nodoc:
446
500
  return nil unless base_path = autoloadable_module?(path_suffix)
447
501
  mod = Module.new
448
502
  into.const_set const_name, mod
503
+ log("constant #{qualified_name} autoloaded (module autovivified from #{File.join(base_path, path_suffix)})")
449
504
  autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
450
505
  autoloaded_constants.uniq!
451
506
  mod
@@ -483,30 +538,36 @@ module ActiveSupport #:nodoc:
483
538
  # it is not possible to load the constant into from_mod, try its parent
484
539
  # module using +const_missing+.
485
540
  def load_missing_constant(from_mod, const_name)
486
- unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
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)
487
543
  raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
488
544
  end
489
545
 
490
- qualified_name = qualified_name_for from_mod, const_name
546
+ qualified_name = qualified_name_for(from_mod, const_name)
491
547
  path_suffix = qualified_name.underscore
492
548
 
493
549
  file_path = search_for_file(path_suffix)
494
550
 
495
551
  if file_path
496
552
  expanded = File.expand_path(file_path)
497
- expanded.sub!(/\.rb\z/, "".freeze)
553
+ expanded.delete_suffix!(".rb")
498
554
 
499
555
  if loading.include?(expanded)
500
556
  raise "Circular dependency detected while autoloading constant #{qualified_name}"
501
557
  else
502
558
  require_or_load(expanded, qualified_name)
503
- raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
504
- return from_mod.const_get(const_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
505
566
  end
506
567
  elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
507
568
  return mod
508
- elsif (parent = from_mod.parent) && parent != from_mod &&
509
- ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
569
+ elsif (parent = from_mod.module_parent) && parent != from_mod &&
570
+ ! from_mod.module_parents.any? { |p| p.const_defined?(const_name, false) }
510
571
  # If our parents do not have a constant named +const_name+ then we are free
511
572
  # to attempt to load upwards. If they do have such a constant, then this
512
573
  # const_missing must be due to from_mod::const_name, which should not
@@ -537,8 +598,8 @@ module ActiveSupport #:nodoc:
537
598
  end
538
599
  end
539
600
 
540
- name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
541
- name_error.set_backtrace(caller.reject { |l| l.starts_with? __FILE__ })
601
+ name_error = uninitialized_constant(qualified_name, const_name, receiver: from_mod)
602
+ name_error.set_backtrace(caller.reject { |l| l.start_with? __FILE__ })
542
603
  raise name_error
543
604
  end
544
605
 
@@ -550,6 +611,7 @@ module ActiveSupport #:nodoc:
550
611
  # as the environment will be in an inconsistent state, e.g. other constants
551
612
  # may have already been unloaded and not accessible.
552
613
  def remove_unloadable_constants!
614
+ log("removing unloadable constants")
553
615
  autoloaded_constants.each { |const| remove_constant const }
554
616
  autoloaded_constants.clear
555
617
  Reference.clear!
@@ -613,7 +675,7 @@ module ActiveSupport #:nodoc:
613
675
 
614
676
  # Determine if the given constant has been automatically loaded.
615
677
  def autoloaded?(desc)
616
- return false if desc.is_a?(Module) && desc.anonymous?
678
+ return false if desc.is_a?(Module) && real_mod_name(desc).nil?
617
679
  name = to_constant_name desc
618
680
  return false unless qualified_const_defined?(name)
619
681
  autoloaded_constants.include?(name)
@@ -666,10 +728,10 @@ module ActiveSupport #:nodoc:
666
728
  # A module, class, symbol, or string may be provided.
667
729
  def to_constant_name(desc) #:nodoc:
668
730
  case desc
669
- when String then desc.sub(/^::/, "")
731
+ when String then desc.delete_prefix("::")
670
732
  when Symbol then desc.to_s
671
733
  when Module
672
- desc.name ||
734
+ real_mod_name(desc) ||
673
735
  raise(ArgumentError, "Anonymous modules have no name to be referenced by")
674
736
  else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
675
737
  end
@@ -677,7 +739,7 @@ module ActiveSupport #:nodoc:
677
739
 
678
740
  def remove_constant(const) #:nodoc:
679
741
  # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
680
- normalized = const.to_s.sub(/\A::/, "")
742
+ normalized = const.to_s.delete_prefix("::")
681
743
  normalized.sub!(/\A(Object::)+/, "")
682
744
 
683
745
  constants = normalized.split("::")
@@ -687,7 +749,7 @@ module ActiveSupport #:nodoc:
687
749
  file_path = search_for_file(const.underscore)
688
750
  if file_path
689
751
  expanded = File.expand_path(file_path)
690
- expanded.sub!(/\.rb\z/, "")
752
+ expanded.delete_suffix!(".rb")
691
753
  loaded.delete(expanded)
692
754
  end
693
755
 
@@ -739,6 +801,27 @@ module ActiveSupport #:nodoc:
739
801
  # The constant is no longer reachable, just skip it.
740
802
  end
741
803
  end
804
+
805
+ def log(message)
806
+ logger.debug("autoloading: #{message}") if logger && verbose
807
+ 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
742
825
  end
743
826
  end
744
827