activesupport 7.1.6 → 8.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (169) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +256 -1133
  3. data/README.rdoc +1 -1
  4. data/lib/active_support/array_inquirer.rb +1 -1
  5. data/lib/active_support/backtrace_cleaner.rb +81 -3
  6. data/lib/active_support/benchmark.rb +21 -0
  7. data/lib/active_support/benchmarkable.rb +3 -2
  8. data/lib/active_support/broadcast_logger.rb +65 -78
  9. data/lib/active_support/cache/file_store.rb +29 -14
  10. data/lib/active_support/cache/mem_cache_store.rb +42 -102
  11. data/lib/active_support/cache/memory_store.rb +11 -6
  12. data/lib/active_support/cache/null_store.rb +2 -2
  13. data/lib/active_support/cache/redis_cache_store.rb +58 -46
  14. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  15. data/lib/active_support/cache/strategy/local_cache.rb +72 -27
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
  17. data/lib/active_support/cache.rb +146 -86
  18. data/lib/active_support/callbacks.rb +102 -126
  19. data/lib/active_support/class_attribute.rb +33 -0
  20. data/lib/active_support/code_generator.rb +9 -0
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +8 -62
  22. data/lib/active_support/concurrency/share_lock.rb +0 -1
  23. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  24. data/lib/active_support/configurable.rb +34 -0
  25. data/lib/active_support/configuration_file.rb +15 -6
  26. data/lib/active_support/continuous_integration.rb +145 -0
  27. data/lib/active_support/core_ext/array/conversions.rb +3 -5
  28. data/lib/active_support/core_ext/array.rb +7 -7
  29. data/lib/active_support/core_ext/benchmark.rb +4 -14
  30. data/lib/active_support/core_ext/big_decimal.rb +1 -1
  31. data/lib/active_support/core_ext/class/attribute.rb +26 -19
  32. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  33. data/lib/active_support/core_ext/class.rb +2 -2
  34. data/lib/active_support/core_ext/date/blank.rb +4 -0
  35. data/lib/active_support/core_ext/date/conversions.rb +2 -2
  36. data/lib/active_support/core_ext/date.rb +5 -5
  37. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -9
  38. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  39. data/lib/active_support/core_ext/date_time/compatibility.rb +3 -5
  40. data/lib/active_support/core_ext/date_time/conversions.rb +4 -6
  41. data/lib/active_support/core_ext/date_time.rb +5 -5
  42. data/lib/active_support/core_ext/digest/uuid.rb +6 -0
  43. data/lib/active_support/core_ext/digest.rb +1 -1
  44. data/lib/active_support/core_ext/enumerable.rb +25 -8
  45. data/lib/active_support/core_ext/erb/util.rb +10 -5
  46. data/lib/active_support/core_ext/file.rb +1 -1
  47. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
  48. data/lib/active_support/core_ext/hash/except.rb +0 -12
  49. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  50. data/lib/active_support/core_ext/hash.rb +8 -8
  51. data/lib/active_support/core_ext/integer.rb +3 -3
  52. data/lib/active_support/core_ext/kernel.rb +3 -3
  53. data/lib/active_support/core_ext/module/attr_internal.rb +16 -6
  54. data/lib/active_support/core_ext/module/delegation.rb +20 -163
  55. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  56. data/lib/active_support/core_ext/module/introspection.rb +3 -0
  57. data/lib/active_support/core_ext/module.rb +11 -11
  58. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  59. data/lib/active_support/core_ext/numeric.rb +3 -3
  60. data/lib/active_support/core_ext/object/blank.rb +45 -1
  61. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  62. data/lib/active_support/core_ext/object/json.rb +24 -11
  63. data/lib/active_support/core_ext/object/to_query.rb +7 -1
  64. data/lib/active_support/core_ext/object/try.rb +2 -2
  65. data/lib/active_support/core_ext/object/with.rb +5 -3
  66. data/lib/active_support/core_ext/object.rb +13 -13
  67. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  68. data/lib/active_support/core_ext/pathname.rb +2 -2
  69. data/lib/active_support/core_ext/range/overlap.rb +4 -4
  70. data/lib/active_support/core_ext/range/sole.rb +17 -0
  71. data/lib/active_support/core_ext/range.rb +4 -4
  72. data/lib/active_support/core_ext/securerandom.rb +4 -4
  73. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  74. data/lib/active_support/core_ext/string/filters.rb +4 -4
  75. data/lib/active_support/core_ext/string/multibyte.rb +13 -4
  76. data/lib/active_support/core_ext/string/output_safety.rb +19 -19
  77. data/lib/active_support/core_ext/string.rb +13 -13
  78. data/lib/active_support/core_ext/symbol.rb +1 -1
  79. data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
  80. data/lib/active_support/core_ext/time/calculations.rb +25 -30
  81. data/lib/active_support/core_ext/time/compatibility.rb +2 -3
  82. data/lib/active_support/core_ext/time/conversions.rb +2 -2
  83. data/lib/active_support/core_ext/time/zones.rb +1 -1
  84. data/lib/active_support/core_ext/time.rb +5 -5
  85. data/lib/active_support/core_ext.rb +1 -2
  86. data/lib/active_support/current_attributes/test_helper.rb +2 -2
  87. data/lib/active_support/current_attributes.rb +58 -50
  88. data/lib/active_support/delegation.rb +200 -0
  89. data/lib/active_support/dependencies/autoload.rb +0 -12
  90. data/lib/active_support/dependencies/interlock.rb +11 -5
  91. data/lib/active_support/dependencies.rb +6 -2
  92. data/lib/active_support/deprecation/constant_accessor.rb +47 -26
  93. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  94. data/lib/active_support/deprecation/reporting.rb +5 -17
  95. data/lib/active_support/deprecation.rb +8 -5
  96. data/lib/active_support/descendants_tracker.rb +9 -87
  97. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  98. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  99. data/lib/active_support/duration.rb +25 -16
  100. data/lib/active_support/editor.rb +70 -0
  101. data/lib/active_support/encrypted_configuration.rb +20 -2
  102. data/lib/active_support/encrypted_file.rb +1 -1
  103. data/lib/active_support/error_reporter.rb +121 -6
  104. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  105. data/lib/active_support/event_reporter.rb +592 -0
  106. data/lib/active_support/evented_file_update_checker.rb +5 -3
  107. data/lib/active_support/execution_context.rb +64 -7
  108. data/lib/active_support/execution_wrapper.rb +1 -2
  109. data/lib/active_support/file_update_checker.rb +9 -7
  110. data/lib/active_support/fork_tracker.rb +2 -38
  111. data/lib/active_support/gem_version.rb +2 -2
  112. data/lib/active_support/gzip.rb +1 -0
  113. data/lib/active_support/hash_with_indifferent_access.rb +66 -45
  114. data/lib/active_support/html_safe_translation.rb +3 -0
  115. data/lib/active_support/i18n_railtie.rb +19 -11
  116. data/lib/active_support/inflector/inflections.rb +31 -15
  117. data/lib/active_support/inflector/transliterate.rb +6 -8
  118. data/lib/active_support/isolated_execution_state.rb +12 -17
  119. data/lib/active_support/json/decoding.rb +6 -4
  120. data/lib/active_support/json/encoding.rb +157 -21
  121. data/lib/active_support/lazy_load_hooks.rb +1 -1
  122. data/lib/active_support/log_subscriber.rb +2 -18
  123. data/lib/active_support/logger.rb +15 -2
  124. data/lib/active_support/logger_thread_safe_level.rb +4 -9
  125. data/lib/active_support/message_encryptors.rb +54 -2
  126. data/lib/active_support/message_pack/extensions.rb +20 -2
  127. data/lib/active_support/message_verifier.rb +21 -0
  128. data/lib/active_support/message_verifiers.rb +57 -3
  129. data/lib/active_support/messages/rotation_coordinator.rb +9 -0
  130. data/lib/active_support/messages/rotator.rb +10 -0
  131. data/lib/active_support/multibyte/chars.rb +14 -4
  132. data/lib/active_support/multibyte.rb +4 -0
  133. data/lib/active_support/notifications/fanout.rb +68 -50
  134. data/lib/active_support/notifications/instrumenter.rb +22 -19
  135. data/lib/active_support/notifications.rb +28 -27
  136. data/lib/active_support/number_helper/number_converter.rb +2 -2
  137. data/lib/active_support/number_helper.rb +22 -0
  138. data/lib/active_support/option_merger.rb +2 -2
  139. data/lib/active_support/ordered_options.rb +53 -15
  140. data/lib/active_support/railtie.rb +36 -20
  141. data/lib/active_support/string_inquirer.rb +1 -1
  142. data/lib/active_support/structured_event_subscriber.rb +99 -0
  143. data/lib/active_support/subscriber.rb +1 -5
  144. data/lib/active_support/syntax_error_proxy.rb +3 -0
  145. data/lib/active_support/tagged_logging.rb +5 -1
  146. data/lib/active_support/test_case.rb +63 -6
  147. data/lib/active_support/testing/assertions.rb +113 -27
  148. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  149. data/lib/active_support/testing/deprecation.rb +5 -12
  150. data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
  151. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  152. data/lib/active_support/testing/isolation.rb +19 -9
  153. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  154. data/lib/active_support/testing/notification_assertions.rb +92 -0
  155. data/lib/active_support/testing/parallelization/server.rb +18 -2
  156. data/lib/active_support/testing/parallelization/worker.rb +4 -2
  157. data/lib/active_support/testing/parallelization.rb +25 -1
  158. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  159. data/lib/active_support/testing/time_helpers.rb +11 -6
  160. data/lib/active_support/time_with_zone.rb +39 -26
  161. data/lib/active_support/values/time_zone.rb +26 -17
  162. data/lib/active_support/xml_mini.rb +14 -4
  163. data/lib/active_support.rb +22 -9
  164. metadata +31 -17
  165. data/lib/active_support/core_ext/range/each.rb +0 -24
  166. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  167. data/lib/active_support/proxy_object.rb +0 -17
  168. data/lib/active_support/ruby_features.rb +0 -7
  169. data/lib/active_support/testing/strict_warnings.rb +0 -39
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/callbacks"
4
+ require "active_support/execution_context"
5
+ require "active_support/core_ext/object/with"
4
6
  require "active_support/core_ext/enumerable"
5
7
  require "active_support/core_ext/module/delegation"
6
8
 
@@ -94,6 +96,8 @@ module ActiveSupport
94
96
 
95
97
  INVALID_ATTRIBUTE_NAMES = [:set, :reset, :resets, :instance, :before_reset, :after_reset, :reset_all, :clear_all] # :nodoc:
96
98
 
99
+ NOT_SET = Object.new.freeze # :nodoc:
100
+
97
101
  class << self
98
102
  # Returns singleton instance for this class in this thread. If none exists, one is created.
99
103
  def instance
@@ -101,45 +105,40 @@ module ActiveSupport
101
105
  end
102
106
 
103
107
  # Declares one or more attributes that will be given both class and instance accessor methods.
104
- def attribute(*names)
108
+ #
109
+ # ==== Options
110
+ #
111
+ # * <tt>:default</tt> - The default value for the attributes. If the value
112
+ # is a proc or lambda, it will be called whenever an instance is
113
+ # constructed. Otherwise, the value will be duplicated with +#dup+.
114
+ # Default values are re-assigned when the attributes are reset.
115
+ def attribute(*names, default: NOT_SET)
105
116
  invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES
106
117
  if invalid_attribute_names.any?
107
118
  raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
108
119
  end
109
120
 
121
+ Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
122
+ Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
123
+
110
124
  ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
111
125
  names.each do |name|
112
126
  owner.define_cached_method(name, namespace: :current_attributes) do |batch|
113
127
  batch <<
114
128
  "def #{name}" <<
115
- "attributes[:#{name}]" <<
129
+ "@attributes[:#{name}]" <<
116
130
  "end"
117
131
  end
118
132
  owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch|
119
133
  batch <<
120
134
  "def #{name}=(value)" <<
121
- "attributes[:#{name}] = value" <<
135
+ "@attributes[:#{name}] = value" <<
122
136
  "end"
123
137
  end
124
138
  end
125
139
  end
126
140
 
127
- ActiveSupport::CodeGenerator.batch(singleton_class, __FILE__, __LINE__) do |owner|
128
- names.each do |name|
129
- owner.define_cached_method(name, namespace: :current_attributes_delegation) do |batch|
130
- batch <<
131
- "def #{name}" <<
132
- "instance.#{name}" <<
133
- "end"
134
- end
135
- owner.define_cached_method("#{name}=", namespace: :current_attributes_delegation) do |batch|
136
- batch <<
137
- "def #{name}=(value)" <<
138
- "instance.#{name} = value" <<
139
- "end"
140
- end
141
- end
142
- end
141
+ self.defaults = defaults.merge(names.index_with { default })
143
142
  end
144
143
 
145
144
  # Calls this callback before #reset is called on the instance. Used for resetting external collaborators that depend on current values.
@@ -155,13 +154,11 @@ module ActiveSupport
155
154
 
156
155
  delegate :set, :reset, to: :instance
157
156
 
158
- def reset_all # :nodoc:
159
- current_instances.each_value(&:reset)
160
- end
161
-
162
157
  def clear_all # :nodoc:
163
- reset_all
164
- current_instances.clear
158
+ if instances = current_instances
159
+ instances.values.each(&:reset)
160
+ instances.clear
161
+ end
165
162
  end
166
163
 
167
164
  private
@@ -170,32 +167,47 @@ module ActiveSupport
170
167
  end
171
168
 
172
169
  def current_instances
173
- IsolatedExecutionState[:current_attributes_instances] ||= {}
170
+ ExecutionContext.current_attributes_instances
174
171
  end
175
172
 
176
173
  def current_instances_key
177
174
  @current_instances_key ||= name.to_sym
178
175
  end
179
176
 
180
- def method_missing(name, *args, &block)
181
- # Caches the method definition as a singleton method of the receiver.
182
- #
183
- # By letting #delegate handle it, we avoid an enclosure that'll capture args.
184
- singleton_class.delegate name, to: :instance
185
-
186
- send(name, *args, &block)
177
+ def method_missing(name, ...)
178
+ instance.public_send(name, ...)
187
179
  end
188
- ruby2_keywords(:method_missing)
189
180
 
190
181
  def respond_to_missing?(name, _)
191
- super || instance.respond_to?(name)
182
+ instance.respond_to?(name) || super
183
+ end
184
+
185
+ def method_added(name)
186
+ super
187
+
188
+ # We try to generate instance delegators early to not rely on method_missing.
189
+ return if name == :initialize
190
+
191
+ # If the added method isn't public, we don't delegate it.
192
+ return unless public_method_defined?(name)
193
+
194
+ # If we already have a class method by that name, we don't override it.
195
+ return if singleton_class.method_defined?(name) || singleton_class.private_method_defined?(name)
196
+
197
+ Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
192
198
  end
193
199
  end
194
200
 
195
- attr_accessor :attributes
201
+ class_attribute :defaults, instance_writer: false, default: {}.freeze
202
+
203
+ attr_writer :attributes
196
204
 
197
205
  def initialize
198
- @attributes = {}
206
+ @attributes = resolve_defaults
207
+ end
208
+
209
+ def attributes
210
+ @attributes.dup
199
211
  end
200
212
 
201
213
  # Expose one or more attributes within a block. Old values are returned after the block concludes.
@@ -208,28 +220,24 @@ module ActiveSupport
208
220
  # end
209
221
  # end
210
222
  # end
211
- def set(set_attributes)
212
- old_attributes = compute_attributes(set_attributes.keys)
213
- assign_attributes(set_attributes)
214
- yield
215
- ensure
216
- assign_attributes(old_attributes)
223
+ def set(attributes, &block)
224
+ with(**attributes, &block)
217
225
  end
218
226
 
219
227
  # Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
220
228
  def reset
221
229
  run_callbacks :reset do
222
- self.attributes = {}
230
+ self.attributes = resolve_defaults
223
231
  end
224
232
  end
225
233
 
226
234
  private
227
- def assign_attributes(new_attributes)
228
- new_attributes.each { |key, value| public_send("#{key}=", value) }
229
- end
230
-
231
- def compute_attributes(keys)
232
- keys.index_with { |key| public_send(key) }
235
+ def resolve_defaults
236
+ defaults.each_with_object({}) do |(key, value), result|
237
+ if value != NOT_SET
238
+ result[key] = Proc === value ? value.call : value.dup
239
+ end
240
+ end
233
241
  end
234
242
  end
235
243
  end
@@ -0,0 +1,200 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
5
+ # option is not used.
6
+ class DelegationError < NoMethodError
7
+ class << self
8
+ def nil_target(method_name, target) # :nodoc:
9
+ new("#{method_name} delegated to #{target}, but #{target} is nil")
10
+ end
11
+ end
12
+ end
13
+
14
+ module Delegation # :nodoc:
15
+ RUBY_RESERVED_KEYWORDS = %w(__ENCODING__ __LINE__ __FILE__ alias and BEGIN begin break
16
+ case class def defined? do else elsif END end ensure false for if in module next nil
17
+ not or redo rescue retry return self super then true undef unless until when while yield)
18
+ RESERVED_METHOD_NAMES = (RUBY_RESERVED_KEYWORDS + %w(_ arg args block)).to_set.freeze
19
+
20
+ class << self
21
+ def generate(owner, methods, location: nil, to: nil, prefix: nil, allow_nil: nil, nilable: true, private: nil, as: nil, signature: nil)
22
+ unless to
23
+ raise ArgumentError, "Delegation needs a target. Supply a keyword argument 'to' (e.g. delegate :hello, to: :greeter)."
24
+ end
25
+
26
+ if prefix == true && /^[^a-z_]/.match?(to)
27
+ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
28
+ end
29
+
30
+ method_prefix = \
31
+ if prefix
32
+ "#{prefix == true ? to : prefix}_"
33
+ else
34
+ ""
35
+ end
36
+
37
+ location ||= caller_locations(1, 1).first
38
+ file, line = location.path, location.lineno
39
+
40
+ receiver = if to.is_a?(Module)
41
+ if to.name.nil?
42
+ raise ArgumentError, "Can't delegate to anonymous class or module: #{to}"
43
+ end
44
+
45
+ unless Inflector.safe_constantize(to.name).equal?(to)
46
+ raise ArgumentError, "Can't delegate to detached class or module: #{to.name}"
47
+ end
48
+
49
+ "::#{to.name}"
50
+ else
51
+ to.to_s
52
+ end
53
+ receiver = "self.#{receiver}" if RESERVED_METHOD_NAMES.include?(receiver)
54
+
55
+ explicit_receiver = false
56
+ receiver_class = if as
57
+ explicit_receiver = true
58
+ as
59
+ elsif to.is_a?(Module)
60
+ to.singleton_class
61
+ elsif receiver == "self.class"
62
+ nilable = false # self.class can't possibly be nil
63
+ owner.singleton_class
64
+ end
65
+
66
+ method_def = []
67
+ method_names = []
68
+
69
+ method_def << "self.private" if private
70
+
71
+ methods.each do |method|
72
+ method_name = prefix ? "#{method_prefix}#{method}" : method
73
+ method_names << method_name.to_sym
74
+
75
+ # Attribute writer methods only accept one argument. Makes sure []=
76
+ # methods still accept two arguments.
77
+ definition = \
78
+ if signature
79
+ signature
80
+ elsif /[^\]]=\z/.match?(method)
81
+ "arg"
82
+ else
83
+ method_object = if receiver_class
84
+ begin
85
+ receiver_class.public_instance_method(method)
86
+ rescue NameError
87
+ raise if explicit_receiver
88
+ # Do nothing. Fall back to `"..."`
89
+ end
90
+ end
91
+
92
+ if method_object
93
+ parameters = method_object.parameters
94
+
95
+ if parameters.map(&:first).intersect?([:opt, :rest, :keyreq, :key, :keyrest])
96
+ "..."
97
+ else
98
+ defn = parameters.filter_map { |type, arg| arg if type == :req }
99
+ defn << "&"
100
+ defn.join(", ")
101
+ end
102
+ else
103
+ "..."
104
+ end
105
+ end
106
+
107
+ # The following generated method calls the target exactly once, storing
108
+ # the returned value in a dummy variable.
109
+ #
110
+ # Reason is twofold: On one hand doing less calls is in general better.
111
+ # On the other hand it could be that the target has side-effects,
112
+ # whereas conceptually, from the user point of view, the delegator should
113
+ # be doing one call.
114
+ if nilable == false
115
+ method_def <<
116
+ "def #{method_name}(#{definition})" <<
117
+ " (#{receiver}).#{method}(#{definition})" <<
118
+ "end"
119
+ elsif allow_nil
120
+ method = method.to_s
121
+
122
+ method_def <<
123
+ "def #{method_name}(#{definition})" <<
124
+ " _ = #{receiver}" <<
125
+ " if !_.nil? || nil.respond_to?(:#{method})" <<
126
+ " _.#{method}(#{definition})" <<
127
+ " end" <<
128
+ "end"
129
+ else
130
+ method = method.to_s
131
+ method_name = method_name.to_s
132
+
133
+ method_def <<
134
+ "def #{method_name}(#{definition})" <<
135
+ " _ = #{receiver}" <<
136
+ " _.#{method}(#{definition})" <<
137
+ "rescue NoMethodError => e" <<
138
+ " if _.nil? && e.name == :#{method}" <<
139
+ " raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
140
+ " else" <<
141
+ " raise" <<
142
+ " end" <<
143
+ "end"
144
+ end
145
+ end
146
+ owner.module_eval(method_def.join(";"), file, line)
147
+ method_names
148
+ end
149
+
150
+ def generate_method_missing(owner, target, allow_nil: nil)
151
+ target = target.to_s
152
+ target = "self.#{target}" if RESERVED_METHOD_NAMES.include?(target) || target == "__target"
153
+
154
+ if allow_nil
155
+ owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
156
+ def respond_to_missing?(name, include_private = false)
157
+ # It may look like an oversight, but we deliberately do not pass
158
+ # +include_private+, because they do not get delegated.
159
+
160
+ return false if name == :marshal_dump || name == :_dump
161
+ #{target}.respond_to?(name) || super
162
+ end
163
+
164
+ def method_missing(method, ...)
165
+ __target = #{target}
166
+ if __target.nil? && !nil.respond_to?(method)
167
+ nil
168
+ elsif __target.respond_to?(method)
169
+ __target.public_send(method, ...)
170
+ else
171
+ super
172
+ end
173
+ end
174
+ RUBY
175
+ else
176
+ owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
177
+ def respond_to_missing?(name, include_private = false)
178
+ # It may look like an oversight, but we deliberately do not pass
179
+ # +include_private+, because they do not get delegated.
180
+
181
+ return false if name == :marshal_dump || name == :_dump
182
+ #{target}.respond_to?(name) || super
183
+ end
184
+
185
+ def method_missing(method, ...)
186
+ __target = #{target}
187
+ if __target.nil? && !nil.respond_to?(method)
188
+ raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')
189
+ elsif __target.respond_to?(method)
190
+ __target.public_send(method, ...)
191
+ else
192
+ super
193
+ end
194
+ end
195
+ RUBY
196
+ end
197
+ end
198
+ end
199
+ end
200
+ end
@@ -27,18 +27,6 @@ module ActiveSupport
27
27
  #
28
28
  # MyLib.eager_load!
29
29
  module Autoload
30
- def self.extended(base) # :nodoc:
31
- if RUBY_VERSION < "3"
32
- base.class_eval do
33
- @_autoloads = nil
34
- @_under_path = nil
35
- @_at_path = nil
36
- @_eager_autoload = false
37
- @_eagerloaded_constants = nil
38
- end
39
- end
40
- end
41
-
42
30
  def autoload(const_name, path = @_at_path)
43
31
  unless path
44
32
  full = [name, @_under_path, const_name.to_s].compact.join("::")
@@ -10,19 +10,24 @@ module ActiveSupport # :nodoc:
10
10
  end
11
11
 
12
12
  def loading(&block)
13
- @lock.exclusive(purpose: :load, compatible: [:load], after_compatible: [:load], &block)
13
+ ActiveSupport.deprecator.warn(
14
+ "ActiveSupport::Dependencies::Interlock#loading is deprecated and " \
15
+ "will be removed in Rails 9.0. The loading interlock is no longer " \
16
+ "used since Rails switched to Zeitwerk for autoloading."
17
+ )
18
+ yield if block
14
19
  end
15
20
 
16
21
  def unloading(&block)
17
- @lock.exclusive(purpose: :unload, compatible: [:load, :unload], after_compatible: [:load, :unload], &block)
22
+ @lock.exclusive(purpose: :unload, compatible: [:unload], after_compatible: [:unload], &block)
18
23
  end
19
24
 
20
25
  def start_unloading
21
- @lock.start_exclusive(purpose: :unload, compatible: [:load, :unload])
26
+ @lock.start_exclusive(purpose: :unload, compatible: [:unload])
22
27
  end
23
28
 
24
29
  def done_unloading
25
- @lock.stop_exclusive(compatible: [:load, :unload])
30
+ @lock.stop_exclusive(compatible: [:unload])
26
31
  end
27
32
 
28
33
  def start_running
@@ -38,7 +43,8 @@ module ActiveSupport # :nodoc:
38
43
  end
39
44
 
40
45
  def permit_concurrent_loads(&block)
41
- @lock.yield_shares(compatible: [:load], &block)
46
+ # Soft deprecated: no deprecation warning for now, but this is a no-op.
47
+ yield if block
42
48
  end
43
49
 
44
50
  def raw_state(&block) # :nodoc:
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
3
  require "active_support/dependencies/interlock"
5
4
 
6
5
  module ActiveSupport # :nodoc:
@@ -22,7 +21,12 @@ module ActiveSupport # :nodoc:
22
21
  # preventing any other thread from being inside a #run_interlock
23
22
  # block at the same time.
24
23
  def self.load_interlock(&block)
25
- interlock.loading(&block)
24
+ ActiveSupport.deprecator.warn(
25
+ "ActiveSupport::Dependencies.load_interlock is deprecated and " \
26
+ "will be removed in Rails 9.0. The loading interlock is no longer " \
27
+ "used since Rails switched to Zeitwerk for autoloading."
28
+ )
29
+ yield if block
26
30
  end
27
31
 
28
32
  # Execute the supplied block while holding an exclusive lock,
@@ -2,28 +2,6 @@
2
2
 
3
3
  module ActiveSupport
4
4
  class Deprecation
5
- # DeprecatedConstantAccessor transforms a constant into a deprecated one by
6
- # hooking +const_missing+.
7
- #
8
- # It takes the names of an old (deprecated) constant and of a new constant
9
- # (both in string form) and a deprecator.
10
- #
11
- # The deprecated constant now returns the same object as the new one rather
12
- # than a proxy object, so it can be used transparently in +rescue+ blocks
13
- # etc.
14
- #
15
- # PLANETS = %w(mercury venus earth mars jupiter saturn uranus neptune pluto)
16
- #
17
- # # (In a later update, the original implementation of `PLANETS` has been removed.)
18
- #
19
- # PLANETS_POST_2006 = %w(mercury venus earth mars jupiter saturn uranus neptune)
20
- # include ActiveSupport::Deprecation::DeprecatedConstantAccessor
21
- # deprecate_constant 'PLANETS', 'PLANETS_POST_2006', deprecator: ActiveSupport::Deprecation.new
22
- #
23
- # PLANETS.map { |planet| planet.capitalize }
24
- # # => DEPRECATION WARNING: PLANETS is deprecated! Use PLANETS_POST_2006 instead.
25
- # (Backtrace information…)
26
- # ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
27
5
  module DeprecatedConstantAccessor
28
6
  def self.included(base)
29
7
  require "active_support/inflector/methods"
@@ -39,11 +17,54 @@ module ActiveSupport
39
17
  super
40
18
  end
41
19
 
42
- def deprecate_constant(const_name, new_constant, message: nil, deprecator: nil)
43
- ActiveSupport.deprecator.warn("DeprecatedConstantAccessor.deprecate_constant without a deprecator is deprecated") unless deprecator
44
- deprecator ||= ActiveSupport::Deprecation._instance
20
+ # Provides a way to rename constants with a deprecation cycle in which
21
+ # both the old and new names work, but using the old one prints a
22
+ # deprecation message.
23
+ #
24
+ # In order to rename <tt>A::B</tt> to <tt>C::D</tt>, you need to delete the
25
+ # definition of <tt>A::B</tt> and declare the deprecation in +A+:
26
+ #
27
+ # require "active_support/deprecation"
28
+ #
29
+ # module A
30
+ # include ActiveSupport::Deprecation::DeprecatedConstantAccessor
31
+ #
32
+ # deprecate_constant "B", "C::D", deprecator: ActiveSupport::Deprecation.new
33
+ # end
34
+ #
35
+ # The first argument is a constant name (no colons). It is the name of
36
+ # the constant you want to deprecate in the enclosing class or module.
37
+ #
38
+ # The second argument is the constant path of the replacement. That
39
+ # has to be a full path even if the replacement is defined in the same
40
+ # namespace as the deprecated one was.
41
+ #
42
+ # In both cases, strings and symbols are supported.
43
+ #
44
+ # The +deprecator+ keyword argument is the object that will print the
45
+ # deprecation message, an instance of ActiveSupport::Deprecation.
46
+ #
47
+ # With that in place, references to <tt>A::B</tt> still work, they
48
+ # evaluate to <tt>C::D</tt> now, and trigger a deprecation warning:
49
+ #
50
+ # DEPRECATION WARNING: A::B is deprecated! Use C::D instead.
51
+ # (called from ...)
52
+ #
53
+ # The message can be customized with the optional +message+ keyword
54
+ # argument.
55
+ #
56
+ # For this to work, a +const_missing+ hook is installed. When client
57
+ # code references the deprecated constant, the callback prints the
58
+ # message and constantizes the replacement.
59
+ #
60
+ # Caveat: If the deprecated constant name is reachable in a different
61
+ # namespace and Ruby constant lookup finds it, the hook won't be
62
+ # called and the deprecation won't work as intended. This may happen,
63
+ # for example, if an ancestor of the enclosing namespace has a
64
+ # constant with the same name. This is an unsupported edge case.
65
+ def deprecate_constant(old_constant_name, new_constant_path, deprecator:, message: nil)
45
66
  class_variable_set(:@@_deprecated_constants, {}) unless class_variable_defined?(:@@_deprecated_constants)
46
- class_variable_get(:@@_deprecated_constants)[const_name.to_s] = { new: new_constant, message: message, deprecator: deprecator }
67
+ class_variable_get(:@@_deprecated_constants)[old_constant_name.to_s] = { new: new_constant_path, message: message, deprecator: deprecator }
47
68
  end
48
69
  end
49
70
  base.singleton_class.prepend extension
@@ -3,7 +3,7 @@
3
3
  module ActiveSupport
4
4
  class Deprecation
5
5
  class DeprecationProxy # :nodoc:
6
- def self.new(*args, &block)
6
+ def self.new(*args, **kwargs, &block)
7
7
  object = args.first
8
8
 
9
9
  return object unless object
@@ -36,11 +36,10 @@ module ActiveSupport
36
36
  # (Backtrace)
37
37
  # # => "#<Object:0x007fb9b34c34b0>"
38
38
  class DeprecatedObjectProxy < DeprecationProxy
39
- def initialize(object, message, deprecator = nil)
39
+ def initialize(object, message, deprecator)
40
40
  @object = object
41
41
  @message = message
42
- ActiveSupport.deprecator.warn("DeprecatedObjectProxy without a deprecator is deprecated") unless deprecator
43
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
42
+ @deprecator = deprecator
44
43
  end
45
44
 
46
45
  private
@@ -86,12 +85,11 @@ module ActiveSupport
86
85
  # example.request.to_s
87
86
  # # => "special_request"
88
87
  class DeprecatedInstanceVariableProxy < DeprecationProxy
89
- def initialize(instance, method, var = "@#{method}", deprecator = nil)
88
+ def initialize(instance, method, var = "@#{method}", deprecator:)
90
89
  @instance = instance
91
90
  @method = method
92
91
  @var = var
93
- ActiveSupport.deprecator.warn("DeprecatedInstanceVariableProxy without a deprecator is deprecated") unless deprecator
94
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
92
+ @deprecator = deprecator
95
93
  end
96
94
 
97
95
  private
@@ -127,13 +125,12 @@ module ActiveSupport
127
125
  super
128
126
  end
129
127
 
130
- def initialize(old_const, new_const, deprecator = nil, message: "#{old_const} is deprecated! Use #{new_const} instead.")
128
+ def initialize(old_const, new_const, deprecator, message: "#{old_const} is deprecated! Use #{new_const} instead.")
131
129
  Kernel.require "active_support/inflector/methods"
132
130
 
133
131
  @old_const = old_const
134
132
  @new_const = new_const
135
- ActiveSupport.deprecator.warn("DeprecatedConstantProxy without a deprecator is deprecated") unless deprecator
136
- @deprecator = deprecator || ActiveSupport::Deprecation._instance
133
+ @deprecator = deprecator
137
134
  @message = message
138
135
  end
139
136
 
@@ -183,9 +180,9 @@ module ActiveSupport
183
180
  target.const_get(name)
184
181
  end
185
182
 
186
- def method_missing(called, *args, &block)
183
+ def method_missing(...)
187
184
  @deprecator.warn(@message, caller_locations)
188
- target.__send__(called, *args, &block)
185
+ target.__send__(...)
189
186
  end
190
187
  end
191
188
  end
@@ -139,7 +139,6 @@ module ActiveSupport
139
139
 
140
140
  def extract_callstack(callstack)
141
141
  return [] if callstack.empty?
142
- return _extract_callstack(callstack) if callstack.first.is_a? String
143
142
 
144
143
  offending_line = callstack.find { |frame|
145
144
  # Code generated with `eval` doesn't have an `absolute_path`, e.g. templates.
@@ -150,24 +149,13 @@ module ActiveSupport
150
149
  [offending_line.path, offending_line.lineno, offending_line.label]
151
150
  end
152
151
 
153
- def _extract_callstack(callstack)
154
- warn "Please pass `caller_locations` to the deprecation API" if $VERBOSE
155
- offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
156
-
157
- if offending_line
158
- if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
159
- md.captures
160
- else
161
- offending_line
162
- end
163
- end
164
- end
165
-
166
- RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
167
- LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
152
+ RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
153
+ private_constant :RAILS_GEM_ROOT
154
+ LIB_DIR = RbConfig::CONFIG["libdir"]
155
+ private_constant :LIB_DIR
168
156
 
169
157
  def ignored_callstack?(path)
170
- path.start_with?(RAILS_GEM_ROOT, LIB_DIR)
158
+ path.start_with?(RAILS_GEM_ROOT, LIB_DIR) || path.include?("<internal:")
171
159
  end
172
160
  end
173
161
  end