appmap 0.23.0 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +17 -8
- data/.travis.yml +6 -0
- data/CHANGELOG.md +19 -0
- data/README.md +29 -12
- data/Rakefile +3 -3
- data/appmap.gemspec +3 -1
- data/exe/appmap +6 -18
- data/lib/appmap.rb +47 -6
- data/lib/appmap/algorithm/prune_class_map.rb +2 -0
- data/lib/appmap/algorithm/stats.rb +4 -2
- data/lib/appmap/class_map.rb +143 -0
- data/lib/appmap/command/record.rb +8 -6
- data/lib/appmap/command/stats.rb +2 -0
- data/lib/appmap/command/upload.rb +4 -2
- data/lib/appmap/event.rb +168 -0
- data/lib/appmap/hook.rb +151 -0
- data/lib/appmap/middleware/remote_recording.rb +14 -20
- data/lib/appmap/rails/action_handler.rb +10 -6
- data/lib/appmap/rails/sql_handler.rb +10 -8
- data/lib/appmap/railtie.rb +31 -18
- data/lib/appmap/rspec.rb +238 -261
- data/lib/appmap/trace.rb +88 -0
- data/lib/appmap/version.rb +1 -1
- data/package-lock.json +90 -92
- data/spec/abstract_controller4_base_spec.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +7 -3
- data/spec/config_spec.rb +25 -0
- data/spec/fixtures/hook/attr_accessor.rb +5 -0
- data/spec/fixtures/hook/class_method.rb +17 -0
- data/spec/fixtures/hook/constructor.rb +7 -0
- data/spec/fixtures/hook/exception_method.rb +11 -0
- data/spec/fixtures/hook/instance_method.rb +23 -0
- data/spec/fixtures/rails4_users_app/app/controllers/api/users_controller.rb +3 -3
- data/spec/fixtures/rails4_users_app/config/database.yml +2 -1
- data/spec/fixtures/rails4_users_app/docker-compose.yml +2 -0
- data/spec/fixtures/rails_users_app/.ruby-version +1 -1
- data/spec/fixtures/rails_users_app/app/controllers/api/users_controller.rb +2 -2
- data/spec/fixtures/rails_users_app/config/database.yml +2 -1
- data/spec/fixtures/rails_users_app/create_app +1 -0
- data/spec/fixtures/rails_users_app/docker-compose.yml +4 -0
- data/spec/fixtures/rails_users_app/spec/models/user_spec.rb +1 -1
- data/spec/hook_spec.rb +357 -0
- data/spec/rails_spec_helper.rb +25 -16
- data/spec/railtie_spec.rb +1 -1
- data/spec/record_sql_rails_pg_spec.rb +1 -2
- data/spec/remote_recording_spec.rb +117 -0
- data/spec/spec_helper.rb +1 -0
- data/test/cli_test.rb +7 -36
- data/test/fixtures/cli_record_test/appmap.yml +2 -1
- data/test/fixtures/cli_record_test/lib/cli_record_test/main.rb +4 -2
- data/test/test_helper.rb +0 -42
- metadata +46 -62
- data/exe/_appmap-record-self +0 -49
- data/lib/appmap/command/inspect.rb +0 -14
- data/lib/appmap/config.rb +0 -65
- data/lib/appmap/config/directory.rb +0 -65
- data/lib/appmap/config/file.rb +0 -13
- data/lib/appmap/config/named_function.rb +0 -21
- data/lib/appmap/config/package_dir.rb +0 -52
- data/lib/appmap/config/path.rb +0 -25
- data/lib/appmap/feature.rb +0 -262
- data/lib/appmap/inspect.rb +0 -91
- data/lib/appmap/inspect/inspector.rb +0 -99
- data/lib/appmap/inspect/parse_node.rb +0 -170
- data/lib/appmap/inspect/parser.rb +0 -15
- data/lib/appmap/parser.rb +0 -60
- data/lib/appmap/rspec/parse_node.rb +0 -41
- data/lib/appmap/rspec/parser.rb +0 -15
- data/lib/appmap/trace/event_handler/rack_handler_webrick.rb +0 -65
- data/lib/appmap/trace/tracer.rb +0 -356
- data/spec/fixtures/rails_users_app/bin/_appmap-record-self +0 -29
- data/spec/rack_handler_webrick_spec.rb +0 -59
- data/test/config_test.rb +0 -149
- data/test/explict_inspect_test.rb +0 -29
- data/test/fixtures/active_record_like/active_record.rb +0 -2
- data/test/fixtures/active_record_like/active_record/aggregations.rb +0 -4
- data/test/fixtures/active_record_like/active_record/association.rb +0 -4
- data/test/fixtures/active_record_like/active_record/associations/join_dependency.rb +0 -6
- data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_base.rb +0 -8
- data/test/fixtures/active_record_like/active_record/associations/join_dependency/join_part.rb +0 -8
- data/test/fixtures/active_record_like/active_record/caps/caps.rb +0 -4
- data/test/fixtures/ignore_non_ruby_file/class.rb +0 -3
- data/test/fixtures/ignore_non_ruby_file/non-ruby.txt +0 -1
- data/test/fixtures/includes_excludes/lib/a/a_1.rb +0 -6
- data/test/fixtures/includes_excludes/lib/a/a_2.rb +0 -6
- data/test/fixtures/includes_excludes/lib/a/x/x_1.rb +0 -8
- data/test/fixtures/includes_excludes/lib/b/b_1.rb +0 -6
- data/test/fixtures/includes_excludes/lib/root_1.rb +0 -4
- data/test/fixtures/inspect_multiple_subdirs/module_a.rb +0 -2
- data/test/fixtures/inspect_multiple_subdirs/module_a/class_a.rb +0 -5
- data/test/fixtures/inspect_multiple_subdirs/module_b.rb +0 -2
- data/test/fixtures/inspect_multiple_subdirs/module_b/class_b.rb +0 -5
- data/test/fixtures/inspect_multiple_subdirs/module_b/class_c.rb +0 -5
- data/test/fixtures/inspect_package/module_a/module_b/class_in_module.rb +0 -6
- data/test/fixtures/parse_file/defs_static_function.rb +0 -96
- data/test/fixtures/parse_file/function_within_class.rb +0 -36
- data/test/fixtures/parse_file/include_public_methods.rb +0 -127
- data/test/fixtures/parse_file/instance_function.rb +0 -17
- data/test/fixtures/parse_file/modules.rb +0 -71
- data/test/fixtures/parse_file/sclass_static_function.rb +0 -88
- data/test/fixtures/parse_file/toplevel_class.rb +0 -13
- data/test/fixtures/parse_file/toplevel_function.rb +0 -14
- data/test/fixtures/trace_test/trace_program_1.rb +0 -44
- data/test/implicit_inspect_test.rb +0 -33
- data/test/include_exclude_test.rb +0 -48
- data/test/prerecorded_trace_test.rb +0 -76
- data/test/trace_test.rb +0 -92
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d42f8555b02b0478b8927c249f6dfa54c9326b7e3122cd5a0b91b5d1fb308fbb
|
4
|
+
data.tar.gz: 525faceacd156300098b897f7ef7789d94d42601f9ccec5d3bfad0f41b62ac54
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8290dde850c68b07c51baa70c074a283907c77c15093c8d6ed9b151da66f1dbad866f37beef6a12c2897195b8b18d147a839dd049336fd52fa1b48fb5e3d8ab1
|
7
|
+
data.tar.gz: '035025557325693fd0a158ce007dab265ec2646a3cf2e729f42348ac0986081f4356a1cec1e66c1bc7d9608025c6889b497e52ec6f510612a3fb5cedd220f22d'
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,18 +1,27 @@
|
|
1
|
-
|
1
|
+
Layout/SpaceInsideArrayLiteralBrackets:
|
2
2
|
Enabled: false
|
3
3
|
|
4
|
-
|
4
|
+
# We have squiggly heredocs
|
5
|
+
Layout/HeredocIndentation:
|
5
6
|
Enabled: false
|
6
7
|
|
7
|
-
Layout/
|
8
|
-
|
8
|
+
Layout/LineLength:
|
9
|
+
Max: 120
|
9
10
|
|
10
|
-
|
11
|
-
Layout/IndentHeredoc:
|
11
|
+
Style/MultilineBlockChain:
|
12
12
|
Enabled: false
|
13
13
|
|
14
|
+
Style/NumericPredicate:
|
15
|
+
Enabled: false
|
16
|
+
|
14
17
|
Style/AndOr:
|
15
18
|
Enabled: false
|
16
19
|
|
17
|
-
|
18
|
-
|
20
|
+
Style/HashEachMethods:
|
21
|
+
Enabled: true
|
22
|
+
|
23
|
+
Style/HashTransformKeys:
|
24
|
+
Enabled: true
|
25
|
+
|
26
|
+
Style/HashTransformValues:
|
27
|
+
Enabled: true
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,22 @@
|
|
1
|
+
# v0.25.0
|
2
|
+
|
3
|
+
* Reports `exceptions` in [function return attributes](https://github.com/applandinc/appmap#function-return-attributes).
|
4
|
+
|
5
|
+
# v0.24.1
|
6
|
+
* Fixes an issue which prevented a remote recording from returning scenario data successfully.
|
7
|
+
* Remote recording routes now return descriptive status codes as intended.
|
8
|
+
* Remote recording routes now have the correct `Content-Type` header.
|
9
|
+
|
10
|
+
# v0.24.0
|
11
|
+
|
12
|
+
Internals of `appmap-ruby` have been changed to record each method event using `alias_method`,
|
13
|
+
rather than `TracePoint`. Performance is much better as a result.
|
14
|
+
|
15
|
+
**WARNING** Breaking changes
|
16
|
+
|
17
|
+
* **Rack** apps no longer generate `http_server_request` events.
|
18
|
+
* **appmap inspect** has been removed. `appmap-ruby` no longer parses the source tree. Instead, it observes the methods as they are loaded by the VM. So, to get a class map, you have to create a recording. The `RSpec` recorder still prints an inventory to `Inventory.appmap.json` when it exits. The class map in this file contains every class and method which was loaded by any of the tests.
|
19
|
+
|
1
20
|
# v0.23.0
|
2
21
|
|
3
22
|
* **appmap stats** command added.
|
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
- [About](#about)
|
2
|
+
- [Testing](#testing)
|
2
3
|
- [Installation](#installation)
|
3
4
|
- [Configuration](#configuration)
|
4
5
|
- [Running](#running)
|
5
6
|
- [RSpec](#rspec)
|
6
7
|
- [Remote recording](#remote-recording)
|
8
|
+
- [Ruby on Rails](#ruby-on-rails)
|
7
9
|
- [Uploading AppMaps](#uploading-appmaps)
|
8
10
|
- [Build status](#build-status)
|
9
11
|
|
@@ -11,21 +13,33 @@
|
|
11
13
|
|
12
14
|
`appmap-ruby` is a Ruby Gem for recording and uploading
|
13
15
|
[AppMaps](https://github.com/applandinc/appmap) of your code.
|
14
|
-
AppMap is a data format which records code structure (modules, classes, and methods), code execution events
|
16
|
+
"AppMap" is a data format which records code structure (modules, classes, and methods), code execution events
|
15
17
|
(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
|
18
|
+
SHA, labels, etc). It's more granular than a performance profile, but it's less
|
17
19
|
granular than a full debug trace. It's designed to be optimal for understanding the design intent and behavior of code.
|
18
20
|
|
19
21
|
There are several ways to record AppMaps of your Ruby program using the `appmap` gem:
|
20
22
|
|
21
|
-
* Run your RSpec tests
|
22
|
-
* Run your application server with AppMap remote recording enabled, and use the AppMap
|
23
|
+
* Run your RSpec tests with the environment variable `APPMAP=true`. An AppMap will be generated for each spec.
|
24
|
+
* Run your application server with AppMap remote recording enabled, and use the AppMap.
|
23
25
|
browser extension to start, stop, and upload recordings.
|
26
|
+
* Run the command `appmap record <program>` to record the entire execution of a program.
|
24
27
|
|
25
28
|
When you record AppMaps on the command line (for example, by running RSpec tests), you use the `appmap upload` command to
|
26
29
|
upload them to the AppLand website. On the AppLand website, you'll be able to
|
27
30
|
visualize the design of your code and share links with collaborators.
|
28
31
|
|
32
|
+
# Testing
|
33
|
+
Before running tests, configure `local.appmap` to point to your local `appmap-ruby` directory.
|
34
|
+
```
|
35
|
+
$ bundle config local.appmap $(pwd)
|
36
|
+
```
|
37
|
+
|
38
|
+
Run the tests via `rake`:
|
39
|
+
```
|
40
|
+
$ bundle exec rake test
|
41
|
+
```
|
42
|
+
|
29
43
|
# Installation
|
30
44
|
|
31
45
|
Add `gem 'appmap'` to your Gemfile just as you would any other dependency.
|
@@ -60,8 +74,6 @@ packages:
|
|
60
74
|
|
61
75
|
* **name** Provides the project name (required)
|
62
76
|
* **packages** A list of source code directories which should be instrumented.
|
63
|
-
* **files** A list of individual files which should be instrumented. This is only used for files which are
|
64
|
-
not part of the `packages` list.
|
65
77
|
|
66
78
|
**packages**
|
67
79
|
|
@@ -69,9 +81,7 @@ Each entry in the `packages` list is a YAML object which has the following keys:
|
|
69
81
|
|
70
82
|
* **path** The path to the source code directory. The path may be relative to the current working directory, or it may
|
71
83
|
be an absolute path.
|
72
|
-
* **
|
73
|
-
is located. In the example above, "controllers" or "models".
|
74
|
-
* **excludes** A list of files and directories which will be ignored. By default, all modules, classes and public
|
84
|
+
* **exclude** A list of files and directories which will be ignored. By default, all modules, classes and public
|
75
85
|
functions are inspected.
|
76
86
|
|
77
87
|
# Running
|
@@ -129,6 +139,8 @@ If you include the `feature` and `feature_group` metadata, these attributes will
|
|
129
139
|
}
|
130
140
|
```
|
131
141
|
|
142
|
+
If you don't explicitly declare `feature` and `feature_group`, then they will be inferred from the spec name and example descriptions.
|
143
|
+
|
132
144
|
## Remote recording
|
133
145
|
|
134
146
|
To manually record ad-hoc AppMaps of your Ruby app, use AppMap remote recording.
|
@@ -159,12 +171,18 @@ $ bundle exec rails server
|
|
159
171
|
|
160
172
|
6. Open the AppApp browser extension and push `Stop`. The recording will be transferred to the AppLand website and opened in your browser.
|
161
173
|
|
174
|
+
## Ruby on Rails
|
175
|
+
|
176
|
+
If your app uses Ruby on Rails, the AppMap Railtie will be automatically enabled. Set the Rails config flag `app.config.appmap.enabled = true` to record the entire execution of your Rails app.
|
177
|
+
|
178
|
+
Note that using this method is kind of a blunt instrument. Recording RSpecs and using Remote Recording are usually better options.
|
179
|
+
|
162
180
|
# Uploading AppMaps
|
163
181
|
|
164
|
-
To upload an AppMap file to AppLand, run the `appmap upload` command. For example:
|
182
|
+
To upload an AppMap file to AppLand, run the `appmap upload` command. For example (with binstubs installed):
|
165
183
|
|
166
184
|
```sh-session
|
167
|
-
$ appmap upload tmp/appmap/rspec
|
185
|
+
$ ./bin/appmap upload tmp/appmap/rspec/*
|
168
186
|
Uploading "tmp/appmap/rspec/Hello_app_says_hello_when_prompted.appmap.json"
|
169
187
|
Scenario Id: 4da4f267-bdea-48e8-bf67-f39463844230
|
170
188
|
Batch Id: a116f1df-ee57-4bde-8eef-851af0f3d7bc
|
@@ -172,4 +190,3 @@ Batch Id: a116f1df-ee57-4bde-8eef-851af0f3d7bc
|
|
172
190
|
|
173
191
|
# Build status
|
174
192
|
[![Build Status](https://travis-ci.org/applandinc/appmap-ruby.svg?branch=master)](https://travis-ci.org/applandinc/appmap-ruby)
|
175
|
-
|
data/Rakefile
CHANGED
@@ -54,7 +54,7 @@ namespace :build do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
namespace :fixtures do
|
59
59
|
RUBY_VERSIONS.each do |ruby_version|
|
60
60
|
namespace ruby_version do
|
@@ -69,7 +69,7 @@ namespace :build do
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
72
|
-
|
72
|
+
|
73
73
|
desc "Build all fixture images"
|
74
74
|
task all: ["#{ruby_version}:all"]
|
75
75
|
end
|
@@ -93,7 +93,7 @@ def run_specs(ruby_version, task_args)
|
|
93
93
|
task.rspec_opts = args.to_a.join(' ')
|
94
94
|
end
|
95
95
|
end
|
96
|
-
|
96
|
+
|
97
97
|
# Set up the environment, then execute the rspec task we
|
98
98
|
# created above.
|
99
99
|
ClimateControl.modify(RUBY_VERSION: ruby_version) do
|
data/appmap.gemspec
CHANGED
@@ -30,11 +30,13 @@ Gem::Specification.new do |spec|
|
|
30
30
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
31
31
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
32
32
|
spec.add_development_dependency 'pry-byebug'
|
33
|
-
spec.add_development_dependency 'rake', '
|
33
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
34
34
|
spec.add_development_dependency 'rdoc'
|
35
|
+
spec.add_development_dependency 'rubocop'
|
35
36
|
|
36
37
|
# Testing
|
37
38
|
spec.add_development_dependency 'climate_control'
|
39
|
+
spec.add_development_dependency 'diffy'
|
38
40
|
spec.add_development_dependency 'launchy'
|
39
41
|
spec.add_development_dependency 'rspec'
|
40
42
|
spec.add_development_dependency 'selenium-webdriver'
|
data/exe/appmap
CHANGED
@@ -42,17 +42,6 @@ module AppMap
|
|
42
42
|
arg_name 'filename'
|
43
43
|
flag %i[c config]
|
44
44
|
|
45
|
-
desc 'Inspect code and generate a classmap file'
|
46
|
-
command :inspect do |c|
|
47
|
-
output_file_flag(c, default_value: default_appmap_file)
|
48
|
-
|
49
|
-
c.action do
|
50
|
-
require 'appmap/command/inspect'
|
51
|
-
appmap = AppMap::Command::Inspect.new(@config).perform
|
52
|
-
@output_file.write JSON.pretty_generate(appmap)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
45
|
desc 'Record the execution of a program and generate an AppMap.'
|
57
46
|
arg_name 'program'
|
58
47
|
command :record do |c|
|
@@ -76,10 +65,10 @@ module AppMap
|
|
76
65
|
ARGV.shift
|
77
66
|
|
78
67
|
require 'appmap/command/record'
|
79
|
-
AppMap::Command::Record.new(@config, program).perform do |
|
80
|
-
@output_file.write JSON.generate(version:
|
81
|
-
|
82
|
-
|
68
|
+
AppMap::Command::Record.new(@config, program).perform do |version, metadata, class_map, events|
|
69
|
+
@output_file.write JSON.generate(version: version,
|
70
|
+
metadata: metadata,
|
71
|
+
classMap: class_map,
|
83
72
|
events: events)
|
84
73
|
end
|
85
74
|
end
|
@@ -173,7 +162,7 @@ module AppMap
|
|
173
162
|
appmap = JSON.parse(File.read(filename))
|
174
163
|
|
175
164
|
warn "Uploading #{filename.inspect}"
|
176
|
-
upload = AppMap::Command::Upload.new(
|
165
|
+
upload = AppMap::Command::Upload.new(appmap, url, user, org)
|
177
166
|
upload.batch_id = batch_id if batch_id
|
178
167
|
upload.perform.tap do |response|
|
179
168
|
batch_id ||= response.batch_id
|
@@ -203,8 +192,7 @@ module AppMap
|
|
203
192
|
protected
|
204
193
|
|
205
194
|
def interpret_config_option(fname)
|
206
|
-
|
207
|
-
AppMap::Config.load_from_file fname
|
195
|
+
AppMap.configure fname
|
208
196
|
end
|
209
197
|
|
210
198
|
def interpret_output_file_option(file_name)
|
data/lib/appmap.rb
CHANGED
@@ -13,12 +13,53 @@ module AppMap
|
|
13
13
|
BATCH_HEADER_NAME = 'AppLand-Scenario-Batch'
|
14
14
|
|
15
15
|
class << self
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
@config = nil
|
17
|
+
@config_file_path = nil
|
18
|
+
|
19
|
+
# configuration gets the AppMap configuration.
|
20
|
+
def configuration
|
21
|
+
raise "AppMap is not configured" unless @config
|
22
|
+
|
23
|
+
@config
|
24
|
+
end
|
25
|
+
|
26
|
+
# configure applies the configuration from a file. This method can only be performed once.
|
27
|
+
# Be sure and call it before +hook+ if you want non-default configuration.
|
28
|
+
#
|
29
|
+
# Default behavior is to configure from "appmap.yml".
|
30
|
+
def configure(config_file_path = 'appmap.yml')
|
31
|
+
if @config
|
32
|
+
return @config if @config_file_path == config_file_path
|
33
|
+
|
34
|
+
raise "AppMap is already configured from #{@config_file_path}, can't reconfigure from #{config_file_path}"
|
35
|
+
end
|
36
|
+
|
37
|
+
warn "Configuring AppMap from path #{config_file_path}"
|
38
|
+
require 'appmap/hook'
|
39
|
+
AppMap::Hook::Config.load_from_file(config_file_path).tap do |config|
|
40
|
+
@config = config
|
41
|
+
@config_file_path = config_file_path
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Activate the code hooks which record function calls as trace events.
|
46
|
+
# Call this function before the program code is loaded by the Ruby VM, otherwise
|
47
|
+
# the load events won't be seen and the hooks won't activate.
|
48
|
+
def hook(config = configure)
|
49
|
+
require 'appmap/hook'
|
50
|
+
AppMap::Hook.hook(config)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Access the AppMap::Tracers, which can be used to start tracing, stop tracing, and record events.
|
54
|
+
def tracing
|
55
|
+
require 'appmap/trace'
|
56
|
+
@tracing ||= Trace::Tracers.new
|
57
|
+
end
|
58
|
+
|
59
|
+
# Build a class map from a config and a list of Ruby methods.
|
60
|
+
def class_map(config, methods)
|
61
|
+
require 'appmap/class_map'
|
62
|
+
AppMap::ClassMap.build_from_methods(config, methods)
|
22
63
|
end
|
23
64
|
end
|
24
65
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AppMap
|
2
4
|
module Algorithm
|
3
5
|
StatsStruct = Struct.new(:appmap)
|
@@ -31,7 +33,7 @@ module AppMap
|
|
31
33
|
comparator = ->(a,b) { b.count <=> a.count }
|
32
34
|
class_frequency.sort!(&comparator)
|
33
35
|
method_frequency.sort!(&comparator)
|
34
|
-
|
36
|
+
|
35
37
|
self
|
36
38
|
end
|
37
39
|
|
@@ -55,7 +57,7 @@ module AppMap
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
Frequency = Struct.new(:name, :count)
|
58
|
-
|
60
|
+
|
59
61
|
def perform(limit: nil)
|
60
62
|
events = appmap['events'] || []
|
61
63
|
frequency_calc = lambda do |key_func|
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/core_ext'
|
4
|
+
|
5
|
+
module AppMap
|
6
|
+
class ClassMap
|
7
|
+
module HasChildren
|
8
|
+
def self.included(base)
|
9
|
+
base.module_eval do
|
10
|
+
def children
|
11
|
+
@children ||= []
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Types
|
18
|
+
class Root
|
19
|
+
include HasChildren
|
20
|
+
end
|
21
|
+
|
22
|
+
Package = Struct.new(:name) do
|
23
|
+
include HasChildren
|
24
|
+
|
25
|
+
def type
|
26
|
+
'package'
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_h
|
30
|
+
{
|
31
|
+
name: name,
|
32
|
+
type: type,
|
33
|
+
children: children.map(&:to_h)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
Class = Struct.new(:name) do
|
38
|
+
include HasChildren
|
39
|
+
|
40
|
+
def type
|
41
|
+
'class'
|
42
|
+
end
|
43
|
+
|
44
|
+
def to_h
|
45
|
+
{
|
46
|
+
name: name,
|
47
|
+
type: type,
|
48
|
+
children: children.map(&:to_h)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
Function = Struct.new(:name) do
|
53
|
+
attr_accessor :static, :location
|
54
|
+
|
55
|
+
def type
|
56
|
+
'function'
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_h
|
60
|
+
{
|
61
|
+
name: name,
|
62
|
+
type: type,
|
63
|
+
location: location,
|
64
|
+
static: static
|
65
|
+
}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class << self
|
71
|
+
def build_from_methods(config, methods)
|
72
|
+
root = Types::Root.new
|
73
|
+
methods.each do |method|
|
74
|
+
package = package_for_method(config.packages, method)
|
75
|
+
add_function root, package.path, method
|
76
|
+
end
|
77
|
+
root.children.map(&:to_h)
|
78
|
+
end
|
79
|
+
|
80
|
+
protected
|
81
|
+
|
82
|
+
def package_for_method(packages, method)
|
83
|
+
location = method.method.source_location
|
84
|
+
location_file, = location
|
85
|
+
location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
|
86
|
+
|
87
|
+
packages.find do |pkg|
|
88
|
+
(location_file.index(pkg.path) == 0) &&
|
89
|
+
!pkg.exclude.find { |p| location_file.index(p) }
|
90
|
+
end or raise "No package found for method #{method}"
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_function(root, package_name, method)
|
94
|
+
location = method.method.source_location
|
95
|
+
location_file, lineno = location
|
96
|
+
location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
|
97
|
+
|
98
|
+
static = method.method.owner.singleton_class?
|
99
|
+
|
100
|
+
object_infos = [
|
101
|
+
{
|
102
|
+
name: package_name,
|
103
|
+
type: 'package'
|
104
|
+
}
|
105
|
+
]
|
106
|
+
object_infos += method.defined_class.split('::').map do |name|
|
107
|
+
{
|
108
|
+
name: name,
|
109
|
+
type: 'class'
|
110
|
+
}
|
111
|
+
end
|
112
|
+
object_infos << {
|
113
|
+
name: method.method.name,
|
114
|
+
type: 'function',
|
115
|
+
location: [ location_file, lineno ].join(':'),
|
116
|
+
static: static
|
117
|
+
}
|
118
|
+
parent = root
|
119
|
+
object_infos.each do |info|
|
120
|
+
parent = find_or_create parent.children, info do
|
121
|
+
Types.const_get(info[:type].classify).new(info[:name].to_s).tap do |type|
|
122
|
+
info.keys.tap do |keys|
|
123
|
+
keys.delete(:name)
|
124
|
+
keys.delete(:type)
|
125
|
+
end.each do |key|
|
126
|
+
type.send "#{key}=", info[key]
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def find_or_create(list, info)
|
134
|
+
obj = list.find { |item| item.type == info[:type] && item.name == info[:name] }
|
135
|
+
return obj if obj
|
136
|
+
|
137
|
+
yield.tap do |new_obj|
|
138
|
+
list << new_obj
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|