appmap 0.39.1 → 0.42.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/CONTRIBUTING.md +22 -0
- data/README.md +105 -50
- data/lib/appmap.rb +5 -0
- data/lib/appmap/class_map.rb +25 -8
- data/lib/appmap/command/record.rb +1 -1
- data/lib/appmap/config.rb +48 -28
- data/lib/appmap/event.rb +14 -4
- data/lib/appmap/hook.rb +7 -0
- data/lib/appmap/hook/method.rb +1 -1
- data/lib/appmap/middleware/remote_recording.rb +1 -1
- data/lib/appmap/minitest.rb +17 -14
- data/lib/appmap/rails/request_handler.rb +8 -3
- data/lib/appmap/railtie.rb +1 -5
- data/lib/appmap/rspec.rb +12 -78
- data/lib/appmap/version.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +2 -2
- data/spec/config_spec.rb +1 -0
- data/spec/fixtures/hook/exclude.rb +15 -0
- data/spec/fixtures/hook/labels.rb +6 -0
- data/spec/fixtures/rails5_users_app/Gemfile +2 -3
- data/spec/fixtures/rails5_users_app/appmap.yml +4 -1
- data/spec/fixtures/rails5_users_app/config/application.rb +2 -0
- data/spec/fixtures/rails5_users_app/docker-compose.yml +3 -0
- data/spec/fixtures/rails5_users_app/spec/controllers/users_controller_api_spec.rb +3 -3
- data/spec/fixtures/rails5_users_app/spec/models/user_spec.rb +2 -12
- data/spec/fixtures/rails6_users_app/Gemfile +2 -3
- data/spec/fixtures/rails6_users_app/appmap.yml +4 -1
- data/spec/fixtures/rails6_users_app/config/application.rb +2 -0
- data/spec/fixtures/rails6_users_app/docker-compose.yml +3 -0
- data/spec/fixtures/rails6_users_app/spec/controllers/users_controller_api_spec.rb +3 -3
- data/spec/fixtures/rails6_users_app/spec/models/user_spec.rb +2 -12
- data/spec/hook_spec.rb +37 -1
- data/spec/record_sql_rails_pg_spec.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/test/fixtures/gem_test/appmap.yml +1 -1
- data/test/fixtures/gem_test/test/parser_test.rb +12 -0
- data/test/fixtures/rspec_recorder/spec/decorated_hello_spec.rb +3 -3
- data/test/fixtures/rspec_recorder/spec/plain_hello_spec.rb +1 -1
- data/test/gem_test.rb +4 -4
- data/test/minitest_test.rb +1 -2
- data/test/rspec_test.rb +1 -7
- metadata +6 -4
- data/spec/rspec_feature_metadata_spec.rb +0 -31
- data/test/fixtures/gem_test/test/to_param_test.rb +0 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50826fcf91733d3bb29aad0b6c77af2516ad08aa87557c3675d4be896f7790db
|
4
|
+
data.tar.gz: 4e5ee3f431b68a69de5ff6b080eeae861f3f7de6fe952ea1e71b5c30d4777bc1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fcd01c7d1a3d70c29e33d8890e8f1ea8d7dc5cd2fbca7724618fcc76190c2d0217b89ba69e4de5003eb80147d44c15335cfdff60e67cdaf3eeaa18655fdf2b4f
|
7
|
+
data.tar.gz: 32c714968c8669f3a9dceb20543a0f09856948f4b4d6b8584ad06d6347cf10f28a4d0386e4f3af1aaccb1709ec2a88fefe88da88104ffbe18dfd0fd81fb5cb95
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,31 @@
|
|
1
|
+
# v0.42.0
|
2
|
+
|
3
|
+
* Remove `feature_group` and `feature` metadata from minitest and RSpec AppMaps.
|
4
|
+
* Add `metadata.source_location`.
|
5
|
+
|
6
|
+
# v0.41.2
|
7
|
+
|
8
|
+
* Don't rely on `gemspec.source_paths` to list all the source locations in a gem. Hook any code that's loaded
|
9
|
+
from inside the `gem_dir`.
|
10
|
+
|
11
|
+
# v0.41.1
|
12
|
+
|
13
|
+
* Make best effort to ensure that class name is not `null` in the appmap.json.
|
14
|
+
* Don't try and instrument gems which are a dependency of the this gem.
|
15
|
+
* Fix a nil exception when applying the exclude list to builtins.
|
16
|
+
|
17
|
+
# v0.41.0
|
18
|
+
|
19
|
+
* Adjust some label names to match `provider.*`, `format.*`.
|
20
|
+
* Add global `exclude` list to *appmap.yml* which can be used to definitively exclude specific classes and methods.
|
21
|
+
|
22
|
+
# v0.40.0
|
23
|
+
|
24
|
+
* Parse source code comments into function labels.
|
25
|
+
|
26
|
+
# v0.39.2
|
27
|
+
* Correctly recognize normalized path info for subengines.
|
28
|
+
|
1
29
|
# v0.39.1
|
2
30
|
* Support Ruby 2.7.
|
3
31
|
* Remove support for Rails 4.
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# Contributing to appmap-ruby
|
2
|
+
|
3
|
+
We are incredibly thankful for the contributions we receive from the community. Before contributing, please take a moment to read our [Contributor License Agreement](https://github.com/applandorg/community/blob/master/docs/CLA%20Instructions.pdf) and our [Code of Conduct](https://github.com/applandorg/community/blob/master/docs/Code%20of%20Conduct%20for%20Contributors.pdf).
|
4
|
+
|
5
|
+
## Contributor License Agreement
|
6
|
+
We require our external contributors to sign a Contributor License Agreement ("CLA") in order to ensure that
|
7
|
+
our projects remain licensed under Free and Open Source licenses such as while allowing
|
8
|
+
Appland to build a sustainable business.
|
9
|
+
|
10
|
+
AppLand is committed to having a true Free and Open Source Software license for our
|
11
|
+
non-commercial software. A CLA enables AppLand to safely commercialize our products while
|
12
|
+
keeping a standard FOSS license with all the rights that license grants to users.
|
13
|
+
|
14
|
+
* [Contributor License Agreement](https://github.com/applandorg/community/blob/master/docs/CLA%20Instructions.pdf)
|
15
|
+
|
16
|
+
|
17
|
+
## Code of Conduct
|
18
|
+
|
19
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and
|
20
|
+
healthy community.
|
21
|
+
|
22
|
+
* [Code of Conduct](https://github.com/applandorg/community/blob/master/docs/Code%20of%20Conduct%20for%20Contributors.pdf)
|
data/README.md
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
|
2
2
|
- [About](#about)
|
3
|
+
- [Supported versions](#supported-versions)
|
3
4
|
- [Installation](#installation)
|
4
5
|
- [Configuration](#configuration)
|
6
|
+
- [Labels](#labels)
|
5
7
|
- [Running](#running)
|
6
8
|
- [RSpec](#rspec)
|
7
9
|
- [Minitest](#minitest)
|
8
10
|
- [Cucumber](#cucumber)
|
9
11
|
- [Remote recording](#remote-recording)
|
10
|
-
- [
|
12
|
+
- [Server process recording](#server-process-recording)
|
13
|
+
- [AppMap for VSCode](#appmap-for-vscode)
|
11
14
|
- [Uploading AppMaps](#uploading-appmaps)
|
12
15
|
- [Development](#development)
|
13
16
|
- [Running tests](#running-tests)
|
@@ -23,21 +26,21 @@
|
|
23
26
|
"AppMap" is a data format which records code structure (modules, classes, and methods), code execution events
|
24
27
|
(function calls and returns), and code metadata (repo name, repo URL, commit
|
25
28
|
SHA, labels, etc). It's more granular than a performance profile, but it's less
|
26
|
-
granular than a full debug trace. It's designed to be optimal for understanding the design intent and
|
29
|
+
granular than a full debug trace. It's designed to be optimal for understanding the design intent and structure of code and key data flows.
|
27
30
|
|
28
31
|
There are several ways to record AppMaps of your Ruby program using the `appmap` gem:
|
29
32
|
|
30
|
-
* Run your RSpec
|
33
|
+
* Run your tests (RSpec, Minitest, Cucumber) with the environment variable `APPMAP=true`. An AppMap will be generated for each spec.
|
31
34
|
* Run your application server with AppMap remote recording enabled, and use the [AppLand
|
32
35
|
browser extension](https://github.com/applandinc/appland-browser-extension) to start,
|
33
36
|
stop, and upload recordings.
|
34
|
-
*
|
37
|
+
* Wrap some code in an `AppMap.record` block, which returns JSON containing the code execution trace.
|
35
38
|
|
36
|
-
Once you have
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
Once you have made a recording, there are two ways to view automatically generated diagrams of the AppMaps.
|
40
|
+
|
41
|
+
The first option is to load the diagrams directly in your IDE, using the [AppMap extension for VSCode](https://marketplace.visualstudio.com/items?itemName=appland.appmap).
|
42
|
+
|
43
|
+
The second option is to upload them to the [AppLand server](https://app.land) using the [AppLand CLI](https://github.com/applandinc/appland-cli/releases).
|
41
44
|
|
42
45
|
### Supported versions
|
43
46
|
|
@@ -48,23 +51,26 @@ Support for new versions is added frequently, please check back regularly for up
|
|
48
51
|
|
49
52
|
# Installation
|
50
53
|
|
51
|
-
|
54
|
+
<a href="https://www.loom.com/share/78ab32a312ff4b85aa8827a37f1cb655"> <p>Quick and easy setup of the AppMap gem for Rails - Watch Video</p> <img style="max-width:300px;" src="https://cdn.loom.com/sessions/thumbnails/78ab32a312ff4b85aa8827a37f1cb655-with-play.gif"> </a>
|
52
55
|
|
53
|
-
|
56
|
+
|
57
|
+
Add `gem 'appmap'` to **beginning** of your Gemfile. We recommend that you add the `appmap` gem to the `:development, :test` group. Your Gemfile should look something like this:
|
54
58
|
|
55
59
|
```
|
56
|
-
|
57
|
-
|
60
|
+
source 'https://rubygems.org'
|
61
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
58
62
|
|
59
|
-
|
63
|
+
# Optional rubRuby version
|
64
|
+
# ruby '2.7.2'
|
60
65
|
|
61
|
-
```
|
62
66
|
group :development, :test do
|
63
67
|
gem 'appmap'
|
64
68
|
end
|
65
69
|
```
|
66
70
|
|
67
|
-
|
71
|
+
Install with `bundle install`, as usual.
|
72
|
+
|
73
|
+
It's important to add the `appmap` gem before any other gems that you may want to instrument. There is more about this in the section on adding gems to the *appmap.yml*.
|
68
74
|
|
69
75
|
**Railtie**
|
70
76
|
|
@@ -72,7 +78,24 @@ If you are using Ruby on Rails, require the railtie after Rails is loaded.
|
|
72
78
|
|
73
79
|
```
|
74
80
|
# application.rb is a good place to do this, along with all the other railties.
|
75
|
-
require 'appmap
|
81
|
+
# Don't require the railtie in environments that don't bundle the appmap gem.
|
82
|
+
require 'appmap/railtie' if defined?(AppMap).
|
83
|
+
```
|
84
|
+
|
85
|
+
**application.rb**
|
86
|
+
|
87
|
+
Add this line to *application.rb*, to enable server recording with `APPMAP_RECORD=true`:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
module MyApp
|
91
|
+
class Application < Rails::Application
|
92
|
+
...
|
93
|
+
|
94
|
+
config.appmap.enabled = true if ENV['APPMAP_RECORD']
|
95
|
+
|
96
|
+
...
|
97
|
+
end
|
98
|
+
end
|
76
99
|
```
|
77
100
|
|
78
101
|
# Configuration
|
@@ -81,15 +104,28 @@ When you run your program, the `appmap` gem reads configuration settings from `a
|
|
81
104
|
file for a typical Rails project:
|
82
105
|
|
83
106
|
```yaml
|
84
|
-
name
|
107
|
+
# 'name' should generally be the same as the code repo name.
|
108
|
+
name: my_project
|
85
109
|
packages:
|
86
110
|
- path: app/controllers
|
87
111
|
- path: app/models
|
112
|
+
- path: app/jobs
|
113
|
+
- path: app/helpers
|
114
|
+
# Include the gems that you want to see in the dependency maps.
|
115
|
+
# These are just examples.
|
88
116
|
- gem: activerecord
|
117
|
+
- gem: devise
|
118
|
+
- gem: aws-sdk
|
119
|
+
- gem: will_paginate
|
120
|
+
exclude:
|
121
|
+
- MyClass
|
122
|
+
- MyClass#my_instance_method
|
123
|
+
- MyClass.my_class_method
|
89
124
|
```
|
90
125
|
|
91
126
|
* **name** Provides the project name (required)
|
92
|
-
* **packages** A list of source code directories which should be
|
127
|
+
* **packages** A list of source code directories which should be recorded.
|
128
|
+
* **exclude** A list of classes and/or methods to definitively exclude from recording.
|
93
129
|
|
94
130
|
**packages**
|
95
131
|
|
@@ -97,13 +133,46 @@ Each entry in the `packages` list is a YAML object which has the following keys:
|
|
97
133
|
|
98
134
|
* **path** The path to the source code directory. The path may be relative to the current working directory, or it may
|
99
135
|
be an absolute path.
|
100
|
-
* **gem** As an alternative to specifying the path, specify the name of a dependency gem. When using `gem`, don't specify `path`.
|
136
|
+
* **gem** As an alternative to specifying the path, specify the name of a dependency gem. When using `gem`, don't specify `path`. In your `Gemfile`, the `appmap` gem **must** be listed **before** any gem that you specify in your *appmap.yml*.
|
101
137
|
* **exclude** A list of files and directories which will be ignored. By default, all modules, classes and public
|
102
|
-
functions are inspected.
|
138
|
+
functions are inspected. See also: global `exclude` list.
|
103
139
|
* **shallow** When set to `true`, only the first function call entry into a package will be recorded. Subsequent function calls within
|
104
140
|
the same package are not recorded unless code execution leaves the package and re-enters it. Default: `true` when using `gem`,
|
105
141
|
`false` when using `path`.
|
106
142
|
|
143
|
+
**exclude**
|
144
|
+
|
145
|
+
Optional list of fully qualified class and method names. Separate class and method names with period (`.`) for class methods and hash (`#`) for instance methods.
|
146
|
+
|
147
|
+
|
148
|
+
# Labels
|
149
|
+
|
150
|
+
The [AppMap data format](https://github.com/applandinc/appmap) provides for class and function `labels`, which can be used to enhance the AppMap visualizations, and to programatically analyze the data.
|
151
|
+
|
152
|
+
You can apply function labels using source code comments in your Ruby code. To apply a labels to a function, add a `@label` or `@labels` line to the comment which immediately precedes a function.
|
153
|
+
|
154
|
+
For example, if you add this comment to your source code:
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
class ApiKey
|
158
|
+
# @labels provider.authentication security
|
159
|
+
def authenticate(key)
|
160
|
+
# logic to verify the key here...
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
Then the AppMap metadata section for this function will include:
|
166
|
+
|
167
|
+
```json
|
168
|
+
{
|
169
|
+
"name": "authenticate",
|
170
|
+
"type": "function",
|
171
|
+
"labels": [ "provider.authentication", "security" ]
|
172
|
+
}
|
173
|
+
```
|
174
|
+
|
175
|
+
|
107
176
|
# Running
|
108
177
|
|
109
178
|
## RSpec
|
@@ -129,10 +198,7 @@ require 'appmap/rspec'
|
|
129
198
|
require File.expand_path("../../config/environment", __FILE__)
|
130
199
|
```
|
131
200
|
|
132
|
-
2)
|
133
|
-
examples.
|
134
|
-
|
135
|
-
3) Run the tests with the environment variable `APPMAP=true`:
|
201
|
+
2) Run the tests with the environment variable `APPMAP=true`:
|
136
202
|
|
137
203
|
```sh-session
|
138
204
|
$ APPMAP=true bundle exec rspec
|
@@ -145,23 +211,6 @@ $ find tmp/appmap/rspec
|
|
145
211
|
Hello_says_hello_when_prompted.appmap.json
|
146
212
|
```
|
147
213
|
|
148
|
-
If you include the `feature` and `feature_group` metadata, these attributes will be exported to the AppMap file in the
|
149
|
-
`metadata` section. It will look something like this:
|
150
|
-
|
151
|
-
```json
|
152
|
-
{
|
153
|
-
...
|
154
|
-
"metadata": {
|
155
|
-
"name": "Hello app says hello when prompted",
|
156
|
-
"feature": "Hello app says hello",
|
157
|
-
"feature_group": "Hello"
|
158
|
-
},
|
159
|
-
...
|
160
|
-
}
|
161
|
-
```
|
162
|
-
|
163
|
-
If you don't explicitly declare `feature` and `feature_group`, then they will be inferred from the spec name and example descriptions.
|
164
|
-
|
165
214
|
## Minitest
|
166
215
|
|
167
216
|
To record Minitest tests, follow these additional steps:
|
@@ -188,13 +237,13 @@ require_relative '../config/environment'
|
|
188
237
|
2) Run your tests as you normally would with the environment variable `APPMAP=true`. For example:
|
189
238
|
|
190
239
|
```
|
191
|
-
$ APPMAP=true bundle exec rake
|
240
|
+
$ APPMAP=true bundle exec rake test
|
192
241
|
```
|
193
242
|
|
194
243
|
or
|
195
244
|
|
196
245
|
```
|
197
|
-
$ APPMAP=true bundle exec -Ilib -Itest test/*
|
246
|
+
$ APPMAP=true bundle exec ruby -Ilib -Itest test/*_test.rb
|
198
247
|
```
|
199
248
|
|
200
249
|
Each Minitest test will output an AppMap file into the directory `tmp/appmap/minitest`. For example:
|
@@ -251,9 +300,9 @@ To manually record ad-hoc AppMaps of your Ruby app, use AppMap remote recording.
|
|
251
300
|
1. Add the AppMap remote recording middleware. For example, in `config/initializers/appmap_remote_recording.rb`:
|
252
301
|
|
253
302
|
```ruby
|
254
|
-
|
303
|
+
if defined?(AppMap)
|
304
|
+
require 'appmap/middleware/remote_recording'
|
255
305
|
|
256
|
-
unless Rails.env.test?
|
257
306
|
Rails.application.config.middleware.insert_after \
|
258
307
|
Rails::Rack::Logger,
|
259
308
|
AppMap::Middleware::RemoteRecording
|
@@ -274,18 +323,24 @@ $ bundle exec rails server
|
|
274
323
|
|
275
324
|
6. Open the AppLand browser extension and push `Stop`. The recording will be transferred to the AppLand website and opened in your browser.
|
276
325
|
|
277
|
-
##
|
326
|
+
## Server process recording
|
327
|
+
|
328
|
+
Run your Rails server with `APPMAP_RECORD=true`. When the server exits, an *appmap.json* file will be written to the project directory. This is a great way to start the server, interact with your app as a user (or through it's API), and then view an AppMap of everything that happened.
|
278
329
|
|
279
|
-
|
330
|
+
Be sure and set `WEB_CONCURRENCY=1`, if you are using a webserver that can run multiple processes. You only want there to be one process while you are recording, otherwise they will both try and write *appmap.json* and one of them will clobber the other.
|
280
331
|
|
281
|
-
|
332
|
+
# AppMap for VSCode
|
333
|
+
|
334
|
+
The [AppMap extension for VSCode](https://marketplace.visualstudio.com/items?itemName=appland.appmap) is a great way to onboard developers to new code, and troubleshoot hard-to-understand bugs with visuals.
|
282
335
|
|
283
336
|
# Uploading AppMaps
|
284
337
|
|
338
|
+
[https://app.land](https://app.land) can be used to store, analyze, and share AppMaps.
|
339
|
+
|
285
340
|
For instructions on uploading, see the documentation of the [AppLand CLI](https://github.com/applandinc/appland-cli).
|
286
341
|
|
287
342
|
# Development
|
288
|
-
[![Build Status](https://travis-ci.
|
343
|
+
[![Build Status](https://travis-ci.com/applandinc/appmap-ruby.svg?branch=master)](https://travis-ci.com/applandinc/appmap-ruby)
|
289
344
|
|
290
345
|
## Running tests
|
291
346
|
|
data/lib/appmap.rb
CHANGED
@@ -50,6 +50,11 @@ module AppMap
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
# Whether to include source and comments in all class maps.
|
54
|
+
def include_source?
|
55
|
+
ENV['APPMAP_SOURCE'] == 'true'
|
56
|
+
end
|
57
|
+
|
53
58
|
# Used to start tracing, stop tracing, and record events.
|
54
59
|
def tracing
|
55
60
|
@tracing ||= Trace::Tracing.new
|
data/lib/appmap/class_map.rb
CHANGED
@@ -113,17 +113,18 @@ module AppMap
|
|
113
113
|
[ method.defined_class, static ? '.' : '#', method.name ].join
|
114
114
|
end
|
115
115
|
|
116
|
+
source, comment = begin
|
117
|
+
[ method.source, method.comment ]
|
118
|
+
rescue MethodSource::SourceNotFoundError
|
119
|
+
[ nil, nil, ]
|
120
|
+
end
|
121
|
+
|
116
122
|
if include_source
|
117
|
-
|
118
|
-
|
119
|
-
comment = method.comment || ''
|
120
|
-
function_info[:comment] = comment unless comment.empty?
|
121
|
-
rescue MethodSource::SourceNotFoundError
|
122
|
-
# pass
|
123
|
-
end
|
123
|
+
function_info[:source] = source unless source.blank?
|
124
|
+
function_info[:comment] = comment unless comment.blank?
|
124
125
|
end
|
125
126
|
|
126
|
-
function_info[:labels] = package.labels
|
127
|
+
function_info[:labels] = parse_labels(comment) + (package.labels || [])
|
127
128
|
object_infos << function_info
|
128
129
|
|
129
130
|
parent = root
|
@@ -141,6 +142,22 @@ module AppMap
|
|
141
142
|
end
|
142
143
|
end
|
143
144
|
|
145
|
+
# Labels can be embedded in the function comment. Label format is similar to YARD and JavaDoc.
|
146
|
+
# The keyword is @labels or @label. The keyword is followed by space-separated labels.
|
147
|
+
# For example:
|
148
|
+
# @label provider.authentication security
|
149
|
+
def parse_labels(comment)
|
150
|
+
return [] unless comment
|
151
|
+
|
152
|
+
comment
|
153
|
+
.split("\n")
|
154
|
+
.map { |line| line.match(/^\s*#\s*@labels?\s+(.*)/) }
|
155
|
+
.compact
|
156
|
+
.map { |match| match[1] }
|
157
|
+
.inject([]) { |accum, labels| accum += labels.split(/\s+/); accum }
|
158
|
+
.sort
|
159
|
+
end
|
160
|
+
|
144
161
|
def find_or_create(list, info)
|
145
162
|
obj = list.find { |item| item.type == info[:type] && item.name == info[:name] }
|
146
163
|
return obj if obj
|
data/lib/appmap/config.rb
CHANGED
@@ -16,20 +16,20 @@ module AppMap
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def build_from_gem(gem, shallow: true, package_name: nil, exclude: [], labels: [])
|
19
|
-
|
20
|
-
|
19
|
+
if %w[method_source activesupport].member?(gem)
|
20
|
+
warn "WARNING: #{gem} cannot be AppMapped because it is a dependency of the appmap gem"
|
21
|
+
return
|
21
22
|
end
|
23
|
+
Package.new(gem_path(gem), gem, package_name, exclude, labels, shallow)
|
22
24
|
end
|
23
25
|
|
24
26
|
private_class_method :new
|
25
27
|
|
26
28
|
protected
|
27
29
|
|
28
|
-
def
|
30
|
+
def gem_path(gem)
|
29
31
|
gemspec = Gem.loaded_specs[gem] or raise "Gem #{gem.inspect} not found"
|
30
|
-
gemspec.
|
31
|
-
File.join(gemspec.gem_dir, path)
|
32
|
-
end
|
32
|
+
gemspec.gem_dir
|
33
33
|
end
|
34
34
|
end
|
35
35
|
|
@@ -57,32 +57,36 @@ module AppMap
|
|
57
57
|
# Methods that should always be hooked, with their containing
|
58
58
|
# package and labels that should be applied to them.
|
59
59
|
HOOKED_METHODS = {
|
60
|
-
'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support',
|
61
|
-
'ActionView::Renderer' => Hook.new(:render, Package.build_from_path('action_view',
|
60
|
+
'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', labels: %w[provider.secure_compare])),
|
61
|
+
'ActionView::Renderer' => Hook.new(:render, Package.build_from_path('action_view', labels: %w[mvc.view])),
|
62
|
+
'ActionDispatch::Cookies::CookieJar' => Hook.new(%i[[]= clear update delete recycle], Package.build_from_path('action_pack', labels: %w[provider.http.cookie])),
|
63
|
+
'ActionDispatch::Cookies::EncryptedCookieJar' => Hook.new(%i[[]=], Package.build_from_path('action_pack', labels: %w[provider.http.cookie crypto])),
|
64
|
+
'CanCan::ControllerAdditions' => Hook.new(%i[authorize! can? cannot?], Package.build_from_path('cancancan', labels: %w[provider.authorization])),
|
65
|
+
'CanCan::Ability' => Hook.new(%i[authorize!], Package.build_from_path('cancancan', labels: %w[provider.authorization])),
|
62
66
|
}.freeze
|
63
67
|
|
64
68
|
BUILTIN_METHODS = {
|
65
69
|
'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGES),
|
66
|
-
'Digest::Instance' => Hook.new(:digest, OPENSSL_PACKAGES),
|
67
70
|
'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGES),
|
68
71
|
'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES),
|
69
72
|
'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final], OPENSSL_PACKAGES),
|
70
73
|
'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGES),
|
71
|
-
'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[http io])),
|
72
|
-
'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[smtp email io])),
|
73
|
-
'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[pop
|
74
|
-
'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[imap email io])),
|
75
|
-
'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[serialization
|
76
|
-
'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[serialization
|
77
|
-
'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[serialization
|
78
|
-
'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[serialization
|
74
|
+
'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http io])),
|
75
|
+
'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.smtp protocol.email io])),
|
76
|
+
'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.pop protocol.email io])),
|
77
|
+
'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.imap protocol.email io])),
|
78
|
+
'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal provider.serialization])),
|
79
|
+
'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml provider.serialization])),
|
80
|
+
'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json provider.serialization])),
|
81
|
+
'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json provider.serialization])),
|
79
82
|
}.freeze
|
80
83
|
|
81
|
-
attr_reader :name, :packages
|
84
|
+
attr_reader :name, :packages, :exclude
|
82
85
|
|
83
|
-
def initialize(name, packages = [])
|
86
|
+
def initialize(name, packages = [], exclude = [])
|
84
87
|
@name = name
|
85
88
|
@packages = packages
|
89
|
+
@exclude = exclude
|
86
90
|
end
|
87
91
|
|
88
92
|
class << self
|
@@ -105,44 +109,60 @@ module AppMap
|
|
105
109
|
shallow = true if shallow.nil?
|
106
110
|
Package.build_from_gem(gem, exclude: package['exclude'] || [], shallow: shallow)
|
107
111
|
else
|
108
|
-
|
112
|
+
Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
|
109
113
|
end
|
110
|
-
end.
|
111
|
-
Config.new config_data['name'], packages
|
114
|
+
end.compact
|
115
|
+
Config.new config_data['name'], packages, config_data['exclude'] || []
|
112
116
|
end
|
113
117
|
end
|
114
118
|
|
115
119
|
def to_h
|
116
120
|
{
|
117
121
|
name: name,
|
118
|
-
packages: packages.map(&:to_h)
|
122
|
+
packages: packages.map(&:to_h),
|
123
|
+
exclude: exclude
|
119
124
|
}
|
120
125
|
end
|
121
126
|
|
127
|
+
# package_for_method finds the Package, if any, which configures the hook
|
128
|
+
# for a method.
|
122
129
|
def package_for_method(method)
|
130
|
+
package_hooked_by_class(method) || package_hooked_by_source_location(method)
|
131
|
+
end
|
132
|
+
|
133
|
+
def package_hooked_by_class(method)
|
123
134
|
defined_class, _, method_name = ::AppMap::Hook.qualify_method_name(method)
|
124
|
-
|
125
|
-
|
135
|
+
return find_package(defined_class, method_name)
|
136
|
+
end
|
126
137
|
|
138
|
+
def package_hooked_by_source_location(method)
|
127
139
|
location = method.source_location
|
128
140
|
location_file, = location
|
129
141
|
return unless location_file
|
130
142
|
|
131
143
|
location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
|
132
|
-
packages.find do |pkg|
|
144
|
+
packages.select { |pkg| pkg.path }.find do |pkg|
|
133
145
|
(location_file.index(pkg.path) == 0) &&
|
134
146
|
!pkg.exclude.find { |p| location_file.index(p) }
|
135
147
|
end
|
136
148
|
end
|
137
149
|
|
138
|
-
def
|
139
|
-
|
150
|
+
def never_hook?(method)
|
151
|
+
defined_class, separator, method_name = ::AppMap::Hook.qualify_method_name(method)
|
152
|
+
return true if exclude.member?(defined_class) || exclude.member?([ defined_class, separator, method_name ].join)
|
140
153
|
end
|
141
154
|
|
155
|
+
# always_hook? indicates a method that should always be hooked.
|
142
156
|
def always_hook?(defined_class, method_name)
|
143
157
|
!!find_package(defined_class, method_name)
|
144
158
|
end
|
145
159
|
|
160
|
+
# included_by_location? indicates a method whose source location matches a method definition that has been
|
161
|
+
# configured for inclusion.
|
162
|
+
def included_by_location?(method)
|
163
|
+
!!package_for_method(method)
|
164
|
+
end
|
165
|
+
|
146
166
|
def find_package(defined_class, method_name)
|
147
167
|
hook = find_hook(defined_class)
|
148
168
|
return nil unless hook
|