appsignal 2.11.2-java → 2.11.7-java
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -0
- data/.semaphore/semaphore.yml +1 -1
- data/CHANGELOG.md +21 -0
- data/Rakefile +9 -6
- data/build_matrix.yml +1 -1
- data/ext/agent.yml +17 -17
- data/lib/appsignal/hooks/action_cable.rb +10 -2
- data/lib/appsignal/hooks/shoryuken.rb +43 -4
- data/lib/appsignal/hooks/sidekiq.rb +5 -1
- data/lib/appsignal/integrations/object.rb +4 -34
- data/lib/appsignal/integrations/object_ruby_19.rb +37 -0
- data/lib/appsignal/integrations/object_ruby_modern.rb +64 -0
- data/lib/appsignal/system.rb +4 -0
- data/lib/appsignal/version.rb +1 -1
- data/spec/lib/appsignal/hooks/action_cable_spec.rb +88 -0
- data/spec/lib/appsignal/hooks/shoryuken_spec.rb +151 -104
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +61 -7
- data/spec/lib/appsignal/integrations/object_19_spec.rb +266 -0
- data/spec/lib/appsignal/integrations/object_spec.rb +29 -10
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b97d29242854a8a2f45cc111ce7ca9ebd4367bc966a103a528a0873e3411ddee
|
4
|
+
data.tar.gz: ca75043dbd897e34817fd24879be0c95fda398c06d1b8a73863da86847eb1f22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f2045ed2f107a293039ad933d1e4f584a1063ff3cd763d728aa230ecc182f8f105d71f90faeef034e0aba6b2740d8805600ec8052389c7c6e67b2fb411e8c9b7
|
7
|
+
data.tar.gz: 88fb1e14b251c8188b45dd4594f65c849bbaa25333434b55353749c424b9d387dfd83c358947a46cdde1ace806c6da0117e99567a601111c4b8d77468826c4f5
|
data/.rubocop.yml
CHANGED
data/.semaphore/semaphore.yml
CHANGED
@@ -31,7 +31,7 @@ global_job_config:
|
|
31
31
|
prologue:
|
32
32
|
commands:
|
33
33
|
- checkout
|
34
|
-
-
|
34
|
+
- rm -f $HOME/.rbenv/plugins/rbenv-gem-rehash/etc/rbenv.d/exec/~gem-rehash.bash
|
35
35
|
- sem-version ruby $RUBY_VERSION
|
36
36
|
- "./support/check_versions"
|
37
37
|
- cache restore $_BUNDLER_CACHE-bundler-$RUBY_VERSION-$GEMSET-$(checksum $BUNDLE_GEMFILE)
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
# 2.11.7
|
4
|
+
- Fix ActionCable integration in test environment using `stub_connection`.
|
5
|
+
PR #705
|
6
|
+
|
7
|
+
# 2.11.6
|
8
|
+
- Prepend Sidekiq middleware to wrap all Sidekiq middleware. Catches more
|
9
|
+
errors and provide more complete performance measurements. PR #698
|
10
|
+
|
11
|
+
# 2.11.5
|
12
|
+
- Add more detailed logging to finish_event calls when the event is unknown, so
|
13
|
+
we know what event is being tried to finish. Commit
|
14
|
+
c888a04d1b9ac947652b29c111c650fb5a5cf71c
|
15
|
+
|
16
|
+
# 2.11.4
|
17
|
+
- Support Ruby 3.0 for Object method instrumentation with keyword arguments
|
18
|
+
(https://docs.appsignal.com/ruby/instrumentation/method-instrumentation.html)
|
19
|
+
PR #693
|
20
|
+
|
21
|
+
# 2.11.3
|
22
|
+
- Support Shoryuken batch workers. PR #687
|
23
|
+
|
3
24
|
# 2.11.2
|
4
25
|
- Support Ruby 3.0. PR #681
|
5
26
|
- Support breadcrumbs. PR #666
|
data/Rakefile
CHANGED
@@ -275,7 +275,8 @@ namespace :publish do
|
|
275
275
|
Dir.chdir("#{File.dirname(__FILE__)}/pkg") do
|
276
276
|
Dir["*.gem"].each do |gem_package|
|
277
277
|
puts "## Publishing gem package: #{gem_package}"
|
278
|
-
system "gem push #{gem_package}"
|
278
|
+
result = system "gem push #{gem_package}"
|
279
|
+
raise "Failed to Push gem" unless result
|
279
280
|
end
|
280
281
|
end
|
281
282
|
end
|
@@ -374,19 +375,21 @@ end
|
|
374
375
|
begin
|
375
376
|
require "rspec/core/rake_task"
|
376
377
|
is_jruby = defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
377
|
-
|
378
|
-
|
379
|
-
|
378
|
+
excludes = []
|
379
|
+
excludes << "spec/lib/appsignal/extension/jruby_spec.rb" unless is_jruby
|
380
|
+
is_ruby19 = RUBY_VERSION < "2.0"
|
381
|
+
excludes << "spec/lib/appsignal/integrations/object_spec.rb" if is_ruby19
|
382
|
+
exclude_pattern = "--exclude-pattern=#{excludes.join(",")}" if excludes.any?
|
380
383
|
|
381
384
|
desc "Run the AppSignal gem test suite."
|
382
385
|
RSpec::Core::RakeTask.new :test do |t|
|
383
|
-
t.rspec_opts =
|
386
|
+
t.rspec_opts = exclude_pattern
|
384
387
|
end
|
385
388
|
|
386
389
|
namespace :test do
|
387
390
|
desc "Run the Appsignal gem test in an extension failure scenario"
|
388
391
|
RSpec::Core::RakeTask.new :failure do |t|
|
389
|
-
t.rspec_opts = "#{
|
392
|
+
t.rspec_opts = "#{exclude_pattern} --tag extension_installation_failure"
|
390
393
|
end
|
391
394
|
end
|
392
395
|
rescue LoadError # rubocop:disable Lint/HandleExceptions
|
data/build_matrix.yml
CHANGED
@@ -32,7 +32,7 @@ semaphore: # Default `.semaphore/semaphore.yml` contents
|
|
32
32
|
prologue:
|
33
33
|
commands:
|
34
34
|
- checkout
|
35
|
-
-
|
35
|
+
- rm -f $HOME/.rbenv/plugins/rbenv-gem-rehash/etc/rbenv.d/exec/~gem-rehash.bash
|
36
36
|
- sem-version ruby $RUBY_VERSION
|
37
37
|
- ./support/check_versions
|
38
38
|
- cache restore $_BUNDLER_CACHE-bundler-$RUBY_VERSION-$GEMSET-$(checksum $BUNDLE_GEMFILE)
|
data/ext/agent.yml
CHANGED
@@ -1,62 +1,62 @@
|
|
1
1
|
---
|
2
|
-
version:
|
2
|
+
version: d98461b
|
3
3
|
mirrors:
|
4
4
|
- https://appsignal-agent-releases.global.ssl.fastly.net
|
5
5
|
- https://d135dj0rjqvssy.cloudfront.net
|
6
6
|
triples:
|
7
7
|
x86_64-darwin:
|
8
8
|
static:
|
9
|
-
checksum:
|
9
|
+
checksum: 178ab2329c7b29cf45140e4707e75c20379fa0c7dfd7f39266a7a95aea510780
|
10
10
|
filename: appsignal-x86_64-darwin-all-static.tar.gz
|
11
11
|
dynamic:
|
12
|
-
checksum:
|
12
|
+
checksum: '0923985cc78c5cf278f45d530679954b94b1a91c87453369116fc05525e29864'
|
13
13
|
filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
|
14
14
|
universal-darwin:
|
15
15
|
static:
|
16
|
-
checksum:
|
16
|
+
checksum: 178ab2329c7b29cf45140e4707e75c20379fa0c7dfd7f39266a7a95aea510780
|
17
17
|
filename: appsignal-x86_64-darwin-all-static.tar.gz
|
18
18
|
dynamic:
|
19
|
-
checksum:
|
19
|
+
checksum: '0923985cc78c5cf278f45d530679954b94b1a91c87453369116fc05525e29864'
|
20
20
|
filename: appsignal-x86_64-darwin-all-dynamic.tar.gz
|
21
21
|
i686-linux:
|
22
22
|
static:
|
23
|
-
checksum:
|
23
|
+
checksum: cb772a8a178edb25d666b650efda80149c98e80e4264435d6176f8a6104f959a
|
24
24
|
filename: appsignal-i686-linux-all-static.tar.gz
|
25
25
|
dynamic:
|
26
|
-
checksum:
|
26
|
+
checksum: c9ac51f4d1b3cc13773d8fa8ea5cfad6af7909144d85186cef9324ec0531bdac
|
27
27
|
filename: appsignal-i686-linux-all-dynamic.tar.gz
|
28
28
|
x86-linux:
|
29
29
|
static:
|
30
|
-
checksum:
|
30
|
+
checksum: cb772a8a178edb25d666b650efda80149c98e80e4264435d6176f8a6104f959a
|
31
31
|
filename: appsignal-i686-linux-all-static.tar.gz
|
32
32
|
dynamic:
|
33
|
-
checksum:
|
33
|
+
checksum: c9ac51f4d1b3cc13773d8fa8ea5cfad6af7909144d85186cef9324ec0531bdac
|
34
34
|
filename: appsignal-i686-linux-all-dynamic.tar.gz
|
35
35
|
x86_64-linux:
|
36
36
|
static:
|
37
|
-
checksum:
|
37
|
+
checksum: d6c280e992d74f97d59da9827ec5707ca4f6776b0568cde1c083c1113e4b7104
|
38
38
|
filename: appsignal-x86_64-linux-all-static.tar.gz
|
39
39
|
dynamic:
|
40
|
-
checksum:
|
40
|
+
checksum: ef1a3f5d4b2ed61ea2ae4d5cb1a261a0e685fb9d3e7ea9efe455498aad386e59
|
41
41
|
filename: appsignal-x86_64-linux-all-dynamic.tar.gz
|
42
42
|
x86_64-linux-musl:
|
43
43
|
static:
|
44
|
-
checksum:
|
44
|
+
checksum: 9cf8ad34392662746a45cfce18113ad19cc29954789e2058f30e527163f2e98a
|
45
45
|
filename: appsignal-x86_64-linux-musl-all-static.tar.gz
|
46
46
|
dynamic:
|
47
|
-
checksum:
|
47
|
+
checksum: 01bd76983227648d9bc41d035e1552fcf18d62f0d6818bf7bb7fc2e7c166513d
|
48
48
|
filename: appsignal-x86_64-linux-musl-all-dynamic.tar.gz
|
49
49
|
x86_64-freebsd:
|
50
50
|
static:
|
51
|
-
checksum:
|
51
|
+
checksum: 403a597cbdbdba08460c5c9e93347ed9ad1d24333204e6d72db342deb266a296
|
52
52
|
filename: appsignal-x86_64-freebsd-all-static.tar.gz
|
53
53
|
dynamic:
|
54
|
-
checksum:
|
54
|
+
checksum: a1faae80ae09a6588c4cd35cb91dfa4b2176fc3cb17fbf79db80194c21e75bf9
|
55
55
|
filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
|
56
56
|
amd64-freebsd:
|
57
57
|
static:
|
58
|
-
checksum:
|
58
|
+
checksum: 403a597cbdbdba08460c5c9e93347ed9ad1d24333204e6d72db342deb266a296
|
59
59
|
filename: appsignal-x86_64-freebsd-all-static.tar.gz
|
60
60
|
dynamic:
|
61
|
-
checksum:
|
61
|
+
checksum: a1faae80ae09a6588c4cd35cb91dfa4b2176fc3cb17fbf79db80194c21e75bf9
|
62
62
|
filename: appsignal-x86_64-freebsd-all-dynamic.tar.gz
|
@@ -56,7 +56,11 @@ module Appsignal
|
|
56
56
|
def install_callbacks
|
57
57
|
ActionCable::Channel::Base.set_callback :subscribe, :around, :prepend => true do |channel, inner|
|
58
58
|
# The request is only the original websocket request
|
59
|
-
|
59
|
+
connection = channel.connection
|
60
|
+
# #env is not available on the Rails ConnectionStub class used in the
|
61
|
+
# Rails app test suite. If we call `#env` it causes an error to occur
|
62
|
+
# in apps' test suites.
|
63
|
+
env = connection.respond_to?(:env) ? connection.env : {}
|
60
64
|
request = ActionDispatch::Request.new(env)
|
61
65
|
env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
|
62
66
|
request.request_id || SecureRandom.uuid
|
@@ -84,7 +88,11 @@ module Appsignal
|
|
84
88
|
|
85
89
|
ActionCable::Channel::Base.set_callback :unsubscribe, :around, :prepend => true do |channel, inner|
|
86
90
|
# The request is only the original websocket request
|
87
|
-
|
91
|
+
connection = channel.connection
|
92
|
+
# #env is not available on the Rails ConnectionStub class used in the
|
93
|
+
# Rails app test suite. If we call `#env` it causes an error to occur
|
94
|
+
# in apps' test suites.
|
95
|
+
env = connection.respond_to?(:env) ? connection.env : {}
|
88
96
|
request = ActionDispatch::Request.new(env)
|
89
97
|
env[Appsignal::Hooks::ActionCableHook::REQUEST_ID] ||=
|
90
98
|
request.request_id || SecureRandom.uuid
|
@@ -5,21 +5,60 @@ module Appsignal
|
|
5
5
|
# @api private
|
6
6
|
class ShoryukenMiddleware
|
7
7
|
def call(worker_instance, queue, sqs_msg, body)
|
8
|
-
|
8
|
+
batch = sqs_msg.is_a?(Array)
|
9
|
+
attributes =
|
10
|
+
if batch
|
11
|
+
# We can't instrument batched message separately, the `yield` will
|
12
|
+
# perform all the batched messages.
|
13
|
+
# To provide somewhat useful metadata, Get first message based on
|
14
|
+
# SentTimestamp, and use its attributes as metadata for the
|
15
|
+
# transaction. We can't combine them all because then they would
|
16
|
+
# overwrite each other and the last message (in an sorted order)
|
17
|
+
# would be used as the source of the metadata. With the
|
18
|
+
# oldest/first message at least some useful information is stored
|
19
|
+
# such as the first received time and the number of retries for the
|
20
|
+
# first message. The newer message should have lower values and
|
21
|
+
# timestamps in their metadata.
|
22
|
+
first_msg = sqs_msg.min do |a, b|
|
23
|
+
a.attributes["SentTimestamp"].to_i <=> b.attributes["SentTimestamp"].to_i
|
24
|
+
end
|
25
|
+
# Add batch => true metadata so people can recognize when a
|
26
|
+
# transaction is about a batch of messages.
|
27
|
+
first_msg.attributes.merge(:batch => true)
|
28
|
+
else
|
29
|
+
sqs_msg.attributes.merge(:message_id => sqs_msg.message_id)
|
30
|
+
end
|
31
|
+
metadata = { :queue => queue }.merge(attributes)
|
9
32
|
options = {
|
10
33
|
:class => worker_instance.class.name,
|
11
34
|
:method => "perform",
|
12
35
|
:metadata => metadata
|
13
36
|
}
|
14
37
|
|
15
|
-
args =
|
38
|
+
args =
|
39
|
+
if batch
|
40
|
+
bodies = {}
|
41
|
+
sqs_msg.each_with_index do |msg, index|
|
42
|
+
# Store all separate bodies on a hash with the key being the
|
43
|
+
# message_id
|
44
|
+
bodies[msg.message_id] = body[index]
|
45
|
+
end
|
46
|
+
bodies
|
47
|
+
else
|
48
|
+
case body
|
49
|
+
when Hash
|
50
|
+
body
|
51
|
+
else
|
52
|
+
{ :params => body }
|
53
|
+
end
|
54
|
+
end
|
16
55
|
options[:params] = Appsignal::Utils::HashSanitizer.sanitize(
|
17
56
|
args,
|
18
57
|
Appsignal.config[:filter_parameters]
|
19
58
|
)
|
20
59
|
|
21
|
-
if
|
22
|
-
options[:queue_start] = Time.at(
|
60
|
+
if attributes.key?("SentTimestamp")
|
61
|
+
options[:queue_start] = Time.at(attributes["SentTimestamp"].to_i / 1000)
|
23
62
|
end
|
24
63
|
|
25
64
|
Appsignal.monitor_transaction("perform_job.shoryuken", options) do
|
@@ -16,7 +16,11 @@ module Appsignal
|
|
16
16
|
|
17
17
|
::Sidekiq.configure_server do |config|
|
18
18
|
config.server_middleware do |chain|
|
19
|
-
chain.
|
19
|
+
if chain.respond_to? :prepend
|
20
|
+
chain.prepend Appsignal::Hooks::SidekiqPlugin
|
21
|
+
else
|
22
|
+
chain.add Appsignal::Hooks::SidekiqPlugin
|
23
|
+
end
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
@@ -4,38 +4,8 @@ if defined?(Appsignal)
|
|
4
4
|
Appsignal::Environment.report_enabled("object_instrumentation")
|
5
5
|
end
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
singleton_class.send(:define_method, method_name) do |*args, &block|
|
12
|
-
name = options.fetch(:name) do
|
13
|
-
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
14
|
-
end
|
15
|
-
Appsignal.instrument name do
|
16
|
-
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.appsignal_instrument_method(method_name, options = {})
|
22
|
-
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
23
|
-
define_method method_name do |*args, &block|
|
24
|
-
name = options.fetch(:name) do
|
25
|
-
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
26
|
-
end
|
27
|
-
Appsignal.instrument name do
|
28
|
-
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.appsignal_reverse_class_name
|
34
|
-
return "AnonymousClass" unless name
|
35
|
-
name.split("::").reverse.join(".")
|
36
|
-
end
|
37
|
-
|
38
|
-
def appsignal_reverse_class_name
|
39
|
-
self.class.appsignal_reverse_class_name
|
40
|
-
end
|
7
|
+
if RUBY_VERSION < "2.0"
|
8
|
+
require "appsignal/integrations/object_ruby_19"
|
9
|
+
else
|
10
|
+
require "appsignal/integrations/object_ruby_modern"
|
41
11
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Object
|
4
|
+
def self.appsignal_instrument_class_method(method_name, options = {})
|
5
|
+
singleton_class.send \
|
6
|
+
:alias_method, "appsignal_uninstrumented_#{method_name}", method_name
|
7
|
+
singleton_class.send(:define_method, method_name) do |*args, &block|
|
8
|
+
name = options.fetch(:name) do
|
9
|
+
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
10
|
+
end
|
11
|
+
Appsignal.instrument name do
|
12
|
+
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.appsignal_instrument_method(method_name, options = {})
|
18
|
+
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
19
|
+
define_method method_name do |*args, &block|
|
20
|
+
name = options.fetch(:name) do
|
21
|
+
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
22
|
+
end
|
23
|
+
Appsignal.instrument name do
|
24
|
+
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.appsignal_reverse_class_name
|
30
|
+
return "AnonymousClass" unless name
|
31
|
+
name.split("::").reverse.join(".")
|
32
|
+
end
|
33
|
+
|
34
|
+
def appsignal_reverse_class_name
|
35
|
+
self.class.appsignal_reverse_class_name
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Object
|
4
|
+
if Appsignal::System.ruby_2_7_or_newer?
|
5
|
+
def self.appsignal_instrument_class_method(method_name, options = {})
|
6
|
+
singleton_class.send \
|
7
|
+
:alias_method, "appsignal_uninstrumented_#{method_name}", method_name
|
8
|
+
singleton_class.send(:define_method, method_name) do |*args, **kwargs, &block|
|
9
|
+
name = options.fetch(:name) do
|
10
|
+
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
11
|
+
end
|
12
|
+
Appsignal.instrument name do
|
13
|
+
send "appsignal_uninstrumented_#{method_name}", *args, **kwargs, &block
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.appsignal_instrument_method(method_name, options = {})
|
19
|
+
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
20
|
+
define_method method_name do |*args, **kwargs, &block|
|
21
|
+
name = options.fetch(:name) do
|
22
|
+
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
23
|
+
end
|
24
|
+
Appsignal.instrument name do
|
25
|
+
send "appsignal_uninstrumented_#{method_name}", *args, **kwargs, &block
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
else
|
30
|
+
def self.appsignal_instrument_class_method(method_name, options = {})
|
31
|
+
singleton_class.send \
|
32
|
+
:alias_method, "appsignal_uninstrumented_#{method_name}", method_name
|
33
|
+
singleton_class.send(:define_method, method_name) do |*args, &block|
|
34
|
+
name = options.fetch(:name) do
|
35
|
+
"#{method_name}.class_method.#{appsignal_reverse_class_name}.other"
|
36
|
+
end
|
37
|
+
Appsignal.instrument name do
|
38
|
+
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.appsignal_instrument_method(method_name, options = {})
|
44
|
+
alias_method "appsignal_uninstrumented_#{method_name}", method_name
|
45
|
+
define_method method_name do |*args, &block|
|
46
|
+
name = options.fetch(:name) do
|
47
|
+
"#{method_name}.#{appsignal_reverse_class_name}.other"
|
48
|
+
end
|
49
|
+
Appsignal.instrument name do
|
50
|
+
send "appsignal_uninstrumented_#{method_name}", *args, &block
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.appsignal_reverse_class_name
|
57
|
+
return "AnonymousClass" unless name
|
58
|
+
name.split("::").reverse.join(".")
|
59
|
+
end
|
60
|
+
|
61
|
+
def appsignal_reverse_class_name
|
62
|
+
self.class.appsignal_reverse_class_name
|
63
|
+
end
|
64
|
+
end
|