appsignal 2.10.8-java → 2.11.0.beta.1-java
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/.rubocop.yml +3 -0
- data/.semaphore/semaphore.yml +75 -61
- data/CHANGELOG.md +21 -0
- data/build_matrix.yml +13 -7
- data/ext/agent.yml +19 -19
- data/ext/appsignal_extension.c +10 -1
- data/ext/base.rb +11 -2
- data/gemfiles/padrino.gemfile +2 -2
- data/gemfiles/rails-4.2.gemfile +9 -2
- data/gemfiles/rails-5.0.gemfile +1 -0
- data/gemfiles/rails-5.1.gemfile +1 -0
- data/gemfiles/rails-5.2.gemfile +1 -0
- data/gemfiles/rails-6.0.gemfile +1 -0
- data/gemfiles/resque-1.gemfile +7 -0
- data/gemfiles/{resque.gemfile → resque-2.gemfile} +1 -1
- data/lib/appsignal.rb +21 -1
- data/lib/appsignal/capistrano.rb +2 -0
- data/lib/appsignal/config.rb +6 -2
- data/lib/appsignal/environment.rb +126 -0
- data/lib/appsignal/extension/jruby.rb +10 -0
- data/lib/appsignal/hooks.rb +2 -0
- data/lib/appsignal/hooks/active_job.rb +89 -0
- data/lib/appsignal/hooks/net_http.rb +2 -0
- data/lib/appsignal/hooks/puma.rb +2 -58
- data/lib/appsignal/hooks/redis.rb +2 -0
- data/lib/appsignal/hooks/resque.rb +60 -0
- data/lib/appsignal/hooks/sequel.rb +2 -0
- data/lib/appsignal/hooks/sidekiq.rb +18 -191
- data/lib/appsignal/integrations/object.rb +4 -0
- data/lib/appsignal/integrations/que.rb +1 -1
- data/lib/appsignal/integrations/resque.rb +9 -12
- data/lib/appsignal/integrations/resque_active_job.rb +9 -24
- data/lib/appsignal/probes/puma.rb +61 -0
- data/lib/appsignal/probes/sidekiq.rb +102 -0
- data/lib/appsignal/rack/js_exception_catcher.rb +5 -2
- data/lib/appsignal/transaction.rb +32 -7
- data/lib/appsignal/utils/deprecation_message.rb +5 -1
- data/lib/appsignal/version.rb +1 -1
- data/lib/puma/plugin/appsignal.rb +2 -1
- data/spec/lib/appsignal/cli/diagnose_spec.rb +2 -1
- data/spec/lib/appsignal/config_spec.rb +6 -1
- data/spec/lib/appsignal/environment_spec.rb +167 -0
- data/spec/lib/appsignal/hooks/activejob_spec.rb +458 -0
- data/spec/lib/appsignal/hooks/puma_spec.rb +2 -181
- data/spec/lib/appsignal/hooks/resque_spec.rb +185 -0
- data/spec/lib/appsignal/hooks/sidekiq_spec.rb +292 -546
- data/spec/lib/appsignal/integrations/padrino_spec.rb +1 -1
- data/spec/lib/appsignal/integrations/que_spec.rb +25 -6
- data/spec/lib/appsignal/integrations/resque_active_job_spec.rb +20 -137
- data/spec/lib/appsignal/integrations/resque_spec.rb +20 -85
- data/spec/lib/appsignal/probes/puma_spec.rb +180 -0
- data/spec/lib/appsignal/probes/sidekiq_spec.rb +204 -0
- data/spec/lib/appsignal/rack/js_exception_catcher_spec.rb +9 -4
- data/spec/lib/appsignal/transaction_spec.rb +35 -20
- data/spec/lib/appsignal_spec.rb +22 -0
- data/spec/lib/puma/appsignal_spec.rb +1 -1
- data/spec/support/helpers/action_mailer_helpers.rb +25 -0
- data/spec/support/helpers/dependency_helper.rb +12 -0
- data/spec/support/helpers/env_helpers.rb +1 -1
- data/spec/support/helpers/environment_metdata_helper.rb +16 -0
- data/spec/support/helpers/transaction_helpers.rb +6 -0
- data/spec/support/stubs/sidekiq/api.rb +2 -2
- metadata +25 -5
@@ -0,0 +1,458 @@
|
|
1
|
+
if DependencyHelper.active_job_present?
|
2
|
+
require "active_job"
|
3
|
+
require "action_mailer"
|
4
|
+
|
5
|
+
describe Appsignal::Hooks::ActiveJobHook do
|
6
|
+
describe "#dependencies_present?" do
|
7
|
+
subject { described_class.new.dependencies_present? }
|
8
|
+
|
9
|
+
context "when ActiveJob constant is found" do
|
10
|
+
before { stub_const "ActiveJob", Class.new }
|
11
|
+
|
12
|
+
it { is_expected.to be_truthy }
|
13
|
+
end
|
14
|
+
|
15
|
+
context "when ActiveJob constant is not found" do
|
16
|
+
before { hide_const "ActiveJob" }
|
17
|
+
|
18
|
+
it { is_expected.to be_falsy }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#install" do
|
23
|
+
it "extends ActiveJob::Base with the AppSignal ActiveJob plugin" do
|
24
|
+
described_class.new.install
|
25
|
+
|
26
|
+
path, _line_number = ActiveJob::Base.method(:execute).source_location
|
27
|
+
expect(path).to end_with("/lib/appsignal/hooks/active_job.rb")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe Appsignal::Hooks::ActiveJobHook::ActiveJobClassInstrumentation do
|
33
|
+
let(:time) { Time.parse("2001-01-01 10:00:00UTC") }
|
34
|
+
let(:namespace) { Appsignal::Transaction::BACKGROUND_JOB }
|
35
|
+
let(:log) { StringIO.new }
|
36
|
+
let(:parameterized_given_args) do
|
37
|
+
{
|
38
|
+
:foo => "Foo",
|
39
|
+
"bar" => "Bar",
|
40
|
+
"baz" => { "1" => "foo" }
|
41
|
+
}
|
42
|
+
end
|
43
|
+
let(:method_given_args) do
|
44
|
+
[
|
45
|
+
"foo",
|
46
|
+
parameterized_given_args
|
47
|
+
]
|
48
|
+
end
|
49
|
+
let(:parameterized_expected_args) do
|
50
|
+
{
|
51
|
+
"_aj_symbol_keys" => ["foo"],
|
52
|
+
"foo" => "Foo",
|
53
|
+
"bar" => "Bar",
|
54
|
+
"baz" => {
|
55
|
+
"_aj_symbol_keys" => [],
|
56
|
+
"1" => "foo"
|
57
|
+
}
|
58
|
+
}
|
59
|
+
end
|
60
|
+
let(:method_expected_args) do
|
61
|
+
[
|
62
|
+
"foo",
|
63
|
+
parameterized_expected_args
|
64
|
+
]
|
65
|
+
end
|
66
|
+
before do
|
67
|
+
ActiveJob::Base.queue_adapter = :inline
|
68
|
+
|
69
|
+
start_agent
|
70
|
+
Appsignal.logger = test_logger(log)
|
71
|
+
class ActiveJobTestJob < ActiveJob::Base
|
72
|
+
def perform(*_args)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
class ActiveJobErrorTestJob < ActiveJob::Base
|
77
|
+
def perform
|
78
|
+
raise "uh oh"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
around { |example| keep_transactions { example.run } }
|
83
|
+
after do
|
84
|
+
Object.send(:remove_const, :ActiveJobTestJob)
|
85
|
+
Object.send(:remove_const, :ActiveJobErrorTestJob)
|
86
|
+
end
|
87
|
+
|
88
|
+
it "reports the name from the ActiveJob integration" do
|
89
|
+
perform_job(ActiveJobTestJob)
|
90
|
+
|
91
|
+
transaction = last_transaction
|
92
|
+
transaction_hash = transaction.to_h
|
93
|
+
expect(transaction_hash).to include(
|
94
|
+
"action" => "ActiveJobTestJob#perform",
|
95
|
+
"error" => nil,
|
96
|
+
"namespace" => namespace,
|
97
|
+
"metadata" => {},
|
98
|
+
"sample_data" => hash_including(
|
99
|
+
"params" => [],
|
100
|
+
"tags" => {
|
101
|
+
"queue" => "default"
|
102
|
+
}
|
103
|
+
)
|
104
|
+
)
|
105
|
+
events = transaction_hash["events"]
|
106
|
+
.sort_by { |e| e["start"] }
|
107
|
+
.map { |event| event["name"] }
|
108
|
+
expect(events).to eq(["perform_start.active_job", "perform.active_job"])
|
109
|
+
end
|
110
|
+
|
111
|
+
context "with error" do
|
112
|
+
it "reports the error on the transaction from the ActiveRecord integration" do
|
113
|
+
expect do
|
114
|
+
perform_job(ActiveJobErrorTestJob)
|
115
|
+
end.to raise_error(RuntimeError, "uh oh")
|
116
|
+
|
117
|
+
transaction = last_transaction
|
118
|
+
transaction_hash = transaction.to_h
|
119
|
+
expect(transaction_hash).to include(
|
120
|
+
"action" => "ActiveJobErrorTestJob#perform",
|
121
|
+
"error" => {
|
122
|
+
"name" => "RuntimeError",
|
123
|
+
"message" => "uh oh",
|
124
|
+
"backtrace" => kind_of(String)
|
125
|
+
},
|
126
|
+
"namespace" => namespace,
|
127
|
+
"metadata" => {},
|
128
|
+
"sample_data" => hash_including(
|
129
|
+
"params" => [],
|
130
|
+
"tags" => {
|
131
|
+
"queue" => "default"
|
132
|
+
}
|
133
|
+
)
|
134
|
+
)
|
135
|
+
events = transaction_hash["events"]
|
136
|
+
.sort_by { |e| e["start"] }
|
137
|
+
.map { |event| event["name"] }
|
138
|
+
expect(events).to eq(["perform_start.active_job", "perform.active_job"])
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "when wrapped in another transaction" do
|
143
|
+
it "does not create a new transaction or close the currently open one" do
|
144
|
+
current_transaction = background_job_transaction
|
145
|
+
allow(current_transaction).to receive(:complete).and_call_original
|
146
|
+
set_current_transaction current_transaction
|
147
|
+
|
148
|
+
perform_job(ActiveJobTestJob)
|
149
|
+
|
150
|
+
expect(created_transactions.count).to eql(1)
|
151
|
+
expect(current_transaction).to_not have_received(:complete)
|
152
|
+
current_transaction.complete
|
153
|
+
|
154
|
+
transaction = current_transaction
|
155
|
+
transaction_hash = transaction.to_h
|
156
|
+
# It does set data on the transaction
|
157
|
+
expect(transaction_hash).to include(
|
158
|
+
"id" => current_transaction.transaction_id,
|
159
|
+
"action" => "ActiveJobTestJob#perform",
|
160
|
+
"error" => nil,
|
161
|
+
"namespace" => namespace,
|
162
|
+
"metadata" => {},
|
163
|
+
"sample_data" => hash_including(
|
164
|
+
"params" => [],
|
165
|
+
"tags" => {
|
166
|
+
"queue" => "default"
|
167
|
+
}
|
168
|
+
)
|
169
|
+
)
|
170
|
+
events = transaction_hash["events"]
|
171
|
+
.reject { |e| e["name"] == "enqueue.active_job" }
|
172
|
+
.sort_by { |e| e["start"] }
|
173
|
+
.map { |event| event["name"] }
|
174
|
+
expect(events).to eq(["perform_start.active_job", "perform.active_job"])
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context "with params" do
|
179
|
+
it "filters the configured params" do
|
180
|
+
Appsignal.config = project_fixture_config("production")
|
181
|
+
Appsignal.config[:filter_parameters] = ["foo"]
|
182
|
+
perform_job(ActiveJobTestJob, method_given_args)
|
183
|
+
|
184
|
+
transaction = last_transaction
|
185
|
+
transaction_hash = transaction.to_h
|
186
|
+
expect(transaction_hash["sample_data"]["params"]).to include(
|
187
|
+
[
|
188
|
+
"foo",
|
189
|
+
{
|
190
|
+
"_aj_symbol_keys" => ["foo"],
|
191
|
+
"foo" => "[FILTERED]",
|
192
|
+
"bar" => "Bar",
|
193
|
+
"baz" => { "_aj_symbol_keys" => [], "1" => "foo" }
|
194
|
+
}
|
195
|
+
]
|
196
|
+
)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context "with provider_job_id", :skip => DependencyHelper.rails_version < Gem::Version.new("5.0.0") do
|
201
|
+
before do
|
202
|
+
module ActiveJob
|
203
|
+
module QueueAdapters
|
204
|
+
# Adapter used in our test suite to add provider data to the job
|
205
|
+
# data, as is done by Rails provided ActiveJob adapters.
|
206
|
+
#
|
207
|
+
# This implementation is based on the
|
208
|
+
# `ActiveJob::QueueAdapters::InlineAdapter`.
|
209
|
+
class AppsignalTestAdapter < InlineAdapter
|
210
|
+
def enqueue(job)
|
211
|
+
Base.execute(job.serialize.merge("provider_job_id" => "my_provider_job_id"))
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
class ProviderWrappedActiveJobTestJob < ActiveJob::Base
|
218
|
+
self.queue_adapter = :appsignal_test
|
219
|
+
|
220
|
+
def perform(*_args)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
after do
|
225
|
+
ActiveJob::QueueAdapters.send(:remove_const, :AppsignalTestAdapter)
|
226
|
+
Object.send(:remove_const, :ProviderWrappedActiveJobTestJob)
|
227
|
+
end
|
228
|
+
|
229
|
+
it "sets provider_job_id as tag" do
|
230
|
+
perform_job(ProviderWrappedActiveJobTestJob)
|
231
|
+
|
232
|
+
transaction = last_transaction
|
233
|
+
transaction_hash = transaction.to_h
|
234
|
+
expect(transaction_hash["sample_data"]["tags"]).to include(
|
235
|
+
"provider_job_id" => "my_provider_job_id"
|
236
|
+
)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
context "with enqueued_at", :skip => DependencyHelper.rails_version < Gem::Version.new("6.0.0") do
|
241
|
+
before do
|
242
|
+
module ActiveJob
|
243
|
+
module QueueAdapters
|
244
|
+
# Adapter used in our test suite to add provider data to the job
|
245
|
+
# data, as is done by Rails provided ActiveJob adapters.
|
246
|
+
#
|
247
|
+
# This implementation is based on the
|
248
|
+
# `ActiveJob::QueueAdapters::InlineAdapter`.
|
249
|
+
class AppsignalTestAdapter < InlineAdapter
|
250
|
+
def enqueue(job)
|
251
|
+
Base.execute(job.serialize.merge("enqueued_at" => "2020-10-10T10:10:10Z"))
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
class ProviderWrappedActiveJobTestJob < ActiveJob::Base
|
258
|
+
self.queue_adapter = :appsignal_test
|
259
|
+
|
260
|
+
def perform(*_args)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
after do
|
265
|
+
ActiveJob::QueueAdapters.send(:remove_const, :AppsignalTestAdapter)
|
266
|
+
Object.send(:remove_const, :ProviderWrappedActiveJobTestJob)
|
267
|
+
end
|
268
|
+
|
269
|
+
it "sets queue time on transaction" do
|
270
|
+
allow_any_instance_of(Appsignal::Transaction).to receive(:set_queue_start).and_call_original
|
271
|
+
perform_job(ProviderWrappedActiveJobTestJob)
|
272
|
+
|
273
|
+
transaction = last_transaction
|
274
|
+
queue_time = Time.parse("2020-10-10T10:10:10Z")
|
275
|
+
expect(transaction).to have_received(:set_queue_start)
|
276
|
+
.with((queue_time.to_f * 1_000).to_i)
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context "with ActionMailer job" do
|
281
|
+
include ActionMailerHelpers
|
282
|
+
|
283
|
+
before do
|
284
|
+
class ActionMailerTestJob < ActionMailer::Base
|
285
|
+
def welcome(_first_arg = nil, _second_arg = nil)
|
286
|
+
end
|
287
|
+
end
|
288
|
+
end
|
289
|
+
after do
|
290
|
+
Object.send(:remove_const, :ActionMailerTestJob)
|
291
|
+
end
|
292
|
+
|
293
|
+
context "without params" do
|
294
|
+
it "sets the Action mailer data on the transaction" do
|
295
|
+
perform_mailer(ActionMailerTestJob, :welcome)
|
296
|
+
|
297
|
+
transaction = last_transaction
|
298
|
+
transaction_hash = transaction.to_h
|
299
|
+
expect(transaction_hash).to include(
|
300
|
+
"action" => "ActionMailerTestJob#welcome",
|
301
|
+
"sample_data" => hash_including(
|
302
|
+
"params" => ["ActionMailerTestJob", "welcome", "deliver_now"],
|
303
|
+
"tags" => { "queue" => "mailers" }
|
304
|
+
)
|
305
|
+
)
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
context "with multiple arguments" do
|
310
|
+
it "sets the arguments on the transaction" do
|
311
|
+
perform_mailer(ActionMailerTestJob, :welcome, method_given_args)
|
312
|
+
|
313
|
+
transaction = last_transaction
|
314
|
+
transaction_hash = transaction.to_h
|
315
|
+
expect(transaction_hash).to include(
|
316
|
+
"action" => "ActionMailerTestJob#welcome",
|
317
|
+
"sample_data" => hash_including(
|
318
|
+
"params" => ["ActionMailerTestJob", "welcome", "deliver_now"] + method_expected_args,
|
319
|
+
"tags" => { "queue" => "mailers" }
|
320
|
+
)
|
321
|
+
)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
if DependencyHelper.rails_version >= Gem::Version.new("5.2.0")
|
326
|
+
context "with parameterized arguments" do
|
327
|
+
it "sets the arguments on the transaction" do
|
328
|
+
perform_mailer(ActionMailerTestJob, :welcome, parameterized_given_args)
|
329
|
+
|
330
|
+
transaction = last_transaction
|
331
|
+
transaction_hash = transaction.to_h
|
332
|
+
expect(transaction_hash).to include(
|
333
|
+
"action" => "ActionMailerTestJob#welcome",
|
334
|
+
"sample_data" => hash_including(
|
335
|
+
"params" => ["ActionMailerTestJob", "welcome", "deliver_now", parameterized_expected_args],
|
336
|
+
"tags" => { "queue" => "mailers" }
|
337
|
+
)
|
338
|
+
)
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
if DependencyHelper.rails_version >= Gem::Version.new("6.0.0")
|
345
|
+
context "with ActionMailer MailDeliveryJob job" do
|
346
|
+
include ActionMailerHelpers
|
347
|
+
|
348
|
+
before do
|
349
|
+
class ActionMailerTestMailDeliveryJob < ActionMailer::Base
|
350
|
+
self.delivery_job = ActionMailer::MailDeliveryJob
|
351
|
+
|
352
|
+
def welcome(*_args)
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
after do
|
357
|
+
Object.send(:remove_const, :ActionMailerTestMailDeliveryJob)
|
358
|
+
end
|
359
|
+
|
360
|
+
it "sets the Action mailer data on the transaction" do
|
361
|
+
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome)
|
362
|
+
|
363
|
+
transaction = last_transaction
|
364
|
+
transaction_hash = transaction.to_h
|
365
|
+
expect(transaction_hash).to include(
|
366
|
+
"action" => "ActionMailerTestMailDeliveryJob#welcome",
|
367
|
+
"sample_data" => hash_including(
|
368
|
+
"params" => [
|
369
|
+
"ActionMailerTestMailDeliveryJob",
|
370
|
+
"welcome",
|
371
|
+
"deliver_now",
|
372
|
+
{ active_job_internal_key => ["args"], "args" => [] }
|
373
|
+
],
|
374
|
+
"tags" => { "queue" => "mailers" }
|
375
|
+
)
|
376
|
+
)
|
377
|
+
end
|
378
|
+
|
379
|
+
context "with method arguments" do
|
380
|
+
it "sets the Action mailer data on the transaction" do
|
381
|
+
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome, method_given_args)
|
382
|
+
|
383
|
+
transaction = last_transaction
|
384
|
+
transaction_hash = transaction.to_h
|
385
|
+
expect(transaction_hash).to include(
|
386
|
+
"action" => "ActionMailerTestMailDeliveryJob#welcome",
|
387
|
+
"sample_data" => hash_including(
|
388
|
+
"params" => [
|
389
|
+
"ActionMailerTestMailDeliveryJob",
|
390
|
+
"welcome",
|
391
|
+
"deliver_now",
|
392
|
+
{
|
393
|
+
active_job_internal_key => ["args"],
|
394
|
+
"args" => method_expected_args
|
395
|
+
}
|
396
|
+
],
|
397
|
+
"tags" => { "queue" => "mailers" }
|
398
|
+
)
|
399
|
+
)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
context "with parameterized arguments" do
|
404
|
+
it "sets the Action mailer data on the transaction" do
|
405
|
+
perform_mailer(ActionMailerTestMailDeliveryJob, :welcome, parameterized_given_args)
|
406
|
+
|
407
|
+
transaction = last_transaction
|
408
|
+
transaction_hash = transaction.to_h
|
409
|
+
expect(transaction_hash).to include(
|
410
|
+
"action" => "ActionMailerTestMailDeliveryJob#welcome",
|
411
|
+
"sample_data" => hash_including(
|
412
|
+
"params" => [
|
413
|
+
"ActionMailerTestMailDeliveryJob",
|
414
|
+
"welcome",
|
415
|
+
"deliver_now",
|
416
|
+
{
|
417
|
+
active_job_internal_key => ["params", "args"],
|
418
|
+
"args" => [],
|
419
|
+
"params" => parameterized_expected_args
|
420
|
+
}
|
421
|
+
],
|
422
|
+
"tags" => { "queue" => "mailers" }
|
423
|
+
)
|
424
|
+
)
|
425
|
+
end
|
426
|
+
end
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
def perform_active_job
|
431
|
+
Timecop.freeze(time) do
|
432
|
+
yield
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
def perform_job(job_class, args = nil)
|
437
|
+
perform_active_job do
|
438
|
+
if args
|
439
|
+
job_class.perform_later(args)
|
440
|
+
else
|
441
|
+
job_class.perform_later
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def perform_mailer(mailer, method, args = nil)
|
447
|
+
perform_active_job { perform_action_mailer(mailer, method, args) }
|
448
|
+
end
|
449
|
+
|
450
|
+
def active_job_internal_key
|
451
|
+
if DependencyHelper.ruby_version >= Gem::Version.new("2.7.0")
|
452
|
+
"_aj_ruby2_keywords"
|
453
|
+
else
|
454
|
+
"_aj_symbol_keys"
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|