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
@@ -102,12 +102,12 @@ module ActiveSupport
102
102
  raise_parsing_error("is empty duration") if parts.empty?
103
103
 
104
104
  # Mixing any of Y, M, D with W is invalid.
105
- if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
105
+ if parts.key?(:weeks) && parts.keys.intersect?(DATE_COMPONENTS)
106
106
  raise_parsing_error("mixing weeks with other date parts not allowed")
107
107
  end
108
108
 
109
109
  # Specifying an empty T part is invalid.
110
- if mode == :time && (parts.keys & TIME_COMPONENTS).empty?
110
+ if mode == :time && !parts.keys.intersect?(TIME_COMPONENTS)
111
111
  raise_parsing_error("time part marker is present but time part is empty")
112
112
  end
113
113
 
@@ -35,7 +35,6 @@ module ActiveSupport
35
35
  # Return pair of duration's parts and whole duration sign.
36
36
  # Parts are summarized (as they can become repetitive due to addition, etc).
37
37
  # Zero parts are removed as not significant.
38
- # If all parts are negative it will negate all of them and return minus as a sign.
39
38
  def normalize
40
39
  parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
41
40
  p[k] += v unless v.zero?
@@ -50,7 +49,7 @@ module ActiveSupport
50
49
  end
51
50
 
52
51
  def week_mixed_with_date?(parts)
53
- parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
52
+ parts.key?(:weeks) && parts.keys.intersect?(DATE_COMPONENTS)
54
53
  end
55
54
 
56
55
  def format_seconds(seconds)
@@ -14,7 +14,7 @@ module ActiveSupport
14
14
  class Duration
15
15
  class Scalar < Numeric # :nodoc:
16
16
  attr_reader :value
17
- delegate :to_i, :to_f, :to_s, to: :value
17
+ delegate :to_i, :to_f, :to_s, to: :@value
18
18
 
19
19
  def initialize(value)
20
20
  @value = value
@@ -221,6 +221,8 @@ module ActiveSupport
221
221
  end
222
222
  end
223
223
 
224
+ Delegation.generate(self, [:to_f, :positive?, :negative?, :zero?, :abs], to: :@value, as: Integer, nilable: false)
225
+
224
226
  def initialize(value, parts, variable = nil) # :nodoc:
225
227
  @value, @parts = value, parts
226
228
  @parts.reject! { |k, v| v.zero? } unless value == 0
@@ -232,7 +234,10 @@ module ActiveSupport
232
234
  end
233
235
  end
234
236
 
235
- # Returns a copy of the parts hash that defines the duration
237
+ # Returns a copy of the parts hash that defines the duration.
238
+ #
239
+ # 5.minutes.parts # => {:minutes=>5}
240
+ # 3.years.parts # => {:years=>3}
236
241
  def parts
237
242
  @parts.dup
238
243
  end
@@ -366,8 +371,8 @@ module ActiveSupport
366
371
  # 1.year.to_i # => 31556952
367
372
  #
368
373
  # In such cases, Ruby's core
369
- # Date[https://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
370
- # Time[https://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
374
+ # Date[https://docs.ruby-lang.org/en/master/Date.html] and
375
+ # Time[https://docs.ruby-lang.org/en/master/Time.html] should be used for precision
371
376
  # date and time arithmetic.
372
377
  def to_i
373
378
  @value.to_i
@@ -504,8 +509,8 @@ module ActiveSupport
504
509
  value.respond_to?(method)
505
510
  end
506
511
 
507
- def method_missing(method, *args, &block)
508
- value.public_send(method, *args, &block)
512
+ def method_missing(...)
513
+ value.public_send(...)
509
514
  end
510
515
 
511
516
  def raise_type_error(other)
@@ -26,12 +26,16 @@ module ActiveSupport
26
26
  class ErrorReporter
27
27
  SEVERITIES = %i(error warning info)
28
28
  DEFAULT_SOURCE = "application"
29
+ DEFAULT_RESCUE = [StandardError].freeze
29
30
 
30
- attr_accessor :logger
31
+ attr_accessor :logger, :debug_mode
32
+
33
+ UnexpectedError = Class.new(Exception)
31
34
 
32
35
  def initialize(*subscribers, logger: nil)
33
36
  @subscribers = subscribers.flatten
34
37
  @logger = logger
38
+ @debug_mode = false
35
39
  end
36
40
 
37
41
  # Evaluates the given block, reporting and swallowing any unhandled error.
@@ -72,7 +76,7 @@ module ActiveSupport
72
76
  # source of the error. Subscribers can use this value to ignore certain
73
77
  # errors. Defaults to <tt>"application"</tt>.
74
78
  def handle(*error_classes, severity: :warning, context: {}, fallback: nil, source: DEFAULT_SOURCE)
75
- error_classes = [StandardError] if error_classes.blank?
79
+ error_classes = DEFAULT_RESCUE if error_classes.empty?
76
80
  yield
77
81
  rescue *error_classes => error
78
82
  report(error, handled: true, severity: severity, context: context, source: source)
@@ -108,13 +112,47 @@ module ActiveSupport
108
112
  # source of the error. Subscribers can use this value to ignore certain
109
113
  # errors. Defaults to <tt>"application"</tt>.
110
114
  def record(*error_classes, severity: :error, context: {}, source: DEFAULT_SOURCE)
111
- error_classes = [StandardError] if error_classes.blank?
115
+ error_classes = DEFAULT_RESCUE if error_classes.empty?
112
116
  yield
113
117
  rescue *error_classes => error
114
118
  report(error, handled: false, severity: severity, context: context, source: source)
115
119
  raise
116
120
  end
117
121
 
122
+ # Either report the given error when in production, or raise it when in development or test.
123
+ #
124
+ # When called in production, after the error is reported, this method will return
125
+ # nil and execution will continue.
126
+ #
127
+ # When called in development, the original error is wrapped in a different error class to ensure
128
+ # it's not being rescued higher in the stack and will be surfaced to the developer.
129
+ #
130
+ # This method is intended for reporting violated assertions about preconditions, or similar
131
+ # cases that can and should be gracefully handled in production, but that aren't supposed to happen.
132
+ #
133
+ # The error can be either an exception instance or a String.
134
+ #
135
+ # example:
136
+ #
137
+ # def edit
138
+ # if published?
139
+ # Rails.error.unexpected("[BUG] Attempting to edit a published article, that shouldn't be possible")
140
+ # return false
141
+ # end
142
+ # # ...
143
+ # end
144
+ #
145
+ def unexpected(error, severity: :warning, context: {}, source: DEFAULT_SOURCE)
146
+ error = RuntimeError.new(error) if error.is_a?(String)
147
+ error.set_backtrace(caller(1)) if error.backtrace.nil?
148
+
149
+ if @debug_mode
150
+ raise UnexpectedError, "#{error.class.name}: #{error.message}", error.backtrace, cause: error
151
+ else
152
+ report(error, handled: true, severity: severity, context: context, source: source)
153
+ end
154
+ end
155
+
118
156
  # Register a new error subscriber. The subscriber must respond to
119
157
  #
120
158
  # report(Exception, handled: Boolean, severity: (:error OR :warning OR :info), context: Hash, source: String)
@@ -6,7 +6,6 @@ require "listen"
6
6
  require "set"
7
7
  require "pathname"
8
8
  require "concurrent/atomic/atomic_boolean"
9
- require "active_support/fork_tracker"
10
9
 
11
10
  module ActiveSupport
12
11
  # Allows you to "listen" to changes in a file system.
@@ -2,7 +2,6 @@
2
2
 
3
3
  require "active_support/error_reporter"
4
4
  require "active_support/callbacks"
5
- require "concurrent/hash"
6
5
 
7
6
  module ActiveSupport
8
7
  class ExecutionWrapper
@@ -104,7 +104,7 @@ module ActiveSupport
104
104
  @watched || begin
105
105
  all = @files.select { |f| File.exist?(f) }
106
106
  all.concat(Dir[@glob]) if @glob
107
- all
107
+ all.tap(&:uniq!)
108
108
  end
109
109
  end
110
110
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveSupport
4
4
  module ForkTracker # :nodoc:
5
- module ModernCoreExt
5
+ module CoreExt
6
6
  def _fork
7
7
  pid = super
8
8
  if pid == 0
@@ -12,27 +12,6 @@ module ActiveSupport
12
12
  end
13
13
  end
14
14
 
15
- module CoreExt
16
- def fork(...)
17
- if block_given?
18
- super do
19
- ForkTracker.check!
20
- yield
21
- end
22
- else
23
- unless pid = super
24
- ForkTracker.check!
25
- end
26
- pid
27
- end
28
- end
29
- end
30
-
31
- module CoreExtPrivate
32
- include CoreExt
33
- private :fork
34
- end
35
-
36
15
  @pid = Process.pid
37
16
  @callbacks = []
38
17
 
@@ -45,23 +24,8 @@ module ActiveSupport
45
24
  end
46
25
  end
47
26
 
48
- if Process.respond_to?(:_fork) # Ruby 3.1+
49
- def check!
50
- # We trust the `_fork` callback
51
- end
52
- else
53
- alias_method :check!, :after_fork_callback
54
- end
55
-
56
27
  def hook!
57
- if Process.respond_to?(:_fork) # Ruby 3.1+
58
- ::Process.singleton_class.prepend(ModernCoreExt)
59
- elsif Process.respond_to?(:fork)
60
- ::Object.prepend(CoreExtPrivate) if RUBY_VERSION < "3.0"
61
- ::Kernel.prepend(CoreExtPrivate)
62
- ::Kernel.singleton_class.prepend(CoreExt)
63
- ::Process.singleton_class.prepend(CoreExt)
64
- end
28
+ ::Process.singleton_class.prepend(CoreExt)
65
29
  end
66
30
 
67
31
  def after_fork(&block)
@@ -8,9 +8,9 @@ module ActiveSupport
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 7
11
- MINOR = 1
12
- TINY = 3
13
- PRE = "4"
11
+ MINOR = 2
12
+ TINY = 1
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -387,15 +387,13 @@ module ActiveSupport
387
387
  _new_hash
388
388
  end
389
389
 
390
+ def to_proc
391
+ proc { |key| self[key] }
392
+ end
393
+
390
394
  private
391
- if Symbol.method_defined?(:name)
392
- def convert_key(key)
393
- key.kind_of?(Symbol) ? key.name : key
394
- end
395
- else
396
- def convert_key(key)
397
- key.kind_of?(Symbol) ? key.to_s : key
398
- end
395
+ def convert_key(key)
396
+ Symbol === key ? key.name : key
399
397
  end
400
398
 
401
399
  def convert_value(value, conversion: nil)
@@ -9,11 +9,14 @@ module ActiveSupport
9
9
  html_safe_options = html_escape_translation_options(options)
10
10
 
11
11
  exception = false
12
+
12
13
  exception_handler = ->(*args) do
13
14
  exception = true
14
15
  I18n.exception_handler.call(*args)
15
16
  end
17
+
16
18
  translation = I18n.translate(key, **html_safe_options, exception_handler: exception_handler)
19
+
17
20
  if exception
18
21
  translation
19
22
  else
@@ -24,11 +27,11 @@ module ActiveSupport
24
27
  end
25
28
  end
26
29
 
27
- private
28
- def html_safe_translation_key?(key)
29
- /(?:_|\b)html\z/.match?(key)
30
- end
30
+ def html_safe_translation_key?(key)
31
+ /(?:_|\b)html\z/.match?(key)
32
+ end
31
33
 
34
+ private
32
35
  def html_escape_translation_options(options)
33
36
  options.each do |name, value|
34
37
  unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
@@ -76,7 +76,7 @@ module ActiveSupport
76
76
  when Hash
77
77
  result = {}
78
78
  value.each do |k, v|
79
- k = k.to_s unless String === k
79
+ k = k.to_s unless Symbol === k || String === k
80
80
  result[k] = jsonify(v)
81
81
  end
82
82
  result
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "active_support/core_ext/module/attribute_accessors"
4
4
  require "active_support/core_ext/class/attribute"
5
+ require "active_support/core_ext/enumerable"
5
6
  require "active_support/subscriber"
6
7
  require "active_support/deprecation/proxy_wrappers"
7
8
 
@@ -61,10 +62,6 @@ module ActiveSupport
61
62
  # that all logs are flushed, and it is called in Rails::Rack::Logger after a
62
63
  # request finishes.
63
64
  class LogSubscriber < Subscriber
64
- # Embed in a String to clear all previous ANSI sequences.
65
- CLEAR = ActiveSupport::Deprecation::DeprecatedObjectProxy.new("\e[0m", "CLEAR is deprecated! Use MODES[:clear] instead.", ActiveSupport.deprecator)
66
- BOLD = ActiveSupport::Deprecation::DeprecatedObjectProxy.new("\e[1m", "BOLD is deprecated! Use MODES[:bold] instead.", ActiveSupport.deprecator)
67
-
68
65
  # ANSI sequence modes
69
66
  MODES = {
70
67
  clear: 0,
@@ -181,14 +178,6 @@ module ActiveSupport
181
178
  end
182
179
 
183
180
  def mode_from(options)
184
- if options.is_a?(TrueClass) || options.is_a?(FalseClass)
185
- ActiveSupport.deprecator.warn(<<~MSG.squish)
186
- Bolding log text with a positional boolean is deprecated and will be removed
187
- in Rails 7.2. Use an option hash instead (eg. `color("my text", :red, bold: true)`).
188
- MSG
189
- options = { bold: options }
190
- end
191
-
192
181
  modes = MODES.values_at(*options.compact_blank.keys)
193
182
 
194
183
  "\e[#{modes.join(";")}m" if modes.any?
@@ -13,6 +13,10 @@ module ActiveSupport
13
13
  # logger = Logger.new(STDOUT)
14
14
  # ActiveSupport::Logger.logger_outputs_to?(logger, STDOUT)
15
15
  # # => true
16
+ #
17
+ # logger = Logger.new('/var/log/rails.log')
18
+ # ActiveSupport::Logger.logger_outputs_to?(logger, '/var/log/rails.log')
19
+ # # => true
16
20
  def self.logger_outputs_to?(logger, *sources)
17
21
  loggers = if logger.is_a?(BroadcastLogger)
18
22
  logger.broadcasts
@@ -21,9 +25,9 @@ module ActiveSupport
21
25
  end
22
26
 
23
27
  logdevs = loggers.map { |logger| logger.instance_variable_get(:@logdev) }
24
- logger_sources = logdevs.filter_map { |logdev| logdev.dev if logdev.respond_to?(:dev) }
28
+ logger_sources = logdevs.filter_map { |logdev| logdev.try(:filename) || logdev.try(:dev) }
25
29
 
26
- (sources & logger_sources).any?
30
+ normalize_sources(sources).intersect?(normalize_sources(logger_sources))
27
31
  end
28
32
 
29
33
  def initialize(*args, **kwargs)
@@ -38,5 +42,14 @@ module ActiveSupport
38
42
  "#{String === msg ? msg : msg.inspect}\n"
39
43
  end
40
44
  end
45
+
46
+ private
47
+ def self.normalize_sources(sources)
48
+ sources.map do |source|
49
+ source = source.path if source.respond_to?(:path)
50
+ source = File.realpath(source) if source.is_a?(String) && File.exist?(source)
51
+ source
52
+ end
53
+ end
41
54
  end
42
55
  end
@@ -7,14 +7,6 @@ module ActiveSupport
7
7
  module LoggerThreadSafeLevel # :nodoc:
8
8
  extend ActiveSupport::Concern
9
9
 
10
- Logger::Severity.constants.each do |severity|
11
- class_eval(<<-EOT, __FILE__, __LINE__ + 1)
12
- def #{severity.downcase}? # def debug?
13
- Logger::#{severity} >= level # DEBUG >= level
14
- end # end
15
- EOT
16
- end
17
-
18
10
  def local_level
19
11
  IsolatedExecutionState[local_level_key]
20
12
  end
@@ -86,8 +86,9 @@ module ActiveSupport
86
86
  unpacker: URI.method(:parse)
87
87
 
88
88
  registry.register_type 14, IPAddr,
89
- packer: :to_s,
90
- unpacker: :new
89
+ packer: method(:write_ipaddr),
90
+ unpacker: method(:read_ipaddr),
91
+ recursive: true
91
92
 
92
93
  registry.register_type 15, Pathname,
93
94
  packer: :to_s,
@@ -221,6 +222,18 @@ module ActiveSupport
221
222
  Set.new(unpacker.read)
222
223
  end
223
224
 
225
+ def write_ipaddr(ipaddr, packer)
226
+ if ipaddr.prefix < 32 || (ipaddr.ipv6? && ipaddr.prefix < 128)
227
+ packer.write("#{ipaddr}/#{ipaddr.prefix}")
228
+ else
229
+ packer.write(ipaddr.to_s)
230
+ end
231
+ end
232
+
233
+ def read_ipaddr(unpacker)
234
+ IPAddr.new(unpacker.read)
235
+ end
236
+
224
237
  def write_hash_with_indifferent_access(hwia, packer)
225
238
  packer.write(hwia.to_h)
226
239
  end
@@ -30,6 +30,18 @@ module ActiveSupport
30
30
  # self.current_user = User.find(id)
31
31
  # end
32
32
  #
33
+ # === Signing is not encryption
34
+ #
35
+ # The signed messages are not encrypted. The payload is merely encoded (Base64 by default) and can be decoded by
36
+ # anyone. The signature is just assuring that the message wasn't tampered with. For example:
37
+ #
38
+ # message = Rails.application.message_verifier('my_purpose').generate('never put secrets here')
39
+ # # => "BAhJIhtuZXZlciBwdXQgc2VjcmV0cyBoZXJlBjoGRVQ=--a0c1c0827919da5e949e989c971249355735e140"
40
+ # Base64.decode64(message.split("--").first) # no key needed
41
+ # # => 'never put secrets here'
42
+ #
43
+ # If you also need to encrypt the contents, you must use ActiveSupport::MessageEncryptor instead.
44
+ #
33
45
  # === Confine messages to a specific purpose
34
46
  #
35
47
  # It's not recommended to use the same verifier for different purposes in your application.
@@ -28,7 +28,7 @@ module ActiveSupport
28
28
 
29
29
  def decode(encoded, url_safe: @url_safe)
30
30
  url_safe ? ::Base64.urlsafe_decode64(encoded) : ::Base64.strict_decode64(encoded)
31
- rescue ArgumentError => error
31
+ rescue StandardError => error
32
32
  throw :invalid_message_format, error
33
33
  end
34
34
 
@@ -59,8 +59,8 @@ module ActiveSupport # :nodoc:
59
59
  end
60
60
 
61
61
  # Forward all undefined methods to the wrapped string.
62
- def method_missing(method, *args, &block)
63
- result = @wrapped_string.__send__(method, *args, &block)
62
+ def method_missing(method, ...)
63
+ result = @wrapped_string.__send__(method, ...)
64
64
  if method.end_with?("!")
65
65
  self if result
66
66
  else
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "mutex_m"
4
3
  require "concurrent/map"
5
4
  require "set"
6
5
  require "active_support/core_ext/object/try"
@@ -49,15 +48,13 @@ module ActiveSupport
49
48
  #
50
49
  # This class is thread safe. All methods are reentrant.
51
50
  class Fanout
52
- include Mutex_m
53
-
54
51
  def initialize
52
+ @mutex = Mutex.new
55
53
  @string_subscribers = Concurrent::Map.new { |h, k| h.compute_if_absent(k) { [] } }
56
54
  @other_subscribers = []
57
55
  @all_listeners_for = Concurrent::Map.new
58
56
  @groups_for = Concurrent::Map.new
59
57
  @silenceable_groups_for = Concurrent::Map.new
60
- super
61
58
  end
62
59
 
63
60
  def inspect # :nodoc:
@@ -67,7 +64,7 @@ module ActiveSupport
67
64
 
68
65
  def subscribe(pattern = nil, callable = nil, monotonic: false, &block)
69
66
  subscriber = Subscribers.new(pattern, callable || block, monotonic)
70
- synchronize do
67
+ @mutex.synchronize do
71
68
  case pattern
72
69
  when String
73
70
  @string_subscribers[pattern] << subscriber
@@ -83,7 +80,7 @@ module ActiveSupport
83
80
  end
84
81
 
85
82
  def unsubscribe(subscriber_or_name)
86
- synchronize do
83
+ @mutex.synchronize do
87
84
  case subscriber_or_name
88
85
  when String
89
86
  @string_subscribers[subscriber_or_name].clear
@@ -300,7 +297,7 @@ module ActiveSupport
300
297
 
301
298
  def all_listeners_for(name)
302
299
  # this is correctly done double-checked locking (Concurrent::Map's lookups have volatile semantics)
303
- @all_listeners_for[name] || synchronize do
300
+ @all_listeners_for[name] || @mutex.synchronize do
304
301
  # use synchronisation when accessing @subscribers
305
302
  @all_listeners_for[name] ||=
306
303
  @string_subscribers[name] + @other_subscribers.select { |s| s.subscribed_to?(name) }
@@ -104,7 +104,7 @@ module ActiveSupport
104
104
  end
105
105
 
106
106
  class Event
107
- attr_reader :name, :time, :end, :transaction_id
107
+ attr_reader :name, :transaction_id
108
108
  attr_accessor :payload
109
109
 
110
110
  def initialize(name, start, ending, transaction_id, payload)
@@ -117,9 +117,19 @@ module ActiveSupport
117
117
  @cpu_time_finish = 0.0
118
118
  @allocation_count_start = 0
119
119
  @allocation_count_finish = 0
120
+ @gc_time_start = 0
121
+ @gc_time_finish = 0
120
122
  end
121
123
 
122
- def record
124
+ def time
125
+ @time / 1000.0 if @time
126
+ end
127
+
128
+ def end
129
+ @end / 1000.0 if @end
130
+ end
131
+
132
+ def record # :nodoc:
123
133
  start!
124
134
  begin
125
135
  yield payload if block_given?
@@ -136,12 +146,14 @@ module ActiveSupport
136
146
  def start!
137
147
  @time = now
138
148
  @cpu_time_start = now_cpu
149
+ @gc_time_start = now_gc
139
150
  @allocation_count_start = now_allocations
140
151
  end
141
152
 
142
153
  # Record information at the time this event finishes
143
154
  def finish!
144
155
  @cpu_time_finish = now_cpu
156
+ @gc_time_finish = now_gc
145
157
  @end = now
146
158
  @allocation_count_finish = now_allocations
147
159
  end
@@ -165,28 +177,17 @@ module ActiveSupport
165
177
  @allocation_count_finish - @allocation_count_start
166
178
  end
167
179
 
168
- def children # :nodoc:
169
- ActiveSupport.deprecator.warn <<~EOM
170
- ActiveSupport::Notifications::Event#children is deprecated and will
171
- be removed in Rails 7.2.
172
- EOM
173
- []
174
- end
175
-
176
- def parent_of?(event) # :nodoc:
177
- ActiveSupport.deprecator.warn <<~EOM
178
- ActiveSupport::Notifications::Event#parent_of? is deprecated and will
179
- be removed in Rails 7.2.
180
- EOM
181
- start = (time - event.time) * 1000
182
- start <= 0 && (start + duration >= event.duration)
180
+ # Returns the time spent in GC (in milliseconds) between the call to #start!
181
+ # and the call to #finish!
182
+ def gc_time
183
+ (@gc_time_finish - @gc_time_start) / 1_000_000.0
183
184
  end
184
185
 
185
186
  # Returns the difference in milliseconds between when the execution of the
186
187
  # event started and when it ended.
187
188
  #
188
- # ActiveSupport::Notifications.subscribe('wait') do |*args|
189
- # @event = ActiveSupport::Notifications::Event.new(*args)
189
+ # ActiveSupport::Notifications.subscribe('wait') do |event|
190
+ # @event = event
190
191
  # end
191
192
  #
192
193
  # ActiveSupport::Notifications.instrument('wait') do
@@ -195,7 +196,7 @@ module ActiveSupport
195
196
  #
196
197
  # @event.duration # => 1000.138
197
198
  def duration
198
- self.end - time
199
+ @end - @time
199
200
  end
200
201
 
201
202
  private
@@ -210,11 +211,21 @@ module ActiveSupport
210
211
  Process.clock_gettime(Process::CLOCK_THREAD_CPUTIME_ID, :float_millisecond)
211
212
  end
212
213
  rescue
213
- def now_cpu # rubocop:disable Lint/DuplicateMethods
214
+ def now_cpu
214
215
  0.0
215
216
  end
216
217
  end
217
218
 
219
+ if GC.respond_to?(:total_time)
220
+ def now_gc
221
+ GC.total_time
222
+ end
223
+ else
224
+ def now_gc
225
+ 0
226
+ end
227
+ end
228
+
218
229
  if GC.stat.key?(:total_allocated_objects)
219
230
  def now_allocations
220
231
  GC.stat(:total_allocated_objects)