appmap 0.47.1 → 0.50.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12dbe41efff7d8fd40a884be376ff1579d961371b780eddf18d4724e7ce5997f
4
- data.tar.gz: fb038cfbcc6c2432b780d4555b8948729fd65a2943a4bc1200c7d6943a62f5a8
3
+ metadata.gz: ce006905408a0ee15ccaee33ecb13e8bfbf1bc7b93f3cc4cbb105e2be9849bee
4
+ data.tar.gz: 4f73e289a332301d6efbf0c7e8985b5e62a8a030f90c4265b221fc1d67c801be
5
5
  SHA512:
6
- metadata.gz: 07a9ec31b08915e1d630a90200393d88db2b85b49b8dc21ffa595e598fa57ff8ed3afa463e3e9dc2ae9485d5dff399eb1724c6aa38cbcceeb0d3c6c0757d0f70
7
- data.tar.gz: 3c6e629772604730b6acc14409c98694624cbfcbbc14a661182b6bb5388664b15a918c3e82ca5b55dd1daf6ca142741d0380a3e88c152f5bafcd57cb9b38dd6c
6
+ metadata.gz: 86108afa917712908800f9303368ee48c8b0bf7d66892e6aadd76a18d76d355106f751984579bf57f9fdd876f7e4071f75a0d6cdf4fd90dfa5ce90a93d1ed9a1
7
+ data.tar.gz: b62b7793fd03c9d3cb43792b0a9b056547b4dd9f75a6b31f582a12d7a19cfdf4eaee7f566b27eaaf9a36dd4477afa396184b31bc753858b979ac5fb6804d3027
data/.dockerignore CHANGED
@@ -2,4 +2,3 @@ vendor
2
2
  node_modules
3
3
  spec/fixtures/rails*_users_app
4
4
  spec/fixtures/rack_users_app
5
-
data/CHANGELOG.md CHANGED
@@ -1,3 +1,47 @@
1
+ # [0.50.0](https://github.com/applandinc/appmap-ruby/compare/v0.49.0...v0.50.0) (2021-06-17)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Remove appmap configuration in test cases which now occurs automatically ([7391c4c](https://github.com/applandinc/appmap-ruby/commit/7391c4c36ed80f98a6b82ccd43f05de488e7cd2f))
7
+
8
+
9
+ ### Features
10
+
11
+ * Direct minitest and rspec startup messages to the Rails log, when available ([15f6444](https://github.com/applandinc/appmap-ruby/commit/15f6444b0fad3ce7d9e91273b6a1116e470c2a89))
12
+ * Enroll railtie, rspec, and minitest helpers automatically ([1709374](https://github.com/applandinc/appmap-ruby/commit/1709374ee7b5183482c55cf4c7386266fa517262))
13
+ * railtie enrolls the app in remote recording ([3a1f8aa](https://github.com/applandinc/appmap-ruby/commit/3a1f8aac1d83c4df04b5da55ed33d418235e348b))
14
+
15
+ # [0.49.0](https://github.com/applandinc/appmap-ruby/compare/v0.48.2...v0.49.0) (2021-06-16)
16
+
17
+
18
+ ### Features
19
+
20
+ * Add refinement to the labels ([6a93396](https://github.com/applandinc/appmap-ruby/commit/6a93396ba73f1b3ed21b4e9e15a2c271af04d866))
21
+
22
+ ## [0.48.2](https://github.com/applandinc/appmap-ruby/compare/v0.48.1...v0.48.2) (2021-05-26)
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * Correct the method-hooking logic to capture some missing model methods ([be529bd](https://github.com/applandinc/appmap-ruby/commit/be529bdce7d4fdf9f1a2fdd32259d792f29f4f13))
28
+
29
+ ## [0.48.1](https://github.com/applandinc/appmap-ruby/compare/v0.48.0...v0.48.1) (2021-05-25)
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * Account for bundle path when normalizing source path ([095c278](https://github.com/applandinc/appmap-ruby/commit/095c27818fc8ae8dfa39b30516d37c6dfd642d9c))
35
+ * Scan exception messages for non-UTF8 characters ([3dcaeae](https://github.com/applandinc/appmap-ruby/commit/3dcaeae44da5e40e432eda41caf5b9ebff5bea12))
36
+
37
+ # [0.48.0](https://github.com/applandinc/appmap-ruby/compare/v0.47.1...v0.48.0) (2021-05-19)
38
+
39
+
40
+ ### Features
41
+
42
+ * Hook the code only when APPMAP=true ([dd9e383](https://github.com/applandinc/appmap-ruby/commit/dd9e383024d1d9205a617d46bd64b90820035533))
43
+ * Remove server process recording from doc and tests ([383ba0a](https://github.com/applandinc/appmap-ruby/commit/383ba0ad444922a0a85409477d11bc7ed06a9160))
44
+
1
45
  ## [0.47.1](https://github.com/applandinc/appmap-ruby/compare/v0.47.0...v0.47.1) (2021-05-13)
2
46
 
3
47
 
data/README.md CHANGED
@@ -1,17 +1,6 @@
1
1
 
2
2
  - [About](#about)
3
- - [Supported versions](#supported-versions)
4
- - [Installation](#installation)
5
- - [Configuration](#configuration)
6
- - [Labels](#labels)
7
- - [Running](#running)
8
- - [RSpec](#rspec)
9
- - [Minitest](#minitest)
10
- - [Cucumber](#cucumber)
11
- - [Remote recording](#remote-recording)
12
- - [AppMap for VSCode](#appmap-for-vscode)
13
- - [AppMap Swagger](#appmap-swagger)
14
- - [Uploading AppMaps](#uploading-appmaps)
3
+ - [Usage](#usage)
15
4
  - [Development](#development)
16
5
  - [Internal architecture](#internal-architecture)
17
6
  - [Running tests](#running-tests)
@@ -19,7 +8,6 @@
19
8
  - [`test/fixtures`](#testfixtures)
20
9
  - [`spec/fixtures`](#specfixtures)
21
10
 
22
-
23
11
  # About
24
12
 
25
13
  `appmap-ruby` is a Ruby Gem for recording
@@ -29,342 +17,9 @@
29
17
  SHA, labels, etc). It's more granular than a performance profile, but it's less
30
18
  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.
31
19
 
32
- There are several ways to record AppMaps of your Ruby program using the `appmap` gem:
33
-
34
- * Run your tests (RSpec, Minitest, Cucumber) with the environment variable `APPMAP=true`. An AppMap will be generated for each spec.
35
- * Run your application server with AppMap remote recording enabled, and use the [AppLand
36
- browser extension](https://github.com/applandinc/appland-browser-extension) to start,
37
- stop, and upload recordings.
38
- * Wrap some code in an `AppMap.record` block, which returns JSON containing the code execution trace.
39
-
40
- Once you have made a recording, there are two ways to view automatically generated diagrams of the AppMaps.
41
-
42
- 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).
43
-
44
- 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).
45
-
46
- ### Supported versions
47
-
48
- * Ruby 2.5, 2.6, 2.7
49
- * Rails 5, 6
50
-
51
- Support for new versions is added frequently, please check back regularly for updates.
52
-
53
- # Installation
54
-
55
- <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>
56
-
57
-
58
- 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:
59
-
60
- ```
61
- source 'https://rubygems.org'
62
- git_source(:github) { |repo| "https://github.com/#{repo}.git" }
63
-
64
- # Optional rubRuby version
65
- # ruby '2.7.2'
66
-
67
- group :development, :test do
68
- gem 'appmap'
69
- end
70
- ```
71
-
72
- Install with `bundle install`, as usual.
73
-
74
- 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*.
75
-
76
- **Railtie**
77
-
78
- If you are using Ruby on Rails, require the railtie after Rails is loaded.
79
-
80
- ```
81
- # application.rb is a good place to do this, along with all the other railties.
82
- # Don't require the railtie in environments that don't bundle the appmap gem.
83
- require 'appmap/railtie' if defined?(AppMap).
84
- ```
85
-
86
- **application.rb**
87
-
88
- Add this line to *application.rb*, to enable server recording with `APPMAP_RECORD=true`:
89
-
90
- ```ruby
91
- module MyApp
92
- class Application < Rails::Application
93
- ...
94
-
95
- config.appmap.enabled = true if ENV['APPMAP_RECORD']
96
-
97
- ...
98
- end
99
- end
100
- ```
101
-
102
- # Configuration
103
-
104
- When you run your program, the `appmap` gem reads configuration settings from `appmap.yml`. Here's a sample configuration
105
- file for a typical Rails project:
106
-
107
- ```yaml
108
- # 'name' should generally be the same as the code repo name.
109
- name: my_project
110
- packages:
111
- - path: app/controllers
112
- - path: app/models
113
- # Exclude sub-paths within the package path
114
- exclude:
115
- - concerns/accessor
116
- - path: app/jobs
117
- - path: app/helpers
118
- # Include the gems that you want to see in the dependency maps.
119
- # These are just examples.
120
- - gem: activerecord
121
- - gem: devise
122
- - gem: aws-sdk
123
- - gem: will_paginate
124
- # Global exclusion of a class name
125
- exclude:
126
- - MyClass
127
- - MyClass#my_instance_method
128
- - MyClass.my_class_method
129
- functions:
130
- - packages: myapp
131
- class: ControllerHelper
132
- function: logged_in_user
133
- labels: [ authentication ]
134
- ```
135
-
136
- * **name** Provides the project name (required)
137
- * **packages** A list of source code directories which should be recorded.
138
- * **exclude** A list of classes and/or methods to definitively exclude from recording.
139
- * **functions** A list of specific functions, scoped by package and class, to record.
140
-
141
- **packages**
142
-
143
- Each entry in the `packages` list is a YAML object which has the following keys:
144
-
145
- * **path** The path to the source code directory. The path may be relative to the current working directory, or it may
146
- be an absolute path.
147
- * **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*.
148
- * **exclude** A list of files and directories which will be ignored. By default, all modules, classes and public
149
- functions are inspected. See also: global `exclude` list.
150
- * **shallow** When set to `true`, only the first function call entry into a package will be recorded. Subsequent function calls within
151
- the same package are not recorded unless code execution leaves the package and re-enters it. Default: `true` when using `gem`,
152
- `false` when using `path`.
153
-
154
- **exclude**
155
-
156
- Optional list of fully qualified class and method names. Separate class and method names with period (`.`) for class methods and hash (`#`) for instance methods.
157
-
158
- **functions**
159
-
160
- Optional list of `class, function` pairs. The `package` name is used to place the function within the class map, and does not have to match
161
- the folder or gem name. The primary use of `functions` is to apply specific labels to functions whose source code is not accessible (e.g., it's in a Gem).
162
- For functions which are part of the application code, use `@label` or `@labels` in code comments to apply labels.
163
-
164
- # Labels
165
-
166
- 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.
167
-
168
- 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.
169
-
170
- For example, if you add this comment to your source code:
171
-
172
- ```ruby
173
- class ApiKey
174
- # @labels provider.authentication security
175
- def authenticate(key)
176
- # logic to verify the key here...
177
- end
178
- end
179
- ```
180
-
181
- Then the AppMap metadata section for this function will include:
182
-
183
- ```json
184
- {
185
- "name": "authenticate",
186
- "type": "function",
187
- "labels": [ "provider.authentication", "security" ]
188
- }
189
- ```
190
-
191
-
192
- # Running
193
-
194
- ## RSpec
195
-
196
- To record RSpec tests, follow these additional steps:
197
-
198
- 1) Require `appmap/rspec` in your `spec_helper.rb` before any other classes are loaded.
199
-
200
- ```ruby
201
- require 'appmap/rspec'
202
- ```
203
-
204
- Note that `spec_helper.rb` in a Rails project typically loads the application's classes this way:
205
-
206
- ```ruby
207
- require File.expand_path("../../config/environment", __FILE__)
208
- ```
209
-
210
- and `appmap/rspec` must be required before this:
211
-
212
- ```ruby
213
- require 'appmap/rspec'
214
- require File.expand_path("../../config/environment", __FILE__)
215
- ```
216
-
217
- 2) Run the tests with the environment variable `APPMAP=true`:
218
-
219
- ```sh-session
220
- $ APPMAP=true bundle exec rspec
221
- ```
222
-
223
- Each RSpec test will output an AppMap file into the directory `tmp/appmap/rspec`. For example:
224
-
225
- ```
226
- $ find tmp/appmap/rspec
227
- Hello_says_hello_when_prompted.appmap.json
228
- ```
229
-
230
- ## Minitest
231
-
232
- To record Minitest tests, follow these additional steps:
233
-
234
- 1) Require `appmap/minitest` in `test_helper.rb`
235
-
236
- ```ruby
237
- require 'appmap/minitest'
238
- ```
239
-
240
- Note that `test_helper.rb` in a Rails project typically loads the application's classes this way:
241
-
242
- ```ruby
243
- require_relative '../config/environment'
244
- ```
245
-
246
- and `appmap/minitest` must be required before this:
247
-
248
- ```ruby
249
- require 'appmap/minitest'
250
- require_relative '../config/environment'
251
- ```
252
-
253
- 2) Run your tests as you normally would with the environment variable `APPMAP=true`. For example:
254
-
255
- ```
256
- $ APPMAP=true bundle exec rake test
257
- ```
258
-
259
- or
260
-
261
- ```
262
- $ APPMAP=true bundle exec ruby -Ilib -Itest test/*_test.rb
263
- ```
264
-
265
- Each Minitest test will output an AppMap file into the directory `tmp/appmap/minitest`. For example:
266
-
267
- ```
268
- $ find tmp/appmap/minitest
269
- Hello_says_hello_when_prompted.appmap.json
270
- ```
271
-
272
- ## Cucumber
273
-
274
- To record Cucumber tests, follow these additional steps:
275
-
276
- 1) Require `appmap/cucumber` in `support/env.rb`:
277
-
278
- ```ruby
279
- require 'appmap/cucumber'
280
- ```
281
-
282
- Be sure to require it before `config/environment` is required.
283
-
284
- 2) Create an `Around` hook in `support/hooks.rb` to record the scenario:
285
-
286
-
287
- ```ruby
288
- if AppMap::Cucumber.enabled?
289
- Around('not @appmap-disable') do |scenario, block|
290
- appmap = AppMap.record do
291
- block.call
292
- end
293
-
294
- AppMap::Cucumber.write_scenario(scenario, appmap)
295
- end
296
- end
297
- ```
298
-
299
- 3) Run the tests with the environment variable `APPMAP=true`:
300
-
301
- ```sh-session
302
- $ APPMAP=true bundle exec cucumber
303
- ```
304
-
305
- Each Cucumber test will output an AppMap file into the directory `tmp/appmap/cucumber`. For example:
306
-
307
- ```
308
- $ find tmp/appmap/cucumber
309
- Hello_Says_hello_when_prompted.appmap.json
310
- ```
311
-
312
- ## Remote recording
313
-
314
- To manually record ad-hoc AppMaps of your Ruby app, use AppMap remote recording.
315
-
316
- 1. Add the AppMap remote recording middleware. For example, in `config/initializers/appmap_remote_recording.rb`:
317
-
318
- ```ruby
319
- if defined?(AppMap)
320
- require 'appmap/middleware/remote_recording'
321
-
322
- Rails.application.config.middleware.insert_after \
323
- Rails::Rack::Logger,
324
- AppMap::Middleware::RemoteRecording
325
- end
326
- ```
327
-
328
- 2. (optional) Download and unpack the [AppLand browser extension](https://github.com/applandinc/appland-browser-extension). Install into Chrome using `chrome://extensions/`. Turn on "Developer Mode" and then load the extension using the "Load unpacked" button.
329
-
330
- 3. Start your Rails application server, with `APPMAP_RECORD=true`. For example:
331
-
332
- ```sh-session
333
- $ APPMAP_RECORD=true bundle exec rails server
334
- ```
335
-
336
- 4. Start the recording
337
-
338
- Option 1: Open the AppLand browser extension and push `Start`.
339
- Option 2: `curl -XPOST localhost:3000/_appmap/record` (be sure and get the port number right)
340
-
341
- 5. Use your app. For example, perform a login flow, or run through a manual UI test.
342
-
343
- 6. Finish the recording.
344
-
345
- Option 1: Open the AppLand browser extension and push `Stop`. The recording will be transferred to the AppLand website and opened in your browser.
346
- Option 2: `curl -XDELETE localhost:3000/_appmap/record > recording.appmap.json` - Saves the recording as a local file.
347
-
348
-
349
- # AppMap for VSCode
350
-
351
- The [AppMap extension for VSCode](https://marketplace.visualstudio.com/items?itemName=appland.appmap) helps you navigate your code more efficiently with interactive, accurate software architecture diagrams right in your IDE. In less than two minutes you can go from installing the AppMap extension to exploring maps of your code's architecture. AppMap helps you:
352
-
353
- * Onboard to code architecture, with no extra work for the team
354
- * Conduct code and design reviews using live and accurate data
355
- * Troubleshoot hard-to-understand bugs using a "top-down" approach.
356
-
357
- Each interactive diagram links directly to the source code, and the information is easy to share.
358
-
359
- # AppMap Swagger
360
-
361
- [appmap_swagger](https://github.com/applandinc/appmap_swagger-ruby) is a tool to generate Swagger files from AppMap data. With `appmap_swagger`, you can add Swagger to your Ruby or Ruby on Rails project, with no need to write or modify code. Use the Swagger UI to interact with your web services API as you build it, and use diffs of Swagger to perform code review of web service changes.
362
-
363
- # Uploading AppMaps
364
-
365
- [https://app.land](https://app.land) can be used to store, analyze, and share AppMaps.
20
+ # Usage
366
21
 
367
- For instructions on uploading, see the documentation of the [AppLand CLI](https://github.com/applandinc/appland-cli).
22
+ Visit the [AppMap for Ruby](https://appland.com/docs/reference/appmap-ruby.html) reference page on AppLand.com for a complete reference guide.
368
23
 
369
24
  # Development
370
25
  [![Build Status](https://travis-ci.com/applandinc/appmap-ruby.svg?branch=master)](https://travis-ci.com/applandinc/appmap-ruby)
data/lib/appmap.rb CHANGED
@@ -51,6 +51,14 @@ module AppMap
51
51
  end
52
52
  end
53
53
 
54
+ def info(msg)
55
+ if defined?(::Rails) && defined?(::Rails.logger)
56
+ ::Rails.logger.info msg
57
+ else
58
+ warn msg
59
+ end
60
+ end
61
+
54
62
  # Used to start tracing, stop tracing, and record events.
55
63
  def tracing
56
64
  @tracing ||= Trace::Tracing.new
@@ -97,5 +105,17 @@ module AppMap
97
105
  end
98
106
  end
99
107
 
100
- require 'appmap/railtie' if defined?(::Rails::Railtie)
101
- AppMap.initialize unless ENV['APPMAP_INITIALIZE'] == 'false'
108
+ if defined?(::Rails::Railtie)
109
+ require 'appmap/railtie'
110
+ end
111
+
112
+ if defined?(::RSpec)
113
+ require 'appmap/rspec'
114
+ end
115
+
116
+ # defined?(::Minitest) returns nil...
117
+ if Gem.loaded_specs['minitest']
118
+ require 'appmap/minitest'
119
+ end
120
+
121
+ AppMap.initialize if ENV['APPMAP'] == 'true'
data/lib/appmap/config.rb CHANGED
@@ -167,8 +167,10 @@ module AppMap
167
167
  ),
168
168
  package_hooks('actionpack',
169
169
  [
170
- method_hook('ActionDispatch::Request::Session', %i[destroy [] dig values []= clear update delete fetch merge], %w[http.session]),
171
- method_hook('ActionDispatch::Cookies::CookieJar', %i[[]= clear update delete recycle], %w[http.session]),
170
+ method_hook('ActionDispatch::Request::Session', %i[[] dig values fetch], %w[http.session.read]),
171
+ method_hook('ActionDispatch::Request::Session', %i[destroy[]= clear update delete merge], %w[http.session.write]),
172
+ method_hook('ActionDispatch::Cookies::CookieJar', %i[[]= clear update delete recycle], %w[http.session.read]),
173
+ method_hook('ActionDispatch::Cookies::CookieJar', %i[[]= clear update delete recycle], %w[http.session.write]),
172
174
  method_hook('ActionDispatch::Cookies::EncryptedCookieJar', %i[[]= clear update delete recycle], %w[http.cookie crypto.encrypt])
173
175
  ],
174
176
  package_name: 'action_dispatch'
@@ -213,9 +215,12 @@ module AppMap
213
215
  # This is happening: Method send_command not found on Net::IMAP
214
216
  # 'Net::IMAP' => TargetMethods.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.email.imap])),
215
217
  # 'Marshal' => TargetMethods.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal])),
216
- 'Psych' => TargetMethods.new(%i[dump dump_stream load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml])),
217
- 'JSON::Ext::Parser' => TargetMethods.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json])),
218
- 'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json])),
218
+ 'Psych' => [
219
+ TargetMethods.new(%i[load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml.parse])),
220
+ TargetMethods.new(%i[dump dump_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml.generate])),
221
+ ],
222
+ 'JSON::Ext::Parser' => TargetMethods.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.parse])),
223
+ 'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.generate])),
219
224
  }.freeze
220
225
 
221
226
  attr_reader :name, :packages, :exclude, :hooked_methods, :builtin_hooks
data/lib/appmap/event.rb CHANGED
@@ -213,7 +213,7 @@ module AppMap
213
213
  exception_backtrace = next_exception.backtrace_locations.try(:[], 0)
214
214
  exceptions << {
215
215
  class: best_class_name(next_exception),
216
- message: next_exception.message,
216
+ message: display_string(next_exception.message),
217
217
  object_id: next_exception.__id__,
218
218
  path: exception_backtrace&.path,
219
219
  lineno: exception_backtrace&.lineno
data/lib/appmap/hook.rb CHANGED
@@ -36,7 +36,7 @@ module AppMap
36
36
 
37
37
  def initialize(config)
38
38
  @config = config
39
- @trace_locations = []
39
+ @trace_enabled = []
40
40
  # Paths that are known to be non-tracing
41
41
  @notrace_paths = Set.new
42
42
  end
@@ -47,10 +47,8 @@ module AppMap
47
47
 
48
48
  hook_builtins
49
49
 
50
- @trace_begin = TracePoint.new(:class, &method(:trace_class))
51
50
  @trace_end = TracePoint.new(:end, &method(:trace_end))
52
-
53
- @trace_begin.enable(&block)
51
+ @trace_end.enable(&block)
54
52
  end
55
53
 
56
54
  # hook_builtins builds hooks for code that is built in to the Ruby standard library.
@@ -96,29 +94,22 @@ module AppMap
96
94
 
97
95
  protected
98
96
 
99
- def trace_class(trace_point)
100
- path = trace_point.path
101
-
102
- return if @notrace_paths.member?(path)
103
-
104
- if config.path_enabled?(path)
105
- location = trace_location(trace_point)
106
- warn "Entering hook-enabled location #{location}" if Hook::LOG || Hook::LOG_HOOK
107
- @trace_locations << location
108
- unless @trace_end.enabled?
109
- warn "Enabling hooking" if Hook::LOG || Hook::LOG_HOOK
110
- @trace_end.enable
111
- end
112
- else
113
- @notrace_paths << path
114
- end
115
- end
116
-
117
97
  def trace_location(trace_point)
118
98
  [ trace_point.path, trace_point.lineno ].join(':')
119
99
  end
120
100
 
121
101
  def trace_end(trace_point)
102
+ location = trace_location(trace_point)
103
+ warn "Class or module ends at location #{trace_location(trace_point)}" if Hook::LOG || Hook::LOG_HOOK
104
+
105
+ path = trace_point.path
106
+ enabled = !@notrace_paths.member?(path) && config.path_enabled?(path)
107
+ if !enabled
108
+ warn "Not hooking - path is not enabled" if Hook::LOG || Hook::LOG_HOOK
109
+ @notrace_paths << path
110
+ return
111
+ end
112
+
122
113
  cls = trace_point.self
123
114
 
124
115
  instance_methods = cls.public_instance_methods(false) - OBJECT_INSTANCE_METHODS
@@ -151,7 +142,8 @@ module AppMap
151
142
  warn "AppMap: Examining #{hook_cls} #{method.name}" if LOG
152
143
 
153
144
  disasm = RubyVM::InstructionSequence.disasm(method)
154
- # Skip methods that have no instruction sequence, as they are obviously trivial.
145
+ # Skip methods that have no instruction sequence, as they are either have no body or they are or native.
146
+ # TODO: Figure out how to tell the difference?
155
147
  next unless disasm
156
148
 
157
149
  package = config.lookup_package(hook_cls, method)
@@ -170,13 +162,6 @@ module AppMap
170
162
  # uninitialized constant Faraday::Connection
171
163
  warn "NameError in #{__FILE__}: #{$!.message}"
172
164
  end
173
-
174
- location = @trace_locations.pop
175
- warn "Leaving hook-enabled location #{location}" if Hook::LOG || Hook::LOG_HOOK
176
- if @trace_locations.empty?
177
- warn "Disabling hooking" if Hook::LOG || Hook::LOG_HOOK
178
- @trace_end.disable
179
- end
180
165
  end
181
166
  end
182
167
  end
@@ -54,15 +54,21 @@ module AppMap
54
54
 
55
55
  @recordings_by_test = {}
56
56
  @event_methods = Set.new
57
+ @recording_count = 0
57
58
 
58
59
  class << self
59
60
  def init
60
- warn 'Configuring AppMap recorder for Minitest'
61
-
62
61
  FileUtils.mkdir_p APPMAP_OUTPUT_DIR
63
62
  end
64
63
 
64
+ def first_recording?
65
+ @recording_count == 0
66
+ end
67
+
65
68
  def begin_test(test, name)
69
+ AppMap.info 'Configuring AppMap recorder for Minitest' if first_recording?
70
+ @recording_count += 1
71
+
66
72
  @recordings_by_test[test.object_id] = Recording.new(test, name)
67
73
  end
68
74
 
@@ -98,7 +104,7 @@ module AppMap
98
104
  if exception
99
105
  m[:exception] = {
100
106
  class: exception.class.name,
101
- message: exception.to_s
107
+ message: AppMap::Event::MethodEvent.display_string(exception.to_s)
102
108
  }
103
109
  end
104
110
  end
@@ -3,7 +3,12 @@
3
3
  module AppMap
4
4
  # Railtie connects the AppMap recorder to Rails-specific features.
5
5
  class Railtie < ::Rails::Railtie
6
- config.appmap = ActiveSupport::OrderedOptions.new
6
+ initializer 'appmap.remote_recording' do
7
+ require 'appmap/middleware/remote_recording'
8
+ Rails.application.config.middleware.insert_after \
9
+ Rails::Rack::Logger,
10
+ AppMap::Middleware::RemoteRecording
11
+ end
7
12
 
8
13
  # appmap.subscribe subscribes to ActiveSupport Notifications so that they can be recorded as
9
14
  # AppMap events.
@@ -15,25 +20,5 @@ module AppMap
15
20
 
16
21
  AppMap::Handler::Rails::RequestHandler::HookMethod.new.activate
17
22
  end
18
-
19
- # appmap.trace begins recording an AppMap trace and writes it to appmap.json.
20
- # This behavior is only activated if the configuration setting app.config.appmap.enabled
21
- # is truthy.
22
- initializer 'appmap.trace', after: 'appmap.subscribe' do |app|
23
- lambda do
24
- return unless app.config.appmap.enabled
25
-
26
- require 'appmap/command/record'
27
- require 'json'
28
- AppMap::Command::Record.new(AppMap.configuration).perform do |version, metadata, class_map, events|
29
- appmap = JSON.generate \
30
- version: version,
31
- metadata: metadata,
32
- classMap: class_map,
33
- events: events
34
- File.open('appmap.json', 'w').write(appmap)
35
- end
36
- end.call
37
- end
38
23
  end
39
- end unless ENV['APPMAP_INITIALIZE'] == 'false'
24
+ end if ENV['APPMAP'] == 'true'
data/lib/appmap/rspec.rb CHANGED
@@ -139,15 +139,21 @@ module AppMap
139
139
 
140
140
  @recordings_by_example = {}
141
141
  @event_methods = Set.new
142
+ @recording_count = 0
142
143
 
143
144
  class << self
144
145
  def init
145
- warn 'Configuring AppMap recorder for RSpec'
146
-
147
146
  FileUtils.mkdir_p APPMAP_OUTPUT_DIR
148
147
  end
149
148
 
149
+ def first_recording?
150
+ @recording_count == 0
151
+ end
152
+
150
153
  def begin_spec(example)
154
+ AppMap.info 'Configuring AppMap recorder for RSpec' if first_recording?
155
+ @recording_count += 1
156
+
151
157
  @recordings_by_example[example.object_id] = Recording.new(example)
152
158
  end
153
159
 
@@ -183,7 +189,7 @@ module AppMap
183
189
  if exception
184
190
  m[:exception] = {
185
191
  class: exception.class.name,
186
- message: exception.to_s
192
+ message: AppMap::Event::MethodEvent.display_string(exception.to_s)
187
193
  }
188
194
  end
189
195
  end
data/lib/appmap/util.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'bundler'
4
+
3
5
  module AppMap
4
6
  module Util
5
7
  class << self
@@ -94,7 +96,7 @@ module AppMap
94
96
  end
95
97
 
96
98
  def normalize_path(path)
97
- if path.index(Dir.pwd) == 0
99
+ if path.index(Dir.pwd) == 0 && !path.index(Bundler.bundle_path.to_s)
98
100
  path[Dir.pwd.length + 1..-1]
99
101
  else
100
102
  path
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.47.1'
6
+ VERSION = '0.50.0'
7
7
 
8
- APPMAP_FORMAT_VERSION = '1.5.0'
8
+ APPMAP_FORMAT_VERSION = '1.5.1'
9
9
  end
@@ -53,3 +53,9 @@ class ToSRaises
53
53
  "hello"
54
54
  end
55
55
  end
56
+
57
+ class ExceptionMethod
58
+ def raise_illegal_utf8_message
59
+ raise "809: unexpected token at 'x\x9C\xED=\x8Bv\xD3ƶ\xBF2\xB8]\xC5\xE9qdI\x96eǫ4\xA4h΅\x84\xE5z\x96\xAA\xD8\xE3\xE3D\xB2\xE4J2\x90E\xF8\xF7\xBB\xF7\xCC\xE81\x92\xE2\x88ā'"
60
+ end
61
+ end
@@ -21,14 +21,6 @@ when 'activerecord'
21
21
  require 'database_cleaner-active_record' if Rails.env.test?
22
22
  end
23
23
 
24
- require 'appmap/railtie' if defined?(AppMap)
25
-
26
- # require "active_storage/engine"
27
- # require "action_mailer/railtie"
28
- # require "action_cable/engine"
29
- # require "sprockets/railtie"
30
- # require "rails/test_unit/railtie"
31
-
32
24
  # Require the gems listed in Gemfile, including any gems
33
25
  # you've limited to :test, :development, or :production.
34
26
  Bundler.require(*Rails.groups)
@@ -38,8 +30,6 @@ module UsersApp
38
30
  # Initialize configuration defaults for originally generated Rails version.
39
31
  config.load_defaults 5.2
40
32
 
41
- config.appmap.enabled = true if ENV['APPMAP'] == 'true' && !Rails.env.test?
42
-
43
33
  # Settings in config/environments/* take precedence over those specified here.
44
34
  # Application configuration can go into files in config/initializers
45
35
  # -- all .rb files in that directory are automatically loaded after loading
@@ -7,8 +7,6 @@ abort("The Rails environment is running in production mode!") if Rails.env.produ
7
7
  require 'rspec/rails'
8
8
  # Add additional requires below this line. Rails is not loaded until this point!
9
9
 
10
- require 'appmap/rspec'
11
-
12
10
  # Requires supporting ruby files with custom matchers and macros, etc, in
13
11
  # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
14
12
  # run as spec files by default. This means that files in spec/support that end
@@ -21,14 +21,6 @@ when 'activerecord'
21
21
  require 'database_cleaner-active_record' if Rails.env.test?
22
22
  end
23
23
 
24
- require 'appmap/railtie' if defined?(AppMap)
25
-
26
- # require "active_storage/engine"
27
- # require "action_mailer/railtie"
28
- # require "action_cable/engine"
29
- # require "sprockets/railtie"
30
- # require "rails/test_unit/railtie"
31
-
32
24
  # Require the gems listed in Gemfile, including any gems
33
25
  # you've limited to :test, :development, or :production.
34
26
  Bundler.require(*Rails.groups)
@@ -38,8 +30,6 @@ module UsersApp
38
30
  # Initialize configuration defaults for originally generated Rails version.
39
31
  config.load_defaults 5.2
40
32
 
41
- config.appmap.enabled = true if ENV['APPMAP'] == 'true' && !Rails.env.test?
42
-
43
33
  # Settings in config/environments/* take precedence over those specified here.
44
34
  # Application configuration can go into files in config/initializers
45
35
  # -- all .rb files in that directory are automatically loaded after loading
@@ -7,8 +7,6 @@ abort("The Rails environment is running in production mode!") if Rails.env.produ
7
7
  require 'rspec/rails'
8
8
  # Add additional requires below this line. Rails is not loaded until this point!
9
9
 
10
- require 'appmap/rspec'
11
-
12
10
  # Requires supporting ruby files with custom matchers and macros, etc, in
13
11
  # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
14
12
  # run as spec files by default. This means that files in spec/support that end
data/spec/hook_spec.rb CHANGED
@@ -583,7 +583,7 @@ describe 'AppMap class Hooking', docker: false do
583
583
  end
584
584
  end
585
585
 
586
- it 'Reports exceptions' do
586
+ it 'reports exceptions' do
587
587
  events_yaml = <<~YAML
588
588
  ---
589
589
  - :id: 1
@@ -615,6 +615,38 @@ describe 'AppMap class Hooking', docker: false do
615
615
  end
616
616
  end
617
617
 
618
+ it 'sanitizes exception messages' do
619
+ events_yaml = <<~YAML
620
+ ---
621
+ - :id: 1
622
+ :event: :call
623
+ :defined_class: ExceptionMethod
624
+ :method_id: raise_illegal_utf8_message
625
+ :path: spec/fixtures/hook/exception_method.rb
626
+ :lineno: 58
627
+ :static: false
628
+ :parameters: []
629
+ :receiver:
630
+ :class: ExceptionMethod
631
+ :value: Exception Method fixture
632
+ - :id: 2
633
+ :event: :return
634
+ :parent_id: 1
635
+ :exceptions:
636
+ - :class: RuntimeError
637
+ :message: '809: unexpected token at ''x__=_v_ƶ_2_]__qdI_eǫ4_h΅__z_____D__J2_E______1__ā'''
638
+ :path: spec/fixtures/hook/exception_method.rb
639
+ :lineno: 59
640
+ YAML
641
+ test_hook_behavior 'spec/fixtures/hook/exception_method.rb', events_yaml do
642
+ begin
643
+ ExceptionMethod.new.raise_illegal_utf8_message
644
+ rescue
645
+ # don't let the exception fail the test
646
+ end
647
+ end
648
+ end
649
+
618
650
  context 'string conversions works for the receiver when' do
619
651
 
620
652
  it 'is missing #to_s' do
data/spec/railtie_spec.rb CHANGED
@@ -4,7 +4,7 @@ describe 'AppMap tracer via Railtie' do
4
4
  include_context 'Rails app pg database', 'spec/fixtures/rails5_users_app' do
5
5
  let(:env) { {} }
6
6
 
7
- let(:cmd) { %(docker-compose run --rm -e RAILS_ENV -e APPMAP app ./bin/rails r "puts Rails.configuration.appmap.enabled.inspect") }
7
+ let(:cmd) { %(docker-compose run --rm -e RAILS_ENV=development -e APPMAP app ./bin/rails r "puts AppMap.instance_variable_get('@configuration').nil?") }
8
8
  let(:command_capture2) do
9
9
  require 'open3'
10
10
  Open3.capture3(env, cmd, chdir: fixture_dir).tap do |result|
@@ -23,20 +23,16 @@ describe 'AppMap tracer via Railtie' do
23
23
  let(:command_output) { command_capture2[0].strip }
24
24
  let(:command_result) { command_capture2[2] }
25
25
 
26
- it 'is disabled by default' do
27
- expect(command_output).to eq('nil')
26
+ describe 'with APPMAP=false' do
27
+ let(:env) { { 'APPMAP' => 'false' } }
28
+ it 'is disabled' do
29
+ expect(command_output).to eq('true')
30
+ end
28
31
  end
29
-
30
32
  describe 'with APPMAP=true' do
31
33
  let(:env) { { 'APPMAP' => 'true' } }
32
34
  it 'is enabled' do
33
- expect(command_output.split("\n")).to include('true')
34
- end
35
- context 'and RAILS_ENV=test' do
36
- let(:env) { { 'APPMAP' => 'true', 'RAILS_ENV' => 'test' } }
37
- it 'is disabled' do
38
- expect(command_output).to eq('nil')
39
- end
35
+ expect(command_output).to eq('false')
40
36
  end
41
37
  end
42
38
  end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'test_helper'
5
+ require 'English'
6
+
7
+ class BundleVendorTest < Minitest::Test
8
+ def perform_bundle_vendor_app(test_name)
9
+ Bundler.with_clean_env do
10
+ Dir.chdir 'test/fixtures/bundle_vendor_app' do
11
+ FileUtils.rm_rf 'tmp'
12
+ FileUtils.mkdir_p 'tmp'
13
+ system 'bundle config --local local.appmap ../../..'
14
+ system 'bundle'
15
+ system(%(bundle exec ruby -Ilib -Itest cli.rb add foobar))
16
+ system({ 'APPMAP' => 'true' }, %(bundle exec ruby -Ilib -Itest cli.rb list))
17
+
18
+ yield
19
+ end
20
+ end
21
+ end
22
+
23
+ def test_record_gem
24
+ perform_bundle_vendor_app 'parser' do
25
+ appmap_file = 'tmp/bundle_vendor_app.appmap.json'
26
+ appmap = JSON.parse(File.read(appmap_file))
27
+ assert appmap['classMap'].find { |co| co['name'] == 'gli' }
28
+ assert appmap['events'].find do |e|
29
+ e['event'] == 'call' &&
30
+ e['defined_class'] = 'Hacer::Todolist' &&
31
+ e['method_id'] == 'list'
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'gli'
4
+ gem 'hacer'
5
+
6
+ appmap_gem_opts = {}
7
+ appmap_gem_opts[:path] = '../../..' if File.exist?('../../../appmap.gemspec')
8
+ gem 'appmap', appmap_gem_opts
@@ -0,0 +1,4 @@
1
+ name: bundle_vendor_app
2
+ packages:
3
+ - gem: gli
4
+ - gem: hacer
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env ruby
2
+ require 'appmap'
3
+ require 'gli'
4
+ require 'hacer'
5
+
6
+ class App
7
+ extend GLI::App
8
+
9
+ program_desc 'A simple todo list'
10
+
11
+ flag [:t,:tasklist], :default_value => File.join(ENV['HOME'],'.todolist')
12
+
13
+ pre do |global_options,command,options,args|
14
+ $todo_list = Hacer::Todolist.new(global_options[:tasklist])
15
+ end
16
+
17
+ command :add do |c|
18
+ c.action do |global_options,options,args|
19
+ $todo_list.create(args)
20
+ end
21
+ end
22
+
23
+ command :list do |c|
24
+ c.action do
25
+ $todo_list.list.each do |todo|
26
+ printf("%5d - %s\n",todo.todo_id,todo.text)
27
+ end
28
+ end
29
+ end
30
+
31
+ command :done do |c|
32
+ c.action do |global_options,options,args|
33
+ id = args.shift.to_i
34
+ $todo_list.list.each do |todo|
35
+ $todo_list.complete(todo) if todo.todo_id == id
36
+ end
37
+ end
38
+ end
39
+ end
40
+
41
+ exit_status = nil
42
+ invoke = -> { exit_status = App.run(ARGV) }
43
+ do_appmap = -> { ENV['APPMAP'] == 'true' }
44
+
45
+ if do_appmap.()
46
+ appmap = AppMap.record do
47
+ invoke.()
48
+ end
49
+ File.write('tmp/bundle_vendor_app.appmap.json', JSON.pretty_generate(appmap))
50
+ else
51
+ invoke.()
52
+ end
53
+ exit exit_status
54
+
data/test/gem_test.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  require 'test_helper'
5
5
  require 'English'
6
6
 
7
- class MinitestTest < Minitest::Test
7
+ class GemTest < Minitest::Test
8
8
  def perform_gem_test(test_name)
9
9
  Bundler.with_clean_env do
10
10
  Dir.chdir 'test/fixtures/gem_test' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.47.1
4
+ version: 0.50.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Gilpin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-13 00:00:00.000000000 Z
11
+ date: 2021-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -455,7 +455,6 @@ files:
455
455
  - spec/fixtures/rails5_users_app/config/initializers/filter_parameter_logging.rb
456
456
  - spec/fixtures/rails5_users_app/config/initializers/inflections.rb
457
457
  - spec/fixtures/rails5_users_app/config/initializers/mime_types.rb
458
- - spec/fixtures/rails5_users_app/config/initializers/record_button.rb
459
458
  - spec/fixtures/rails5_users_app/config/initializers/wrap_parameters.rb
460
459
  - spec/fixtures/rails5_users_app/config/locales/en.yml
461
460
  - spec/fixtures/rails5_users_app/config/routes.rb
@@ -527,7 +526,6 @@ files:
527
526
  - spec/fixtures/rails6_users_app/config/initializers/filter_parameter_logging.rb
528
527
  - spec/fixtures/rails6_users_app/config/initializers/inflections.rb
529
528
  - spec/fixtures/rails6_users_app/config/initializers/mime_types.rb
530
- - spec/fixtures/rails6_users_app/config/initializers/record_button.rb
531
529
  - spec/fixtures/rails6_users_app/config/initializers/wrap_parameters.rb
532
530
  - spec/fixtures/rails6_users_app/config/locales/en.yml
533
531
  - spec/fixtures/rails6_users_app/config/routes.rb
@@ -557,9 +555,13 @@ files:
557
555
  - spec/remote_recording_spec.rb
558
556
  - spec/spec_helper.rb
559
557
  - spec/util_spec.rb
558
+ - test/bundle_vendor_test.rb
560
559
  - test/cucumber_test.rb
561
560
  - test/expectations/openssl_test_key_sign1.json
562
561
  - test/expectations/openssl_test_key_sign2.json
562
+ - test/fixtures/bundle_vendor_app/Gemfile
563
+ - test/fixtures/bundle_vendor_app/appmap.yml
564
+ - test/fixtures/bundle_vendor_app/cli.rb
563
565
  - test/fixtures/cli_record_test/appmap.yml
564
566
  - test/fixtures/cli_record_test/lib/cli_record_test/main.rb
565
567
  - test/fixtures/cucumber4_recorder/Gemfile
@@ -1,3 +0,0 @@
1
- require 'appmap/middleware/remote_recording'
2
- Rails.application.config.middleware.insert_after Rails::Rack::Logger, AppMap::Middleware::RemoteRecording \
3
- unless Rails.env.test?
@@ -1,3 +0,0 @@
1
- require 'appmap/middleware/remote_recording'
2
- Rails.application.config.middleware.insert_after Rails::Rack::Logger, AppMap::Middleware::RemoteRecording \
3
- unless Rails.env.test?