appmap 0.43.0 → 0.44.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +17 -2
- data/CHANGELOG.md +9 -0
- data/README.md +28 -1
- data/appmap.gemspec +0 -2
- data/lib/appmap.rb +3 -7
- data/lib/appmap/class_map.rb +7 -10
- data/lib/appmap/command/record.rb +1 -1
- data/lib/appmap/config.rb +88 -28
- data/lib/appmap/cucumber.rb +1 -1
- data/lib/appmap/hook.rb +25 -20
- data/lib/appmap/middleware/remote_recording.rb +1 -1
- data/lib/appmap/minitest.rb +22 -20
- data/lib/appmap/record.rb +1 -1
- data/lib/appmap/rspec.rb +22 -21
- data/lib/appmap/util.rb +16 -0
- data/lib/appmap/version.rb +1 -1
- data/patch +1447 -0
- data/spec/abstract_controller_base_spec.rb +2 -4
- data/spec/class_map_spec.rb +3 -11
- data/spec/config_spec.rb +31 -1
- data/spec/fixtures/hook/custom_instance_method.rb +11 -0
- data/spec/fixtures/hook/method_named_call.rb +11 -0
- data/spec/hook_spec.rb +134 -10
- data/test/expectations/openssl_test_key_sign1.json +2 -4
- data/test/gem_test.rb +1 -1
- data/test/rspec_test.rb +0 -13
- metadata +7 -7
- data/exe/appmap +0 -154
- data/test/cli_test.rb +0 -116
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 02dfa68caaa5e5413c68ae06e89664a676624c9dbb301e2ebbc2a7ae26359afd
|
4
|
+
data.tar.gz: af97e3fb4bb428a238b854ef30969f232c48d4f3a59956865a4cc95788ac35e8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a0f2454acf5d10c48aaf1abb6e4b23d674acbbfe495770a25ff83093c38dd81cf0bb1c85dfbae4691773586ace8500c3f9b879b31749c4142851f460ed1a87c
|
7
|
+
data.tar.gz: a2860fb0258b96d95e5ca98f7d13b382c86b635ce1c2d97084c1f651a55973d2ffe31e360695fdec963292081e527b96876c6b42976a90573b9201e14032d1f5
|
data/.travis.yml
CHANGED
@@ -13,11 +13,26 @@ services:
|
|
13
13
|
# necessary.
|
14
14
|
before_script:
|
15
15
|
- unset RAILS_ENV
|
16
|
-
|
16
|
+
|
17
|
+
cache:
|
18
|
+
bundler: true
|
19
|
+
directories:
|
20
|
+
- $HOME/docker
|
21
|
+
|
22
|
+
# https://stackoverflow.com/a/41975912
|
23
|
+
before_cache:
|
24
|
+
# Save tagged docker images
|
25
|
+
- >
|
26
|
+
mkdir -p $HOME/docker && docker images -a --filter='dangling=false' --format '{{.Repository}}:{{.Tag}} {{.ID}}'
|
27
|
+
| xargs -n 2 -t sh -c 'test -e $HOME/docker/$1.tar.gz || docker save $0 | gzip -2 > $HOME/docker/$1.tar.gz'
|
28
|
+
|
29
|
+
before_install:
|
30
|
+
# Load cached docker images
|
31
|
+
- if [[ -d $HOME/docker ]]; then ls $HOME/docker/*.tar.gz | xargs -I {file} sh -c "zcat {file} | docker load"; fi
|
32
|
+
|
17
33
|
jobs:
|
18
34
|
include:
|
19
35
|
- stage: test
|
20
36
|
script:
|
21
37
|
- mkdir tmp
|
22
38
|
- bundle exec rake test
|
23
|
-
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# v0.44.0
|
2
|
+
|
3
|
+
* Support recording and labeling of indivudal functions via `functions:` section in *appmap.yml*.
|
4
|
+
* Remove deprecated `exe/appmap`.
|
5
|
+
* Add `test_status` and `exception` fields to AppMap metadata.
|
6
|
+
* Write AppMap file atomically, by writing to a temp file first and then moving it into place.
|
7
|
+
* Remove printing of `Inventory.json` file.
|
8
|
+
* Remove source code from `classMap`.
|
9
|
+
|
1
10
|
# v0.43.0
|
2
11
|
|
3
12
|
* Record `name` and `class` of each entry in Hash-like parameters, messages, and return values.
|
data/README.md
CHANGED
@@ -11,6 +11,7 @@
|
|
11
11
|
- [Remote recording](#remote-recording)
|
12
12
|
- [Server process recording](#server-process-recording)
|
13
13
|
- [AppMap for VSCode](#appmap-for-vscode)
|
14
|
+
- [AppMap Swagger](#appmap-swagger)
|
14
15
|
- [Uploading AppMaps](#uploading-appmaps)
|
15
16
|
- [Development](#development)
|
16
17
|
- [Running tests](#running-tests)
|
@@ -109,6 +110,9 @@ name: my_project
|
|
109
110
|
packages:
|
110
111
|
- path: app/controllers
|
111
112
|
- path: app/models
|
113
|
+
# Exclude sub-paths within the package path
|
114
|
+
exclude:
|
115
|
+
- concerns/accessor
|
112
116
|
- path: app/jobs
|
113
117
|
- path: app/helpers
|
114
118
|
# Include the gems that you want to see in the dependency maps.
|
@@ -117,15 +121,22 @@ packages:
|
|
117
121
|
- gem: devise
|
118
122
|
- gem: aws-sdk
|
119
123
|
- gem: will_paginate
|
124
|
+
# Global exclusion of a class name
|
120
125
|
exclude:
|
121
126
|
- MyClass
|
122
127
|
- MyClass#my_instance_method
|
123
128
|
- MyClass.my_class_method
|
129
|
+
functions:
|
130
|
+
- packages: myapp
|
131
|
+
class: ControllerHelper
|
132
|
+
function: logged_in_user
|
133
|
+
labels: [ authentication ]
|
124
134
|
```
|
125
135
|
|
126
136
|
* **name** Provides the project name (required)
|
127
137
|
* **packages** A list of source code directories which should be recorded.
|
128
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.
|
129
140
|
|
130
141
|
**packages**
|
131
142
|
|
@@ -144,6 +155,11 @@ Each entry in the `packages` list is a YAML object which has the following keys:
|
|
144
155
|
|
145
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.
|
146
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.
|
147
163
|
|
148
164
|
# Labels
|
149
165
|
|
@@ -329,9 +345,20 @@ Run your Rails server with `APPMAP_RECORD=true`. When the server exits, an *appm
|
|
329
345
|
|
330
346
|
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.
|
331
347
|
|
348
|
+
|
332
349
|
# AppMap for VSCode
|
333
350
|
|
334
|
-
The [AppMap extension for VSCode](https://marketplace.visualstudio.com/items?itemName=appland.appmap)
|
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.
|
335
362
|
|
336
363
|
# Uploading AppMaps
|
337
364
|
|
data/appmap.gemspec
CHANGED
data/lib/appmap.rb
CHANGED
@@ -43,6 +43,7 @@ module AppMap
|
|
43
43
|
# Call this function before the program code is loaded by the Ruby VM, otherwise
|
44
44
|
# the load events won't be seen and the hooks won't activate.
|
45
45
|
def initialize(config_file_path = 'appmap.yml')
|
46
|
+
raise "AppMap configuration file #{config_file_path} does not exist" unless ::File.exists?(config_file_path)
|
46
47
|
warn "Configuring AppMap from path #{config_file_path}"
|
47
48
|
Config.load_from_file(config_file_path).tap do |configuration|
|
48
49
|
self.configuration = configuration
|
@@ -50,11 +51,6 @@ module AppMap
|
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
53
|
-
# Whether to include source and comments in all class maps.
|
54
|
-
def include_source?
|
55
|
-
ENV['APPMAP_SOURCE'] == 'true'
|
56
|
-
end
|
57
|
-
|
58
54
|
# Used to start tracing, stop tracing, and record events.
|
59
55
|
def tracing
|
60
56
|
@tracing ||= Trace::Tracing.new
|
@@ -88,8 +84,8 @@ module AppMap
|
|
88
84
|
end
|
89
85
|
|
90
86
|
# Builds a class map from a config and a list of Ruby methods.
|
91
|
-
def class_map(methods
|
92
|
-
ClassMap.build_from_methods(methods
|
87
|
+
def class_map(methods)
|
88
|
+
ClassMap.build_from_methods(methods)
|
93
89
|
end
|
94
90
|
|
95
91
|
# Returns default metadata detected from the Ruby system and from the
|
data/lib/appmap/class_map.rb
CHANGED
@@ -71,17 +71,17 @@ module AppMap
|
|
71
71
|
end
|
72
72
|
|
73
73
|
class << self
|
74
|
-
def build_from_methods(methods
|
74
|
+
def build_from_methods(methods)
|
75
75
|
root = Types::Root.new
|
76
76
|
methods.each do |method|
|
77
|
-
add_function root, method
|
77
|
+
add_function root, method
|
78
78
|
end
|
79
79
|
root.children.map(&:to_h)
|
80
80
|
end
|
81
81
|
|
82
82
|
protected
|
83
83
|
|
84
|
-
def add_function(root, method
|
84
|
+
def add_function(root, method)
|
85
85
|
package = method.package
|
86
86
|
static = method.static
|
87
87
|
|
@@ -113,16 +113,13 @@ module AppMap
|
|
113
113
|
[ method.defined_class, static ? '.' : '#', method.name ].join
|
114
114
|
end
|
115
115
|
|
116
|
-
|
117
|
-
|
116
|
+
comment = begin
|
117
|
+
method.comment
|
118
118
|
rescue MethodSource::SourceNotFoundError
|
119
|
-
|
119
|
+
nil
|
120
120
|
end
|
121
121
|
|
122
|
-
|
123
|
-
function_info[:source] = source unless source.blank?
|
124
|
-
function_info[:comment] = comment unless comment.blank?
|
125
|
-
end
|
122
|
+
function_info[:comment] = comment unless comment.blank?
|
126
123
|
|
127
124
|
function_info[:labels] = parse_labels(comment) + (package.labels || [])
|
128
125
|
object_infos << function_info
|
data/lib/appmap/config.rb
CHANGED
@@ -49,44 +49,89 @@ module AppMap
|
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
52
|
-
|
52
|
+
Function = Struct.new(:package, :cls, :labels, :function_names) do
|
53
|
+
def to_h
|
54
|
+
{
|
55
|
+
package: package,
|
56
|
+
class: cls,
|
57
|
+
labels: labels,
|
58
|
+
functions: function_names.map(&:to_sym)
|
59
|
+
}.compact
|
60
|
+
end
|
53
61
|
end
|
54
62
|
|
55
|
-
|
63
|
+
class Hook
|
64
|
+
attr_reader :method_names, :package
|
65
|
+
|
66
|
+
def initialize(method_names, package)
|
67
|
+
@method_names = method_names
|
68
|
+
@package = package
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_h
|
72
|
+
{
|
73
|
+
package: package.name,
|
74
|
+
method_names: method_names
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
OPENSSL_PACKAGES = ->(labels) { Package.build_from_path('openssl', package_name: 'openssl', labels: labels) }
|
56
80
|
|
57
81
|
# Methods that should always be hooked, with their containing
|
58
82
|
# package and labels that should be applied to them.
|
59
83
|
HOOKED_METHODS = {
|
60
|
-
'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', labels: %w[
|
84
|
+
'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.build_from_path('active_support', labels: %w[crypto.secure_compare])),
|
61
85
|
'ActionView::Renderer' => Hook.new(:render, Package.build_from_path('action_view', labels: %w[mvc.view])),
|
62
|
-
'ActionDispatch::
|
63
|
-
'ActionDispatch::Cookies::
|
64
|
-
'
|
65
|
-
'CanCan::
|
86
|
+
'ActionDispatch::Request::Session' => Hook.new(%i[destroy [] dig values []= clear update delete fetch merge], Package.build_from_path('action_pack', labels: %w[http.session])),
|
87
|
+
'ActionDispatch::Cookies::CookieJar' => Hook.new(%i[[]= clear update delete recycle], Package.build_from_path('action_pack', labels: %w[http.cookie])),
|
88
|
+
'ActionDispatch::Cookies::EncryptedCookieJar' => Hook.new(%i[[]=], Package.build_from_path('action_pack', labels: %w[http.cookie crypto.encrypt])),
|
89
|
+
'CanCan::ControllerAdditions' => Hook.new(%i[authorize! can? cannot?], Package.build_from_path('cancancan', labels: %w[security.authorization])),
|
90
|
+
'CanCan::Ability' => Hook.new(%i[authorize!], Package.build_from_path('cancancan', labels: %w[security.authorization])),
|
91
|
+
'ActionController::Instrumentation' => [
|
92
|
+
Hook.new(%i[process_action send_file send_data redirect_to], Package.build_from_path('action_view', labels: %w[mvc.controller])),
|
93
|
+
Hook.new(%i[render], Package.build_from_path('action_view', labels: %w[mvc.view])),
|
94
|
+
]
|
66
95
|
}.freeze
|
67
96
|
|
68
97
|
BUILTIN_METHODS = {
|
69
|
-
'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGES),
|
70
|
-
'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGES),
|
71
|
-
'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES),
|
72
|
-
'OpenSSL::Cipher' =>
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
'
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
'
|
81
|
-
'
|
98
|
+
'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGES.(%w[crypto.pkey])),
|
99
|
+
'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGES.(%w[crypto.x509])),
|
100
|
+
'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES.(%w[crypto.pkcs5])),
|
101
|
+
'OpenSSL::Cipher' => [
|
102
|
+
Hook.new(%i[encrypt], OPENSSL_PACKAGES.(%w[crypto.encrypt])),
|
103
|
+
Hook.new(%i[decrypt], OPENSSL_PACKAGES.(%w[crypto.decrypt]))
|
104
|
+
],
|
105
|
+
'ActiveSupport::Callbacks::CallbackSequence' => [
|
106
|
+
Hook.new(:invoke_before, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[mvc.before_action])),
|
107
|
+
Hook.new(:invoke_after, Package.build_from_path('active_support', package_name: 'active_support', labels: %w[mvc.after_action])),
|
108
|
+
],
|
109
|
+
'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGES.(%w[crypto.x509])),
|
110
|
+
'Net::HTTP' => Hook.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http])),
|
111
|
+
'Net::SMTP' => Hook.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.email.smtp])),
|
112
|
+
'Net::POP3' => Hook.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.email.pop])),
|
113
|
+
'Net::IMAP' => Hook.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.email.imap])),
|
114
|
+
'Marshal' => Hook.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal])),
|
115
|
+
'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])),
|
116
|
+
'JSON::Ext::Parser' => Hook.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json])),
|
117
|
+
'JSON::Ext::Generator::State' => Hook.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json])),
|
82
118
|
}.freeze
|
83
119
|
|
84
|
-
attr_reader :name, :packages, :exclude
|
120
|
+
attr_reader :name, :packages, :exclude, :builtin_methods
|
85
121
|
|
86
|
-
def initialize(name, packages
|
122
|
+
def initialize(name, packages, exclude: [], functions: [])
|
87
123
|
@name = name
|
88
124
|
@packages = packages
|
89
125
|
@exclude = exclude
|
126
|
+
@builtin_methods = BUILTIN_METHODS
|
127
|
+
@functions = functions
|
128
|
+
@hooked_methods = HOOKED_METHODS.dup
|
129
|
+
functions.each do |func|
|
130
|
+
package_options = {}
|
131
|
+
package_options[:labels] = func.labels if func.labels
|
132
|
+
@hooked_methods[func.cls] ||= []
|
133
|
+
@hooked_methods[func.cls] << Hook.new(func.function_names, Package.build_from_path(func.package, package_options))
|
134
|
+
end
|
90
135
|
end
|
91
136
|
|
92
137
|
class << self
|
@@ -98,6 +143,16 @@ module AppMap
|
|
98
143
|
|
99
144
|
# Loads configuration from a Hash.
|
100
145
|
def load(config_data)
|
146
|
+
functions = (config_data['functions'] || []).map do |function_data|
|
147
|
+
package = function_data['package']
|
148
|
+
cls = function_data['class']
|
149
|
+
functions = function_data['function'] || function_data['functions']
|
150
|
+
raise 'AppMap class configuration should specify package, class and function(s)' unless package && cls && functions
|
151
|
+
functions = Array(functions).map(&:to_sym)
|
152
|
+
labels = function_data['label'] || function_data['labels']
|
153
|
+
labels = Array(labels).map(&:to_s) if labels
|
154
|
+
Function.new(package, cls, labels, functions)
|
155
|
+
end
|
101
156
|
packages = (config_data['packages'] || []).map do |package|
|
102
157
|
gem = package['gem']
|
103
158
|
path = package['path']
|
@@ -112,7 +167,8 @@ module AppMap
|
|
112
167
|
Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
|
113
168
|
end
|
114
169
|
end.compact
|
115
|
-
|
170
|
+
exclude = config_data['exclude'] || []
|
171
|
+
Config.new config_data['name'], packages, exclude: exclude, functions: functions
|
116
172
|
end
|
117
173
|
end
|
118
174
|
|
@@ -120,6 +176,7 @@ module AppMap
|
|
120
176
|
{
|
121
177
|
name: name,
|
122
178
|
packages: packages.map(&:to_h),
|
179
|
+
functions: @functions.map(&:to_h),
|
123
180
|
exclude: exclude
|
124
181
|
}
|
125
182
|
end
|
@@ -164,14 +221,17 @@ module AppMap
|
|
164
221
|
end
|
165
222
|
|
166
223
|
def find_package(defined_class, method_name)
|
167
|
-
|
168
|
-
return nil unless
|
224
|
+
hooks = find_hooks(defined_class)
|
225
|
+
return nil unless hooks
|
169
226
|
|
170
|
-
Array(
|
227
|
+
hook = Array(hooks).find do |hook|
|
228
|
+
Array(hook.method_names).include?(method_name)
|
229
|
+
end
|
230
|
+
hook ? hook.package : nil
|
171
231
|
end
|
172
232
|
|
173
|
-
def
|
174
|
-
|
233
|
+
def find_hooks(defined_class)
|
234
|
+
Array(@hooked_methods[defined_class] || @builtin_methods[defined_class])
|
175
235
|
end
|
176
236
|
end
|
177
237
|
end
|
data/lib/appmap/cucumber.rb
CHANGED
@@ -50,7 +50,7 @@ module AppMap
|
|
50
50
|
appmap['metadata'] = update_metadata(scenario, appmap['metadata'])
|
51
51
|
scenario_filename = AppMap::Util.scenario_filename(appmap['metadata']['name'])
|
52
52
|
|
53
|
-
|
53
|
+
AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, scenario_filename), JSON.generate(appmap))
|
54
54
|
end
|
55
55
|
|
56
56
|
def enabled?
|
data/lib/appmap/hook.rb
CHANGED
@@ -32,6 +32,7 @@ module AppMap
|
|
32
32
|
end
|
33
33
|
|
34
34
|
attr_reader :config
|
35
|
+
|
35
36
|
def initialize(config)
|
36
37
|
@config = config
|
37
38
|
end
|
@@ -59,6 +60,10 @@ module AppMap
|
|
59
60
|
|
60
61
|
hook = lambda do |hook_cls|
|
61
62
|
lambda do |method_id|
|
63
|
+
# Don't try and trace the AppMap methods or there will be
|
64
|
+
# a stack overflow in the defined hook method.
|
65
|
+
return if (hook_cls&.name || '').split('::')[0] == AppMap.name
|
66
|
+
|
62
67
|
method = begin
|
63
68
|
hook_cls.public_instance_method(method_id)
|
64
69
|
rescue NameError
|
@@ -78,11 +83,9 @@ module AppMap
|
|
78
83
|
config.always_hook?(hook_cls, method.name) ||
|
79
84
|
config.included_by_location?(method)
|
80
85
|
|
81
|
-
|
86
|
+
package = config.package_for_method(method)
|
82
87
|
|
83
|
-
|
84
|
-
# a stack overflow in the defined hook method.
|
85
|
-
next if /\AAppMap[:\.]/.match?(hook_method.method_display_name)
|
88
|
+
hook_method = Hook::Method.new(package, hook_cls, method)
|
86
89
|
|
87
90
|
hook_method.activate
|
88
91
|
end
|
@@ -112,25 +115,27 @@ module AppMap
|
|
112
115
|
end
|
113
116
|
end
|
114
117
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
118
|
+
config.builtin_methods.each do |class_name, hooks|
|
119
|
+
Array(hooks).each do |hook|
|
120
|
+
require hook.package.package_name if hook.package.package_name
|
121
|
+
Array(hook.method_names).each do |method_name|
|
122
|
+
method_name = method_name.to_sym
|
119
123
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
124
|
+
cls = class_from_string.(class_name)
|
125
|
+
method = \
|
126
|
+
begin
|
127
|
+
cls.instance_method(method_name)
|
128
|
+
rescue NameError
|
129
|
+
cls.method(method_name) rescue nil
|
130
|
+
end
|
127
131
|
|
128
|
-
|
132
|
+
next if config.never_hook?(method)
|
129
133
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
+
if method
|
135
|
+
Hook::Method.new(hook.package, cls, method).activate
|
136
|
+
else
|
137
|
+
warn "Method #{method_name} not found on #{cls.name}"
|
138
|
+
end
|
134
139
|
end
|
135
140
|
end
|
136
141
|
end
|