activesupport 7.2.2.2 → 8.0.3

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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +182 -162
  3. data/README.rdoc +1 -1
  4. data/lib/active_support/backtrace_cleaner.rb +2 -2
  5. data/lib/active_support/benchmark.rb +21 -0
  6. data/lib/active_support/benchmarkable.rb +3 -2
  7. data/lib/active_support/broadcast_logger.rb +61 -74
  8. data/lib/active_support/cache/file_store.rb +14 -4
  9. data/lib/active_support/cache/mem_cache_store.rb +13 -15
  10. data/lib/active_support/cache/memory_store.rb +9 -5
  11. data/lib/active_support/cache/null_store.rb +2 -2
  12. data/lib/active_support/cache/redis_cache_store.rb +6 -3
  13. data/lib/active_support/cache/strategy/local_cache.rb +56 -20
  14. data/lib/active_support/cache.rb +19 -14
  15. data/lib/active_support/callbacks.rb +8 -5
  16. data/lib/active_support/class_attribute.rb +33 -0
  17. data/lib/active_support/code_generator.rb +9 -0
  18. data/lib/active_support/concurrency/share_lock.rb +0 -1
  19. data/lib/active_support/configuration_file.rb +15 -6
  20. data/lib/active_support/core_ext/array/conversions.rb +3 -3
  21. data/lib/active_support/core_ext/benchmark.rb +7 -9
  22. data/lib/active_support/core_ext/class/attribute.rb +24 -20
  23. data/lib/active_support/core_ext/date/conversions.rb +2 -0
  24. data/lib/active_support/core_ext/date_and_time/compatibility.rb +2 -2
  25. data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
  26. data/lib/active_support/core_ext/enumerable.rb +25 -8
  27. data/lib/active_support/core_ext/erb/util.rb +2 -2
  28. data/lib/active_support/core_ext/hash/deep_merge.rb +1 -0
  29. data/lib/active_support/core_ext/hash/except.rb +0 -12
  30. data/lib/active_support/core_ext/module/attr_internal.rb +3 -4
  31. data/lib/active_support/core_ext/module/introspection.rb +3 -0
  32. data/lib/active_support/core_ext/object/json.rb +15 -9
  33. data/lib/active_support/core_ext/object/to_query.rb +2 -1
  34. data/lib/active_support/core_ext/object/try.rb +2 -2
  35. data/lib/active_support/core_ext/range/overlap.rb +3 -3
  36. data/lib/active_support/core_ext/range/sole.rb +17 -0
  37. data/lib/active_support/core_ext/range.rb +1 -0
  38. data/lib/active_support/core_ext/securerandom.rb +24 -8
  39. data/lib/active_support/core_ext/string/filters.rb +3 -3
  40. data/lib/active_support/core_ext/string/multibyte.rb +2 -2
  41. data/lib/active_support/core_ext/thread/backtrace/location.rb +2 -7
  42. data/lib/active_support/core_ext/time/calculations.rb +14 -2
  43. data/lib/active_support/core_ext/time/compatibility.rb +9 -1
  44. data/lib/active_support/core_ext/time/conversions.rb +2 -0
  45. data/lib/active_support/current_attributes.rb +14 -7
  46. data/lib/active_support/delegation.rb +0 -2
  47. data/lib/active_support/dependencies.rb +0 -1
  48. data/lib/active_support/deprecation/reporting.rb +0 -19
  49. data/lib/active_support/deprecation.rb +1 -1
  50. data/lib/active_support/duration.rb +14 -10
  51. data/lib/active_support/encrypted_configuration.rb +20 -2
  52. data/lib/active_support/error_reporter.rb +36 -3
  53. data/lib/active_support/evented_file_update_checker.rb +0 -1
  54. data/lib/active_support/execution_wrapper.rb +1 -1
  55. data/lib/active_support/file_update_checker.rb +1 -1
  56. data/lib/active_support/gem_version.rb +4 -4
  57. data/lib/active_support/hash_with_indifferent_access.rb +34 -31
  58. data/lib/active_support/i18n_railtie.rb +19 -11
  59. data/lib/active_support/isolated_execution_state.rb +0 -2
  60. data/lib/active_support/json/decoding.rb +4 -2
  61. data/lib/active_support/json/encoding.rb +25 -7
  62. data/lib/active_support/lazy_load_hooks.rb +1 -1
  63. data/lib/active_support/logger_thread_safe_level.rb +6 -3
  64. data/lib/active_support/message_encryptors.rb +2 -2
  65. data/lib/active_support/message_verifier.rb +9 -0
  66. data/lib/active_support/message_verifiers.rb +5 -3
  67. data/lib/active_support/messages/rotator.rb +5 -0
  68. data/lib/active_support/multibyte/chars.rb +4 -1
  69. data/lib/active_support/notifications/fanout.rb +0 -1
  70. data/lib/active_support/number_helper.rb +22 -0
  71. data/lib/active_support/railtie.rb +6 -0
  72. data/lib/active_support/tagged_logging.rb +5 -0
  73. data/lib/active_support/testing/assertions.rb +79 -21
  74. data/lib/active_support/testing/isolation.rb +0 -2
  75. data/lib/active_support/testing/time_helpers.rb +2 -1
  76. data/lib/active_support/time_with_zone.rb +22 -13
  77. data/lib/active_support/values/time_zone.rb +11 -9
  78. data/lib/active_support.rb +10 -3
  79. metadata +22 -7
  80. data/lib/active_support/proxy_object.rb +0 -20
  81. data/lib/active_support/testing/strict_warnings.rb +0 -43
@@ -108,15 +108,18 @@ module ActiveSupport
108
108
  # ==== Options
109
109
  #
110
110
  # * <tt>:default</tt> - The default value for the attributes. If the value
111
- # is a proc or lambda, it will be called whenever an instance is
112
- # constructed. Otherwise, the value will be duplicated with +#dup+.
113
- # Default values are re-assigned when the attributes are reset.
111
+ # is a proc or lambda, it will be called whenever an instance is
112
+ # constructed. Otherwise, the value will be duplicated with +#dup+.
113
+ # Default values are re-assigned when the attributes are reset.
114
114
  def attribute(*names, default: NOT_SET)
115
115
  invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES
116
116
  if invalid_attribute_names.any?
117
117
  raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
118
118
  end
119
119
 
120
+ Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
121
+ Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
122
+
120
123
  ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
121
124
  names.each do |name|
122
125
  owner.define_cached_method(name, namespace: :current_attributes) do |batch|
@@ -134,9 +137,6 @@ module ActiveSupport
134
137
  end
135
138
  end
136
139
 
137
- Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
138
- Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
139
-
140
140
  self.defaults = defaults.merge(names.index_with { default })
141
141
  end
142
142
 
@@ -185,9 +185,16 @@ module ActiveSupport
185
185
 
186
186
  def method_added(name)
187
187
  super
188
+
189
+ # We try to generate instance delegators early to not rely on method_missing.
188
190
  return if name == :initialize
191
+
192
+ # If the added method isn't public, we don't delegate it.
189
193
  return unless public_method_defined?(name)
190
- return if respond_to?(name, true)
194
+
195
+ # If we already have a class method by that name, we don't override it.
196
+ return if singleton_class.method_defined?(name) || singleton_class.private_method_defined?(name)
197
+
191
198
  Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
192
199
  end
193
200
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
-
5
3
  module ActiveSupport
6
4
  # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
7
5
  # option is not used.
@@ -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:
@@ -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,6 @@ module ActiveSupport
150
149
  [offending_line.path, offending_line.lineno, offending_line.label]
151
150
  end
152
151
 
153
- def _extract_callstack(callstack)
154
- ActiveSupport.deprecator.warn(<<~MESSAGE)
155
- Passing the result of `caller` to ActiveSupport::Deprecation#warn is deprecated and will be removed in Rails 8.0.
156
-
157
- Please pass the result of `caller_locations` instead.
158
- MESSAGE
159
-
160
- offending_line = callstack.find { |line| !ignored_callstack?(line) } || callstack.first
161
-
162
- if offending_line
163
- if md = offending_line.match(/^(.+?):(\d+)(?::in `(.*?)')?/)
164
- md.captures
165
- else
166
- offending_line
167
- end
168
- end
169
- end
170
-
171
152
  RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/" # :nodoc:
172
153
  LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
173
154
 
@@ -68,7 +68,7 @@ module ActiveSupport
68
68
  # and the second is a library name.
69
69
  #
70
70
  # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
71
- def initialize(deprecation_horizon = "8.0", gem_name = "Rails")
71
+ def initialize(deprecation_horizon = "8.1", gem_name = "Rails")
72
72
  self.gem_name = gem_name
73
73
  self.deprecation_horizon = deprecation_horizon
74
74
  # By default, warnings are not silenced and debugging is off.
@@ -491,17 +491,21 @@ module ActiveSupport
491
491
  if @parts.empty?
492
492
  time.since(sign * value)
493
493
  else
494
- @parts.inject(time) do |t, (type, number)|
495
- if type == :seconds
496
- t.since(sign * number)
497
- elsif type == :minutes
498
- t.since(sign * number * 60)
499
- elsif type == :hours
500
- t.since(sign * number * 3600)
501
- else
502
- t.advance(type => sign * number)
503
- end
494
+ @parts.each do |type, number|
495
+ t = time
496
+ time =
497
+ if type == :seconds
498
+ t.since(sign * number)
499
+ elsif type == :minutes
500
+ t.since(sign * number * 60)
501
+ elsif type == :hours
502
+ t.since(sign * number * 3600)
503
+ else
504
+ t.advance(type => sign * number)
505
+ end
504
506
  end
507
+
508
+ time
505
509
  end
506
510
  end
507
511
 
@@ -43,6 +43,12 @@ module ActiveSupport
43
43
  end
44
44
  end
45
45
 
46
+ class InvalidKeyError < RuntimeError
47
+ def initialize(content_path, key)
48
+ super "Key '#{key}' is invalid, it must respond to '#to_sym' from configuration in '#{content_path}'."
49
+ end
50
+ end
51
+
46
52
  delegate_missing_to :options
47
53
 
48
54
  def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
@@ -61,7 +67,11 @@ module ActiveSupport
61
67
  end
62
68
 
63
69
  def validate! # :nodoc:
64
- deserialize(read)
70
+ deserialize(read).each_key do |key|
71
+ key.to_sym
72
+ rescue NoMethodError
73
+ raise InvalidKeyError.new(content_path, key)
74
+ end
65
75
  end
66
76
 
67
77
  # Returns the decrypted content as a Hash with symbolized keys.
@@ -73,7 +83,7 @@ module ActiveSupport
73
83
  # # => { some_secret: 123, some_namespace: { another_secret: 789 } }
74
84
  #
75
85
  def config
76
- @config ||= deserialize(read).deep_symbolize_keys
86
+ @config ||= deep_symbolize_keys(deserialize(read))
77
87
  end
78
88
 
79
89
  def inspect # :nodoc:
@@ -81,6 +91,14 @@ module ActiveSupport
81
91
  end
82
92
 
83
93
  private
94
+ def deep_symbolize_keys(hash)
95
+ hash.deep_transform_keys do |key|
96
+ key.to_sym
97
+ rescue NoMethodError
98
+ raise InvalidKeyError.new(content_path, key)
99
+ end
100
+ end
101
+
84
102
  def deep_transform(hash)
85
103
  return hash unless hash.is_a?(Hash)
86
104
 
@@ -144,9 +144,9 @@ module ActiveSupport
144
144
  #
145
145
  def unexpected(error, severity: :warning, context: {}, source: DEFAULT_SOURCE)
146
146
  error = RuntimeError.new(error) if error.is_a?(String)
147
- error.set_backtrace(caller(1)) if error.backtrace.nil?
148
147
 
149
148
  if @debug_mode
149
+ ensure_backtrace(error)
150
150
  raise UnexpectedError, "#{error.class.name}: #{error.message}", error.backtrace, cause: error
151
151
  else
152
152
  report(error, handled: true, severity: severity, context: context, source: source)
@@ -207,8 +207,15 @@ module ActiveSupport
207
207
  #
208
208
  # Rails.error.report(error)
209
209
  #
210
+ # The +error+ argument must be an instance of Exception.
211
+ #
212
+ # Rails.error.report(Exception.new("Something went wrong"))
213
+ #
214
+ # Otherwise you can use #unexpected to report an error which does accept a
215
+ # string argument.
210
216
  def report(error, handled: true, severity: handled ? :warning : :error, context: {}, source: DEFAULT_SOURCE)
211
217
  return if error.instance_variable_defined?(:@__rails_error_reported)
218
+ ensure_backtrace(error)
212
219
 
213
220
  unless SEVERITIES.include?(severity)
214
221
  raise ArgumentError, "severity must be one of #{SEVERITIES.map(&:inspect).join(", ")}, got: #{severity.inspect}"
@@ -231,11 +238,37 @@ module ActiveSupport
231
238
  end
232
239
  end
233
240
 
234
- unless error.frozen?
235
- error.instance_variable_set(:@__rails_error_reported, true)
241
+ while error
242
+ unless error.frozen?
243
+ error.instance_variable_set(:@__rails_error_reported, true)
244
+ end
245
+ error = error.cause
236
246
  end
237
247
 
238
248
  nil
239
249
  end
250
+
251
+ private
252
+ def ensure_backtrace(error)
253
+ return if error.frozen? # re-raising won't add a backtrace
254
+ return unless error.backtrace.nil?
255
+
256
+ begin
257
+ # We could use Exception#set_backtrace, but until Ruby 3.4
258
+ # it only support setting `Exception#backtrace` and not
259
+ # `Exception#backtrace_locations`. So raising the exception
260
+ # is a good way to build a real backtrace.
261
+ raise error
262
+ rescue error.class => error
263
+ end
264
+
265
+ count = 0
266
+ while error.backtrace_locations.first&.path == __FILE__
267
+ count += 1
268
+ error.backtrace_locations.shift
269
+ end
270
+
271
+ error.backtrace.shift(count)
272
+ end
240
273
  end
241
274
  end
@@ -3,7 +3,6 @@
3
3
  gem "listen", "~> 3.5"
4
4
  require "listen"
5
5
 
6
- require "set"
7
6
  require "pathname"
8
7
  require "concurrent/atomic/atomic_boolean"
9
8
 
@@ -89,7 +89,7 @@ module ActiveSupport
89
89
  instance = run!
90
90
  begin
91
91
  yield
92
- rescue => error
92
+ rescue Exception => error
93
93
  error_reporter&.report(error, handled: false, source: source)
94
94
  raise
95
95
  ensure
@@ -120,7 +120,7 @@ module ActiveSupport
120
120
  # healthy to consider this edge case because with mtimes in the future
121
121
  # reloading is not triggered.
122
122
  def max_mtime(paths)
123
- time_now = Time.now
123
+ time_now = Time.at(0, Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond), :nanosecond)
124
124
  max_mtime = nil
125
125
 
126
126
  # Time comparisons are performed with #compare_without_coercion because
@@ -7,10 +7,10 @@ module ActiveSupport
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 7
11
- MINOR = 2
12
- TINY = 2
13
- PRE = "2"
10
+ MAJOR = 8
11
+ MINOR = 0
12
+ TINY = 3
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -262,9 +262,7 @@ module ActiveSupport
262
262
  # hash[:a][:c] # => "c"
263
263
  # dup[:a][:c] # => "c"
264
264
  def dup
265
- self.class.new(self).tap do |new_hash|
266
- set_defaults(new_hash)
267
- end
265
+ copy_defaults(self.class.new(self))
268
266
  end
269
267
 
270
268
  # This method has the same semantics of +update+, except it does not
@@ -313,10 +311,6 @@ module ActiveSupport
313
311
  end
314
312
  alias_method :without, :except
315
313
 
316
- def stringify_keys!; self end
317
- def deep_stringify_keys!; self end
318
- def stringify_keys; dup end
319
- def deep_stringify_keys; dup end
320
314
  undef :symbolize_keys!
321
315
  undef :deep_symbolize_keys!
322
316
  def symbolize_keys; to_hash.symbolize_keys! end
@@ -342,21 +336,26 @@ module ActiveSupport
342
336
  NOT_GIVEN = Object.new # :nodoc:
343
337
 
344
338
  def transform_keys(hash = NOT_GIVEN, &block)
345
- return to_enum(:transform_keys) if NOT_GIVEN.equal?(hash) && !block_given?
346
- dup.tap { |h| h.transform_keys!(hash, &block) }
339
+ if NOT_GIVEN.equal?(hash)
340
+ if block_given?
341
+ self.class.new(super(&block))
342
+ else
343
+ to_enum(:transform_keys)
344
+ end
345
+ else
346
+ self.class.new(super)
347
+ end
347
348
  end
348
349
 
349
350
  def transform_keys!(hash = NOT_GIVEN, &block)
350
- return to_enum(:transform_keys!) if NOT_GIVEN.equal?(hash) && !block_given?
351
-
352
- if hash.nil?
353
- super
354
- elsif NOT_GIVEN.equal?(hash)
355
- keys.each { |key| self[yield(key)] = delete(key) }
356
- elsif block_given?
357
- keys.each { |key| self[hash[key] || yield(key)] = delete(key) }
351
+ if NOT_GIVEN.equal?(hash)
352
+ if block_given?
353
+ replace(copy_defaults(transform_keys(&block)))
354
+ else
355
+ return to_enum(:transform_keys!)
356
+ end
358
357
  else
359
- keys.each { |key| self[hash[key] || key] = delete(key) }
358
+ replace(copy_defaults(transform_keys(hash, &block)))
360
359
  end
361
360
 
362
361
  self
@@ -378,13 +377,9 @@ module ActiveSupport
378
377
 
379
378
  # Convert to a regular hash with string keys.
380
379
  def to_hash
381
- _new_hash = Hash.new
382
- set_defaults(_new_hash)
383
-
384
- each do |key, value|
385
- _new_hash[key] = convert_value(value, conversion: :to_hash)
386
- end
387
- _new_hash
380
+ copy = Hash[self]
381
+ copy.transform_values! { |v| convert_value_to_hash(v) }
382
+ copy_defaults(copy)
388
383
  end
389
384
 
390
385
  def to_proc
@@ -398,11 +393,7 @@ module ActiveSupport
398
393
 
399
394
  def convert_value(value, conversion: nil)
400
395
  if value.is_a? Hash
401
- if conversion == :to_hash
402
- value.to_hash
403
- else
404
- value.nested_under_indifferent_access
405
- end
396
+ value.nested_under_indifferent_access
406
397
  elsif value.is_a?(Array)
407
398
  if conversion != :assignment || value.frozen?
408
399
  value = value.dup
@@ -413,12 +404,24 @@ module ActiveSupport
413
404
  end
414
405
  end
415
406
 
416
- def set_defaults(target)
407
+ def convert_value_to_hash(value)
408
+ if value.is_a? Hash
409
+ value.to_hash
410
+ elsif value.is_a?(Array)
411
+ value.map { |e| convert_value_to_hash(e) }
412
+ else
413
+ value
414
+ end
415
+ end
416
+
417
+
418
+ def copy_defaults(target)
417
419
  if default_proc
418
420
  target.default_proc = default_proc.dup
419
421
  else
420
422
  target.default = default
421
423
  end
424
+ target
422
425
  end
423
426
 
424
427
  def update_with_single_argument(other_hash, block)
@@ -14,15 +14,18 @@ module I18n
14
14
 
15
15
  config.eager_load_namespaces << I18n
16
16
 
17
- # Set the i18n configuration after initialization since a lot of
18
- # configuration is still usually done in application initializers.
19
- config.after_initialize do |app|
17
+ # Make sure i18n is ready before eager loading, in case any eager loaded
18
+ # code needs it.
19
+ config.before_eager_load do |app|
20
20
  I18n::Railtie.initialize_i18n(app)
21
21
  end
22
22
 
23
- # Trigger i18n config before any eager loading has happened
24
- # so it's ready if any classes require it when eager loaded.
25
- config.before_eager_load do |app|
23
+ # i18n initialization needs to run after application initialization, since
24
+ # initializers may configure i18n.
25
+ #
26
+ # If the application eager loaded, this was done on before_eager_load. The
27
+ # hook is still OK, though, because initialize_i18n is idempotent.
28
+ config.after_initialize do |app|
26
29
  I18n::Railtie.initialize_i18n(app)
27
30
  end
28
31
 
@@ -49,7 +52,8 @@ module I18n
49
52
  when :load_path
50
53
  I18n.load_path += value
51
54
  when :raise_on_missing_translations
52
- setup_raise_on_missing_translations_config(app)
55
+ strict = value == :strict
56
+ setup_raise_on_missing_translations_config(app, strict)
53
57
  else
54
58
  I18n.public_send("#{setting}=", value)
55
59
  end
@@ -62,8 +66,9 @@ module I18n
62
66
 
63
67
  if app.config.reloading_enabled?
64
68
  directories = watched_dirs_with_extensions(reloadable_paths)
65
- reloader = app.config.file_watcher.new(I18n.load_path.dup, directories) do
66
- I18n.load_path.keep_if { |p| File.exist?(p) }
69
+ root_load_paths = I18n.load_path.select { |path| path.to_s.start_with?(Rails.root.to_s) }
70
+ reloader = app.config.file_watcher.new(root_load_paths, directories) do
71
+ I18n.load_path.delete_if { |path| path.to_s.start_with?(Rails.root.to_s) && !File.exist?(path) }
67
72
  I18n.load_path |= reloadable_paths.flat_map(&:existent)
68
73
  end
69
74
 
@@ -71,17 +76,20 @@ module I18n
71
76
  app.reloader.to_run do
72
77
  reloader.execute_if_updated { require_unload_lock! }
73
78
  end
74
- reloader.execute
75
79
  end
76
80
 
77
81
  @i18n_inited = true
78
82
  end
79
83
 
80
- def self.setup_raise_on_missing_translations_config(app)
84
+ def self.setup_raise_on_missing_translations_config(app, strict)
81
85
  ActiveSupport.on_load(:action_view) do
82
86
  ActionView::Helpers::TranslationHelper.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations
83
87
  end
84
88
 
89
+ ActiveSupport.on_load(:active_model_translation) do
90
+ ActiveModel::Translation.raise_on_missing_translations = app.config.i18n.raise_on_missing_translations if strict
91
+ end
92
+
85
93
  if app.config.i18n.raise_on_missing_translations &&
86
94
  I18n.exception_handler.is_a?(I18n::ExceptionHandler) # Only override the i18n gem's default exception handler.
87
95
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "fiber"
4
-
5
3
  module ActiveSupport
6
4
  module IsolatedExecutionState # :nodoc:
7
5
  @isolation_level = nil
@@ -14,11 +14,13 @@ module ActiveSupport
14
14
  DATETIME_REGEX = /\A(?:\d{4}-\d{2}-\d{2}|\d{4}-\d{1,2}-\d{1,2}[T \t]+\d{1,2}:\d{2}:\d{2}(\.[0-9]*)?(([ \t]*)Z|[-+]\d{2}?(:\d{2})?)?)\z/
15
15
 
16
16
  class << self
17
- # Parses a JSON string (JavaScript Object Notation) into a hash.
17
+ # Parses a JSON string (JavaScript Object Notation) into a Ruby object.
18
18
  # See http://www.json.org for more info.
19
19
  #
20
20
  # ActiveSupport::JSON.decode("{\"team\":\"rails\",\"players\":\"36\"}")
21
- # => {"team" => "rails", "players" => "36"}
21
+ # # => {"team" => "rails", "players" => "36"}
22
+ # ActiveSupport::JSON.decode("2.39")
23
+ # # => 2.39
22
24
  def decode(json)
23
25
  data = ::JSON.parse(json, quirks_mode: true)
24
26
 
@@ -13,12 +13,30 @@ module ActiveSupport
13
13
  end
14
14
 
15
15
  module JSON
16
- # Dumps objects in JSON (JavaScript Object Notation).
17
- # See http://www.json.org for more info.
18
- #
19
- # ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
20
- # # => "{\"team\":\"rails\",\"players\":\"36\"}"
21
16
  class << self
17
+ # Dumps objects in JSON (JavaScript Object Notation).
18
+ # See http://www.json.org for more info.
19
+ #
20
+ # ActiveSupport::JSON.encode({ team: 'rails', players: '36' })
21
+ # # => "{\"team\":\"rails\",\"players\":\"36\"}"
22
+ #
23
+ # Generates JSON that is safe to include in JavaScript as it escapes
24
+ # U+2028 (Line Separator) and U+2029 (Paragraph Separator):
25
+ #
26
+ # ActiveSupport::JSON.encode({ key: "\u2028" })
27
+ # # => "{\"key\":\"\\u2028\"}"
28
+ #
29
+ # By default, it also generates JSON that is safe to include in HTML, as
30
+ # it escapes <tt><</tt>, <tt>></tt>, and <tt>&</tt>:
31
+ #
32
+ # ActiveSupport::JSON.encode({ key: "<>&" })
33
+ # # => "{\"key\":\"\\u003c\\u003e\\u0026\"}"
34
+ #
35
+ # This can be changed with the +escape_html_entities+ option, or the
36
+ # global escape_html_entities_in_json configuration option.
37
+ #
38
+ # ActiveSupport::JSON.encode({ key: "<>&" }, escape_html_entities: false)
39
+ # # => "{\"key\":\"<>&\"}"
22
40
  def encode(value, options = nil)
23
41
  Encoding.json_encoder.new(options).encode(value)
24
42
  end
@@ -36,14 +54,14 @@ module ActiveSupport
36
54
  # Encode the given object into a JSON string
37
55
  def encode(value)
38
56
  unless options.empty?
39
- value = value.as_json(options.dup)
57
+ value = value.as_json(options.dup.freeze)
40
58
  end
41
59
  json = stringify(jsonify(value))
42
60
 
43
61
  # Rails does more escaping than the JSON gem natively does (we
44
62
  # escape \u2028 and \u2029 and optionally >, <, & to work around
45
63
  # certain browser problems).
46
- if Encoding.escape_html_entities_in_json
64
+ if @options.fetch(:escape_html_entities, Encoding.escape_html_entities_in_json)
47
65
  json.gsub!(">", '\u003e')
48
66
  json.gsub!("<", '\u003c')
49
67
  json.gsub!("&", '\u0026')
@@ -53,7 +53,7 @@ module ActiveSupport
53
53
  # loaded. If the component has already loaded, the block is executed
54
54
  # immediately.
55
55
  #
56
- # Options:
56
+ # ==== Options
57
57
  #
58
58
  # * <tt>:yield</tt> - Yields the object that run_load_hooks to +block+.
59
59
  # * <tt>:run_once</tt> - Given +block+ will run only once.
@@ -7,6 +7,11 @@ module ActiveSupport
7
7
  module LoggerThreadSafeLevel # :nodoc:
8
8
  extend ActiveSupport::Concern
9
9
 
10
+ def initialize(...)
11
+ super
12
+ @local_level_key = :"logger_thread_safe_level_#{object_id}"
13
+ end
14
+
10
15
  def local_level
11
16
  IsolatedExecutionState[local_level_key]
12
17
  end
@@ -40,8 +45,6 @@ module ActiveSupport
40
45
  end
41
46
 
42
47
  private
43
- def local_level_key
44
- @local_level_key ||= :"logger_thread_safe_level_#{object_id}"
45
- end
48
+ attr_reader :local_level_key
46
49
  end
47
50
  end
@@ -28,8 +28,8 @@ module ActiveSupport
28
28
  # <tt>transitional = false</tt>.
29
29
 
30
30
  ##
31
- # :method: initialize
32
- # :call-seq: initialize(&secret_generator)
31
+ # :singleton-method: new
32
+ # :call-seq: new(&secret_generator)
33
33
  #
34
34
  # Initializes a new instance. +secret_generator+ must accept a salt and a
35
35
  # +secret_length+ kwarg, and return a suitable secret (string) or secrets
@@ -154,6 +154,8 @@ module ActiveSupport
154
154
  # not URL-safe. In other words, they can contain "+" and "/". If you want to
155
155
  # generate URL-safe strings (in compliance with "Base 64 Encoding with URL
156
156
  # and Filename Safe Alphabet" in RFC 4648), you can pass +true+.
157
+ # Note that MessageVerifier will always accept both URL-safe and URL-unsafe
158
+ # encoded messages, to allow a smooth transition between the two settings.
157
159
  #
158
160
  # [+:force_legacy_metadata_serializer+]
159
161
  # Whether to use the legacy metadata serializer, which serializes the
@@ -318,6 +320,13 @@ module ActiveSupport
318
320
  end
319
321
 
320
322
  private
323
+ def decode(encoded, url_safe: @url_safe)
324
+ catch :invalid_message_format do
325
+ return super
326
+ end
327
+ super(encoded, url_safe: !url_safe)
328
+ end
329
+
321
330
  def sign_encoded(encoded)
322
331
  digest = generate_digest(encoded)
323
332
  encoded << SEPARATOR << digest
@@ -28,8 +28,8 @@ module ActiveSupport
28
28
  # <tt>transitional = false</tt>.
29
29
 
30
30
  ##
31
- # :method: initialize
32
- # :call-seq: initialize(&secret_generator)
31
+ # :singleton-method: new
32
+ # :call-seq: new(&secret_generator)
33
33
  #
34
34
  # Initializes a new instance. +secret_generator+ must accept a salt, and
35
35
  # return a suitable secret (string). +secret_generator+ may also accept
@@ -59,7 +59,9 @@ module ActiveSupport
59
59
 
60
60
  ##
61
61
  # :method: rotate
62
- # :call-seq: rotate(**options)
62
+ # :call-seq:
63
+ # rotate(**options)
64
+ # rotate(&block)
63
65
  #
64
66
  # Adds +options+ to the list of option sets. Messages will be signed using
65
67
  # the first set in the list. When verifying, however, each set will be