appmap 0.18.1 → 0.19.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 536929649e77a1a136aabc2ffbf8df4b43a1f51420334b4d556f5978aa205e06
4
- data.tar.gz: b052fa2b3853f962932216a21f2c0d07052e7288695787bc60fccba4ed97cf1f
3
+ metadata.gz: dbd73ea1339788825987a09e80097ad54fcaf6297ae5a7bbe01aa39b5462a8bb
4
+ data.tar.gz: 6338cbb3203543ce4db169fef9cfcf31acfd6ea44da94220bca5c00638368520
5
5
  SHA512:
6
- metadata.gz: fca12acdd2d3a096cc1ecb8bb8d5e19fb5f88ad91e67de83aa71d59e9cdaa0a70733d1904e4d29078ea94eddbc3005d1cb33ea4e86f0fad95270ec7b3bd35e67
7
- data.tar.gz: 395891a44f2d185bcb5bd447bbe39d1d16a07c30c065c6fb7a45ea5979f54f7c1b30efab1baf6043838a9e9d166e1f14ff4412b9a04743d03c8a818a4c082b15
6
+ metadata.gz: 6d0b9d851b266a230a40ac85e9991d183919c67b1c09e155009ffb3cd2de660e40521e6253534ae6a3e06867238810d6f5def1c9b10f094f81ed2498e79ee4ea
7
+ data.tar.gz: 4a6f75650e36876086782f778eb1fbb90f7d15a9e35eb28f245ce942285b3cfd95c76c16e5d01a5c90867455668609e974a41dfd829a6e7a91487833ebee49c9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ # v0.19.0
2
+
3
+ * **RSpec** feature and feature group names can be inferred from example group and example names.
4
+ * Stop using `ActiveSupport::Inflector.transliterate`, since it can cause exceptions.
5
+ * Handle StandardError which occurs while calling `#inspect` of an object.
6
+
1
7
  # v0.18.1
2
8
 
3
9
  * Now tested with Rails 4, 5, and 6.
data/README.md CHANGED
@@ -1,35 +1,54 @@
1
1
  - [About](#about)
2
2
  - [Installation](#installation)
3
3
  - [Configuration](#configuration)
4
- - [`packages`](#packages)
5
4
  - [Running](#running)
6
5
  - [RSpec](#rspec)
7
- - [Rails](#rails)
8
- - [Uploading](#uploading)
6
+ - [Remote recording](#remote-recording)
7
+ - [Uploading AppMaps](#uploading-appmaps)
9
8
  - [Build status](#build-status)
10
9
 
11
10
  # About
12
11
 
13
- `appmap-ruby` is a Ruby client for recording and uploading [AppMap](https://github.com/applandinc/appmap) data.
14
-
12
+ `appmap-ruby` is a Ruby Gem for recording and uploading
13
+ [AppMaps](https://github.com/applandinc/appmap) of your code.
15
14
  AppMap is a data format which records code structure (modules, classes, and methods), code execution events
16
- (function calls and returns), and code metadata (repo name, repo URL, commit SHA, etc).
15
+ (function calls and returns), and code metadata (repo name, repo URL, commit
16
+ SHA, etc). It's more granular than a performance profile, but it's less
17
+ granular than a full debug trace. It's designed to be optimal for understanding the design intent and behavior of code.
18
+
19
+ There are several ways to record AppMaps of your Ruby program using the `appmap` gem:
17
20
 
18
- The normal usage of an AppMap client is to run a test case (such as an RSpec test) with AppMap instrumentation enabled.
19
- The AppMap client will observe and record information about the code execution, and store it in an AppMap file.
21
+ * Run your RSpec tests. An AppMap will be generated for each one.
22
+ * Run your application server with AppMap remote recording enabled, and use the AppMap
23
+ browser extension to start, stop, and upload recordings.
20
24
 
21
- The command `appmap upload` is then used to upload the AppMap file to the App.Land server, which processes the file into
22
- useful displays such as graphical depiction of the code structure and execution.
25
+ When you record AppMaps on the command line (for example, by running RSpec tests), you use the `appmap upload` command to
26
+ upload them to the AppLand website. On the AppLand website, you'll be able to
27
+ visualize the design of your code and share links with collaborators.
23
28
 
24
29
  # Installation
25
30
 
26
- Add `gem 'appmap'` to your Gemfile just as you would any other dependency. You can place the gem in the `test` group.
31
+ Add `gem 'appmap'` to your Gemfile just as you would any other dependency.
32
+
33
+ **Global installation**
34
+
35
+ ```
36
+ gem 'appmap'
37
+ ```
38
+
39
+ **Install in test, development groups**
40
+
41
+ ```
42
+ group :development, :test do
43
+ gem 'appmap'
44
+ end
45
+ ```
27
46
 
28
47
  Then install with `bundle`.
29
48
 
30
49
  # Configuration
31
50
 
32
- When you run the AppMap client, it will look for configuration settings in `appmap.yml`. Here's a sample configuration
51
+ When you run your program, the `appmap` gem reads configuration settings from `appmap.yml`. Here's a sample configuration
33
52
  file for a typical Rails project:
34
53
 
35
54
  ```yaml
@@ -44,7 +63,7 @@ packages:
44
63
  * **files** A list of individual files which should be instrumented. This is only used for files which are
45
64
  not part of the `packages` list.
46
65
 
47
- ## `packages`
66
+ **packages**
48
67
 
49
68
  Each entry in the `packages` list is a YAML object which has the following keys:
50
69
 
@@ -61,71 +80,90 @@ Each entry in the `packages` list is a YAML object which has the following keys:
61
80
 
62
81
  To instrument RSpec tests, follow these steps:
63
82
 
64
- 1) Include the `appmap` gem in your Gemfile
65
- 2) Require `appmap/rspec` in your `spec_helper.rb` or `rails_helper.rb`
66
- 3) Add `appmap: true` to the tests you want to instrument
67
- 4) Add `feature: '<feature name>'` and `feature_group: '<feature group name>'` to your
83
+ 1) Include the `appmap` gem in your Gemfile.
84
+ 2) Require `appmap/rspec` in your `spec_helper.rb`.
85
+ 3) Add `appmap: true` to the tests you want to instrument.
86
+ 4) Export the environment variable `APPMAP=true`.
87
+ 5) *Optional* Add `feature: '<feature name>'` and `feature_group: '<feature group name>'` annotations to your
68
88
  examples.
69
- 5) Export the environment variable `APPMAP=true`
70
89
 
71
90
  Here's an example of an appmap-enabled RSpec test:
72
91
 
73
92
  ```ruby
74
- describe Hello, feature_group: 'Greeting' do
75
- it 'says hello', feature: 'Print a greeting to the console', appmap: true do
76
- expect(Hello.new.say_hello).to eq('Hello!')
93
+ describe Hello, appmap: true do
94
+ describe 'says hello' do
95
+ it 'when prompted' do
96
+ expect(Hello.new.say_hello).to eq('Hello!')
97
+ end
77
98
  end
78
99
  end
79
100
  ```
80
101
 
81
- Then run the tests:
102
+ Run the tests like this:
82
103
 
83
104
  ```sh-session
84
- $ APPMAP=true bundle exec rspec
105
+ $ APPMAP=true bundle exec rspec -t appmap
85
106
  ```
86
107
 
87
108
  Each RSpec test will output a data file into the directory `tmp/appmap/rspec`. For example:
88
109
 
89
110
  ```
90
111
  $ find tmp/appmap/rspec
91
- Hello says hello.json
112
+ Hello_says_hello_when_prompted.appmap.json
92
113
  ```
93
114
 
94
115
  If you include the `feature` and `feature_group` metadata, these attributes will be exported to the AppMap file in the
95
116
  `metadata` section. It will look something like this:
96
117
 
97
118
  ```json
98
-
119
+ {
120
+ ...
121
+ "metadata": {
122
+ "name": "Hello app says hello when prompted",
123
+ "feature": "Hello app says hello",
124
+ "feature_group": "Hello"
125
+ },
126
+ ...
127
+ }
99
128
  ```
100
129
 
101
- ## Rails
130
+ ## Remote recording
102
131
 
103
- To capture ad-hoc AppMaps of your Rails app, use the AppMap Railtie.
132
+ To manually record ad-hoc AppMaps of your Ruby app, use AppMap remote recording.
104
133
 
105
- 1) Include the `appmap` gem in your Gemfile and require both `appmap` and `appmap/railtie`, like this:
134
+ 1. Add the AppMap remote recording middleware. For example, in `config/initializers/appmap_remote_recording.rb`:
106
135
 
107
136
  ```ruby
108
- gem "appmap", require: %w[appmap appmap/railtie]
137
+ require 'appmap/middleware/remote_recording'
138
+
139
+ unless Rails.env.test?
140
+ Rails.application.config.middleware.insert_after \
141
+ Rails::Rack::Logger,
142
+ AppMap::Middleware::RemoteRecording
143
+ end
109
144
  ```
110
145
 
111
- 2) Export `APPMAP=true` when you start your Rails application server. For example:
146
+ 2. Start your Rails application server. For example:
112
147
 
113
148
  ```sh-session
114
- $ APPMAP=true bundle exec rails server
149
+ $ bundle exec rails server
115
150
  ```
116
151
 
117
- When the Rails app exits, an `appmap.json` file will be written to the project root directory. You can upload it using
118
- the `appmap upload` command.
152
+ 3. Open the AppApp browser extension and push `Start`.
153
+
154
+ 4. Use your app. For example, perform a login flow, or run through a manual UI test.
155
+
156
+ 5. Open the AppApp browser extension and push `Stop`. The recording will be transferred to the AppLand website and opened in your browser.
119
157
 
120
- # Uploading
158
+ # Uploading AppMaps
121
159
 
122
- To upload an AppMap file to App.Land, run the `appmap upload` command. For example:
160
+ To upload an AppMap file to AppLand, run the `appmap upload` command. For example:
123
161
 
124
162
  ```sh-session
125
- $ appmap upload tmp/appmap/rspec/Hello says hello.json
126
- Full classMap contains 1 classes
127
- Pruned classMap contains 1 classes
128
- Uploaded new scenario: d49f3d16-e9f2-4775-a731-6cb95193927e
163
+ $ appmap upload tmp/appmap/rspec/Hello_app_says_hello_when_prompted.appmap.json
164
+ Uploading "tmp/appmap/rspec/Hello_app_says_hello_when_prompted.appmap.json"
165
+ Scenario Id: 4da4f267-bdea-48e8-bf67-f39463844230
166
+ Batch Id: a116f1df-ee57-4bde-8eef-851af0f3d7bc
129
167
  ```
130
168
 
131
169
  # Build status
data/lib/appmap/rspec.rb CHANGED
@@ -29,14 +29,14 @@ module AppMap
29
29
  FileUtils.mkdir_p APPMAP_OUTPUT_DIR
30
30
  end
31
31
 
32
- # TODO: Optionally populate the 'layout' from appmap config or RSpec metadata.
33
- def save(example_name, events, feature_name: nil, feature_group_name: nil)
32
+ def save(example_name, events, feature_name: nil, feature_group_name: nil, labels: nil)
34
33
  require 'appmap/command/record'
35
34
  metadata = AppMap::Command::Record.detect_metadata.tap do |m|
36
35
  m[:name] = example_name
37
36
  m[:app] = @config.name
38
37
  m[:feature] = feature_name if feature_name
39
38
  m[:feature_group] = feature_group_name if feature_group_name
39
+ m[:labels] = labels if labels
40
40
  m[:frameworks] ||= []
41
41
  m[:frameworks] << {
42
42
  name: 'rspec',
@@ -58,7 +58,7 @@ module AppMap
58
58
  # https://github.com/rails/rails/blob/v5.2.4/activesupport/lib/active_support/inflector/transliterate.rb#L92
59
59
  def sanitize_filename(fname, separator: '_')
60
60
  # Replace accented chars with their ASCII equivalents.
61
- fname = ActiveSupport::Inflector.transliterate(fname)
61
+ fname = fname.encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
62
62
 
63
63
  # Turn unwanted chars into the separator.
64
64
  fname.gsub!(/[^a-z0-9\-_]+/i, separator)
@@ -83,6 +83,17 @@ module AppMap
83
83
  annotations[:feature]
84
84
  end
85
85
 
86
+ def labels
87
+ labels = metadata[:appmap]
88
+ if labels.is_a?(Array)
89
+ labels
90
+ elsif labels.is_a?(String) || labels.is_a?(Symbol)
91
+ [ labels ]
92
+ else
93
+ []
94
+ end
95
+ end
96
+
86
97
  def feature_group
87
98
  return nil unless annotations
88
99
 
@@ -119,6 +130,10 @@ module AppMap
119
130
 
120
131
  alias_method :example_obj, :example
121
132
 
133
+ def description?
134
+ true
135
+ end
136
+
122
137
  def description
123
138
  example.description
124
139
  end
@@ -255,28 +270,46 @@ module AppMap
255
270
 
256
271
  example = examples[loc]
257
272
  description = []
258
- scope = ScopeExample.new(example)
273
+ leaf = scope = ScopeExample.new(example)
259
274
  feature_group = feature = nil
275
+ labels = scope.labels.map(&:to_s).map(&:strip).reject(&:blank?).map(&:downcase).uniq
276
+
260
277
  while scope
261
278
  description << scope.description
262
279
  feature ||= scope.feature
263
280
  feature_group ||= scope.feature_group
264
281
  scope = scope.parent
265
282
  end
266
- description.reject! { |d| d.nil? || d == '' }
283
+ description.reject!(&:nil?).reject(&:blank?)
284
+ default_description = description.last
267
285
  description.reverse!
268
286
 
269
- description.each do |token|
270
- token = token.gsub 'it should behave like', ''
271
- token.gsub! ' ', ' '
272
- token.gsub! '/', '_'
273
- token.strip!
287
+ normalize = lambda do |desc|
288
+ desc.gsub('it should behave like', '')
289
+ .gsub(/Controller$/, '')
290
+ .gsub(/\s+/, ' ')
291
+ .strip
292
+ end
293
+
294
+ full_description = normalize.call(description.join(' '))
295
+
296
+ compute_feature_name = lambda do
297
+ return 'unknown' if description.empty?
298
+
299
+ feature_description = description.dup
300
+ num_tokens = [2, feature_description.length - 1].min
301
+ feature_description[0...num_tokens].map(&:strip).join(' ')
274
302
  end
275
- full_description = description.join(' ')
276
303
 
277
- recorder.save full_description, events,
278
- feature_name: feature,
279
- feature_group_name: feature_group
304
+ feature_group ||= normalize.call(default_description).underscore.gsub('/', '_').humanize
305
+ feature_name = feature || compute_feature_name.call if feature_group
306
+ feature_name = normalize.call(feature_name) if feature_name
307
+
308
+ recorder.save full_description,
309
+ events,
310
+ feature_name: feature_name,
311
+ feature_group_name: feature_group,
312
+ labels: labels.blank? ? nil : labels
280
313
  end
281
314
  end
282
315
  end
@@ -87,15 +87,24 @@ module AppMap
87
87
  def display_string(value)
88
88
  return nil unless value
89
89
 
90
- begin
91
- value_string = value.to_s
92
- rescue NoMethodError
93
- value_string = value.inspect
94
- rescue StandardError
95
- warn $!.message
90
+ last_resort_string = lambda do
91
+ warn "AppMap encountered an error inspecting a #{value.class.name}: #{$!.message}"
96
92
  '*Error inspecting variable*'
97
93
  end
98
94
 
95
+ value_string = \
96
+ begin
97
+ value.to_s
98
+ rescue NoMethodError
99
+ begin
100
+ value.inspect
101
+ rescue StandardError
102
+ last_resort_string.call
103
+ end
104
+ rescue StandardError
105
+ last_resort_string.call
106
+ end
107
+
99
108
  value_string[0...LIMIT].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
100
109
  end
101
110
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppMap
4
- VERSION = '0.18.1'
4
+ VERSION = '0.19.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.1
4
+ version: 0.19.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Gilpin