appmap 0.28.1 → 0.34.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 +32 -1
- data/README.md +54 -2
- data/Rakefile +1 -1
- data/appmap.gemspec +2 -0
- data/lib/appmap.rb +25 -14
- data/lib/appmap/class_map.rb +25 -27
- data/lib/appmap/config.rb +115 -0
- data/lib/appmap/cucumber.rb +19 -2
- data/lib/appmap/event.rb +25 -16
- data/lib/appmap/hook.rb +89 -139
- data/lib/appmap/hook/method.rb +83 -0
- data/lib/appmap/metadata.rb +1 -1
- data/lib/appmap/minitest.rb +141 -0
- data/lib/appmap/open.rb +57 -0
- data/lib/appmap/rails/action_handler.rb +7 -7
- data/lib/appmap/rails/sql_handler.rb +10 -8
- data/lib/appmap/record.rb +27 -0
- data/lib/appmap/rspec.rb +2 -2
- data/lib/appmap/trace.rb +16 -8
- data/lib/appmap/util.rb +19 -0
- data/lib/appmap/version.rb +1 -1
- data/spec/abstract_controller4_base_spec.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +9 -2
- data/spec/config_spec.rb +3 -3
- data/spec/fixtures/hook/compare.rb +7 -0
- data/spec/fixtures/hook/instance_method.rb +4 -0
- data/spec/hook_spec.rb +222 -37
- data/spec/open_spec.rb +19 -0
- data/spec/record_sql_rails_pg_spec.rb +56 -33
- data/spec/util_spec.rb +1 -1
- data/test/cli_test.rb +12 -2
- data/test/fixtures/minitest_recorder/Gemfile +5 -0
- data/test/fixtures/minitest_recorder/appmap.yml +3 -0
- data/test/fixtures/minitest_recorder/lib/hello.rb +5 -0
- data/test/fixtures/minitest_recorder/test/hello_test.rb +12 -0
- data/test/fixtures/openssl_recorder/Gemfile +3 -0
- data/test/fixtures/openssl_recorder/appmap.yml +3 -0
- data/test/fixtures/openssl_recorder/lib/openssl_cert_sign.rb +94 -0
- data/test/fixtures/openssl_recorder/lib/openssl_encrypt.rb +34 -0
- data/test/fixtures/openssl_recorder/lib/openssl_key_sign.rb +28 -0
- data/test/fixtures/process_recorder/appmap.yml +3 -0
- data/test/fixtures/process_recorder/hello.rb +9 -0
- data/test/minitest_test.rb +38 -0
- data/test/openssl_test.rb +203 -0
- data/test/record_process_test.rb +35 -0
- data/test/test_helper.rb +1 -0
- metadata +51 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39576d59d2648a009f888fa9c5720d8fdb91b04fcb4d08949a05a0a5c453d0c8
|
4
|
+
data.tar.gz: 8b1d5442421d9dd37c3db4fa0329321e6b6e6ebc87e94bb67be3da9b3f1467b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 40bc1a630b65a83f7516085ec4cd25a72f83e574a9c2f150f8e302c9756c376a1fc820d490393d9625aeb3f5b781ca44d34431efe2722a7a0031b49334ccbca7
|
7
|
+
data.tar.gz: 9d32eb02deebdfb16e99ee0f157f5c9de6cb77cafa46233867e0173577609d4390b98a675f67c11868d57370066666bf9f9da39b2ee2ab25ba16cbaf4ebcd8ca
|
data/CHANGELOG.md
CHANGED
@@ -1,7 +1,38 @@
|
|
1
|
+
# v0.34.1
|
2
|
+
* Ensure that capturing events doesn't change the behavior of a hooked method that uses
|
3
|
+
`Time.now`. For example, if a test expects that `Time.now` will be called a certain
|
4
|
+
number of times by a hooked method, that expectation will now be met.
|
5
|
+
* Make sure `appmap/cucumber` requires `appmap`.
|
6
|
+
|
7
|
+
# v0.34.0
|
8
|
+
|
9
|
+
* Records builtin security and I/O methods from `OpenSSL`, `Net`, and `IO`.
|
10
|
+
|
11
|
+
# v0.33.0
|
12
|
+
|
13
|
+
* Added command `AppMap.open` to open an AppMap in the browser.
|
14
|
+
|
15
|
+
# v0.32.0
|
16
|
+
|
17
|
+
* Removes un-necessary fields from `return` events.
|
18
|
+
|
19
|
+
# v0.31.0
|
20
|
+
|
21
|
+
* Add the ability to hook methods by default, and optionally add labels to them in the
|
22
|
+
classmap. Use it to hook `ActiveSupport::SecurityUtils.secure_compare`.
|
23
|
+
|
24
|
+
# v0.30.0
|
25
|
+
|
26
|
+
* Add support for Minitest.
|
27
|
+
|
28
|
+
# v0.29.0
|
29
|
+
|
30
|
+
* Add `lib/appmap/record.rb`, which can be `require`d to record the rest of the process.
|
31
|
+
|
1
32
|
# v0.28.1
|
33
|
+
|
2
34
|
* Fix the `defined_class` recorded in an appmap for an instance method included in a class
|
3
35
|
at runtime.
|
4
|
-
|
5
36
|
* Only include the `static` attribute on `call` events in an appmap. Determine its value
|
6
37
|
based on the receiver of the method call.
|
7
38
|
|
data/README.md
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
- [Configuration](#configuration)
|
4
4
|
- [Running](#running)
|
5
5
|
- [RSpec](#rspec)
|
6
|
+
- [Minitest](#minitest)
|
6
7
|
- [Cucumber](#cucumber)
|
7
8
|
- [Remote recording](#remote-recording)
|
8
9
|
- [Ruby on Rails](#ruby-on-rails)
|
@@ -32,7 +33,7 @@ There are several ways to record AppMaps of your Ruby program using the `appmap`
|
|
32
33
|
|
33
34
|
Once you have recorded some AppMaps (for example, by running RSpec tests), you use the `appland upload` command
|
34
35
|
to upload them to the AppLand server. This command, and some others, is provided
|
35
|
-
by the [AppLand CLI](https://github.com/applandinc/appland-cli/releases)
|
36
|
+
by the [AppLand CLI](https://github.com/applandinc/appland-cli/releases).
|
36
37
|
Then, on the [AppLand website](https://app.land), you can
|
37
38
|
visualize the design of your code and share links with collaborators.
|
38
39
|
|
@@ -86,12 +87,25 @@ Each entry in the `packages` list is a YAML object which has the following keys:
|
|
86
87
|
|
87
88
|
To record RSpec tests, follow these additional steps:
|
88
89
|
|
89
|
-
1) Require `appmap/rspec` in your `spec_helper.rb
|
90
|
+
1) Require `appmap/rspec` in your `spec_helper.rb` before any other classes are loaded.
|
90
91
|
|
91
92
|
```ruby
|
92
93
|
require 'appmap/rspec'
|
93
94
|
```
|
94
95
|
|
96
|
+
Note that `spec_helper.rb` in a Rails project typically loads the application's classes this way:
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
require File.expand_path("../../config/environment", __FILE__)
|
100
|
+
```
|
101
|
+
|
102
|
+
and `appmap/rspec` must be required before this:
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
require 'appmap/rspec'
|
106
|
+
require File.expand_path("../../config/environment", __FILE__)
|
107
|
+
```
|
108
|
+
|
95
109
|
2) *Optional* Add `feature: '<feature name>'` and `feature_group: '<feature group name>'` annotations to your
|
96
110
|
examples.
|
97
111
|
|
@@ -125,6 +139,42 @@ If you include the `feature` and `feature_group` metadata, these attributes will
|
|
125
139
|
|
126
140
|
If you don't explicitly declare `feature` and `feature_group`, then they will be inferred from the spec name and example descriptions.
|
127
141
|
|
142
|
+
## Minitest
|
143
|
+
|
144
|
+
To record Minitest tests, follow these additional steps:
|
145
|
+
|
146
|
+
1) Require `appmap/minitest` in `test_helper.rb`
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
require 'appmap/minitest'
|
150
|
+
```
|
151
|
+
|
152
|
+
Note that `test_helper.rb` in a Rails project typically loads the application's classes this way:
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
require_relative '../config/environment'
|
156
|
+
```
|
157
|
+
|
158
|
+
and `appmap/rspec` must be required before this:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
require 'appmap/rspec'
|
162
|
+
require_relative '../config/environment'
|
163
|
+
```
|
164
|
+
|
165
|
+
2) Run the tests with the environment variable `APPMAP=true`:
|
166
|
+
|
167
|
+
```sh-session
|
168
|
+
$ APPMAP=true bundle exec -Ilib -Itest test/*
|
169
|
+
```
|
170
|
+
|
171
|
+
Each Minitest test will output an AppMap file into the directory `tmp/appmap/minitest`. For example:
|
172
|
+
|
173
|
+
```
|
174
|
+
$ find tmp/appmap/minitest
|
175
|
+
Hello_says_hello_when_prompted.appmap.json
|
176
|
+
```
|
177
|
+
|
128
178
|
## Cucumber
|
129
179
|
|
130
180
|
To record Cucumber tests, follow these additional steps:
|
@@ -135,6 +185,8 @@ To record Cucumber tests, follow these additional steps:
|
|
135
185
|
require 'appmap/cucumber'
|
136
186
|
```
|
137
187
|
|
188
|
+
Be sure to require it before `config/environment` is required.
|
189
|
+
|
138
190
|
2) Create an `Around` hook in `support/hooks.rb` to record the scenario:
|
139
191
|
|
140
192
|
|
data/Rakefile
CHANGED
data/appmap.gemspec
CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_dependency 'faraday'
|
27
27
|
spec.add_dependency 'gli'
|
28
28
|
spec.add_dependency 'parser'
|
29
|
+
spec.add_dependency 'rack'
|
29
30
|
|
30
31
|
spec.add_development_dependency 'bundler', '~> 1.16'
|
31
32
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
@@ -41,4 +42,5 @@ Gem::Specification.new do |spec|
|
|
41
42
|
spec.add_development_dependency 'rspec'
|
42
43
|
spec.add_development_dependency 'selenium-webdriver'
|
43
44
|
spec.add_development_dependency 'webdrivers', '~> 4.0'
|
45
|
+
spec.add_development_dependency 'timecop'
|
44
46
|
end
|
data/lib/appmap.rb
CHANGED
@@ -8,19 +8,26 @@ rescue NameError
|
|
8
8
|
end
|
9
9
|
|
10
10
|
require 'appmap/version'
|
11
|
+
require 'appmap/hook'
|
12
|
+
require 'appmap/config'
|
13
|
+
require 'appmap/trace'
|
14
|
+
require 'appmap/class_map'
|
15
|
+
require 'appmap/metadata'
|
16
|
+
require 'appmap/util'
|
17
|
+
require 'appmap/open'
|
11
18
|
|
12
19
|
module AppMap
|
13
20
|
class << self
|
14
21
|
@configuration = nil
|
15
22
|
@configuration_file_path = nil
|
16
23
|
|
17
|
-
#
|
24
|
+
# Gets the configuration. If there is no configuration, the default
|
18
25
|
# configuration is initialized.
|
19
26
|
def configuration
|
20
|
-
@configuration ||=
|
27
|
+
@configuration ||= initialize
|
21
28
|
end
|
22
29
|
|
23
|
-
#
|
30
|
+
# Sets the configuration. This is only expected to happen once per
|
24
31
|
# Ruby process.
|
25
32
|
def configuration=(config)
|
26
33
|
warn 'AppMap is already configured' if @configuration && config
|
@@ -28,24 +35,24 @@ module AppMap
|
|
28
35
|
@configuration = config
|
29
36
|
end
|
30
37
|
|
31
|
-
#
|
38
|
+
# Configures AppMap for recording. Default behavior is to configure from "appmap.yml".
|
32
39
|
# This method also activates the code hooks which record function calls as trace events.
|
33
40
|
# Call this function before the program code is loaded by the Ruby VM, otherwise
|
34
41
|
# the load events won't be seen and the hooks won't activate.
|
35
42
|
def initialize(config_file_path = 'appmap.yml')
|
36
43
|
warn "Configuring AppMap from path #{config_file_path}"
|
37
|
-
|
38
|
-
|
39
|
-
|
44
|
+
Config.load_from_file(config_file_path).tap do |configuration|
|
45
|
+
self.configuration = configuration
|
46
|
+
Hook.new(configuration).enable
|
47
|
+
end
|
40
48
|
end
|
41
49
|
|
42
|
-
#
|
50
|
+
# Used to start tracing, stop tracing, and record events.
|
43
51
|
def tracing
|
44
|
-
require 'appmap/trace'
|
45
52
|
@tracing ||= Trace::Tracing.new
|
46
53
|
end
|
47
54
|
|
48
|
-
#
|
55
|
+
# Records the events which occur while processing a block,
|
49
56
|
# and returns an AppMap as a Hash.
|
50
57
|
def record
|
51
58
|
tracer = tracing.trace
|
@@ -66,16 +73,20 @@ module AppMap
|
|
66
73
|
}
|
67
74
|
end
|
68
75
|
|
69
|
-
#
|
76
|
+
# Uploads an AppMap to the AppLand website and displays it.
|
77
|
+
def open(appmap = nil, &block)
|
78
|
+
appmap ||= AppMap.record(&block)
|
79
|
+
AppMap::Open.new(appmap).perform
|
80
|
+
end
|
81
|
+
|
82
|
+
# Builds a class map from a config and a list of Ruby methods.
|
70
83
|
def class_map(methods)
|
71
|
-
require 'appmap/class_map'
|
72
84
|
ClassMap.build_from_methods(configuration, methods)
|
73
85
|
end
|
74
86
|
|
75
|
-
#
|
87
|
+
# Returns default metadata detected from the Ruby system and from the
|
76
88
|
# filesystem.
|
77
89
|
def detect_metadata
|
78
|
-
require 'appmap/metadata'
|
79
90
|
@metadata ||= Metadata.detect.freeze
|
80
91
|
@metadata.deep_dup
|
81
92
|
end
|
data/lib/appmap/class_map.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'active_support/core_ext'
|
4
|
-
|
5
3
|
module AppMap
|
6
4
|
class ClassMap
|
7
5
|
module HasChildren
|
@@ -50,7 +48,7 @@ module AppMap
|
|
50
48
|
end
|
51
49
|
end
|
52
50
|
Function = Struct.new(:name) do
|
53
|
-
attr_accessor :static, :location
|
51
|
+
attr_accessor :static, :location, :labels
|
54
52
|
|
55
53
|
def type
|
56
54
|
'function'
|
@@ -61,8 +59,9 @@ module AppMap
|
|
61
59
|
name: name,
|
62
60
|
type: type,
|
63
61
|
location: location,
|
64
|
-
static: static
|
65
|
-
|
62
|
+
static: static,
|
63
|
+
labels: labels
|
64
|
+
}.delete_if { |k,v| v.nil? || v == [] }
|
66
65
|
end
|
67
66
|
end
|
68
67
|
end
|
@@ -71,35 +70,21 @@ module AppMap
|
|
71
70
|
def build_from_methods(config, methods)
|
72
71
|
root = Types::Root.new
|
73
72
|
methods.each do |method|
|
74
|
-
package = package_for_method(
|
75
|
-
|
73
|
+
package = config.package_for_method(method) \
|
74
|
+
or raise "No package found for method #{method}"
|
75
|
+
add_function root, package, method
|
76
76
|
end
|
77
77
|
root.children.map(&:to_h)
|
78
78
|
end
|
79
79
|
|
80
80
|
protected
|
81
81
|
|
82
|
-
def
|
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
|
-
|
82
|
+
def add_function(root, package, method)
|
98
83
|
static = method.static
|
99
84
|
|
100
85
|
object_infos = [
|
101
86
|
{
|
102
|
-
name:
|
87
|
+
name: package.path,
|
103
88
|
type: 'package'
|
104
89
|
}
|
105
90
|
]
|
@@ -109,12 +94,25 @@ module AppMap
|
|
109
94
|
type: 'class'
|
110
95
|
}
|
111
96
|
end
|
112
|
-
|
113
|
-
name: method.
|
97
|
+
function_info = {
|
98
|
+
name: method.name,
|
114
99
|
type: 'function',
|
115
|
-
location: [ location_file, lineno ].join(':'),
|
116
100
|
static: static
|
117
101
|
}
|
102
|
+
location = method.source_location
|
103
|
+
|
104
|
+
function_info[:location] = \
|
105
|
+
if location
|
106
|
+
location_file, lineno = location
|
107
|
+
location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
|
108
|
+
[ location_file, lineno ].join(':')
|
109
|
+
else
|
110
|
+
[ method.defined_class, static ? '.' : '#', method.name ].join
|
111
|
+
end
|
112
|
+
|
113
|
+
function_info[:labels] = package.labels if package.labels
|
114
|
+
object_infos << function_info
|
115
|
+
|
118
116
|
parent = root
|
119
117
|
object_infos.each do |info|
|
120
118
|
parent = find_or_create parent.children, info do
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppMap
|
4
|
+
class Config
|
5
|
+
Package = Struct.new(:path, :package_name, :exclude, :labels) do
|
6
|
+
def initialize(path, package_name: nil, exclude: [], labels: [])
|
7
|
+
super path, package_name, exclude, labels
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_h
|
11
|
+
{
|
12
|
+
path: path,
|
13
|
+
package_name: package_name,
|
14
|
+
exclude: exclude.blank? ? nil : exclude,
|
15
|
+
labels: labels.blank? ? nil : labels
|
16
|
+
}.compact
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Hook = Struct.new(:method_names, :package) do
|
21
|
+
end
|
22
|
+
|
23
|
+
OPENSSL_PACKAGE = Package.new('openssl', package_name: 'openssl', labels: %w[security crypto])
|
24
|
+
|
25
|
+
# Methods that should always be hooked, with their containing
|
26
|
+
# package and labels that should be applied to them.
|
27
|
+
HOOKED_METHODS = {
|
28
|
+
'ActiveSupport::SecurityUtils' => Hook.new(:secure_compare, Package.new('active_support', package_name: 'active_support', labels: %w[security crypto]))
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
BUILTIN_METHODS = {
|
32
|
+
'OpenSSL::PKey::PKey' => Hook.new(:sign, OPENSSL_PACKAGE),
|
33
|
+
'Digest::Instance' => Hook.new(:digest, OPENSSL_PACKAGE),
|
34
|
+
'OpenSSL::X509::Request' => Hook.new(%i[sign verify], OPENSSL_PACKAGE),
|
35
|
+
'OpenSSL::PKCS5' => Hook.new(%i[pbkdf2_hmac_sha1 pbkdf2_hmac], OPENSSL_PACKAGE),
|
36
|
+
'OpenSSL::Cipher' => Hook.new(%i[encrypt decrypt final], OPENSSL_PACKAGE),
|
37
|
+
'OpenSSL::X509::Certificate' => Hook.new(:sign, OPENSSL_PACKAGE),
|
38
|
+
'Logger' => Hook.new(:add, Package.new('logger', labels: %w[log io])),
|
39
|
+
'Net::HTTP' => Hook.new(:request, Package.new('net/http', package_name: 'net/http', labels: %w[http io])),
|
40
|
+
'Net::SMTP' => Hook.new(:send, Package.new('net/smtp', package_name: 'net/smtp', labels: %w[smtp email io])),
|
41
|
+
'Net::POP3' => Hook.new(:mails, Package.new('net/pop3', package_name: 'net/pop', labels: %w[pop pop3 email io])),
|
42
|
+
'Net::IMAP' => Hook.new(:send_command, Package.new('net/imap', package_name: 'net/imap', labels: %w[imap email io])),
|
43
|
+
'IO' => Hook.new(%i[read write open close], Package.new('io', labels: %w[io])),
|
44
|
+
'Marshal' => Hook.new(%i[dump load], Package.new('marshal', labels: %w[serialization marshal])),
|
45
|
+
'Psych' => Hook.new(%i[dump dump_stream load load_stream parse parse_stream], Package.new('yaml', package_name: 'psych', labels: %w[serialization yaml])),
|
46
|
+
'JSON::Ext::Parser' => Hook.new(:parse, Package.new('json', package_name: 'json', labels: %w[serialization json])),
|
47
|
+
'JSON::Ext::Generator::State' => Hook.new(:generate, Package.new('json', package_name: 'json', labels: %w[serialization json]))
|
48
|
+
}.freeze
|
49
|
+
|
50
|
+
attr_reader :name, :packages
|
51
|
+
|
52
|
+
def initialize(name, packages = [])
|
53
|
+
@name = name
|
54
|
+
@packages = packages
|
55
|
+
end
|
56
|
+
|
57
|
+
class << self
|
58
|
+
# Loads configuration data from a file, specified by the file name.
|
59
|
+
def load_from_file(config_file_name)
|
60
|
+
require 'yaml'
|
61
|
+
load YAML.safe_load(::File.read(config_file_name))
|
62
|
+
end
|
63
|
+
|
64
|
+
# Loads configuration from a Hash.
|
65
|
+
def load(config_data)
|
66
|
+
packages = (config_data['packages'] || []).map do |package|
|
67
|
+
Package.new(package['path'], exclude: package['exclude'] || [])
|
68
|
+
end
|
69
|
+
Config.new config_data['name'], packages
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_h
|
74
|
+
{
|
75
|
+
name: name,
|
76
|
+
packages: packages.map(&:to_h)
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
def package_for_method(method)
|
81
|
+
defined_class, _, method_name = ::AppMap::Hook.qualify_method_name(method)
|
82
|
+
package = find_package(defined_class, method_name)
|
83
|
+
return package if package
|
84
|
+
|
85
|
+
location = method.source_location
|
86
|
+
location_file, = location
|
87
|
+
return unless location_file
|
88
|
+
|
89
|
+
location_file = location_file[Dir.pwd.length + 1..-1] if location_file.index(Dir.pwd) == 0
|
90
|
+
packages.find do |pkg|
|
91
|
+
(location_file.index(pkg.path) == 0) &&
|
92
|
+
!pkg.exclude.find { |p| location_file.index(p) }
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def included_by_location?(method)
|
97
|
+
!!package_for_method(method)
|
98
|
+
end
|
99
|
+
|
100
|
+
def always_hook?(defined_class, method_name)
|
101
|
+
!!find_package(defined_class, method_name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def find_package(defined_class, method_name)
|
105
|
+
hook = find_hook(defined_class)
|
106
|
+
return nil unless hook
|
107
|
+
|
108
|
+
Array(hook.method_names).include?(method_name) ? hook.package : nil
|
109
|
+
end
|
110
|
+
|
111
|
+
def find_hook(defined_class)
|
112
|
+
HOOKED_METHODS[defined_class] || BUILTIN_METHODS[defined_class]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|