activesupport 7.1.3.4 → 7.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +123 -1084
  3. data/lib/active_support/array_inquirer.rb +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +15 -3
  5. data/lib/active_support/broadcast_logger.rb +5 -4
  6. data/lib/active_support/cache/file_store.rb +15 -10
  7. data/lib/active_support/cache/mem_cache_store.rb +16 -74
  8. data/lib/active_support/cache/memory_store.rb +2 -1
  9. data/lib/active_support/cache/redis_cache_store.rb +16 -13
  10. data/lib/active_support/cache/serializer_with_fallback.rb +0 -23
  11. data/lib/active_support/cache.rb +61 -68
  12. data/lib/active_support/callbacks.rb +74 -113
  13. data/lib/active_support/code_generator.rb +15 -10
  14. data/lib/active_support/core_ext/array/conversions.rb +0 -2
  15. data/lib/active_support/core_ext/class/subclasses.rb +15 -35
  16. data/lib/active_support/core_ext/date/blank.rb +4 -0
  17. data/lib/active_support/core_ext/date/conversions.rb +0 -2
  18. data/lib/active_support/core_ext/date_and_time/compatibility.rb +28 -1
  19. data/lib/active_support/core_ext/date_time/blank.rb +4 -0
  20. data/lib/active_support/core_ext/date_time/conversions.rb +0 -4
  21. data/lib/active_support/core_ext/digest/uuid.rb +6 -0
  22. data/lib/active_support/core_ext/erb/util.rb +5 -0
  23. data/lib/active_support/core_ext/hash/keys.rb +4 -4
  24. data/lib/active_support/core_ext/module/attr_internal.rb +17 -6
  25. data/lib/active_support/core_ext/module/delegation.rb +20 -148
  26. data/lib/active_support/core_ext/module/deprecation.rb +1 -4
  27. data/lib/active_support/core_ext/numeric/conversions.rb +3 -3
  28. data/lib/active_support/core_ext/object/blank.rb +45 -1
  29. data/lib/active_support/core_ext/object/duplicable.rb +24 -15
  30. data/lib/active_support/core_ext/object/instance_variables.rb +11 -19
  31. data/lib/active_support/core_ext/object/json.rb +1 -1
  32. data/lib/active_support/core_ext/object/with.rb +5 -3
  33. data/lib/active_support/core_ext/pathname/blank.rb +4 -0
  34. data/lib/active_support/core_ext/range/overlap.rb +1 -1
  35. data/lib/active_support/core_ext/securerandom.rb +8 -24
  36. data/lib/active_support/core_ext/string/conversions.rb +1 -1
  37. data/lib/active_support/core_ext/string/filters.rb +1 -1
  38. data/lib/active_support/core_ext/string/output_safety.rb +0 -7
  39. data/lib/active_support/core_ext/time/calculations.rb +18 -28
  40. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  41. data/lib/active_support/core_ext/time/conversions.rb +0 -2
  42. data/lib/active_support/core_ext.rb +0 -1
  43. data/lib/active_support/current_attributes.rb +34 -40
  44. data/lib/active_support/delegation.rb +202 -0
  45. data/lib/active_support/dependencies/autoload.rb +0 -12
  46. data/lib/active_support/deprecation/constant_accessor.rb +47 -26
  47. data/lib/active_support/deprecation/proxy_wrappers.rb +9 -12
  48. data/lib/active_support/deprecation/reporting.rb +7 -2
  49. data/lib/active_support/deprecation.rb +8 -5
  50. data/lib/active_support/descendants_tracker.rb +9 -87
  51. data/lib/active_support/duration/iso8601_parser.rb +2 -2
  52. data/lib/active_support/duration/iso8601_serializer.rb +1 -2
  53. data/lib/active_support/duration.rb +11 -6
  54. data/lib/active_support/error_reporter.rb +41 -3
  55. data/lib/active_support/evented_file_update_checker.rb +0 -1
  56. data/lib/active_support/execution_wrapper.rb +0 -1
  57. data/lib/active_support/file_update_checker.rb +1 -1
  58. data/lib/active_support/fork_tracker.rb +2 -38
  59. data/lib/active_support/gem_version.rb +3 -3
  60. data/lib/active_support/hash_with_indifferent_access.rb +6 -8
  61. data/lib/active_support/html_safe_translation.rb +7 -4
  62. data/lib/active_support/json/encoding.rb +1 -1
  63. data/lib/active_support/log_subscriber.rb +1 -12
  64. data/lib/active_support/logger.rb +15 -2
  65. data/lib/active_support/logger_thread_safe_level.rb +0 -8
  66. data/lib/active_support/message_pack/extensions.rb +15 -2
  67. data/lib/active_support/message_verifier.rb +12 -0
  68. data/lib/active_support/messages/codec.rb +1 -1
  69. data/lib/active_support/multibyte/chars.rb +2 -2
  70. data/lib/active_support/notifications/fanout.rb +4 -7
  71. data/lib/active_support/notifications/instrumenter.rb +32 -21
  72. data/lib/active_support/notifications.rb +28 -27
  73. data/lib/active_support/number_helper/number_converter.rb +2 -2
  74. data/lib/active_support/option_merger.rb +2 -2
  75. data/lib/active_support/ordered_options.rb +53 -15
  76. data/lib/active_support/proxy_object.rb +8 -5
  77. data/lib/active_support/railtie.rb +4 -11
  78. data/lib/active_support/string_inquirer.rb +1 -1
  79. data/lib/active_support/subscriber.rb +1 -0
  80. data/lib/active_support/syntax_error_proxy.rb +1 -11
  81. data/lib/active_support/tagged_logging.rb +4 -1
  82. data/lib/active_support/test_case.rb +3 -1
  83. data/lib/active_support/testing/assertions.rb +4 -4
  84. data/lib/active_support/testing/constant_stubbing.rb +30 -8
  85. data/lib/active_support/testing/deprecation.rb +5 -12
  86. data/lib/active_support/testing/isolation.rb +18 -8
  87. data/lib/active_support/testing/method_call_assertions.rb +2 -16
  88. data/lib/active_support/testing/setup_and_teardown.rb +2 -0
  89. data/lib/active_support/testing/strict_warnings.rb +5 -4
  90. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  91. data/lib/active_support/testing/time_helpers.rb +3 -3
  92. data/lib/active_support/time_with_zone.rb +7 -3
  93. data/lib/active_support/values/time_zone.rb +10 -1
  94. data/lib/active_support/xml_mini.rb +11 -2
  95. data/lib/active_support.rb +3 -2
  96. metadata +35 -15
  97. data/lib/active_support/deprecation/instance_delegator.rb +0 -65
  98. data/lib/active_support/ruby_features.rb +0 -7
@@ -150,7 +150,7 @@ module ActiveSupport
150
150
  def halted_callback_hook(filter, name)
151
151
  end
152
152
 
153
- module Conditionals # :nodoc:
153
+ module Conditionals # :nodoc: all
154
154
  class Value
155
155
  def initialize(&block)
156
156
  @block = block
@@ -159,128 +159,76 @@ module ActiveSupport
159
159
  end
160
160
  end
161
161
 
162
- module Filters
162
+ module Filters # :nodoc: all
163
163
  Environment = Struct.new(:target, :halted, :value)
164
164
 
165
165
  class Before
166
- def self.build(callback_sequence, user_callback, user_conditions, chain_config, filter, name)
166
+ def initialize(user_callback, user_conditions, chain_config, filter, name)
167
167
  halted_lambda = chain_config[:terminator]
168
-
169
- if user_conditions.any?
170
- halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
171
- else
172
- halting(callback_sequence, user_callback, halted_lambda, filter, name)
173
- end
168
+ @user_callback, @user_conditions, @halted_lambda, @filter, @name = user_callback, user_conditions, halted_lambda, filter, name
169
+ freeze
174
170
  end
171
+ attr_reader :user_callback, :user_conditions, :halted_lambda, :filter, :name
175
172
 
176
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions, halted_lambda, filter, name)
177
- callback_sequence.before do |env|
178
- target = env.target
179
- value = env.value
180
- halted = env.halted
173
+ def call(env)
174
+ target = env.target
175
+ value = env.value
176
+ halted = env.halted
181
177
 
182
- if !halted && user_conditions.all? { |c| c.call(target, value) }
183
- result_lambda = -> { user_callback.call target, value }
184
- env.halted = halted_lambda.call(target, result_lambda)
185
- if env.halted
186
- target.send :halted_callback_hook, filter, name
187
- end
178
+ if !halted && user_conditions.all? { |c| c.call(target, value) }
179
+ result_lambda = -> { user_callback.call target, value }
180
+ env.halted = halted_lambda.call(target, result_lambda)
181
+ if env.halted
182
+ target.send :halted_callback_hook, filter, name
188
183
  end
189
-
190
- env
191
184
  end
192
- end
193
- private_class_method :halting_and_conditional
194
-
195
- def self.halting(callback_sequence, user_callback, halted_lambda, filter, name)
196
- callback_sequence.before do |env|
197
- target = env.target
198
- value = env.value
199
- halted = env.halted
200
185
 
201
- unless halted
202
- result_lambda = -> { user_callback.call target, value }
203
- env.halted = halted_lambda.call(target, result_lambda)
204
- if env.halted
205
- target.send :halted_callback_hook, filter, name
206
- end
207
- end
186
+ env
187
+ end
208
188
 
209
- env
210
- end
189
+ def apply(callback_sequence)
190
+ callback_sequence.before(self)
211
191
  end
212
- private_class_method :halting
213
192
  end
214
193
 
215
194
  class After
216
- def self.build(callback_sequence, user_callback, user_conditions, chain_config)
217
- if chain_config[:skip_after_callbacks_if_terminated]
218
- if user_conditions.any?
219
- halting_and_conditional(callback_sequence, user_callback, user_conditions)
220
- else
221
- halting(callback_sequence, user_callback)
222
- end
223
- else
224
- if user_conditions.any?
225
- conditional callback_sequence, user_callback, user_conditions
226
- else
227
- simple callback_sequence, user_callback
228
- end
229
- end
195
+ attr_reader :user_callback, :user_conditions, :halting
196
+ def initialize(user_callback, user_conditions, chain_config)
197
+ halting = chain_config[:skip_after_callbacks_if_terminated]
198
+ @user_callback, @user_conditions, @halting = user_callback, user_conditions, halting
199
+ freeze
230
200
  end
231
201
 
232
- def self.halting_and_conditional(callback_sequence, user_callback, user_conditions)
233
- callback_sequence.after do |env|
234
- target = env.target
235
- value = env.value
236
- halted = env.halted
237
-
238
- if !halted && user_conditions.all? { |c| c.call(target, value) }
239
- user_callback.call target, value
240
- end
202
+ def call(env)
203
+ target = env.target
204
+ value = env.value
205
+ halted = env.halted
241
206
 
242
- env
207
+ if (!halted || !@halting) && user_conditions.all? { |c| c.call(target, value) }
208
+ user_callback.call target, value
243
209
  end
244
- end
245
- private_class_method :halting_and_conditional
246
-
247
- def self.halting(callback_sequence, user_callback)
248
- callback_sequence.after do |env|
249
- unless env.halted
250
- user_callback.call env.target, env.value
251
- end
252
210
 
253
- env
254
- end
211
+ env
255
212
  end
256
- private_class_method :halting
257
-
258
- def self.conditional(callback_sequence, user_callback, user_conditions)
259
- callback_sequence.after do |env|
260
- target = env.target
261
- value = env.value
262
213
 
263
- if user_conditions.all? { |c| c.call(target, value) }
264
- user_callback.call target, value
265
- end
266
-
267
- env
268
- end
214
+ def apply(callback_sequence)
215
+ callback_sequence.after(self)
269
216
  end
270
- private_class_method :conditional
217
+ end
271
218
 
272
- def self.simple(callback_sequence, user_callback)
273
- callback_sequence.after do |env|
274
- user_callback.call env.target, env.value
219
+ class Around
220
+ def initialize(user_callback, user_conditions)
221
+ @user_callback, @user_conditions = user_callback, user_conditions
222
+ freeze
223
+ end
275
224
 
276
- env
277
- end
225
+ def apply(callback_sequence)
226
+ callback_sequence.around(@user_callback, @user_conditions)
278
227
  end
279
- private_class_method :simple
280
228
  end
281
229
  end
282
230
 
283
- class Callback # :nodoc:#
231
+ class Callback # :nodoc:
284
232
  def self.build(chain, filter, kind, options)
285
233
  if filter.is_a?(String)
286
234
  raise ArgumentError, <<-MSG.squish
@@ -302,6 +250,8 @@ module ActiveSupport
302
250
  @filter = filter
303
251
  @if = check_conditionals(options[:if])
304
252
  @unless = check_conditionals(options[:unless])
253
+
254
+ compiled
305
255
  end
306
256
 
307
257
  def merge_conditional_options(chain, if_option:, unless_option:)
@@ -329,19 +279,26 @@ module ActiveSupport
329
279
  end
330
280
  end
331
281
 
282
+ def compiled
283
+ @compiled ||=
284
+ begin
285
+ user_conditions = conditions_lambdas
286
+ user_callback = CallTemplate.build(@filter, self)
287
+
288
+ case kind
289
+ when :before
290
+ Filters::Before.new(user_callback.make_lambda, user_conditions, chain_config, @filter, name)
291
+ when :after
292
+ Filters::After.new(user_callback.make_lambda, user_conditions, chain_config)
293
+ when :around
294
+ Filters::Around.new(user_callback, user_conditions)
295
+ end
296
+ end
297
+ end
298
+
332
299
  # Wraps code with filter
333
300
  def apply(callback_sequence)
334
- user_conditions = conditions_lambdas
335
- user_callback = CallTemplate.build(@filter, self)
336
-
337
- case kind
338
- when :before
339
- Filters::Before.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter, name)
340
- when :after
341
- Filters::After.build(callback_sequence, user_callback.make_lambda, user_conditions, chain_config)
342
- when :around
343
- callback_sequence.around(user_callback, user_conditions)
344
- end
301
+ compiled.apply(callback_sequence)
345
302
  end
346
303
 
347
304
  def current_scopes
@@ -368,14 +325,16 @@ module ActiveSupport
368
325
  end
369
326
 
370
327
  def conditions_lambdas
371
- @if.map { |c| CallTemplate.build(c, self).make_lambda } +
328
+ conditions =
329
+ @if.map { |c| CallTemplate.build(c, self).make_lambda } +
372
330
  @unless.map { |c| CallTemplate.build(c, self).inverted_lambda }
331
+ conditions.empty? ? EMPTY_ARRAY : conditions
373
332
  end
374
333
  end
375
334
 
376
335
  # A future invocation of user-supplied code (either as a callback,
377
336
  # or a condition filter).
378
- module CallTemplate # :nodoc:
337
+ module CallTemplate # :nodoc: all
379
338
  class MethodCall
380
339
  def initialize(method)
381
340
  @method_name = method
@@ -562,16 +521,18 @@ module ActiveSupport
562
521
  @call_template = call_template
563
522
  @user_conditions = user_conditions
564
523
 
565
- @before = []
566
- @after = []
524
+ @before = nil
525
+ @after = nil
567
526
  end
568
527
 
569
- def before(&before)
528
+ def before(before)
529
+ @before ||= []
570
530
  @before.unshift(before)
571
531
  self
572
532
  end
573
533
 
574
- def after(&after)
534
+ def after(after)
535
+ @after ||= []
575
536
  @after.push(after)
576
537
  self
577
538
  end
@@ -595,11 +556,11 @@ module ActiveSupport
595
556
  end
596
557
 
597
558
  def invoke_before(arg)
598
- @before.each { |b| b.call(arg) }
559
+ @before&.each { |b| b.call(arg) }
599
560
  end
600
561
 
601
562
  def invoke_after(arg)
602
- @after.each { |a| a.call(arg) }
563
+ @after&.each { |a| a.call(arg) }
603
564
  end
604
565
  end
605
566
 
@@ -9,16 +9,19 @@ module ActiveSupport
9
9
  @cache = METHOD_CACHES[namespace]
10
10
  @sources = []
11
11
  @methods = {}
12
+ @canonical_methods = {}
12
13
  end
13
14
 
14
- def define_cached_method(name, as: name)
15
- name = name.to_sym
16
- as = as.to_sym
17
- @methods.fetch(name) do
18
- unless @cache.method_defined?(as)
15
+ def define_cached_method(canonical_name, as: nil)
16
+ canonical_name = canonical_name.to_sym
17
+ as = (as || canonical_name).to_sym
18
+
19
+ @methods.fetch(as) do
20
+ unless @cache.method_defined?(canonical_name) || @canonical_methods[canonical_name]
19
21
  yield @sources
20
22
  end
21
- @methods[name] = as
23
+ @canonical_methods[canonical_name] = true
24
+ @methods[as] = canonical_name
22
25
  end
23
26
  end
24
27
 
@@ -26,8 +29,10 @@ module ActiveSupport
26
29
  unless @sources.empty?
27
30
  @cache.module_eval("# frozen_string_literal: true\n" + @sources.join(";"), path, line)
28
31
  end
29
- @methods.each do |name, as|
30
- owner.define_method(name, @cache.instance_method(as))
32
+ @canonical_methods.clear
33
+
34
+ @methods.each do |as, canonical_name|
35
+ owner.define_method(as, @cache.instance_method(canonical_name))
31
36
  end
32
37
  end
33
38
  end
@@ -52,8 +57,8 @@ module ActiveSupport
52
57
  @namespaces = Hash.new { |h, k| h[k] = MethodSet.new(k) }
53
58
  end
54
59
 
55
- def define_cached_method(name, namespace:, as: name, &block)
56
- @namespaces[namespace].define_cached_method(name, as: as, &block)
60
+ def define_cached_method(canonical_name, namespace:, as: nil, &block)
61
+ @namespaces[namespace].define_cached_method(canonical_name, as: as, &block)
57
62
  end
58
63
 
59
64
  def execute
@@ -104,8 +104,6 @@ class Array
104
104
  end
105
105
  end
106
106
  alias_method :to_formatted_s, :to_fs
107
- alias_method :to_default_s, :to_s
108
- deprecate to_default_s: :to_s, deprecator: ActiveSupport.deprecator
109
107
 
110
108
  # Returns a string that represents the array in XML by invoking +to_xml+
111
109
  # on each element. Active Record collections delegate their representation
@@ -1,43 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/ruby_features"
4
3
  require "active_support/descendants_tracker"
5
4
 
6
5
  class Class
7
- if ActiveSupport::RubyFeatures::CLASS_SUBCLASSES
8
- # Returns an array with all classes that are < than its receiver.
9
- #
10
- # class C; end
11
- # C.descendants # => []
12
- #
13
- # class B < C; end
14
- # C.descendants # => [B]
15
- #
16
- # class A < B; end
17
- # C.descendants # => [B, A]
18
- #
19
- # class D < C; end
20
- # C.descendants # => [B, A, D]
21
- def descendants
22
- subclasses.concat(subclasses.flat_map(&:descendants))
23
- end
24
- else
25
- def descendants
26
- ObjectSpace.each_object(singleton_class).reject do |k|
27
- k.singleton_class? || k == self
28
- end
29
- end
30
-
31
- # Returns an array with the direct children of +self+.
32
- #
33
- # class Foo; end
34
- # class Bar < Foo; end
35
- # class Baz < Bar; end
36
- #
37
- # Foo.subclasses # => [Bar]
38
- def subclasses
39
- descendants.select { |descendant| descendant.superclass == self }
40
- end
6
+ # Returns an array with all classes that are < than its receiver.
7
+ #
8
+ # class C; end
9
+ # C.descendants # => []
10
+ #
11
+ # class B < C; end
12
+ # C.descendants # => [B]
13
+ #
14
+ # class A < B; end
15
+ # C.descendants # => [B, A]
16
+ #
17
+ # class D < C; end
18
+ # C.descendants # => [B, A, D]
19
+ def descendants
20
+ subclasses.concat(subclasses.flat_map(&:descendants))
41
21
  end
42
22
 
43
23
  prepend ActiveSupport::DescendantsTracker::ReloadedClassesFiltering
@@ -11,4 +11,8 @@ class Date # :nodoc:
11
11
  def blank?
12
12
  false
13
13
  end
14
+
15
+ def present?
16
+ true
17
+ end
14
18
  end
@@ -56,8 +56,6 @@ class Date
56
56
  end
57
57
  end
58
58
  alias_method :to_formatted_s, :to_fs
59
- alias_method :to_default_s, :to_s
60
- deprecate to_default_s: :to_s, deprecator: ActiveSupport.deprecator
61
59
 
62
60
  # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005"
63
61
  def readable_inspect
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support/core_ext/module/attribute_accessors"
4
+ require "active_support/core_ext/module/redefine_method"
4
5
 
5
6
  module DateAndTime
6
7
  module Compatibility
@@ -11,7 +12,33 @@ module DateAndTime
11
12
  # of the receiver. For backwards compatibility we're overriding
12
13
  # this behavior, but new apps will have an initializer that sets
13
14
  # this to true, because the new behavior is preferred.
14
- mattr_accessor :preserve_timezone, instance_writer: false, default: false
15
+ mattr_accessor :preserve_timezone, instance_accessor: false, default: nil
16
+
17
+ singleton_class.silence_redefinition_of_method :preserve_timezone
18
+
19
+ #--
20
+ # This re-implements the behaviour of the mattr_reader, instead
21
+ # of prepending on to it, to avoid overcomplicating a module that
22
+ # is in turn included in several places. This will all go away in
23
+ # Rails 8.0 anyway.
24
+ def self.preserve_timezone # :nodoc:
25
+ if @@preserve_timezone.nil?
26
+ # Only warn once, the first time the value is used (which should
27
+ # be the first time #to_time is called).
28
+ ActiveSupport.deprecator.warn(
29
+ "to_time will always preserve the timezone offset of the receiver in Rails 8.0. " \
30
+ "To opt in to the new behavior, set `ActiveSupport.to_time_preserves_timezone = true`."
31
+ )
32
+
33
+ @@preserve_timezone = false
34
+ end
35
+
36
+ @@preserve_timezone
37
+ end
38
+
39
+ def preserve_timezone # :nodoc:
40
+ Compatibility.preserve_timezone
41
+ end
15
42
 
16
43
  # Change the output of <tt>ActiveSupport::TimeZone.utc_to_local</tt>.
17
44
  #
@@ -11,4 +11,8 @@ class DateTime # :nodoc:
11
11
  def blank?
12
12
  false
13
13
  end
14
+
15
+ def present?
16
+ true
17
+ end
14
18
  end
@@ -40,10 +40,6 @@ class DateTime
40
40
  end
41
41
  end
42
42
  alias_method :to_formatted_s, :to_fs
43
- if instance_methods(false).include?(:to_s)
44
- alias_method :to_default_s, :to_s
45
- deprecate to_default_s: :to_s, deprecator: ActiveSupport.deprecator
46
- end
47
43
 
48
44
 
49
45
  # Returns a formatted string of the offset from UTC, or an alternative
@@ -53,6 +53,12 @@ module Digest
53
53
  SecureRandom.uuid
54
54
  end
55
55
 
56
+ # Returns the nil UUID. This is a special form of UUID that is specified to
57
+ # have all 128 bits set to zero.
58
+ def self.nil_uuid
59
+ "00000000-0000-0000-0000-000000000000"
60
+ end
61
+
56
62
  def self.pack_uuid_namespace(namespace)
57
63
  if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
58
64
  namespace
@@ -188,6 +188,11 @@ class ERB
188
188
  else
189
189
  raise NotImplementedError, source.matched
190
190
  end
191
+
192
+ unless source.eos? || source.exist?(start_re) || source.exist?(finish_re)
193
+ tokens << [:TEXT, source.rest]
194
+ source.terminate
195
+ end
191
196
  end
192
197
 
193
198
  tokens
@@ -8,13 +8,13 @@ class Hash
8
8
  # hash.stringify_keys
9
9
  # # => {"name"=>"Rob", "age"=>"28"}
10
10
  def stringify_keys
11
- transform_keys(&:to_s)
11
+ transform_keys { |k| Symbol === k ? k.name : k.to_s }
12
12
  end
13
13
 
14
14
  # Destructively converts all keys to strings. Same as
15
15
  # +stringify_keys+, but modifies +self+.
16
16
  def stringify_keys!
17
- transform_keys!(&:to_s)
17
+ transform_keys! { |k| Symbol === k ? k.name : k.to_s }
18
18
  end
19
19
 
20
20
  # Returns a new hash with all keys converted to symbols, as long as
@@ -82,14 +82,14 @@ class Hash
82
82
  # hash.deep_stringify_keys
83
83
  # # => {"person"=>{"name"=>"Rob", "age"=>"28"}}
84
84
  def deep_stringify_keys
85
- deep_transform_keys(&:to_s)
85
+ deep_transform_keys { |k| Symbol === k ? k.name : k.to_s }
86
86
  end
87
87
 
88
88
  # Destructively converts all keys to strings.
89
89
  # This includes the keys from the root hash and from all
90
90
  # nested hashes and arrays.
91
91
  def deep_stringify_keys!
92
- deep_transform_keys!(&:to_s)
92
+ deep_transform_keys! { |k| Symbol === k ? k.name : k.to_s }
93
93
  end
94
94
 
95
95
  # Returns a new hash with all keys converted to symbols, as long as
@@ -19,16 +19,27 @@ class Module
19
19
  end
20
20
  alias_method :attr_internal, :attr_internal_accessor
21
21
 
22
- class << self; attr_accessor :attr_internal_naming_format end
23
- self.attr_internal_naming_format = "@_%s"
22
+ class << self
23
+ attr_reader :attr_internal_naming_format
24
24
 
25
- private
26
- def attr_internal_ivar_name(attr)
27
- Module.attr_internal_naming_format % attr
25
+ def attr_internal_naming_format=(format)
26
+ if format.start_with?("@")
27
+ ActiveSupport.deprecator.warn <<~MESSAGE
28
+ Setting `attr_internal_naming_format` with a `@` prefix is deprecated and will be removed in Rails 8.0.
29
+
30
+ You can simply replace #{format.inspect} by #{format.delete_prefix("@").inspect}.
31
+ MESSAGE
32
+
33
+ format = format.delete_prefix("@")
34
+ end
35
+ @attr_internal_naming_format = format
28
36
  end
37
+ end
38
+ self.attr_internal_naming_format = "_%s"
29
39
 
40
+ private
30
41
  def attr_internal_define(attr_name, type)
31
- internal_name = attr_internal_ivar_name(attr_name).delete_prefix("@")
42
+ internal_name = Module.attr_internal_naming_format % attr_name
32
43
  # use native attr_* methods as they are faster on some Ruby implementations
33
44
  public_send("attr_#{type}", internal_name)
34
45
  attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer