appmap 0.66.2 → 0.68.1
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 +4 -4
- data/CHANGELOG.md +41 -0
- data/lib/appmap/builtin_hooks/json.yml +4 -0
- data/lib/appmap/builtin_hooks/net/http.yml +3 -0
- data/lib/appmap/builtin_hooks/openssl.yml +16 -0
- data/lib/appmap/builtin_hooks/yaml.yml +10 -0
- data/lib/appmap/command/agent_setup/validate.rb +8 -1
- data/lib/appmap/command/inspect.rb +0 -1
- data/lib/appmap/config.rb +156 -137
- data/lib/appmap/event.rb +6 -2
- data/lib/appmap/gem_hooks/actionpack.yml +40 -0
- data/lib/appmap/gem_hooks/actionview.yml +13 -0
- data/lib/appmap/gem_hooks/activesupport.yml +12 -0
- data/lib/appmap/gem_hooks/cancancan.yml +6 -0
- data/lib/appmap/handler/net_http.rb +7 -2
- data/lib/appmap/hook.rb +46 -30
- data/lib/appmap/minitest.rb +1 -0
- data/lib/appmap/rspec.rb +1 -0
- data/lib/appmap/util.rb +8 -0
- data/lib/appmap/version.rb +1 -1
- data/lib/appmap.rb +2 -2
- data/spec/config_spec.rb +224 -63
- data/spec/depends/api_spec.rb +1 -1
- data/spec/rails_spec_helper.rb +6 -1
- data/test/agent_setup_validate_test.rb +18 -10
- data/test/fixtures/mocha_mock_app/Gemfile +5 -0
- data/test/fixtures/mocha_mock_app/appmap.yml +5 -0
- data/test/fixtures/mocha_mock_app/lib/sheep.rb +5 -0
- data/test/fixtures/mocha_mock_app/test/sheep_test.rb +18 -0
- data/test/mock_compatibility_test.rb +45 -0
- metadata +15 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7da5837e4371cd2ce6aa6858e6ca88a50d38103657c054d0752ee7ec03d1f499
|
4
|
+
data.tar.gz: 81a5b8d75c7b4f5bad51a9f12cf3ea37ea38d1d7ca07a67073739cc96038775c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c2b4f67ecaf2f47344635cf3d16cbc8ab8821e10cd24966ff708baaa8c35c6a71bdb6a76d0eaf9415596f6d05c8c5b04c57115fede5bd6f4a0b4a1743e4ebc9
|
7
|
+
data.tar.gz: 712feeb618cea02bbaf17711a8c292f1670ef8c62974e2d3c706af88a31b27872afe139e7fc409f1da9800021035544736f2754970b4817234fabdf0190b113e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,44 @@
|
|
1
|
+
## [0.68.1](https://github.com/applandinc/appmap-ruby/compare/v0.68.0...v0.68.1) (2021-11-12)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Support new style of `functions` syntax in appmap.yml ([dca327c](https://github.com/applandinc/appmap-ruby/commit/dca327c98db1bddf849056995541306a5fc07eea))
|
7
|
+
|
8
|
+
# [0.68.0](https://github.com/applandinc/appmap-ruby/compare/v0.67.1...v0.68.0) (2021-11-05)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Require weakref ([2f94f80](https://github.com/applandinc/appmap-ruby/commit/2f94f808bd3327aa3fc7fd8e6a3428a5da3a29bb))
|
14
|
+
|
15
|
+
|
16
|
+
### Features
|
17
|
+
|
18
|
+
* Externalize config of hooks ([8080222](https://github.com/applandinc/appmap-ruby/commit/8080222ce5b61d9824eaf20410d7b9b94b679890))
|
19
|
+
* Support loading hook config via path env vars ([4856483](https://github.com/applandinc/appmap-ruby/commit/48564837784f8b0e87c4286ad3e2f6cb2d272dcf))
|
20
|
+
|
21
|
+
## [0.67.1](https://github.com/applandinc/appmap-ruby/compare/v0.67.0...v0.67.1) (2021-11-02)
|
22
|
+
|
23
|
+
|
24
|
+
### Bug Fixes
|
25
|
+
|
26
|
+
* Don't try to index AppMaps when inspecting ([ca18861](https://github.com/applandinc/appmap-ruby/commit/ca188619bd7085caa75a06eeeb5d5a92213251ac))
|
27
|
+
|
28
|
+
# [0.67.0](https://github.com/applandinc/appmap-ruby/compare/v0.66.2...v0.67.0) (2021-10-21)
|
29
|
+
|
30
|
+
|
31
|
+
### Bug Fixes
|
32
|
+
|
33
|
+
* Ensure rack is available, and handle nil HTTP response ([5e81dc4](https://github.com/applandinc/appmap-ruby/commit/5e81dc4310c9b7f2d81c31339f8490639c845f76))
|
34
|
+
* Handle WeakRef ([852ee04](https://github.com/applandinc/appmap-ruby/commit/852ee047880f9d1492be38772ed3f0cc1a670cb5))
|
35
|
+
|
36
|
+
|
37
|
+
### Features
|
38
|
+
|
39
|
+
* APPMAP_AUTOREQUIRE and APPMAP_INITIALIZE env vars to customize loading behavior ([369807e](https://github.com/applandinc/appmap-ruby/commit/369807e4c90243e296b324e70805bd09d0f5fc4a))
|
40
|
+
* Perform GC before running each test ([84c895e](https://github.com/applandinc/appmap-ruby/commit/84c895e95fe8caa270cc1412e239599bfcc1b467))
|
41
|
+
|
1
42
|
## [0.66.2](https://github.com/applandinc/appmap-ruby/compare/v0.66.1...v0.66.2) (2021-10-07)
|
2
43
|
|
3
44
|
|
@@ -0,0 +1,16 @@
|
|
1
|
+
- method: OpenSSL::PKey::PKey#sign
|
2
|
+
label: crypto.pkey
|
3
|
+
- methods:
|
4
|
+
- OpenSSL::X509::Request#sign
|
5
|
+
- OpenSSL::X509::Request#verify
|
6
|
+
label: crypto.x509
|
7
|
+
- method: OpenSSL::X509::Certificate#sign
|
8
|
+
label: crypto.x509
|
9
|
+
- methods:
|
10
|
+
- OpenSSL::PKCS5#pbkdf2_hmac
|
11
|
+
- OpenSSL::PKCS5#pbkdf2_hmac_sha1
|
12
|
+
label: crypto.pkcs5
|
13
|
+
- method: OpenSSL::Cipher#encrypt
|
14
|
+
label: crypto.encrypt
|
15
|
+
- method: OpenSSL::Cipher#decrypt
|
16
|
+
label: crypto.decrypt
|
@@ -10,7 +10,14 @@ module AppMap
|
|
10
10
|
|
11
11
|
class Validate < ValidateStruct
|
12
12
|
def perform
|
13
|
-
|
13
|
+
schema_path = File.expand_path('../../../../../config-schema.yml', __FILE__)
|
14
|
+
schema = YAML.safe_load(File.read(schema_path))
|
15
|
+
result = {
|
16
|
+
version: 2,
|
17
|
+
errors: config_validator.valid? ? [] : config_validator.violations.map(&:to_h),
|
18
|
+
schema: schema
|
19
|
+
}
|
20
|
+
puts JSON.pretty_generate(result)
|
14
21
|
end
|
15
22
|
|
16
23
|
private
|
data/lib/appmap/config.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'pathname'
|
3
4
|
require 'set'
|
4
5
|
require 'yaml'
|
5
6
|
require 'appmap/util'
|
@@ -11,17 +12,20 @@ require 'appmap/depends/configuration'
|
|
11
12
|
|
12
13
|
module AppMap
|
13
14
|
class Config
|
14
|
-
# Specifies a code
|
15
|
+
# Specifies a logical code package be mapped.
|
16
|
+
# This can be a project source folder, a Gem, or a builtin.
|
17
|
+
#
|
15
18
|
# Options:
|
16
19
|
#
|
20
|
+
# * +path+ indicates a relative path to a code folder.
|
17
21
|
# * +gem+ may indicate a gem name that "owns" the path
|
18
|
-
# * +
|
22
|
+
# * +require_name+ can be used to make sure that the code is required so that it can be loaded. This is generally used with
|
19
23
|
# builtins, or when the path to be required is not automatically required when bundler requires the gem.
|
20
24
|
# * +exclude+ can be used used to exclude sub-paths. Generally not used with +gem+.
|
21
25
|
# * +labels+ is used to apply labels to matching code. This is really only useful when the package will be applied to
|
22
26
|
# specific functions, via TargetMethods.
|
23
27
|
# * +shallow+ indicates shallow mapping, in which only the entrypoint to a gem is recorded.
|
24
|
-
Package = Struct.new(:path, :gem, :
|
28
|
+
Package = Struct.new(:name, :path, :gem, :require_name, :exclude, :labels, :shallow, :builtin) do
|
25
29
|
# This is for internal use only.
|
26
30
|
private_methods :gem
|
27
31
|
|
@@ -43,20 +47,24 @@ module AppMap
|
|
43
47
|
class << self
|
44
48
|
# Builds a package for a path, such as `app/models` in a Rails app. Generally corresponds to a `path:` entry
|
45
49
|
# in appmap.yml. Also used for mapping specific methods via TargetMethods.
|
46
|
-
def build_from_path(path, shallow: false,
|
47
|
-
Package.new(path, nil,
|
50
|
+
def build_from_path(path, shallow: false, require_name: nil, exclude: [], labels: [])
|
51
|
+
Package.new(path, path, nil, require_name, exclude, labels, shallow)
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_from_builtin(path, shallow: false, require_name: nil, exclude: [], labels: [])
|
55
|
+
Package.new(path, path, nil, require_name, exclude, labels, shallow, true)
|
48
56
|
end
|
49
57
|
|
50
58
|
# Builds a package for gem. Generally corresponds to a `gem:` entry in appmap.yml. Also used when mapping
|
51
59
|
# a builtin.
|
52
|
-
def build_from_gem(gem, shallow: true,
|
60
|
+
def build_from_gem(gem, shallow: true, require_name: nil, exclude: [], labels: [], optional: false, force: false)
|
53
61
|
if !force && %w[method_source activesupport].member?(gem)
|
54
62
|
warn "WARNING: #{gem} cannot be AppMapped because it is a dependency of the appmap gem"
|
55
63
|
return
|
56
64
|
end
|
57
65
|
path = gem_path(gem, optional)
|
58
66
|
if path
|
59
|
-
Package.new(path, gem,
|
67
|
+
Package.new(gem, path, gem, require_name, exclude, labels, shallow)
|
60
68
|
else
|
61
69
|
AppMap::Util.startup_message "#{gem} is not available in the bundle"
|
62
70
|
end
|
@@ -74,19 +82,16 @@ module AppMap
|
|
74
82
|
end
|
75
83
|
end
|
76
84
|
|
77
|
-
def name
|
78
|
-
gem || path
|
79
|
-
end
|
80
|
-
|
81
85
|
def to_h
|
82
86
|
{
|
87
|
+
name: name,
|
83
88
|
path: path,
|
84
|
-
package_name: package_name,
|
85
89
|
gem: gem,
|
86
|
-
|
90
|
+
require_name: require_name,
|
91
|
+
handler_class: handler_class ? handler_class.name : nil,
|
87
92
|
exclude: Util.blank?(exclude) ? nil : exclude,
|
88
93
|
labels: Util.blank?(labels) ? nil : labels,
|
89
|
-
shallow: shallow
|
94
|
+
shallow: shallow.nil? ? nil : shallow,
|
90
95
|
}.compact
|
91
96
|
end
|
92
97
|
end
|
@@ -96,12 +101,12 @@ module AppMap
|
|
96
101
|
attr_reader :method_names, :package
|
97
102
|
|
98
103
|
def initialize(method_names, package)
|
99
|
-
@method_names = method_names
|
104
|
+
@method_names = Array(method_names).map(&:to_sym)
|
100
105
|
@package = package
|
101
106
|
end
|
102
107
|
|
103
108
|
def include_method?(method_name)
|
104
|
-
|
109
|
+
method_names.include?(method_name.to_sym)
|
105
110
|
end
|
106
111
|
|
107
112
|
def to_h
|
@@ -110,6 +115,8 @@ module AppMap
|
|
110
115
|
method_names: method_names
|
111
116
|
}
|
112
117
|
end
|
118
|
+
|
119
|
+
alias as_json to_h
|
113
120
|
end
|
114
121
|
private_constant :TargetMethods
|
115
122
|
|
@@ -117,11 +124,11 @@ module AppMap
|
|
117
124
|
# entry in appmap.yml. When the Config is initialized, each Function is converted into
|
118
125
|
# a Package and TargetMethods. It's called a Function rather than a Method, because Function
|
119
126
|
# is the AppMap terminology.
|
120
|
-
Function = Struct.new(:package, :cls, :labels, :function_names, :builtin, :
|
127
|
+
Function = Struct.new(:package, :cls, :labels, :function_names, :builtin, :require_name) do # :nodoc:
|
121
128
|
def to_h
|
122
129
|
{
|
123
130
|
package: package,
|
124
|
-
|
131
|
+
require_name: require_name,
|
125
132
|
class: cls,
|
126
133
|
labels: labels,
|
127
134
|
functions: function_names.map(&:to_sym),
|
@@ -138,9 +145,15 @@ module AppMap
|
|
138
145
|
private_constant :MethodHook
|
139
146
|
|
140
147
|
class << self
|
141
|
-
def package_hooks(
|
148
|
+
def package_hooks(methods, path: nil, gem: nil, force: false, builtin: false, handler_class: nil, require_name: nil)
|
142
149
|
Array(methods).map do |method|
|
143
|
-
package =
|
150
|
+
package = if builtin
|
151
|
+
Package.build_from_builtin(path, require_name: require_name, labels: method.labels, shallow: false)
|
152
|
+
elsif gem
|
153
|
+
Package.build_from_gem(gem, require_name: require_name, labels: method.labels, shallow: false, force: force, optional: true)
|
154
|
+
elsif path
|
155
|
+
Package.build_from_path(path, require_name: require_name, labels: method.labels, shallow: false)
|
156
|
+
end
|
144
157
|
next unless package
|
145
158
|
|
146
159
|
package.handler_class = handler_class if handler_class
|
@@ -151,87 +164,107 @@ module AppMap
|
|
151
164
|
def method_hook(cls, method_names, labels)
|
152
165
|
MethodHook.new(cls, method_names, labels)
|
153
166
|
end
|
167
|
+
|
168
|
+
def declare_hook(hook_decl)
|
169
|
+
hook_decl = YAML.load(hook_decl) if hook_decl.is_a?(String)
|
170
|
+
|
171
|
+
methods_decl = hook_decl['methods'] || hook_decl['method']
|
172
|
+
methods_decl = Array(methods_decl) unless methods_decl.is_a?(Hash)
|
173
|
+
labels_decl = Array(hook_decl['labels'] || hook_decl['label'])
|
174
|
+
|
175
|
+
methods = methods_decl.map do |name|
|
176
|
+
class_name, method_name, static = name.include?('.') ? name.split('.', 2) + [ true ] : name.split('#', 2) + [ false ]
|
177
|
+
method_hook class_name, [ method_name ], labels_decl
|
178
|
+
end
|
179
|
+
|
180
|
+
require_name = hook_decl['require_name']
|
181
|
+
gem_name = hook_decl['gem']
|
182
|
+
path = hook_decl['path']
|
183
|
+
builtin = hook_decl['builtin']
|
184
|
+
|
185
|
+
options = {
|
186
|
+
builtin: builtin,
|
187
|
+
gem: gem_name,
|
188
|
+
path: path,
|
189
|
+
require_name: require_name || gem_name || path,
|
190
|
+
force: hook_decl['force']
|
191
|
+
}.compact
|
192
|
+
|
193
|
+
handler_class = hook_decl['handler_class']
|
194
|
+
options[:handler_class] = Util::class_from_string(handler_class) if handler_class
|
195
|
+
|
196
|
+
package_hooks(methods, **options)
|
197
|
+
end
|
198
|
+
|
199
|
+
def declare_hook_deprecated(hook_decl)
|
200
|
+
function_name = hook_decl['name']
|
201
|
+
package, cls, functions = []
|
202
|
+
if function_name
|
203
|
+
package, cls, _, function = Util.parse_function_name(function_name)
|
204
|
+
functions = Array(function)
|
205
|
+
else
|
206
|
+
package = hook_decl['package']
|
207
|
+
cls = hook_decl['class']
|
208
|
+
functions = hook_decl['function'] || hook_decl['functions']
|
209
|
+
raise %q(AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions') unless package && cls && functions
|
210
|
+
end
|
211
|
+
|
212
|
+
functions = Array(functions).map(&:to_sym)
|
213
|
+
labels = hook_decl['label'] || hook_decl['labels']
|
214
|
+
req = hook_decl['require']
|
215
|
+
builtin = hook_decl['builtin']
|
216
|
+
|
217
|
+
package_options = {}
|
218
|
+
package_options[:labels] = Array(labels).map(&:to_s) if labels if labels
|
219
|
+
package_options[:require_name] = req
|
220
|
+
package_options[:require_name] ||= package if builtin
|
221
|
+
tm = TargetMethods.new(functions, Package.build_from_path(package, **package_options))
|
222
|
+
ClassTargetMethods.new(cls, tm)
|
223
|
+
end
|
224
|
+
|
225
|
+
def builtin_hooks_path
|
226
|
+
[ [ __dir__, 'builtin_hooks' ].join('/') ] + ( ENV['APPMAP_BUILTIN_HOOKS_PATH'] || '').split(/[;:]/)
|
227
|
+
end
|
228
|
+
|
229
|
+
def gem_hooks_path
|
230
|
+
[ [ __dir__, 'gem_hooks' ].join('/') ] + ( ENV['APPMAP_GEM_HOOKS_PATH'] || '').split(/[;:]/)
|
231
|
+
end
|
232
|
+
|
233
|
+
def load_hooks
|
234
|
+
loader = lambda do |dir, &block|
|
235
|
+
basename = dir.split('/').compact.join('/')
|
236
|
+
[].tap do |hooks|
|
237
|
+
Dir.glob(Pathname.new(dir).join('**').join('*.yml').to_s).each do |yaml_file|
|
238
|
+
path = yaml_file[basename.length + 1...-4]
|
239
|
+
YAML.load(File.read(yaml_file)).map do |config|
|
240
|
+
block.call path, config
|
241
|
+
config
|
242
|
+
end.each do |config|
|
243
|
+
hooks << declare_hook(config)
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end.compact
|
247
|
+
end
|
248
|
+
|
249
|
+
builtin_hooks = builtin_hooks_path.map do |path|
|
250
|
+
loader.(path) do |path, config|
|
251
|
+
config['path'] = path
|
252
|
+
config['builtin'] = true
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
gem_hooks = gem_hooks_path.map do |path|
|
257
|
+
loader.(path) do |path, config|
|
258
|
+
config['gem'] = path
|
259
|
+
config['builtin'] = false
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
(builtin_hooks + gem_hooks).flatten
|
264
|
+
end
|
154
265
|
end
|
155
266
|
|
156
|
-
|
157
|
-
# predefined labels specified here. If any of these hooks are not desired, they can be disabled in the +exclude+ section
|
158
|
-
# of appmap.yml.
|
159
|
-
METHOD_HOOKS = [
|
160
|
-
package_hooks('actionview',
|
161
|
-
[
|
162
|
-
method_hook('ActionView::Renderer', :render, %w[mvc.view]),
|
163
|
-
method_hook('ActionView::TemplateRenderer', :render, %w[mvc.view]),
|
164
|
-
method_hook('ActionView::PartialRenderer', :render, %w[mvc.view])
|
165
|
-
],
|
166
|
-
handler_class: AppMap::Handler::Rails::Template::RenderHandler,
|
167
|
-
package_name: 'action_view'
|
168
|
-
),
|
169
|
-
package_hooks('actionview',
|
170
|
-
[
|
171
|
-
method_hook('ActionView::Resolver', %i[find_all find_all_anywhere], %w[mvc.template.resolver])
|
172
|
-
],
|
173
|
-
handler_class: AppMap::Handler::Rails::Template::ResolverHandler,
|
174
|
-
package_name: 'action_view'
|
175
|
-
),
|
176
|
-
package_hooks('actionpack',
|
177
|
-
[
|
178
|
-
method_hook('ActionDispatch::Request::Session', %i[[] dig values fetch], %w[http.session.read]),
|
179
|
-
method_hook('ActionDispatch::Request::Session', %i[destroy []= clear update delete merge], %w[http.session.write]),
|
180
|
-
method_hook('ActionDispatch::Cookies::CookieJar', %i[[] fetch], %w[http.session.read]),
|
181
|
-
method_hook('ActionDispatch::Cookies::CookieJar', %i[[]= clear update delete recycle], %w[http.session.write]),
|
182
|
-
method_hook('ActionDispatch::Cookies::EncryptedCookieJar', %i[[]= clear update delete recycle], %w[http.cookie crypto.encrypt])
|
183
|
-
],
|
184
|
-
package_name: 'action_dispatch'
|
185
|
-
),
|
186
|
-
package_hooks('cancancan',
|
187
|
-
[
|
188
|
-
method_hook('CanCan::ControllerAdditions', %i[authorize! can? cannot?], %w[security.authorization]),
|
189
|
-
method_hook('CanCan::Ability', %i[authorize?], %w[security.authorization])
|
190
|
-
]
|
191
|
-
),
|
192
|
-
package_hooks('actionpack',
|
193
|
-
[
|
194
|
-
method_hook('ActionController::Instrumentation', %i[process_action send_file send_data redirect_to], %w[mvc.controller])
|
195
|
-
],
|
196
|
-
package_name: 'action_controller'
|
197
|
-
)
|
198
|
-
].flatten.freeze
|
199
|
-
|
200
|
-
OPENSSL_PACKAGES = ->(labels) { Package.build_from_path('openssl', package_name: 'openssl', labels: labels) }
|
201
|
-
|
202
|
-
# Hook functions which are builtin to Ruby. Because they are builtins, they may be loaded before appmap.
|
203
|
-
# Therefore, we can't rely on TracePoint to report the loading of this code.
|
204
|
-
BUILTIN_HOOKS = {
|
205
|
-
'OpenSSL::PKey::PKey' => TargetMethods.new(:sign, OPENSSL_PACKAGES.(%w[crypto.pkey])),
|
206
|
-
'OpenSSL::X509::Request' => TargetMethods.new(%i[sign verify], OPENSSL_PACKAGES.(%w[crypto.x509])),
|
207
|
-
'OpenSSL::PKCS5' => TargetMethods.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGES.(%w[crypto.pkcs5])),
|
208
|
-
'OpenSSL::Cipher' => [
|
209
|
-
TargetMethods.new(%i[encrypt], OPENSSL_PACKAGES.(%w[crypto.encrypt])),
|
210
|
-
TargetMethods.new(%i[decrypt], OPENSSL_PACKAGES.(%w[crypto.decrypt]))
|
211
|
-
],
|
212
|
-
'ActiveSupport::Callbacks::CallbackSequence' => [
|
213
|
-
TargetMethods.new(:invoke_before, Package.build_from_gem('activesupport', force: true, package_name: 'active_support', labels: %w[mvc.before_action])),
|
214
|
-
TargetMethods.new(:invoke_after, Package.build_from_gem('activesupport', force: true, package_name: 'active_support', labels: %w[mvc.after_action])),
|
215
|
-
],
|
216
|
-
'ActiveSupport::SecurityUtils' => TargetMethods.new(:secure_compare, Package.build_from_gem('activesupport', force: true, package_name: 'active_support/security_utils', labels: %w[crypto.secure_compare])),
|
217
|
-
'OpenSSL::X509::Certificate' => TargetMethods.new(:sign, OPENSSL_PACKAGES.(%w[crypto.x509])),
|
218
|
-
'Net::HTTP' => TargetMethods.new(:request, Package.build_from_path('net/http', package_name: 'net/http', labels: %w[protocol.http]).tap do |package|
|
219
|
-
package.handler_class = AppMap::Handler::NetHTTP
|
220
|
-
end),
|
221
|
-
'Net::SMTP' => TargetMethods.new(:send, Package.build_from_path('net/smtp', package_name: 'net/smtp', labels: %w[protocol.email.smtp])),
|
222
|
-
'Net::POP3' => TargetMethods.new(:mails, Package.build_from_path('net/pop3', package_name: 'net/pop', labels: %w[protocol.email.pop])),
|
223
|
-
# This is happening: Method send_command not found on Net::IMAP
|
224
|
-
# 'Net::IMAP' => TargetMethods.new(:send_command, Package.build_from_path('net/imap', package_name: 'net/imap', labels: %w[protocol.email.imap])),
|
225
|
-
# 'Marshal' => TargetMethods.new(%i[dump load], Package.build_from_path('marshal', labels: %w[format.marshal])),
|
226
|
-
'Psych' => [
|
227
|
-
TargetMethods.new(%i[load load_stream parse parse_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml.parse])),
|
228
|
-
TargetMethods.new(%i[dump dump_stream], Package.build_from_path('yaml', package_name: 'psych', labels: %w[format.yaml.generate])),
|
229
|
-
],
|
230
|
-
'JSON::Ext::Parser' => TargetMethods.new(:parse, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.parse])),
|
231
|
-
'JSON::Ext::Generator::State' => TargetMethods.new(:generate, Package.build_from_path('json', package_name: 'json', labels: %w[format.json.generate])),
|
232
|
-
}.freeze
|
233
|
-
|
234
|
-
attr_reader :name, :appmap_dir, :packages, :exclude, :swagger_config, :depends_config, :hooked_methods, :builtin_hooks
|
267
|
+
attr_reader :name, :appmap_dir, :packages, :exclude, :swagger_config, :depends_config, :gem_hooks, :builtin_hooks
|
235
268
|
|
236
269
|
def initialize(name,
|
237
270
|
packages: [],
|
@@ -246,31 +279,22 @@ module AppMap
|
|
246
279
|
@depends_config = depends_config
|
247
280
|
@hook_paths = Set.new(packages.map(&:path))
|
248
281
|
@exclude = exclude
|
249
|
-
@builtin_hooks = BUILTIN_HOOKS.dup
|
250
282
|
@functions = functions
|
251
283
|
|
252
|
-
@
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
package_options[:labels] = func.labels if func.labels
|
259
|
-
package_options[:package_name] = func.package_name
|
260
|
-
package_options[:package_name] ||= func.package if func.builtin
|
261
|
-
hook = TargetMethods.new(func.function_names, Package.build_from_path(func.package, **package_options))
|
262
|
-
if func.builtin
|
263
|
-
@builtin_hooks[func.cls] ||= []
|
264
|
-
@builtin_hooks[func.cls] << hook
|
284
|
+
@builtin_hooks = Hash.new { |h,k| h[k] = [] }
|
285
|
+
@gem_hooks = Hash.new { |h,k| h[k] = [] }
|
286
|
+
|
287
|
+
(functions + self.class.load_hooks).each_with_object(Hash.new { |h,k| h[k] = [] }) do |cls_target_methods, gem_hooks|
|
288
|
+
hooks = if cls_target_methods.target_methods.package.builtin
|
289
|
+
@builtin_hooks
|
265
290
|
else
|
266
|
-
@
|
291
|
+
@gem_hooks
|
267
292
|
end
|
293
|
+
hooks[cls_target_methods.cls] << cls_target_methods.target_methods
|
268
294
|
end
|
269
295
|
|
270
|
-
@
|
271
|
-
Array(hooks).
|
272
|
-
@hook_paths << hook.package.path
|
273
|
-
end
|
296
|
+
@gem_hooks.each_value do |hooks|
|
297
|
+
@hook_paths += Array(hooks).map { |hook| hook.package.path }.compact
|
274
298
|
end
|
275
299
|
end
|
276
300
|
|
@@ -334,24 +358,15 @@ module AppMap
|
|
334
358
|
}.compact
|
335
359
|
|
336
360
|
if config_data['functions']
|
337
|
-
config_params[:functions] = config_data['functions'].map do |
|
338
|
-
|
339
|
-
|
340
|
-
if function_name
|
341
|
-
package, cls, _, function = Util.parse_function_name(function_name)
|
342
|
-
functions = Array(function)
|
361
|
+
config_params[:functions] = config_data['functions'].map do |hook_decl|
|
362
|
+
if hook_decl['name'] || hook_decl['package']
|
363
|
+
declare_hook_deprecated(hook_decl)
|
343
364
|
else
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
raise %q(AppMap config 'function' element should specify 'package', 'class' and 'function' or 'functions') unless package && cls && functions
|
365
|
+
# Support the same syntax within the 'functions' that's used for externalized
|
366
|
+
# hook config.
|
367
|
+
declare_hook(hook_decl)
|
348
368
|
end
|
349
|
-
|
350
|
-
functions = Array(functions).map(&:to_sym)
|
351
|
-
labels = function_data['label'] || function_data['labels']
|
352
|
-
labels = Array(labels).map(&:to_s) if labels
|
353
|
-
Function.new(package, cls, labels, functions, function_data['builtin'], function_data['require'])
|
354
|
-
end
|
369
|
+
end.flatten
|
355
370
|
end
|
356
371
|
|
357
372
|
config_params[:packages] = \
|
@@ -366,7 +381,11 @@ module AppMap
|
|
366
381
|
shallow = package['shallow']
|
367
382
|
# shallow is true by default for gems
|
368
383
|
shallow = true if shallow.nil?
|
369
|
-
|
384
|
+
|
385
|
+
require_name = \
|
386
|
+
package['package'] || #deprecated
|
387
|
+
package['require_name']
|
388
|
+
Package.build_from_gem(gem, require_name: require_name, exclude: package['exclude'] || [], shallow: shallow)
|
370
389
|
else
|
371
390
|
Package.build_from_path(path, exclude: package['exclude'] || [], shallow: package['shallow'])
|
372
391
|
end
|
@@ -417,8 +436,8 @@ module AppMap
|
|
417
436
|
|
418
437
|
# Hook a method which is specified by class and method name.
|
419
438
|
def package_for_code_object
|
420
|
-
|
421
|
-
|
439
|
+
class_name = cls.to_s.index('#<Class:') == 0 ? cls.to_s['#<Class:'.length...-1] : cls.name
|
440
|
+
Array(config.gem_hooks[class_name])
|
422
441
|
.find { |hook| hook.include_method?(method.name) }
|
423
442
|
&.package
|
424
443
|
end
|
data/lib/appmap/event.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'weakref'
|
4
|
+
|
3
5
|
module AppMap
|
4
6
|
module Event
|
5
7
|
@@id_counter = 0
|
@@ -111,10 +113,12 @@ module AppMap
|
|
111
113
|
rescue NoMethodError
|
112
114
|
begin
|
113
115
|
value.inspect
|
114
|
-
rescue
|
116
|
+
rescue
|
115
117
|
last_resort_string.call
|
116
118
|
end
|
117
|
-
rescue
|
119
|
+
rescue WeakRef::RefError
|
120
|
+
nil
|
121
|
+
rescue
|
118
122
|
last_resort_string.call
|
119
123
|
end
|
120
124
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
- methods:
|
2
|
+
- ActionDispatch::Request::Session#[]
|
3
|
+
- ActionDispatch::Request::Session#dig
|
4
|
+
- ActionDispatch::Request::Session#values
|
5
|
+
- ActionDispatch::Request::Session#fetch
|
6
|
+
- ActionDispatch::Cookies::CookieJar#[]
|
7
|
+
- ActionDispatch::Cookies::CookieJar#fetch
|
8
|
+
label: http.session.read
|
9
|
+
require_name: action_dispatch
|
10
|
+
- methods:
|
11
|
+
- ActionDispatch::Request::Session#destroy
|
12
|
+
- ActionDispatch::Request::Session#[]=
|
13
|
+
- ActionDispatch::Request::Session#clear
|
14
|
+
- ActionDispatch::Request::Session#update
|
15
|
+
- ActionDispatch::Request::Session#delete
|
16
|
+
- ActionDispatch::Request::Session#merge
|
17
|
+
- ActionDispatch::Cookies::CookieJar#[]=
|
18
|
+
- ActionDispatch::Cookies::CookieJar#clear
|
19
|
+
- ActionDispatch::Cookies::CookieJar#update
|
20
|
+
- ActionDispatch::Cookies::CookieJar#delete
|
21
|
+
- ActionDispatch::Cookies::CookieJar#recycle!
|
22
|
+
label: http.session.write
|
23
|
+
require_name: action_dispatch
|
24
|
+
- methods:
|
25
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#[]=
|
26
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#clear
|
27
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#update
|
28
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#delete
|
29
|
+
- ActionDispatch::Cookies::EncryptedCookieJar#recycle
|
30
|
+
labels:
|
31
|
+
- http.cookie
|
32
|
+
- crypto.encrypt
|
33
|
+
require_name: action_dispatch
|
34
|
+
- methods:
|
35
|
+
- ActionController::Instrumentation#process_action
|
36
|
+
- ActionController::Instrumentation#send_file
|
37
|
+
- ActionController::Instrumentation#send_data
|
38
|
+
- ActionController::Instrumentation#redirect_to
|
39
|
+
label: mvc.controller
|
40
|
+
require_name: action_controller
|
@@ -0,0 +1,13 @@
|
|
1
|
+
- methods:
|
2
|
+
- ActionView::Renderer#render
|
3
|
+
- ActionView::TemplateRenderer#render
|
4
|
+
- ActionView::PartialRenderer#render
|
5
|
+
label: mvc.view
|
6
|
+
handler_class: AppMap::Handler::Rails::Template::RenderHandler
|
7
|
+
require_name: action_view
|
8
|
+
- methods:
|
9
|
+
- ActionView::Resolver#find_all
|
10
|
+
- ActionView::Resolver#find_all_anywhere
|
11
|
+
label: mvc.template.resolver
|
12
|
+
handler_class: AppMap::Handler::Rails::Template::ResolverHandler
|
13
|
+
require_name: action_view
|
@@ -0,0 +1,12 @@
|
|
1
|
+
- method: ActiveSupport::Callbacks::CallbackSequence#invoke_before
|
2
|
+
label: mvc.before_action
|
3
|
+
require_name: active_support
|
4
|
+
force: true
|
5
|
+
- method: ActiveSupport::Callbacks::CallbackSequence#invoke_after
|
6
|
+
label: mvc.after_action
|
7
|
+
require_name: active_support
|
8
|
+
force: true
|
9
|
+
- method: ActiveSupport::SecurityUtils#secure_compare
|
10
|
+
label: crypto.secure_compare
|
11
|
+
require_name: active_support/security_utils
|
12
|
+
force: true
|