appmap 0.102.2 → 0.103.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.
data/lib/appmap/config.rb CHANGED
@@ -1,17 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'pathname'
4
- require 'set'
5
- require 'yaml'
6
- require 'appmap/util'
7
- require 'appmap/handler'
8
- require 'appmap/service/guesser'
9
- require 'appmap/swagger/configuration'
10
- require 'appmap/depends/configuration'
11
- require_relative './hook_log'
3
+ require "pathname"
4
+ require "set"
5
+ require "yaml"
6
+ require "appmap/util"
7
+ require "appmap/handler"
8
+ require "appmap/service/guesser"
9
+ require "appmap/swagger/configuration"
10
+ require "appmap/depends/configuration"
11
+ require_relative "hook_log"
12
+
13
+ # rubocop:disable Metrics/CyclomaticComplexity
14
+ # rubocop:disable Metrics/AbcSize
15
+ # rubocop:disable Metrics/MethodLength
16
+ # rubocop:disable Metrics/ClassLength
17
+ # rubocop:disable Metrics/PerceivedComplexity
18
+ # rubocop:disable Style/Documentation
19
+ # rubocop:disable Layout/IndentationWidth
12
20
 
13
21
  module AppMap
14
22
  class Config
23
+ RECORD_AROUND_LABELS = %w[job.perform cli.command message.handle].freeze
24
+
15
25
  # Specifies a logical code package be mapped.
16
26
  # This can be a project source folder, a Gem, or a builtin.
17
27
  #
@@ -32,8 +42,12 @@ module AppMap
32
42
  # Specifies the class that will convert code events into event objects.
33
43
  attr_writer :handler_class
34
44
 
45
+ def record_around?
46
+ RECORD_AROUND_LABELS.find { |label| labels&.member?(label) }
47
+ end
48
+
35
49
  def handler_class
36
- require 'appmap/handler/function_handler'
50
+ require "appmap/handler/function_handler"
37
51
  @handler_class || AppMap::Handler::FunctionHandler
38
52
  end
39
53
 
@@ -51,7 +65,7 @@ module AppMap
51
65
  def subpackage(location, config)
52
66
  return self if gem
53
67
 
54
- path = location.split('/')[0...-1].join('/')
68
+ path = location.split("/")[0...-1].join("/")
55
69
  clone.tap do |pkg|
56
70
  pkg.name = path
57
71
  pkg.path = path
@@ -106,7 +120,7 @@ module AppMap
106
120
  handler_class: handler_class ? handler_class.name : nil,
107
121
  exclude: Util.blank?(exclude) ? nil : exclude,
108
122
  labels: Util.blank?(labels) ? nil : labels,
109
- shallow: shallow.nil? ? nil : shallow,
123
+ shallow: shallow.nil? ? nil : shallow
110
124
  }.compact
111
125
  end
112
126
  end
@@ -131,7 +145,7 @@ module AppMap
131
145
  }
132
146
  end
133
147
 
134
- alias as_json to_h
148
+ alias_method :as_json, :to_h
135
149
  end
136
150
  private_constant :TargetMethods
137
151
 
@@ -158,7 +172,7 @@ module AppMap
158
172
 
159
173
  MethodHook = Struct.new(:cls, :method_names, :labels) # :nodoc:
160
174
  private_constant :MethodHook
161
-
175
+
162
176
  class << self
163
177
  def package_hooks(methods, path: nil, gem: nil, force: false, builtin: false, handler_class: nil, require_name: nil)
164
178
  Array(methods).map do |method|
@@ -182,52 +196,52 @@ module AppMap
182
196
 
183
197
  def declare_hook(hook_decl)
184
198
  hook_decl = YAML.load(hook_decl) if hook_decl.is_a?(String)
185
-
186
- methods_decl = hook_decl['methods'] || hook_decl['method']
199
+
200
+ methods_decl = hook_decl["methods"] || hook_decl["method"]
187
201
  methods_decl = Array(methods_decl) unless methods_decl.is_a?(Hash)
188
- labels_decl = Array(hook_decl['labels'] || hook_decl['label'])
202
+ labels_decl = Array(hook_decl["labels"] || hook_decl["label"])
189
203
 
190
204
  methods = methods_decl.map do |name|
191
- class_name, method_name, static = name.include?('.') ? name.split('.', 2) + [ true ] : name.split('#', 2) + [ false ]
192
- method_hook class_name, [ method_name ], labels_decl
205
+ class_name, method_name, static = name.include?(".") ? name.split(".", 2) + [true] : name.split("#", 2) + [false]
206
+ method_hook class_name, [method_name], labels_decl
193
207
  end
194
208
 
195
- require_name = hook_decl['require_name']
196
- gem_name = hook_decl['gem']
197
- path = hook_decl['path']
198
- builtin = hook_decl['builtin']
209
+ require_name = hook_decl["require_name"]
210
+ gem_name = hook_decl["gem"]
211
+ path = hook_decl["path"]
212
+ builtin = hook_decl["builtin"]
199
213
 
200
214
  options = {
201
215
  builtin: builtin,
202
216
  gem: gem_name,
203
217
  path: path,
204
218
  require_name: require_name || gem_name || path,
205
- force: hook_decl['force']
219
+ force: hook_decl["force"]
206
220
  }.compact
207
221
 
208
- handler_class = hook_decl['handler_class']
222
+ handler_class = hook_decl["handler_class"]
209
223
  options[:handler_class] = Handler.find(handler_class) if handler_class
210
224
 
211
225
  package_hooks(methods, **options)
212
226
  end
213
227
 
214
228
  def declare_hook_deprecated(hook_decl)
215
- function_name = hook_decl['name']
229
+ function_name = hook_decl["name"]
216
230
  package, cls, functions = []
217
231
  if function_name
218
232
  package, cls, _, function = Util.parse_function_name(function_name)
219
233
  functions = Array(function)
220
234
  else
221
- package = hook_decl['package']
222
- cls = hook_decl['class']
223
- functions = hook_decl['function'] || hook_decl['functions']
224
- raise %q(AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions') unless package && cls && functions
235
+ package = hook_decl["package"]
236
+ cls = hook_decl["class"]
237
+ functions = hook_decl["function"] || hook_decl["functions"]
238
+ raise "AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions'" unless package && cls && functions
225
239
  end
226
240
 
227
241
  functions = Array(functions).map(&:to_sym)
228
- labels = hook_decl['label'] || hook_decl['labels']
229
- req = hook_decl['require']
230
- builtin = hook_decl['builtin']
242
+ labels = hook_decl["label"] || hook_decl["labels"]
243
+ req = hook_decl["require"]
244
+ builtin = hook_decl["builtin"]
231
245
 
232
246
  package_options = {}
233
247
  package_options[:labels] = Array(labels).map(&:to_s) if labels
@@ -238,20 +252,20 @@ module AppMap
238
252
  end
239
253
 
240
254
  def builtin_hooks_path
241
- [ [ __dir__, 'builtin_hooks' ].join('/') ] + ( ENV['APPMAP_BUILTIN_HOOKS_PATH'] || '').split(/[;:]/)
255
+ [[__dir__, "builtin_hooks"].join("/")] + (ENV["APPMAP_BUILTIN_HOOKS_PATH"] || "").split(/[;:]/)
242
256
  end
243
257
 
244
258
  def gem_hooks_path
245
- [ [ __dir__, 'gem_hooks' ].join('/') ] + ( ENV['APPMAP_GEM_HOOKS_PATH'] || '').split(/[;:]/)
259
+ [[__dir__, "gem_hooks"].join("/")] + (ENV["APPMAP_GEM_HOOKS_PATH"] || "").split(/[;:]/)
246
260
  end
247
261
 
248
262
  def load_hooks
249
263
  loader = lambda do |dir, &block|
250
- basename = dir.split('/').compact.join('/')
264
+ basename = dir.split("/").compact.join("/")
251
265
  [].tap do |hooks|
252
- Dir.glob(Pathname.new(dir).join('**').join('*.yml').to_s).each do |yaml_file|
266
+ Dir.glob(Pathname.new(dir).join("**").join("*.yml").to_s).each do |yaml_file|
253
267
  path = yaml_file[basename.length + 1...-4]
254
- YAML.load(File.read(yaml_file)).map do |config|
268
+ YAML.load_file(yaml_file).map do |config|
255
269
  block.call path, config
256
270
  config
257
271
  end.each do |config|
@@ -262,16 +276,16 @@ module AppMap
262
276
  end
263
277
 
264
278
  builtin_hooks = builtin_hooks_path.map do |path|
265
- loader.(path) do |path, config|
266
- config['path'] = path
267
- config['builtin'] = true
279
+ loader.call(path) do |path, config|
280
+ config["path"] = path
281
+ config["builtin"] = true
268
282
  end
269
283
  end
270
284
 
271
285
  gem_hooks = gem_hooks_path.map do |path|
272
- loader.(path) do |path, config|
273
- config['gem'] = path
274
- config['builtin'] = false
286
+ loader.call(path) do |path, config|
287
+ config["gem"] = path
288
+ config["builtin"] = false
275
289
  end
276
290
  end
277
291
 
@@ -296,10 +310,10 @@ module AppMap
296
310
  @exclude = exclude
297
311
  @functions = functions
298
312
 
299
- @builtin_hooks = Hash.new { |h,k| h[k] = [] }
300
- @gem_hooks = Hash.new { |h,k| h[k] = [] }
301
-
302
- (functions + self.class.load_hooks).each_with_object(Hash.new { |h,k| h[k] = [] }) do |cls_target_methods, gem_hooks|
313
+ @builtin_hooks = Hash.new { |h, k| h[k] = [] }
314
+ @gem_hooks = Hash.new { |h, k| h[k] = [] }
315
+
316
+ (functions + self.class.load_hooks).each_with_object(Hash.new { |h, k| h[k] = [] }) do |cls_target_methods, gem_hooks|
303
317
  hooks = if cls_target_methods.target_methods.package.builtin
304
318
  @builtin_hooks
305
319
  else
@@ -318,11 +332,11 @@ module AppMap
318
332
  def load_from_file(config_file_name)
319
333
  logo = lambda do
320
334
  Util.color(<<~LOGO, :magenta)
321
- ___ __ ___
322
- / _ | ___ ___ / |/ /__ ____
323
- / __ |/ _ \\/ _ \\/ /|_/ / _ `/ _ \\
324
- /_/ |_/ .__/ .__/_/ /_/\\_,_/ .__/
325
- /_/ /_/ /_/
335
+ ___ __ ___
336
+ / _ | ___ ___ / |/ /__ ____
337
+ / __ |/ _ \\/ _ \\/ /|_/ / _ `/ _ \\
338
+ /_/ |_/ .__/ .__/_/ /_/\\_,_/ .__/
339
+ /_/ /_/ /_/
326
340
  LOGO
327
341
  end
328
342
 
@@ -331,43 +345,43 @@ module AppMap
331
345
  config_data = if config_present
332
346
  YAML.safe_load(::File.read(config_file_name))
333
347
  else
334
- warn logo.()
335
- warn ''
336
- warn Util.color(%Q|NOTICE: The AppMap config file #{config_file_name} was not found!|, :magenta, bold: true)
337
- warn ''
348
+ warn logo.call
349
+ warn ""
350
+ warn Util.color(%(NOTICE: The AppMap config file #{config_file_name} was not found!), :magenta, bold: true)
351
+ warn ""
338
352
  warn Util.color(<<~MISSING_FILE_MSG, :magenta)
339
- AppMap uses this file to customize its behavior. For example, you can use
340
- the 'packages' setting to indicate which local file paths and dependency
341
- gems you want to include in the AppMap. Since you haven't provided specific
342
- settings, the appmap gem will use these default options:
353
+ AppMap uses this file to customize its behavior. For example, you can use
354
+ the 'packages' setting to indicate which local file paths and dependency
355
+ gems you want to include in the AppMap. Since you haven't provided specific
356
+ settings, the appmap gem will use these default options:
343
357
  MISSING_FILE_MSG
344
358
  {}
345
359
  end
346
360
 
347
361
  load(config_data).tap do |config|
348
362
  {
349
- 'name' => config.name,
350
- 'language' => 'ruby',
351
- 'appmap_dir' => AppMap::DEFAULT_APPMAP_DIR,
352
- 'packages' => config.packages.select{|p| p.path}.map do |pkg|
353
- { 'path' => pkg.path }
363
+ "name" => config.name,
364
+ "language" => "ruby",
365
+ "appmap_dir" => AppMap::DEFAULT_APPMAP_DIR,
366
+ "packages" => config.packages.select { |p| p.path }.map do |pkg|
367
+ {"path" => pkg.path}
354
368
  end,
355
- 'exclude' => []
369
+ "exclude" => []
356
370
  }.compact.tap do |config_yaml|
357
371
  unless config_present
358
372
  warn Util.color(YAML.dump(config_yaml), :magenta)
359
373
  dirname = Pathname.new(config_file_name).dirname.expand_path
360
374
  if Dir.exist?(dirname) && File.writable?(dirname)
361
375
  warn Util.color(<<~CONFIG_FILE_MSG, :magenta)
362
- This file will be saved to #{Pathname.new(config_file_name).expand_path},
363
- where you can customize it.
376
+ This file will be saved to #{Pathname.new(config_file_name).expand_path},
377
+ where you can customize it.
364
378
  CONFIG_FILE_MSG
365
379
  File.write(config_file_name, YAML.dump(config_yaml))
366
380
  end
367
381
  warn Util.color(<<~CONFIG_FILE_MSG, :magenta)
368
- For more information, see https://appmap.io/docs/reference/appmap-ruby.html#configuration
382
+ For more information, see https://appmap.io/docs/reference/appmap-ruby.html#configuration
369
383
  CONFIG_FILE_MSG
370
- warn logo.()
384
+ warn logo.call
371
385
  end
372
386
  end
373
387
  end
@@ -375,14 +389,14 @@ module AppMap
375
389
 
376
390
  # Loads configuration from a Hash.
377
391
  def load(config_data)
378
- name = config_data['name'] || Service::Guesser.guess_name
392
+ name = config_data["name"] || Service::Guesser.guess_name
379
393
  config_params = {
380
- exclude: config_data['exclude']
394
+ exclude: config_data["exclude"]
381
395
  }.compact
382
396
 
383
- if config_data['functions']
384
- config_params[:functions] = config_data['functions'].map do |hook_decl|
385
- if hook_decl['name'] || hook_decl['package']
397
+ if config_data["functions"]
398
+ config_params[:functions] = config_data["functions"].map do |hook_decl|
399
+ if hook_decl["name"] || hook_decl["package"]
386
400
  declare_hook_deprecated(hook_decl)
387
401
  else
388
402
  # Support the same syntax within the 'functions' that's used for externalized
@@ -393,24 +407,24 @@ module AppMap
393
407
  end
394
408
 
395
409
  config_params[:packages] = \
396
- if config_data['packages']
397
- config_data['packages'].map do |package|
398
- gem = package['gem']
399
- path = package['path']
400
- raise %q(AppMap config 'package' element should specify 'gem' or 'path', not both) if gem && path
401
- raise %q(AppMap config 'package' element should specify 'gem' or 'path') unless gem || path
410
+ if config_data["packages"]
411
+ config_data["packages"].map do |package|
412
+ gem = package["gem"]
413
+ path = package["path"]
414
+ raise "AppMap config 'package' element should specify 'gem' or 'path', not both" if gem && path
415
+ raise "AppMap config 'package' element should specify 'gem' or 'path'" unless gem || path
402
416
 
403
417
  if gem
404
- shallow = package['shallow']
418
+ shallow = package["shallow"]
405
419
  # shallow is true by default for gems
406
420
  shallow = true if shallow.nil?
407
421
 
408
422
  require_name = \
409
- package['package'] || #deprecated
410
- package['require_name']
411
- Package.build_from_gem(gem, require_name: require_name, exclude: package['exclude'] || [], shallow: shallow)
423
+ package["package"] || # deprecated
424
+ package["require_name"]
425
+ Package.build_from_gem(gem, require_name: require_name, exclude: package["exclude"] || [], shallow: shallow)
412
426
  else
413
- Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
427
+ Package.build_from_path(path, exclude: package["exclude"] || [], shallow: package["shallow"])
414
428
  end
415
429
  end.compact
416
430
  else
@@ -419,12 +433,12 @@ module AppMap
419
433
  end
420
434
  end
421
435
 
422
- if config_data['swagger']
423
- swagger_config = Swagger::Configuration.load(config_data['swagger'])
436
+ if config_data["swagger"]
437
+ swagger_config = Swagger::Configuration.load(config_data["swagger"])
424
438
  config_params[:swagger_config] = swagger_config
425
439
  end
426
- if config_data['depends']
427
- depends_config = Depends::Configuration.load(config_data['depends'])
440
+ if config_data["depends"]
441
+ depends_config = Depends::Configuration.load(config_data["depends"])
428
442
  config_params[:depends_config] = depends_config
429
443
  end
430
444
 
@@ -447,27 +461,35 @@ module AppMap
447
461
  @hook_paths.find { |hook_path| path.index(hook_path) == 0 }
448
462
  end
449
463
 
464
+ HookConfig = Struct.new(:package, :labels) do
465
+ end
466
+
450
467
  # Looks up a class and method in the config, to find the matching Package configuration.
451
- # This class is only used after +path_enabled?+ has returned `true`.
452
- LookupPackage = Struct.new(:config, :cls, :method) do
453
- def package
468
+ # This class is only used after +path_enabled?+ has returned `true`.
469
+ LookupHookConfig = Struct.new(:config, :cls, :method) do
470
+ def hook_config
454
471
  # Global "excludes" configuration can be used to ignore any class/method.
455
472
  return if config.never_hook?(cls, method)
456
473
 
457
- package_for_code_object || package_for_location
474
+ pkg = package_for_code_object || package_for_location
475
+ return unless pkg
476
+
477
+ comment = method.comment
478
+ labels = (pkg.labels || []) + ClassMap.parse_labels(comment)
479
+ HookConfig.new(pkg, labels)
458
480
  end
459
481
 
460
482
  # Hook a method which is specified by class and method name.
461
483
  def package_for_code_object
462
484
  class_name = begin
463
- cls.to_s.index('#<Class:') == 0 ? cls.to_s['#<Class:'.length...-1] : cls.name
464
- rescue
465
- # Calling #to_s on some Rails classes
466
- # (e.g. those generated to represent
467
- # associations) will raise an exception. Fall
468
- # back to using the class name.
469
- cls.name
470
- end
485
+ (cls.to_s.index("#<Class:") == 0) ? cls.to_s["#<Class:".length...-1] : cls.name
486
+ rescue
487
+ # Calling #to_s on some Rails classes
488
+ # (e.g. those generated to represent
489
+ # associations) will raise an exception. Fall
490
+ # back to using the class name.
491
+ cls.name
492
+ end
471
493
  Array(config.gem_hooks[class_name])
472
494
  .find { |hook| hook.include_method?(method.name) }
473
495
  &.package
@@ -482,20 +504,20 @@ module AppMap
482
504
  location_file = AppMap::Util.normalize_path(location_file)
483
505
 
484
506
  pkg = config
485
- .packages
486
- .select { |pkg| pkg.path }
487
- .select do |pkg|
507
+ .packages
508
+ .select { |pkg| pkg.path }
509
+ .select do |pkg|
488
510
  (location_file.index(pkg.path) == 0) &&
489
511
  !pkg.exclude.find { |p| location_file.index(p) }
490
512
  end
491
- .min { |a, b| b.path <=> a.path } # Longest matching package first
513
+ .min { |a, b| b.path <=> a.path } # Longest matching package first
492
514
 
493
515
  pkg.subpackage(location_file, config) if pkg
494
516
  end
495
517
  end
496
518
 
497
- def lookup_package(cls, method)
498
- LookupPackage.new(self, cls, method).package
519
+ def lookup_hook_config(cls, method)
520
+ LookupHookConfig.new(self, cls, method).hook_config
499
521
  end
500
522
 
501
523
  def never_hook?(cls, method)
@@ -505,7 +527,7 @@ module AppMap
505
527
  end
506
528
 
507
529
  _, separator, = ::AppMap::Hook.qualify_method_name(method)
508
- if exclude.member?(cls.name) || exclude.member?([ cls.name, separator, method.name ].join)
530
+ if exclude.member?(cls.name) || exclude.member?([cls.name, separator, method.name].join)
509
531
  HookLog.log "Hooking of #{method} disabled by configuration" if HookLog.enabled?
510
532
  return true
511
533
  end
@@ -1,20 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../appmap'
4
- require_relative './util'
5
- require_relative './detect_enabled'
6
- require 'fileutils'
3
+ require_relative "../appmap"
4
+ require_relative "util"
5
+ require_relative "detect_enabled"
6
+ require "fileutils"
7
7
 
8
8
  module AppMap
9
9
  module Cucumber
10
- APPMAP_OUTPUT_DIR = 'tmp/appmap/cucumber'
10
+ APPMAP_OUTPUT_DIR = File.join(AppMap.output_dir, "cucumber")
11
11
 
12
12
  ScenarioAttributes = Struct.new(:name, :feature, :feature_group)
13
13
 
14
14
  ProviderStruct = Struct.new(:scenario) do
15
15
  def feature_group
16
16
  # e.g. <Cucumber::Core::Ast::Location::Precise: cucumber/api/features/authenticate.feature:1>
17
- feature_path.split('/').last.split('.')[0]
17
+ feature_path.split("/").last.split(".")[0]
18
18
  end
19
19
  end
20
20
 
@@ -34,7 +34,7 @@ module AppMap
34
34
  # versions 4.0 and later.
35
35
  class Provider4 < ProviderStruct
36
36
  def attributes
37
- ScenarioAttributes.new(scenario.name, scenario.name.split(' ')[0..1].join(' '), feature_group)
37
+ ScenarioAttributes.new(scenario.name, scenario.name.split(" ")[0..1].join(" "), feature_group)
38
38
  end
39
39
 
40
40
  def feature_path
@@ -46,14 +46,14 @@ module AppMap
46
46
  def init
47
47
  AppMap::DetectEnabled.discourage_conflicting_recording_methods :cucumber
48
48
 
49
- warn 'Configuring AppMap recorder for Cucumber'
49
+ warn "Configuring AppMap recorder for Cucumber"
50
50
 
51
51
  FileUtils.mkdir_p APPMAP_OUTPUT_DIR
52
52
  end
53
53
 
54
54
  def write_scenario(scenario, appmap)
55
- appmap['metadata'] = update_metadata(scenario, appmap['metadata'])
56
- scenario_filename = AppMap::Util.scenario_filename(appmap['metadata']['name'])
55
+ appmap["metadata"] = update_metadata(scenario, appmap["metadata"])
56
+ scenario_filename = AppMap::Util.scenario_filename(appmap["metadata"]["name"])
57
57
 
58
58
  AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, scenario_filename), appmap)
59
59
  end
@@ -69,11 +69,11 @@ module AppMap
69
69
  protected
70
70
 
71
71
  def cucumber_version
72
- Gem.loaded_specs['cucumber']&.version&.to_s
72
+ Gem.loaded_specs["cucumber"]&.version&.to_s
73
73
  end
74
74
 
75
75
  def provider(scenario)
76
- major, = cucumber_version.split('.').map(&:to_i)
76
+ major, = cucumber_version.split(".").map(&:to_i)
77
77
  if major < 4
78
78
  ProviderBefore4
79
79
  else
@@ -85,19 +85,19 @@ module AppMap
85
85
  attributes = provider(scenario).attributes
86
86
 
87
87
  base_metadata.tap do |m|
88
- m['name'] = attributes.name
89
- m['feature'] = attributes.feature
90
- m['feature_group'] = attributes.feature_group
91
- m['labels'] ||= []
92
- m['labels'] += (scenario.tags&.map(&:name) || [])
93
- m['frameworks'] ||= []
94
- m['frameworks'] << {
95
- 'name' => 'cucumber',
96
- 'version' => Gem.loaded_specs['cucumber']&.version&.to_s
88
+ m["name"] = attributes.name
89
+ m["feature"] = attributes.feature
90
+ m["feature_group"] = attributes.feature_group
91
+ m["labels"] ||= []
92
+ m["labels"] += (scenario.tags&.map(&:name) || [])
93
+ m["frameworks"] ||= []
94
+ m["frameworks"] << {
95
+ "name" => "cucumber",
96
+ "version" => Gem.loaded_specs["cucumber"]&.version&.to_s
97
97
  }
98
- m['recorder'] = {
99
- 'name' => 'cucumber',
100
- 'type' => 'tests'
98
+ m["recorder"] = {
99
+ "name" => "cucumber",
100
+ "type" => "tests"
101
101
  }
102
102
  end
103
103
  end
@@ -106,7 +106,7 @@ module AppMap
106
106
  end
107
107
 
108
108
  if AppMap::Cucumber.enabled?
109
- require 'appmap'
109
+ require "appmap"
110
110
 
111
111
  AppMap::Cucumber.run
112
112
  end