appmap 0.32.0 → 0.34.4
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/.gitignore +1 -1
- data/.rbenv-gemsets +1 -0
- data/CHANGELOG.md +28 -0
- data/README.md +8 -2
- data/Rakefile +10 -3
- data/appmap.gemspec +6 -0
- data/ext/appmap/appmap.c +36 -0
- data/ext/appmap/extconf.rb +6 -0
- data/lib/appmap.rb +22 -10
- data/lib/appmap/class_map.rb +11 -6
- data/lib/appmap/config.rb +51 -25
- data/lib/appmap/cucumber.rb +19 -2
- data/lib/appmap/hook.rb +41 -16
- data/lib/appmap/hook/method.rb +32 -7
- data/lib/appmap/open.rb +57 -0
- data/lib/appmap/rails/sql_handler.rb +5 -10
- data/lib/appmap/rspec.rb +1 -1
- data/lib/appmap/util.rb +18 -1
- data/lib/appmap/version.rb +1 -1
- data/spec/fixtures/hook/instance_method.rb +4 -0
- data/spec/fixtures/hook/singleton_method.rb +21 -12
- data/spec/hook_spec.rb +141 -13
- data/spec/open_spec.rb +19 -0
- data/test/cli_test.rb +10 -0
- data/test/fixtures/openssl_recorder/Gemfile +3 -0
- data/test/fixtures/openssl_recorder/appmap.yml +3 -0
- data/test/fixtures/openssl_recorder/lib/openssl_cert_sign.rb +94 -0
- data/test/fixtures/openssl_recorder/lib/openssl_encrypt.rb +34 -0
- data/test/fixtures/openssl_recorder/lib/openssl_key_sign.rb +28 -0
- data/test/openssl_test.rb +203 -0
- metadata +71 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3a509a94faa2a85ff11702ae55ab3df975f04416d93d169b0a24989fd520b9d
|
4
|
+
data.tar.gz: 3895b9c788413f85738ba487e717bf48e4dfc2e8c0792f30c6f9f6ba7dc5e9c8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 07fe201395cad27e731402b5b2ff0efd653056dad16b9301657034df0b6e7fd3a205768ef3656874f55109edfc4d788d752f8bc73f4ff6874c815104547ce281
|
7
|
+
data.tar.gz: cbbd1551c352519954e9ddc9dd27279d6d232663fb67fd5ed4369a603a382f140593aa195aa08f35a57b0a14d38bef07bfef0aa7bd942d660a865fb3682d500d
|
data/.gitignore
CHANGED
data/.rbenv-gemsets
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
appmap-ruby
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
# v0.34.4
|
2
|
+
* Make sure `AppMap:Rails::SQLExaminer::ActiveRecordExaminer.server_version` only calls
|
3
|
+
`ActiveRecord::Base.connection.database_version` if it's available.
|
4
|
+
* Fix `AppMap:Rails::SQLExaminer::ActiveRecordExaminer.database_type` returns `:postgres`
|
5
|
+
in all supported versions of Rails.
|
6
|
+
|
7
|
+
# v0.34.3
|
8
|
+
* Fix a crash in `singleton_method_owner_name` that occurred if `__attached__.class` returned
|
9
|
+
something other than a `Module` or a `Class`.
|
10
|
+
|
11
|
+
# v0.34.2
|
12
|
+
* Add an extension that gets the name of the owner of a singleton method without calling
|
13
|
+
any methods that may have been redefined (e.g. `#to_s` or `.name`).
|
14
|
+
|
15
|
+
# v0.34.1
|
16
|
+
* Ensure that capturing events doesn't change the behavior of a hooked method that uses
|
17
|
+
`Time.now`. For example, if a test expects that `Time.now` will be called a certain
|
18
|
+
number of times by a hooked method, that expectation will now be met.
|
19
|
+
* Make sure `appmap/cucumber` requires `appmap`.
|
20
|
+
|
21
|
+
# v0.34.0
|
22
|
+
|
23
|
+
* Records builtin security and I/O methods from `OpenSSL`, `Net`, and `IO`.
|
24
|
+
|
25
|
+
# v0.33.0
|
26
|
+
|
27
|
+
* Added command `AppMap.open` to open an AppMap in the browser.
|
28
|
+
|
1
29
|
# v0.32.0
|
2
30
|
|
3
31
|
* Removes un-necessary fields from `return` events.
|
data/README.md
CHANGED
@@ -267,7 +267,13 @@ $ bundle config local.appmap $(pwd)
|
|
267
267
|
Run the tests via `rake`:
|
268
268
|
```
|
269
269
|
$ bundle exec rake test
|
270
|
-
```
|
270
|
+
```
|
271
|
+
|
272
|
+
The `test` target will build the native extension first, then run the tests. If you need
|
273
|
+
to build the extension separately, run
|
274
|
+
```
|
275
|
+
$ bundle exec rake compile
|
276
|
+
```
|
271
277
|
|
272
278
|
## Using fixture apps
|
273
279
|
|
@@ -286,7 +292,7 @@ resources such as a PostgreSQL database.
|
|
286
292
|
To build the fixture container images, first run:
|
287
293
|
|
288
294
|
```sh-session
|
289
|
-
$ bundle exec rake fixtures:all
|
295
|
+
$ bundle exec rake build:fixtures:all
|
290
296
|
```
|
291
297
|
|
292
298
|
This will build the `appmap.gem`, along with a Docker image for each fixture app.
|
data/Rakefile
CHANGED
@@ -6,6 +6,13 @@ require 'rdoc/task'
|
|
6
6
|
|
7
7
|
require 'open3'
|
8
8
|
|
9
|
+
require "rake/extensiontask"
|
10
|
+
|
11
|
+
desc 'build the native extension'
|
12
|
+
Rake::ExtensionTask.new("appmap") do |ext|
|
13
|
+
ext.lib_dir = "lib/appmap"
|
14
|
+
end
|
15
|
+
|
9
16
|
namespace 'gem' do
|
10
17
|
require 'bundler/gem_tasks'
|
11
18
|
end
|
@@ -104,7 +111,7 @@ end
|
|
104
111
|
namespace :spec do
|
105
112
|
RUBY_VERSIONS.each do |ruby_version|
|
106
113
|
desc ruby_version
|
107
|
-
task ruby_version, [:specs] => ["build:fixtures:#{ruby_version}:all"] do |_, task_args|
|
114
|
+
task ruby_version, [:specs] => ["compile", "build:fixtures:#{ruby_version}:all"] do |_, task_args|
|
108
115
|
run_specs(ruby_version, task_args)
|
109
116
|
end.tap do|t|
|
110
117
|
desc "Run all specs"
|
@@ -119,13 +126,13 @@ Rake::RDocTask.new do |rd|
|
|
119
126
|
rd.title = 'AppMap'
|
120
127
|
end
|
121
128
|
|
122
|
-
Rake::TestTask.new(:
|
129
|
+
Rake::TestTask.new(minitest: 'compile') do |t|
|
123
130
|
t.libs << 'test'
|
124
131
|
t.libs << 'lib'
|
125
132
|
t.test_files = FileList['test/*_test.rb']
|
126
133
|
end
|
127
134
|
|
128
|
-
task spec:
|
135
|
+
task spec: %i[spec:all]
|
129
136
|
|
130
137
|
task test: %i[spec:all minitest]
|
131
138
|
|
data/appmap.gemspec
CHANGED
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
19
19
|
spec.files = `git ls-files --no-deleted`.split("
|
20
20
|
")
|
21
|
+
spec.extensions << "ext/appmap/extconf.rb"
|
22
|
+
|
21
23
|
spec.bindir = 'exe'
|
22
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
25
|
spec.require_paths = ['lib']
|
@@ -26,6 +28,7 @@ Gem::Specification.new do |spec|
|
|
26
28
|
spec.add_dependency 'faraday'
|
27
29
|
spec.add_dependency 'gli'
|
28
30
|
spec.add_dependency 'parser'
|
31
|
+
spec.add_dependency 'rack'
|
29
32
|
|
30
33
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
31
34
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
@@ -33,6 +36,7 @@ Gem::Specification.new do |spec|
|
|
33
36
|
spec.add_development_dependency 'rake', '>= 12.3.3'
|
34
37
|
spec.add_development_dependency 'rdoc'
|
35
38
|
spec.add_development_dependency 'rubocop'
|
39
|
+
spec.add_development_dependency "rake-compiler"
|
36
40
|
|
37
41
|
# Testing
|
38
42
|
spec.add_development_dependency 'climate_control'
|
@@ -41,4 +45,6 @@ Gem::Specification.new do |spec|
|
|
41
45
|
spec.add_development_dependency 'rspec'
|
42
46
|
spec.add_development_dependency 'selenium-webdriver'
|
43
47
|
spec.add_development_dependency 'webdrivers', '~> 4.0'
|
48
|
+
spec.add_development_dependency 'timecop'
|
49
|
+
spec.add_development_dependency 'hashie'
|
44
50
|
end
|
data/ext/appmap/appmap.c
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <ruby/intern.h>
|
3
|
+
|
4
|
+
// Seems like CLASS_OR_MODULE_P should really be in a header file in
|
5
|
+
// the ruby source -- it's in object.c and duplicated in eval.c. In
|
6
|
+
// the future, we'll fail if it does get moved to a header.
|
7
|
+
#define CLASS_OR_MODULE_P(obj) \
|
8
|
+
(!SPECIAL_CONST_P(obj) && \
|
9
|
+
(BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE))
|
10
|
+
|
11
|
+
static VALUE singleton_method_owner_name(VALUE klass, VALUE method)
|
12
|
+
{
|
13
|
+
VALUE owner = rb_funcall(method, rb_intern("owner"), 0);
|
14
|
+
VALUE attached = rb_ivar_get(owner, rb_intern("__attached__"));
|
15
|
+
if (!CLASS_OR_MODULE_P(attached)) {
|
16
|
+
attached = rb_funcall(attached, rb_intern("class"), 0);
|
17
|
+
}
|
18
|
+
|
19
|
+
// Did __attached__.class return an object that's a Module or a
|
20
|
+
// Class?
|
21
|
+
if (CLASS_OR_MODULE_P(attached)) {
|
22
|
+
// Yup, get it's name
|
23
|
+
return rb_mod_name(attached);
|
24
|
+
}
|
25
|
+
|
26
|
+
// Nope (which seems weird, but whatever). Fall back to calling
|
27
|
+
// #to_s on the method's owner and hope for the best.
|
28
|
+
return rb_funcall(owner, rb_intern("to_s"), 0);
|
29
|
+
}
|
30
|
+
|
31
|
+
void Init_appmap() {
|
32
|
+
VALUE appmap = rb_define_module("AppMap");
|
33
|
+
VALUE hook = rb_define_class_under(appmap, "Hook", rb_cObject);
|
34
|
+
|
35
|
+
rb_define_singleton_method(hook, "singleton_method_owner_name", singleton_method_owner_name, 1);
|
36
|
+
}
|
data/lib/appmap.rb
CHANGED
@@ -14,19 +14,23 @@ require 'appmap/trace'
|
|
14
14
|
require 'appmap/class_map'
|
15
15
|
require 'appmap/metadata'
|
16
16
|
require 'appmap/util'
|
17
|
+
require 'appmap/open'
|
18
|
+
|
19
|
+
# load extension
|
20
|
+
require 'appmap/appmap'
|
17
21
|
|
18
22
|
module AppMap
|
19
23
|
class << self
|
20
24
|
@configuration = nil
|
21
25
|
@configuration_file_path = nil
|
22
26
|
|
23
|
-
#
|
27
|
+
# Gets the configuration. If there is no configuration, the default
|
24
28
|
# configuration is initialized.
|
25
29
|
def configuration
|
26
|
-
@configuration ||=
|
30
|
+
@configuration ||= initialize
|
27
31
|
end
|
28
32
|
|
29
|
-
#
|
33
|
+
# Sets the configuration. This is only expected to happen once per
|
30
34
|
# Ruby process.
|
31
35
|
def configuration=(config)
|
32
36
|
warn 'AppMap is already configured' if @configuration && config
|
@@ -34,22 +38,24 @@ module AppMap
|
|
34
38
|
@configuration = config
|
35
39
|
end
|
36
40
|
|
37
|
-
#
|
41
|
+
# Configures AppMap for recording. Default behavior is to configure from "appmap.yml".
|
38
42
|
# This method also activates the code hooks which record function calls as trace events.
|
39
43
|
# Call this function before the program code is loaded by the Ruby VM, otherwise
|
40
44
|
# the load events won't be seen and the hooks won't activate.
|
41
45
|
def initialize(config_file_path = 'appmap.yml')
|
42
46
|
warn "Configuring AppMap from path #{config_file_path}"
|
43
|
-
|
44
|
-
|
47
|
+
Config.load_from_file(config_file_path).tap do |configuration|
|
48
|
+
self.configuration = configuration
|
49
|
+
Hook.new(configuration).enable
|
50
|
+
end
|
45
51
|
end
|
46
52
|
|
47
|
-
#
|
53
|
+
# Used to start tracing, stop tracing, and record events.
|
48
54
|
def tracing
|
49
55
|
@tracing ||= Trace::Tracing.new
|
50
56
|
end
|
51
57
|
|
52
|
-
#
|
58
|
+
# Records the events which occur while processing a block,
|
53
59
|
# and returns an AppMap as a Hash.
|
54
60
|
def record
|
55
61
|
tracer = tracing.trace
|
@@ -70,12 +76,18 @@ module AppMap
|
|
70
76
|
}
|
71
77
|
end
|
72
78
|
|
73
|
-
#
|
79
|
+
# Uploads an AppMap to the AppLand website and displays it.
|
80
|
+
def open(appmap = nil, &block)
|
81
|
+
appmap ||= AppMap.record(&block)
|
82
|
+
AppMap::Open.new(appmap).perform
|
83
|
+
end
|
84
|
+
|
85
|
+
# Builds a class map from a config and a list of Ruby methods.
|
74
86
|
def class_map(methods)
|
75
87
|
ClassMap.build_from_methods(configuration, methods)
|
76
88
|
end
|
77
89
|
|
78
|
-
#
|
90
|
+
# Returns default metadata detected from the Ruby system and from the
|
79
91
|
# filesystem.
|
80
92
|
def detect_metadata
|
81
93
|
@metadata ||= Metadata.detect.freeze
|
data/lib/appmap/class_map.rb
CHANGED
@@ -61,7 +61,7 @@ module AppMap
|
|
61
61
|
location: location,
|
62
62
|
static: static,
|
63
63
|
labels: labels
|
64
|
-
}.delete_if {|k,v| v.nil?}
|
64
|
+
}.delete_if { |k,v| v.nil? || v == [] }
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
@@ -100,11 +100,16 @@ module AppMap
|
|
100
100
|
static: static
|
101
101
|
}
|
102
102
|
location = method.source_location
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
103
|
+
|
104
|
+
function_info[:location] = \
|
105
|
+
if location
|
106
|
+
location_file, lineno = location
|
107
|
+
location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
|
108
|
+
[ location_file, lineno ].join(':')
|
109
|
+
else
|
110
|
+
[ method.defined_class, static ? '.' : '#', method.name ].join
|
111
|
+
end
|
112
|
+
|
108
113
|
function_info[:labels] = package.labels if package.labels
|
109
114
|
object_infos << function_info
|
110
115
|
|
data/lib/appmap/config.rb
CHANGED
@@ -1,31 +1,54 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module AppMap
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
class Config
|
5
|
+
Package = Struct.new(:path, :package_name, :exclude, :labels) do
|
6
|
+
def initialize(path, package_name: nil, exclude: [], labels: [])
|
7
|
+
super path, package_name, exclude, labels
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
{
|
12
|
+
path: path,
|
13
|
+
package_name: package_name,
|
14
|
+
exclude: exclude.blank? ? nil : exclude,
|
15
|
+
labels: labels.blank? ? nil : labels
|
16
|
+
}.compact
|
17
|
+
end
|
7
18
|
end
|
8
19
|
|
9
|
-
|
10
|
-
{
|
11
|
-
path: path,
|
12
|
-
package_name: package_name,
|
13
|
-
exclude: exclude.blank? ? nil : exclude,
|
14
|
-
labels: labels.blank? ? nil : labels
|
15
|
-
}.compact
|
20
|
+
Hook = Struct.new(:method_names, :package) do
|
16
21
|
end
|
17
|
-
end
|
18
22
|
|
19
|
-
|
23
|
+
OPENSSL_PACKAGE = Package.new('openssl', package_name: 'openssl', labels: %w[security crypto])
|
24
|
+
|
20
25
|
# Methods that should always be hooked, with their containing
|
21
26
|
# package and labels that should be applied to them.
|
22
27
|
HOOKED_METHODS = {
|
23
|
-
'ActiveSupport::SecurityUtils' =>
|
24
|
-
|
25
|
-
|
26
|
-
|
28
|
+
'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.new('active_support', package_name: 'active_support', labels: %w[security crypto]))
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
BUILTIN_METHODS = {
|
32
|
+
'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGE),
|
33
|
+
'Digest::Instance' => Hook.new(:digest, OPENSSL_PACKAGE),
|
34
|
+
'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGE),
|
35
|
+
'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGE),
|
36
|
+
'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final], OPENSSL_PACKAGE),
|
37
|
+
'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGE),
|
38
|
+
'Logger' => Hook.new(:add, Package.new('logger', labels: %w[log io])),
|
39
|
+
'Net::HTTP' => Hook.new(:request, Package.new('net/http', package_name: 'net/http', labels: %w[http io])),
|
40
|
+
'Net::SMTP' => Hook.new(:send, Package.new('net/smtp', package_name: 'net/smtp', labels: %w[smtp email io])),
|
41
|
+
'Net::POP3' => Hook.new(:mails, Package.new('net/pop3', package_name: 'net/pop', labels: %w[pop pop3 email io])),
|
42
|
+
'Net::IMAP' => Hook.new(:send_command, Package.new('net/imap', package_name: 'net/imap', labels: %w[imap email io])),
|
43
|
+
'IO' => Hook.new(%i[read write open close], Package.new('io', labels: %w[io])),
|
44
|
+
'Marshal' => Hook.new(%i[dump load], Package.new('marshal', labels: %w[serialization marshal])),
|
45
|
+
'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.new('yaml', package_name: 'psych', labels: %w[serialization yaml])),
|
46
|
+
'JSON::Ext::Parser' => Hook.new(:parse, Package.new('json', package_name: 'json', labels: %w[serialization json])),
|
47
|
+
'JSON::Ext::Generator::State' => Hook.new(:generate, Package.new('json', package_name: 'json', labels: %w[serialization json]))
|
48
|
+
}.freeze
|
27
49
|
|
28
50
|
attr_reader :name, :packages
|
51
|
+
|
29
52
|
def initialize(name, packages = [])
|
30
53
|
@name = name
|
31
54
|
@packages = packages
|
@@ -41,7 +64,7 @@ module AppMap
|
|
41
64
|
# Loads configuration from a Hash.
|
42
65
|
def load(config_data)
|
43
66
|
packages = (config_data['packages'] || []).map do |package|
|
44
|
-
Package.new(package['path'],
|
67
|
+
Package.new(package['path'], exclude: package['exclude'] || [])
|
45
68
|
end
|
46
69
|
Config.new config_data['name'], packages
|
47
70
|
end
|
@@ -55,9 +78,9 @@ module AppMap
|
|
55
78
|
end
|
56
79
|
|
57
80
|
def package_for_method(method)
|
58
|
-
defined_class, _, method_name = Hook.qualify_method_name(method)
|
59
|
-
|
60
|
-
return
|
81
|
+
defined_class, _, method_name = ::AppMap::Hook.qualify_method_name(method)
|
82
|
+
package = find_package(defined_class, method_name)
|
83
|
+
return package if package
|
61
84
|
|
62
85
|
location = method.source_location
|
63
86
|
location_file, = location
|
@@ -75,15 +98,18 @@ module AppMap
|
|
75
98
|
end
|
76
99
|
|
77
100
|
def always_hook?(defined_class, method_name)
|
78
|
-
!!
|
101
|
+
!!find_package(defined_class, method_name)
|
79
102
|
end
|
80
103
|
|
81
|
-
def
|
82
|
-
|
104
|
+
def find_package(defined_class, method_name)
|
105
|
+
hook = find_hook(defined_class)
|
106
|
+
return nil unless hook
|
107
|
+
|
108
|
+
Array(hook.method_names).include?(method_name) ? hook.package : nil
|
83
109
|
end
|
84
110
|
|
85
|
-
def
|
86
|
-
HOOKED_METHODS[defined_class] ||
|
111
|
+
def find_hook(defined_class)
|
112
|
+
HOOKED_METHODS[defined_class] || BUILTIN_METHODS[defined_class]
|
87
113
|
end
|
88
114
|
end
|
89
115
|
end
|
data/lib/appmap/cucumber.rb
CHANGED
@@ -4,6 +4,8 @@ require 'appmap/util'
|
|
4
4
|
|
5
5
|
module AppMap
|
6
6
|
module Cucumber
|
7
|
+
APPMAP_OUTPUT_DIR = 'tmp/appmap/cucumber'
|
8
|
+
|
7
9
|
ScenarioAttributes = Struct.new(:name, :feature, :feature_group)
|
8
10
|
|
9
11
|
ProviderStruct = Struct.new(:scenario) do
|
@@ -38,18 +40,27 @@ module AppMap
|
|
38
40
|
end
|
39
41
|
|
40
42
|
class << self
|
43
|
+
def init
|
44
|
+
warn 'Configuring AppMap recorder for Cucumber'
|
45
|
+
|
46
|
+
FileUtils.mkdir_p APPMAP_OUTPUT_DIR
|
47
|
+
end
|
48
|
+
|
41
49
|
def write_scenario(scenario, appmap)
|
42
50
|
appmap['metadata'] = update_metadata(scenario, appmap['metadata'])
|
43
51
|
scenario_filename = AppMap::Util.scenario_filename(appmap['metadata']['name'])
|
44
52
|
|
45
|
-
|
46
|
-
File.write(File.join('tmp/appmap/cucumber', scenario_filename), JSON.generate(appmap))
|
53
|
+
File.write(File.join(APPMAP_OUTPUT_DIR, scenario_filename), JSON.generate(appmap))
|
47
54
|
end
|
48
55
|
|
49
56
|
def enabled?
|
50
57
|
ENV['APPMAP'] == 'true'
|
51
58
|
end
|
52
59
|
|
60
|
+
def run
|
61
|
+
init
|
62
|
+
end
|
63
|
+
|
53
64
|
protected
|
54
65
|
|
55
66
|
def cucumber_version
|
@@ -87,3 +98,9 @@ module AppMap
|
|
87
98
|
end
|
88
99
|
end
|
89
100
|
end
|
101
|
+
|
102
|
+
if AppMap::Cucumber.enabled?
|
103
|
+
require 'appmap'
|
104
|
+
|
105
|
+
AppMap::Cucumber.run
|
106
|
+
end
|