appsignal 4.5.17 → 4.6.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -0
- data/CLAUDE.md +177 -0
- data/README.md +17 -0
- data/appsignal.gemspec +1 -0
- data/build_matrix.yml +29 -0
- data/lib/appsignal/auth_check.rb +3 -3
- data/lib/appsignal/check_in/cron.rb +1 -1
- data/lib/appsignal/check_in/event.rb +1 -1
- data/lib/appsignal/check_in/scheduler.rb +3 -1
- data/lib/appsignal/check_in.rb +9 -6
- data/lib/appsignal/cli/diagnose.rb +1 -1
- data/lib/appsignal/cli.rb +1 -1
- data/lib/appsignal/config.rb +231 -30
- data/lib/appsignal/custom_marker.rb +3 -3
- data/lib/appsignal/environment.rb +7 -1
- data/lib/appsignal/event_formatter/action_view/render_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/active_record/instantiation_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/active_record/sql_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/elastic_search/search_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/faraday/request_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/mongo_ruby_driver/query_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/rom/sql_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/sequel/sql_formatter.rb +1 -1
- data/lib/appsignal/event_formatter/view_component/render_formatter.rb +1 -1
- data/lib/appsignal/event_formatter.rb +41 -3
- data/lib/appsignal/extension.rb +1 -1
- data/lib/appsignal/garbage_collection.rb +1 -1
- data/lib/appsignal/helpers/instrumentation.rb +82 -35
- data/lib/appsignal/helpers/metrics.rb +12 -9
- data/lib/appsignal/hooks/action_cable.rb +1 -1
- data/lib/appsignal/hooks/action_mailer.rb +1 -0
- data/lib/appsignal/hooks/active_job.rb +1 -1
- data/lib/appsignal/hooks/active_support_notifications.rb +1 -1
- data/lib/appsignal/hooks/at_exit.rb +1 -1
- data/lib/appsignal/hooks/celluloid.rb +1 -1
- data/lib/appsignal/hooks/data_mapper.rb +1 -1
- data/lib/appsignal/hooks/delayed_job.rb +1 -1
- data/lib/appsignal/hooks/dry_monitor.rb +1 -1
- data/lib/appsignal/hooks/excon.rb +1 -1
- data/lib/appsignal/hooks/gvl.rb +1 -1
- data/lib/appsignal/hooks/http.rb +1 -1
- data/lib/appsignal/hooks/mongo_ruby_driver.rb +1 -1
- data/lib/appsignal/hooks/mri.rb +1 -1
- data/lib/appsignal/hooks/net_http.rb +1 -1
- data/lib/appsignal/hooks/ownership.rb +1 -1
- data/lib/appsignal/hooks/passenger.rb +1 -1
- data/lib/appsignal/hooks/puma.rb +1 -1
- data/lib/appsignal/hooks/que.rb +1 -1
- data/lib/appsignal/hooks/rake.rb +1 -1
- data/lib/appsignal/hooks/redis.rb +1 -1
- data/lib/appsignal/hooks/redis_client.rb +1 -1
- data/lib/appsignal/hooks/resque.rb +1 -1
- data/lib/appsignal/hooks/sequel.rb +2 -1
- data/lib/appsignal/hooks/shoryuken.rb +1 -1
- data/lib/appsignal/hooks/sidekiq.rb +1 -0
- data/lib/appsignal/hooks/unicorn.rb +1 -1
- data/lib/appsignal/hooks/webmachine.rb +1 -1
- data/lib/appsignal/hooks.rb +5 -3
- data/lib/appsignal/integrations/action_cable.rb +1 -1
- data/lib/appsignal/integrations/active_support_notifications.rb +1 -1
- data/lib/appsignal/integrations/capistrano/capistrano_2_tasks.rb +1 -1
- data/lib/appsignal/integrations/data_mapper.rb +1 -1
- data/lib/appsignal/integrations/delayed_job_plugin.rb +1 -1
- data/lib/appsignal/integrations/dry_monitor.rb +1 -1
- data/lib/appsignal/integrations/excon.rb +1 -1
- data/lib/appsignal/integrations/http.rb +1 -1
- data/lib/appsignal/integrations/mongo_ruby_driver.rb +1 -1
- data/lib/appsignal/integrations/net_http.rb +1 -1
- data/lib/appsignal/integrations/object.rb +18 -2
- data/lib/appsignal/integrations/ownership.rb +1 -1
- data/lib/appsignal/integrations/puma.rb +1 -1
- data/lib/appsignal/integrations/que.rb +1 -1
- data/lib/appsignal/integrations/railtie.rb +2 -2
- data/lib/appsignal/integrations/rake.rb +2 -2
- data/lib/appsignal/integrations/redis.rb +1 -1
- data/lib/appsignal/integrations/redis_client.rb +1 -1
- data/lib/appsignal/integrations/resque.rb +2 -2
- data/lib/appsignal/integrations/shoryuken.rb +1 -1
- data/lib/appsignal/integrations/sidekiq.rb +3 -3
- data/lib/appsignal/integrations/unicorn.rb +1 -1
- data/lib/appsignal/integrations/webmachine.rb +1 -1
- data/lib/appsignal/internal_errors.rb +2 -2
- data/lib/appsignal/loaders.rb +1 -1
- data/lib/appsignal/logger.rb +36 -19
- data/lib/appsignal/marker.rb +1 -1
- data/lib/appsignal/probes/gvl.rb +4 -3
- data/lib/appsignal/probes/helpers.rb +1 -1
- data/lib/appsignal/probes/mri.rb +3 -3
- data/lib/appsignal/probes/sidekiq.rb +4 -3
- data/lib/appsignal/probes.rb +20 -15
- data/lib/appsignal/rack.rb +1 -1
- data/lib/appsignal/sample_data.rb +31 -12
- data/lib/appsignal/span.rb +1 -1
- data/lib/appsignal/system.rb +9 -8
- data/lib/appsignal/transaction.rb +72 -52
- data/lib/appsignal/transmitter.rb +1 -1
- data/lib/appsignal/utils.rb +1 -1
- data/lib/appsignal/version.rb +2 -1
- data/lib/appsignal.rb +22 -14
- data/lib/puma/plugin/appsignal.rb +1 -1
- data/sig/appsignal.rbi +2599 -0
- data/sig/appsignal.rbs +2420 -0
- metadata +20 -6
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
class Hooks
|
5
|
-
#
|
5
|
+
# @!visibility private
|
6
6
|
module SequelLogExtension
|
7
7
|
# Add query instrumentation
|
8
8
|
def log_yield(sql, args = nil)
|
@@ -19,6 +19,7 @@ module Appsignal
|
|
19
19
|
|
20
20
|
module SequelLogConnectionExtension
|
21
21
|
# Add query instrumentation
|
22
|
+
# @!visibility private
|
22
23
|
def log_connection_yield(sql, conn, args = nil)
|
23
24
|
Appsignal.instrument(
|
24
25
|
"sql.sequel",
|
data/lib/appsignal/hooks.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Appsignal
|
4
|
-
#
|
4
|
+
# @!visibility private
|
5
5
|
class Hooks
|
6
6
|
class << self
|
7
7
|
def register(name, hook)
|
@@ -38,8 +38,10 @@ module Appsignal
|
|
38
38
|
@installed = true
|
39
39
|
rescue => ex
|
40
40
|
logger = Appsignal.internal_logger
|
41
|
-
logger.error(
|
42
|
-
|
41
|
+
logger.error(
|
42
|
+
"Error while installing #{name} hook: #{ex.class}: #{ex.message}\n" \
|
43
|
+
"#{ex.backtrace.join("\n")}"
|
44
|
+
)
|
43
45
|
end
|
44
46
|
end
|
45
47
|
|
@@ -2,7 +2,17 @@
|
|
2
2
|
|
3
3
|
Appsignal::Environment.report_enabled("object_instrumentation") if defined?(Appsignal)
|
4
4
|
|
5
|
+
# Extensions to Object for AppSignal method instrumentation.
|
6
|
+
#
|
7
|
+
# @see https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html
|
8
|
+
# Method instrumentation documentation.
|
5
9
|
class Object
|
10
|
+
# Instruments a class method with AppSignal monitoring.
|
11
|
+
#
|
12
|
+
# @param method_name [Symbol] The name of the class method to instrument.
|
13
|
+
# @param options [Hash<Symbol, String>] Options for instrumentation.
|
14
|
+
# @option options [String] :name Custom event name for the instrumentation.
|
15
|
+
# @return [Symbol]
|
6
16
|
# @see https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html
|
7
17
|
# Method instrumentation documentation.
|
8
18
|
def self.appsignal_instrument_class_method(method_name, options = {})
|
@@ -22,6 +32,12 @@ class Object
|
|
22
32
|
end
|
23
33
|
end
|
24
34
|
|
35
|
+
# Instruments an instance method with AppSignal monitoring.
|
36
|
+
#
|
37
|
+
# @param method_name [Symbol] The name of the instance method to instrument.
|
38
|
+
# @param options [Hash<Symbol, String>] Options for instrumentation.
|
39
|
+
# @option options [String] :name Custom event name for the instrumentation.
|
40
|
+
# @return [Symbol]
|
25
41
|
# @see https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html
|
26
42
|
# Method instrumentation documentation.
|
27
43
|
def self.appsignal_instrument_method(method_name, options = {})
|
@@ -37,14 +53,14 @@ class Object
|
|
37
53
|
ruby2_keywords method_name if respond_to?(:ruby2_keywords, true)
|
38
54
|
end
|
39
55
|
|
40
|
-
#
|
56
|
+
# @!visibility private
|
41
57
|
def self.appsignal_reverse_class_name
|
42
58
|
return "AnonymousClass" unless name
|
43
59
|
|
44
60
|
name.split("::").reverse.join(".")
|
45
61
|
end
|
46
62
|
|
47
|
-
#
|
63
|
+
# @!visibility private
|
48
64
|
def appsignal_reverse_class_name
|
49
65
|
self.class.appsignal_reverse_class_name
|
50
66
|
end
|
@@ -7,7 +7,7 @@ require "appsignal/rack/rails_instrumentation"
|
|
7
7
|
|
8
8
|
module Appsignal
|
9
9
|
module Integrations
|
10
|
-
#
|
10
|
+
# @!visibility private
|
11
11
|
class Railtie < ::Rails::Railtie
|
12
12
|
config.appsignal = ActiveSupport::OrderedOptions.new
|
13
13
|
config.appsignal.start_at = :on_load
|
@@ -77,7 +77,7 @@ module Appsignal
|
|
77
77
|
|
78
78
|
# Report errors reported by the Rails error reporter using {Appsignal.report_error}.
|
79
79
|
#
|
80
|
-
#
|
80
|
+
# @!visibility private
|
81
81
|
class RailsErrorReporterSubscriber
|
82
82
|
class << self
|
83
83
|
def report(error, handled:, severity:, context: {}, source: nil) # rubocop:disable Lint/UnusedMethodArgument
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
module Integrations
|
5
|
-
#
|
5
|
+
# @!visibility private
|
6
6
|
module RakeIntegration
|
7
7
|
IGNORED_ERRORS = [
|
8
8
|
# Normal exits from the application we do not need to report
|
@@ -49,7 +49,7 @@ module Appsignal
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
#
|
52
|
+
# @!visibility private
|
53
53
|
module RakeIntegrationHelper
|
54
54
|
# Register an `at_exit` hook when a task is executed. This will stop
|
55
55
|
# AppSignal when _all_ tasks are executed and Rake exits.
|
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
module Integrations
|
5
|
-
#
|
5
|
+
# @!visibility private
|
6
6
|
module ResqueIntegration
|
7
7
|
def perform
|
8
8
|
transaction = Appsignal::Transaction.create(Appsignal::Transaction::BACKGROUND_JOB)
|
@@ -25,7 +25,7 @@ module Appsignal
|
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
#
|
28
|
+
# @!visibility private
|
29
29
|
class ResqueHelpers
|
30
30
|
def self.arguments(payload)
|
31
31
|
case payload["class"]
|
@@ -11,7 +11,7 @@ module Appsignal
|
|
11
11
|
# about completing the transaction.
|
12
12
|
#
|
13
13
|
# Introduced in Sidekiq 5.1.
|
14
|
-
#
|
14
|
+
# @!visibility private
|
15
15
|
class SidekiqDeathHandler
|
16
16
|
def call(_job_context, exception)
|
17
17
|
return unless Appsignal.config[:sidekiq_report_errors] == "discard"
|
@@ -24,7 +24,7 @@ module Appsignal
|
|
24
24
|
# Error handler for Sidekiq to report errors from jobs and internal Sidekiq
|
25
25
|
# errors.
|
26
26
|
#
|
27
|
-
#
|
27
|
+
# @!visibility private
|
28
28
|
class SidekiqErrorHandler
|
29
29
|
# Sidekiq 7.1.5 introduced the third sidekiq_config argument. It is not
|
30
30
|
# given on older Sidekiq versions.
|
@@ -49,7 +49,7 @@ module Appsignal
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
#
|
52
|
+
# @!visibility private
|
53
53
|
class SidekiqMiddleware
|
54
54
|
include Appsignal::Hooks::Helpers
|
55
55
|
|
@@ -1,17 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Appsignal
|
4
|
-
# @api private
|
5
4
|
class InternalError < StandardError; end
|
6
5
|
|
7
|
-
# @api private
|
8
6
|
class NotStartedError < InternalError
|
7
|
+
# @!visibility private
|
9
8
|
MESSAGE = <<~MESSAGE
|
10
9
|
The AppSignal Ruby gem was not started!
|
11
10
|
|
12
11
|
This error was raised by calling `Appsignal.check_if_started!`
|
13
12
|
MESSAGE
|
14
13
|
|
14
|
+
# @return [String]
|
15
15
|
def message
|
16
16
|
MESSAGE
|
17
17
|
end
|
data/lib/appsignal/loaders.rb
CHANGED
data/lib/appsignal/logger.rb
CHANGED
@@ -13,6 +13,8 @@ module Appsignal
|
|
13
13
|
# If called again, it will return the result of the first call.
|
14
14
|
# This is useful for ensuring that a block is not executed multiple
|
15
15
|
# times when it is broadcasted to multiple loggers.
|
16
|
+
#
|
17
|
+
# @!visibility private
|
16
18
|
class BlockOnce
|
17
19
|
def initialize(&block)
|
18
20
|
@block = block
|
@@ -44,9 +46,13 @@ module Appsignal
|
|
44
46
|
end
|
45
47
|
end
|
46
48
|
|
49
|
+
# @!visibility private
|
47
50
|
PLAINTEXT = 0
|
51
|
+
# @!visibility private
|
48
52
|
LOGFMT = 1
|
53
|
+
# @!visibility private
|
49
54
|
JSON = 2
|
55
|
+
# @!visibility private
|
50
56
|
SEVERITY_MAP = {
|
51
57
|
DEBUG => 2,
|
52
58
|
INFO => 3,
|
@@ -55,14 +61,17 @@ module Appsignal
|
|
55
61
|
FATAL => 7
|
56
62
|
}.freeze
|
57
63
|
|
64
|
+
# Logging severity threshold
|
65
|
+
# @return [Integer]
|
58
66
|
attr_reader :level
|
59
67
|
|
60
68
|
# Create a new logger instance
|
61
69
|
#
|
62
|
-
# @param group Name of the group for this logger.
|
63
|
-
# @param level Minimum log level to report. Log lines below this
|
64
|
-
#
|
65
|
-
# @param
|
70
|
+
# @param group [String] Name of the group for this logger.
|
71
|
+
# @param level [Integer] Minimum log level to report. Log lines below this
|
72
|
+
# level will be ignored.
|
73
|
+
# @param format [Integer] Format to use to parse log line attributes.
|
74
|
+
# @param attributes [Hash<String, String>] Default attributes for all log lines.
|
66
75
|
# @return [void]
|
67
76
|
def initialize(group, level: INFO, format: PLAINTEXT, attributes: {})
|
68
77
|
raise TypeError, "group must be a string" unless group.is_a? String
|
@@ -77,9 +86,9 @@ module Appsignal
|
|
77
86
|
@loggers = []
|
78
87
|
end
|
79
88
|
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
89
|
+
# Sets the formatter for this logger and all broadcasted loggers.
|
90
|
+
# @param formatter [Proc] The formatter to use for log messages.
|
91
|
+
# @return [Proc]
|
83
92
|
def formatter=(formatter)
|
84
93
|
super
|
85
94
|
@loggers.each { |logger| logger.formatter = formatter }
|
@@ -87,7 +96,7 @@ module Appsignal
|
|
87
96
|
|
88
97
|
# We support the various methods in the Ruby
|
89
98
|
# logger class by supplying this method.
|
90
|
-
#
|
99
|
+
# @!visibility private
|
91
100
|
def add(severity, message = nil, group = nil, &block) # rubocop:disable Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity
|
92
101
|
# If we do not need to broadcast to any loggers and the severity is
|
93
102
|
# below the log level, we can return early.
|
@@ -142,40 +151,40 @@ module Appsignal
|
|
142
151
|
alias log add
|
143
152
|
|
144
153
|
# Log a debug level message
|
145
|
-
# @param message Message to log
|
146
|
-
# @param attributes Attributes to tag the log with
|
154
|
+
# @param message [String] Message to log
|
155
|
+
# @param attributes [Hash<String, Object>] Attributes to tag the log with
|
147
156
|
# @return [void]
|
148
157
|
def debug(message = nil, attributes = {}, &block)
|
149
158
|
add_with_attributes(DEBUG, message, @group, attributes, &block)
|
150
159
|
end
|
151
160
|
|
152
161
|
# Log an info level message
|
153
|
-
# @param message Message to log
|
154
|
-
# @param attributes Attributes to tag the log with
|
162
|
+
# @param message [String] Message to log
|
163
|
+
# @param attributes [Hash<String, Object>] Attributes to tag the log with
|
155
164
|
# @return [void]
|
156
165
|
def info(message = nil, attributes = {}, &block)
|
157
166
|
add_with_attributes(INFO, message, @group, attributes, &block)
|
158
167
|
end
|
159
168
|
|
160
169
|
# Log a warn level message
|
161
|
-
# @param message Message to log
|
162
|
-
# @param attributes Attributes to tag the log with
|
170
|
+
# @param message [String] Message to log
|
171
|
+
# @param attributes [Hash<String, Object>] Attributes to tag the log with
|
163
172
|
# @return [void]
|
164
173
|
def warn(message = nil, attributes = {}, &block)
|
165
174
|
add_with_attributes(WARN, message, @group, attributes, &block)
|
166
175
|
end
|
167
176
|
|
168
177
|
# Log an error level message
|
169
|
-
# @param message Message to log
|
170
|
-
# @param attributes Attributes to tag the log with
|
178
|
+
# @param message [String, Exception] Message to log
|
179
|
+
# @param attributes [Hash<String, Object>] Attributes to tag the log with
|
171
180
|
# @return [void]
|
172
181
|
def error(message = nil, attributes = {}, &block)
|
173
182
|
add_with_attributes(ERROR, message, @group, attributes, &block)
|
174
183
|
end
|
175
184
|
|
176
185
|
# Log a fatal level message
|
177
|
-
# @param message Message to log
|
178
|
-
# @param attributes Attributes to tag the log with
|
186
|
+
# @param message [String, Exception] Message to log
|
187
|
+
# @param attributes [Hash<String, Object>] Attributes to tag the log with
|
179
188
|
# @return [void]
|
180
189
|
def fatal(message = nil, attributes = {}, &block)
|
181
190
|
add_with_attributes(FATAL, message, @group, attributes, &block)
|
@@ -185,13 +194,15 @@ module Appsignal
|
|
185
194
|
#
|
186
195
|
# Returns the number of characters written.
|
187
196
|
#
|
188
|
-
# @param message Message to log
|
197
|
+
# @param message [String] Message to log
|
189
198
|
# @return [Integer]
|
190
199
|
def <<(message)
|
191
200
|
info(message)
|
192
201
|
message.length
|
193
202
|
end
|
194
203
|
|
204
|
+
# Temporarily silences the logger to a specified level while executing a block.
|
205
|
+
#
|
195
206
|
# When using ActiveSupport::TaggedLogging without the broadcast feature,
|
196
207
|
# the passed logger is required to respond to the `silence` method.
|
197
208
|
#
|
@@ -199,6 +210,9 @@ module Appsignal
|
|
199
210
|
#
|
200
211
|
# - https://github.com/rails/rails/blob/e11ebc04cfbe41c06cdfb70ee5a9fdbbd98bb263/activesupport/lib/active_support/logger.rb#L60-L76
|
201
212
|
# - https://github.com/rails/rails/blob/e11ebc04cfbe41c06cdfb70ee5a9fdbbd98bb263/activesupport/lib/active_support/logger_silence.rb
|
213
|
+
#
|
214
|
+
# @param severity [Integer] The minimum severity level to log during the block.
|
215
|
+
# @return [Object] The return value of the block.
|
202
216
|
def silence(severity = ERROR, &block)
|
203
217
|
previous_level = @level
|
204
218
|
@level = severity
|
@@ -209,6 +223,9 @@ module Appsignal
|
|
209
223
|
@silenced = false
|
210
224
|
end
|
211
225
|
|
226
|
+
# Adds a logger to broadcast log messages to.
|
227
|
+
# @param logger [Logger] The logger to add to the broadcast list.
|
228
|
+
# @return [Array<Logger>]
|
212
229
|
def broadcast_to(logger)
|
213
230
|
@loggers << logger
|
214
231
|
end
|
data/lib/appsignal/marker.rb
CHANGED
@@ -20,7 +20,7 @@ module Appsignal
|
|
20
20
|
# @see Appsignal::CLI::NotifyOfDeploy
|
21
21
|
# @see https://docs.appsignal.com/appsignal/terminology.html#markers
|
22
22
|
# Terminology: Deploy marker
|
23
|
-
#
|
23
|
+
# @!visibility private
|
24
24
|
class Marker
|
25
25
|
# Path used on the AppSignal Push API
|
26
26
|
# https://push.appsignal.com/1/markers
|
data/lib/appsignal/probes/gvl.rb
CHANGED
@@ -2,21 +2,22 @@
|
|
2
2
|
|
3
3
|
module Appsignal
|
4
4
|
module Probes
|
5
|
+
# @!visibility private
|
5
6
|
class GvlProbe
|
6
7
|
include Helpers
|
7
8
|
|
8
|
-
#
|
9
|
+
# @!visibility private
|
9
10
|
def self.dependencies_present?
|
10
11
|
defined?(::GVLTools) && gvltools_0_2_or_newer? && ruby_3_2_or_newer? &&
|
11
12
|
!Appsignal::System.jruby?
|
12
13
|
end
|
13
14
|
|
14
|
-
#
|
15
|
+
# @!visibility private
|
15
16
|
def self.gvltools_0_2_or_newer?
|
16
17
|
Gem::Version.new(::GVLTools::VERSION) >= Gem::Version.new("0.2.0")
|
17
18
|
end
|
18
19
|
|
19
|
-
#
|
20
|
+
# @!visibility private
|
20
21
|
def self.ruby_3_2_or_newer?
|
21
22
|
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("3.2.0")
|
22
23
|
end
|