appmap 0.25.2 → 0.28.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/.gitignore +1 -2
- data/CHANGELOG.md +30 -0
- data/README.md +123 -39
- data/exe/appmap +3 -57
- data/lib/appmap.rb +51 -32
- data/lib/appmap/algorithm/stats.rb +2 -1
- data/lib/appmap/class_map.rb +1 -1
- data/lib/appmap/command/record.rb +2 -61
- data/lib/appmap/cucumber.rb +89 -0
- data/lib/appmap/event.rb +6 -6
- data/lib/appmap/hook.rb +27 -13
- data/lib/appmap/metadata.rb +62 -0
- data/lib/appmap/middleware/remote_recording.rb +2 -7
- data/lib/appmap/rails/action_handler.rb +2 -2
- data/lib/appmap/rails/sql_handler.rb +2 -2
- data/lib/appmap/railtie.rb +2 -2
- data/lib/appmap/rspec.rb +20 -38
- data/lib/appmap/trace.rb +11 -11
- data/lib/appmap/util.rb +40 -0
- data/lib/appmap/version.rb +1 -1
- data/package-lock.json +3 -3
- data/spec/abstract_controller4_base_spec.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +1 -1
- data/spec/fixtures/hook/singleton_method.rb +54 -0
- data/spec/fixtures/rails_users_app/Gemfile +1 -0
- data/spec/fixtures/rails_users_app/features/api_users.feature +13 -0
- data/spec/fixtures/rails_users_app/features/support/env.rb +4 -0
- data/spec/fixtures/rails_users_app/features/support/hooks.rb +11 -0
- data/spec/fixtures/rails_users_app/features/support/steps.rb +18 -0
- data/spec/hook_spec.rb +107 -23
- data/spec/rails_spec_helper.rb +2 -0
- data/spec/rspec_feature_metadata_spec.rb +2 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/util_spec.rb +21 -0
- data/test/cli_test.rb +2 -15
- data/test/cucumber_test.rb +72 -0
- data/test/fixtures/cucumber4_recorder/Gemfile +5 -0
- data/test/fixtures/cucumber4_recorder/appmap.yml +3 -0
- data/test/fixtures/cucumber4_recorder/features/say_hello.feature +5 -0
- data/test/fixtures/cucumber4_recorder/features/support/env.rb +5 -0
- data/test/fixtures/cucumber4_recorder/features/support/hooks.rb +11 -0
- data/test/fixtures/cucumber4_recorder/features/support/steps.rb +9 -0
- data/test/fixtures/cucumber4_recorder/lib/hello.rb +7 -0
- data/test/fixtures/cucumber_recorder/Gemfile +5 -0
- data/test/fixtures/cucumber_recorder/appmap.yml +3 -0
- data/test/fixtures/cucumber_recorder/features/say_hello.feature +5 -0
- data/test/fixtures/cucumber_recorder/features/support/env.rb +5 -0
- data/test/fixtures/cucumber_recorder/features/support/hooks.rb +11 -0
- data/test/fixtures/cucumber_recorder/features/support/steps.rb +9 -0
- data/test/fixtures/cucumber_recorder/lib/hello.rb +7 -0
- data/test/fixtures/rspec_recorder/Gemfile +1 -1
- data/test/fixtures/rspec_recorder/spec/decorated_hello_spec.rb +12 -0
- data/test/rspec_test.rb +5 -0
- metadata +26 -4
- data/lib/appmap/command/upload.rb +0 -101
- data/spec/fixtures/hook/class_method.rb +0 -17
data/lib/appmap/rspec.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'appmap/util'
|
4
|
+
|
3
5
|
module AppMap
|
4
6
|
# Integration of AppMap with RSpec. When enabled with APPMAP=true, the AppMap tracer will
|
5
7
|
# be activated around each scenario which has the metadata key `:appmap`.
|
@@ -8,10 +10,7 @@ module AppMap
|
|
8
10
|
LOG = false
|
9
11
|
|
10
12
|
def self.metadata
|
11
|
-
|
12
|
-
@metadata ||= AppMap::Command::Record.detect_metadata
|
13
|
-
@metadata.freeze
|
14
|
-
@metadata.dup
|
13
|
+
AppMap.detect_metadata
|
15
14
|
end
|
16
15
|
|
17
16
|
module FeatureAnnotations
|
@@ -107,7 +106,17 @@ module AppMap
|
|
107
106
|
|
108
107
|
def parent
|
109
108
|
# An example group always has a parent; but it might be 'self'...
|
110
|
-
|
109
|
+
|
110
|
+
# DEPRECATION WARNING: `Module#parent` has been renamed to `module_parent`. `parent` is deprecated and will be
|
111
|
+
# removed in Rails 6.1. (called from parent at /Users/kgilpin/source/appland/appmap-ruby/lib/appmap/rspec.rb:110)
|
112
|
+
example_group_parent = \
|
113
|
+
if example_group.respond_to?(:module_parent)
|
114
|
+
example_group.module_parent
|
115
|
+
else
|
116
|
+
example_group.parent
|
117
|
+
end
|
118
|
+
|
119
|
+
example_group_parent != example_group ? ScopeExampleGroup.new(example_group_parent) : nil
|
111
120
|
end
|
112
121
|
end
|
113
122
|
|
@@ -129,7 +138,7 @@ module AppMap
|
|
129
138
|
|
130
139
|
AppMap::RSpec.add_event_methods @trace.event_methods
|
131
140
|
|
132
|
-
class_map = AppMap.class_map(
|
141
|
+
class_map = AppMap.class_map(@trace.event_methods)
|
133
142
|
|
134
143
|
description = []
|
135
144
|
scope = ScopeExample.new(example)
|
@@ -180,7 +189,6 @@ module AppMap
|
|
180
189
|
end
|
181
190
|
|
182
191
|
@recordings_by_example = {}
|
183
|
-
@config = nil
|
184
192
|
@event_methods = Set.new
|
185
193
|
|
186
194
|
class << self
|
@@ -188,10 +196,6 @@ module AppMap
|
|
188
196
|
warn 'Configuring AppMap recorder for RSpec'
|
189
197
|
|
190
198
|
FileUtils.mkdir_p APPMAP_OUTPUT_DIR
|
191
|
-
|
192
|
-
require 'appmap/hook'
|
193
|
-
@config = AppMap.configure
|
194
|
-
AppMap::Hook.hook(@config)
|
195
199
|
end
|
196
200
|
|
197
201
|
def begin_spec(example)
|
@@ -214,9 +218,9 @@ module AppMap
|
|
214
218
|
end
|
215
219
|
|
216
220
|
def save(example_name, class_map, events: nil, feature_name: nil, feature_group_name: nil, labels: nil)
|
217
|
-
metadata = RSpec.metadata.
|
221
|
+
metadata = RSpec.metadata.tap do |m|
|
218
222
|
m[:name] = example_name
|
219
|
-
m[:app] =
|
223
|
+
m[:app] = AppMap.configuration.name
|
220
224
|
m[:feature] = feature_name if feature_name
|
221
225
|
m[:feature_group] = feature_group_name if feature_group_name
|
222
226
|
m[:labels] = labels if labels
|
@@ -236,13 +240,13 @@ module AppMap
|
|
236
240
|
classMap: class_map,
|
237
241
|
events: events
|
238
242
|
}.compact
|
239
|
-
fname =
|
243
|
+
fname = AppMap::Util.scenario_filename(example_name)
|
240
244
|
|
241
|
-
File.write(File.join(APPMAP_OUTPUT_DIR,
|
245
|
+
File.write(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
|
242
246
|
end
|
243
247
|
|
244
248
|
def print_inventory
|
245
|
-
class_map = AppMap.class_map(@
|
249
|
+
class_map = AppMap.class_map(@event_methods)
|
246
250
|
save 'Inventory', class_map, labels: %w[inventory]
|
247
251
|
end
|
248
252
|
|
@@ -256,28 +260,6 @@ module AppMap
|
|
256
260
|
print_inventory
|
257
261
|
end
|
258
262
|
end
|
259
|
-
|
260
|
-
private
|
261
|
-
|
262
|
-
# Cribbed from v5 version of ActiveSupport:Inflector#parameterize:
|
263
|
-
# https://github.com/rails/rails/blob/v5.2.4/activesupport/lib/active_support/inflector/transliterate.rb#L92
|
264
|
-
def sanitize_filename(fname, separator: '_')
|
265
|
-
# Replace accented chars with their ASCII equivalents.
|
266
|
-
fname = fname.encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
|
267
|
-
|
268
|
-
# Turn unwanted chars into the separator.
|
269
|
-
fname.gsub!(/[^a-z0-9\-_]+/i, separator)
|
270
|
-
|
271
|
-
re_sep = Regexp.escape(separator)
|
272
|
-
re_duplicate_separator = /#{re_sep}{2,}/
|
273
|
-
re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
|
274
|
-
|
275
|
-
# No more than one of the separator in a row.
|
276
|
-
fname.gsub!(re_duplicate_separator, separator)
|
277
|
-
|
278
|
-
# Finally, Remove leading/trailing separator.
|
279
|
-
fname.gsub(re_leading_trailing_separator, '')
|
280
|
-
end
|
281
263
|
end
|
282
264
|
end
|
283
265
|
end
|
data/lib/appmap/trace.rb
CHANGED
@@ -2,38 +2,38 @@
|
|
2
2
|
|
3
3
|
module AppMap
|
4
4
|
module Trace
|
5
|
-
ScopedMethod = Struct.new(:defined_class, :method)
|
5
|
+
ScopedMethod = Struct.new(:defined_class, :method, :static)
|
6
6
|
|
7
|
-
class
|
7
|
+
class Tracing
|
8
8
|
def initialize
|
9
|
-
@
|
9
|
+
@Tracing = []
|
10
10
|
end
|
11
11
|
|
12
12
|
def empty?
|
13
|
-
@
|
13
|
+
@Tracing.empty?
|
14
14
|
end
|
15
15
|
|
16
16
|
def trace(enable: true)
|
17
17
|
Tracer.new.tap do |tracer|
|
18
|
-
@
|
18
|
+
@Tracing << tracer
|
19
19
|
tracer.enable if enable
|
20
20
|
end
|
21
|
-
end
|
21
|
+
end
|
22
22
|
|
23
23
|
def enabled?
|
24
|
-
@
|
24
|
+
@Tracing.any?(&:enabled?)
|
25
25
|
end
|
26
26
|
|
27
27
|
def record_event(event, defined_class: nil, method: nil)
|
28
|
-
@
|
28
|
+
@Tracing.each do |tracer|
|
29
29
|
tracer.record_event(event, defined_class: defined_class, method: method)
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
33
|
def delete(tracer)
|
34
|
-
return unless @
|
34
|
+
return unless @Tracing.member?(tracer)
|
35
35
|
|
36
|
-
@
|
36
|
+
@Tracing.delete(tracer)
|
37
37
|
tracer.disable
|
38
38
|
end
|
39
39
|
end
|
@@ -67,7 +67,7 @@ module AppMap
|
|
67
67
|
return unless @enabled
|
68
68
|
|
69
69
|
@events << event
|
70
|
-
@methods << Trace::ScopedMethod.new(defined_class, method) if defined_class && method
|
70
|
+
@methods << Trace::ScopedMethod.new(defined_class, method, event.static) if (defined_class && method && event.event == :call)
|
71
71
|
end
|
72
72
|
|
73
73
|
# Gets a unique list of the methods that were invoked by the program.
|
data/lib/appmap/util.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AppMap
|
4
|
+
module Util
|
5
|
+
class << self
|
6
|
+
# scenario_filename builds a suitable file name from a scenario name.
|
7
|
+
# Special characters are removed, and the file name is truncated to fit within
|
8
|
+
# shell limitations.
|
9
|
+
def scenario_filename(name, max_length: 255, separator: '_', extension: '.appmap.json')
|
10
|
+
# Cribbed from v5 version of ActiveSupport:Inflector#parameterize:
|
11
|
+
# https://github.com/rails/rails/blob/v5.2.4/activesupport/lib/active_support/inflector/transliterate.rb#L92
|
12
|
+
# Replace accented chars with their ASCII equivalents.
|
13
|
+
|
14
|
+
fname = name.encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
|
15
|
+
|
16
|
+
# Turn unwanted chars into the separator.
|
17
|
+
fname.gsub!(/[^a-z0-9\-_]+/i, separator)
|
18
|
+
|
19
|
+
re_sep = Regexp.escape(separator)
|
20
|
+
re_duplicate_separator = /#{re_sep}{2,}/
|
21
|
+
re_leading_trailing_separator = /^#{re_sep}|#{re_sep}$/i
|
22
|
+
|
23
|
+
# No more than one of the separator in a row.
|
24
|
+
fname.gsub!(re_duplicate_separator, separator)
|
25
|
+
|
26
|
+
# Finally, Remove leading/trailing separator.
|
27
|
+
fname.gsub!(re_leading_trailing_separator, '')
|
28
|
+
|
29
|
+
if (fname.length + extension.length) > max_length
|
30
|
+
require 'base64'
|
31
|
+
require 'digest'
|
32
|
+
fname_digest = Base64.urlsafe_encode64 Digest::MD5.digest(fname), padding: false
|
33
|
+
fname[max_length - fname_digest.length - extension.length - 1..-1] = [ '-', fname_digest ].join
|
34
|
+
end
|
35
|
+
|
36
|
+
[ fname, extension ].join
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/appmap/version.rb
CHANGED
data/package-lock.json
CHANGED
@@ -551,9 +551,9 @@
|
|
551
551
|
}
|
552
552
|
},
|
553
553
|
"lodash": {
|
554
|
-
"version": "4.17.
|
555
|
-
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.
|
556
|
-
"integrity": "sha512-
|
554
|
+
"version": "4.17.19",
|
555
|
+
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz",
|
556
|
+
"integrity": "sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ=="
|
557
557
|
},
|
558
558
|
"longest": {
|
559
559
|
"version": "1.0.1",
|
@@ -57,8 +57,8 @@ describe 'AbstractControllerBase' do
|
|
57
57
|
method_id: build_user
|
58
58
|
path: app/controllers/api/users_controller.rb
|
59
59
|
lineno: 23
|
60
|
-
static: false
|
61
60
|
thread_id: .*
|
61
|
+
static: false
|
62
62
|
parameters:
|
63
63
|
- name: params
|
64
64
|
class: ActiveSupport::HashWithIndifferentAccess
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class SingletonMethod
|
4
|
+
class << self
|
5
|
+
def say_default
|
6
|
+
'default'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def SingletonMethod.say_class_defined
|
11
|
+
'defined with explicit class scope'
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.say_self_defined
|
15
|
+
'defined with self class scope'
|
16
|
+
end
|
17
|
+
|
18
|
+
# When called, do_include calls +include+ to bring in the module
|
19
|
+
# AddMethod. AddMethod defines a new instance method, which gets
|
20
|
+
# added to the singleton class of SingletonMethod.
|
21
|
+
def do_include
|
22
|
+
class << self
|
23
|
+
SingletonMethod.include(AddMethod)
|
24
|
+
end
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.new_with_instance_method
|
29
|
+
SingletonMethod.new.tap do |m|
|
30
|
+
def m.say_instance_defined
|
31
|
+
'defined for an instance'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_s
|
37
|
+
'Singleton Method fixture'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module AddMethod
|
42
|
+
def self.included(base)
|
43
|
+
base.module_eval do
|
44
|
+
define_method "added_method" do
|
45
|
+
_added_method
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def _added_method
|
51
|
+
'defined by including a module'
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
@@ -39,6 +39,7 @@ appmap_options = \
|
|
39
39
|
gem 'appmap', appmap_options
|
40
40
|
|
41
41
|
group :development, :test do
|
42
|
+
gem 'cucumber-rails', require: false
|
42
43
|
gem 'rspec-rails'
|
43
44
|
# Required for Sequel, since without ActiveRecord, the Rails transactional fixture support
|
44
45
|
# isn't activated.
|
@@ -0,0 +1,13 @@
|
|
1
|
+
Feature: /api/users
|
2
|
+
|
3
|
+
@appmap-disable
|
4
|
+
Scenario: A user can be created
|
5
|
+
When I create a user
|
6
|
+
Then the response status should be 201
|
7
|
+
|
8
|
+
Scenario: When a user is created, it should be in the user list
|
9
|
+
Given I create a user
|
10
|
+
And the response status should be 201
|
11
|
+
When I list the users
|
12
|
+
Then the response status should be 200
|
13
|
+
And the response should include the user
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
When 'I create a user' do
|
4
|
+
@response = post '/api/users', login: 'alice'
|
5
|
+
end
|
6
|
+
|
7
|
+
Then(/the response status should be (\d+)/) do |status|
|
8
|
+
expect(@response.status).to eq(status.to_i)
|
9
|
+
end
|
10
|
+
|
11
|
+
When 'I list the users' do
|
12
|
+
@response = get '/api/users'
|
13
|
+
@users = JSON.parse(@response.body)
|
14
|
+
end
|
15
|
+
|
16
|
+
Then 'the response should include the user' do
|
17
|
+
expect(@users.map { |u| u['login'] }).to include('alice')
|
18
|
+
end
|
data/spec/hook_spec.rb
CHANGED
@@ -5,6 +5,16 @@ require 'appmap/hook'
|
|
5
5
|
require 'appmap/event'
|
6
6
|
require 'diffy'
|
7
7
|
|
8
|
+
# Show nulls as the literal +null+, rather than just leaving the field
|
9
|
+
# empty. This make some of the expected YAML below easier to
|
10
|
+
# understand.
|
11
|
+
module ShowYamlNulls
|
12
|
+
def visit_NilClass(o)
|
13
|
+
@emitter.scalar('null', nil, 'tag:yaml.org,2002:null', true, false, Psych::Nodes::Scalar::ANY)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
Psych::Visitors::YAMLTree.prepend(ShowYamlNulls)
|
17
|
+
|
8
18
|
describe 'AppMap class Hooking' do
|
9
19
|
def collect_events(tracer)
|
10
20
|
[].tap do |events|
|
@@ -30,24 +40,28 @@ describe 'AppMap class Hooking' do
|
|
30
40
|
end.to_yaml
|
31
41
|
end
|
32
42
|
|
33
|
-
def invoke_test_file(file, &block)
|
43
|
+
def invoke_test_file(file, setup: nil, &block)
|
44
|
+
AppMap.configuration = nil
|
34
45
|
package = AppMap::Hook::Package.new(file, [])
|
35
46
|
config = AppMap::Hook::Config.new('hook_spec', [ package ])
|
47
|
+
AppMap.configuration = config
|
36
48
|
AppMap::Hook.hook(config)
|
49
|
+
|
50
|
+
setup_result = setup.call if setup
|
37
51
|
|
38
52
|
tracer = AppMap.tracing.trace
|
39
53
|
AppMap::Event.reset_id_counter
|
40
54
|
begin
|
41
55
|
load file
|
42
|
-
yield
|
56
|
+
yield setup_result
|
43
57
|
ensure
|
44
58
|
AppMap.tracing.delete(tracer)
|
45
59
|
end
|
46
60
|
[ config, tracer ]
|
47
61
|
end
|
48
62
|
|
49
|
-
def test_hook_behavior(file, events_yaml, &block)
|
50
|
-
config, tracer = invoke_test_file(file, &block)
|
63
|
+
def test_hook_behavior(file, events_yaml, setup: nil, &block)
|
64
|
+
config, tracer = invoke_test_file(file, setup: setup, &block)
|
51
65
|
|
52
66
|
events = collect_events(tracer)
|
53
67
|
expect(Diffy::Diff.new(events, events_yaml).to_s).to eq('')
|
@@ -55,6 +69,10 @@ describe 'AppMap class Hooking' do
|
|
55
69
|
[ config, tracer ]
|
56
70
|
end
|
57
71
|
|
72
|
+
after do
|
73
|
+
AppMap.configuration = nil
|
74
|
+
end
|
75
|
+
|
58
76
|
it 'hooks an instance method that takes no arguments' do
|
59
77
|
events_yaml = <<~YAML
|
60
78
|
---
|
@@ -90,10 +108,10 @@ describe 'AppMap class Hooking' do
|
|
90
108
|
end
|
91
109
|
|
92
110
|
it 'builds a class map of invoked methods' do
|
93
|
-
|
111
|
+
_, tracer = invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
|
94
112
|
InstanceMethod.new.say_default
|
95
113
|
end
|
96
|
-
class_map = AppMap.class_map(
|
114
|
+
class_map = AppMap.class_map(tracer.event_methods).to_yaml
|
97
115
|
expect(Diffy::Diff.new(class_map, <<~YAML).to_s).to eq('')
|
98
116
|
---
|
99
117
|
- :name: spec/fixtures/hook/instance_method.rb
|
@@ -202,7 +220,7 @@ describe 'AppMap class Hooking' do
|
|
202
220
|
:parameters:
|
203
221
|
- :name: :kw
|
204
222
|
:class: NilClass
|
205
|
-
:value:
|
223
|
+
:value: null
|
206
224
|
:kind: :key
|
207
225
|
:receiver:
|
208
226
|
:class: InstanceMethod
|
@@ -232,7 +250,7 @@ describe 'AppMap class Hooking' do
|
|
232
250
|
:parameters:
|
233
251
|
- :name: :block
|
234
252
|
:class: NilClass
|
235
|
-
:value:
|
253
|
+
:value: null
|
236
254
|
:kind: :block
|
237
255
|
:receiver:
|
238
256
|
:class: InstanceMethod
|
@@ -254,15 +272,15 @@ describe 'AppMap class Hooking' do
|
|
254
272
|
---
|
255
273
|
- :id: 1
|
256
274
|
:event: :call
|
257
|
-
:defined_class:
|
275
|
+
:defined_class: SingletonMethod
|
258
276
|
:method_id: say_default
|
259
|
-
:path: spec/fixtures/hook/
|
277
|
+
:path: spec/fixtures/hook/singleton_method.rb
|
260
278
|
:lineno: 5
|
261
279
|
:static: true
|
262
280
|
:parameters: []
|
263
281
|
:receiver:
|
264
282
|
:class: Class
|
265
|
-
:value:
|
283
|
+
:value: SingletonMethod
|
266
284
|
- :id: 2
|
267
285
|
:event: :return
|
268
286
|
:parent_id: 1
|
@@ -270,8 +288,8 @@ describe 'AppMap class Hooking' do
|
|
270
288
|
:class: String
|
271
289
|
:value: default
|
272
290
|
YAML
|
273
|
-
test_hook_behavior 'spec/fixtures/hook/
|
274
|
-
expect(
|
291
|
+
test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml do
|
292
|
+
expect(SingletonMethod.say_default).to eq('default')
|
275
293
|
end
|
276
294
|
end
|
277
295
|
|
@@ -280,15 +298,15 @@ describe 'AppMap class Hooking' do
|
|
280
298
|
---
|
281
299
|
- :id: 1
|
282
300
|
:event: :call
|
283
|
-
:defined_class:
|
301
|
+
:defined_class: SingletonMethod
|
284
302
|
:method_id: say_class_defined
|
285
|
-
:path: spec/fixtures/hook/
|
303
|
+
:path: spec/fixtures/hook/singleton_method.rb
|
286
304
|
:lineno: 10
|
287
305
|
:static: true
|
288
306
|
:parameters: []
|
289
307
|
:receiver:
|
290
308
|
:class: Class
|
291
|
-
:value:
|
309
|
+
:value: SingletonMethod
|
292
310
|
- :id: 2
|
293
311
|
:event: :return
|
294
312
|
:parent_id: 1
|
@@ -296,8 +314,8 @@ describe 'AppMap class Hooking' do
|
|
296
314
|
:class: String
|
297
315
|
:value: defined with explicit class scope
|
298
316
|
YAML
|
299
|
-
test_hook_behavior 'spec/fixtures/hook/
|
300
|
-
expect(
|
317
|
+
test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml do
|
318
|
+
expect(SingletonMethod.say_class_defined).to eq('defined with explicit class scope')
|
301
319
|
end
|
302
320
|
end
|
303
321
|
|
@@ -306,15 +324,15 @@ describe 'AppMap class Hooking' do
|
|
306
324
|
---
|
307
325
|
- :id: 1
|
308
326
|
:event: :call
|
309
|
-
:defined_class:
|
327
|
+
:defined_class: SingletonMethod
|
310
328
|
:method_id: say_self_defined
|
311
|
-
:path: spec/fixtures/hook/
|
329
|
+
:path: spec/fixtures/hook/singleton_method.rb
|
312
330
|
:lineno: 14
|
313
331
|
:static: true
|
314
332
|
:parameters: []
|
315
333
|
:receiver:
|
316
334
|
:class: Class
|
317
|
-
:value:
|
335
|
+
:value: SingletonMethod
|
318
336
|
- :id: 2
|
319
337
|
:event: :return
|
320
338
|
:parent_id: 1
|
@@ -322,11 +340,77 @@ describe 'AppMap class Hooking' do
|
|
322
340
|
:class: String
|
323
341
|
:value: defined with self class scope
|
324
342
|
YAML
|
325
|
-
test_hook_behavior 'spec/fixtures/hook/
|
326
|
-
expect(
|
343
|
+
test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml do
|
344
|
+
expect(SingletonMethod.say_self_defined).to eq('defined with self class scope')
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
|
349
|
+
it 'hooks an included method' do
|
350
|
+
events_yaml = <<~YAML
|
351
|
+
---
|
352
|
+
- :id: 1
|
353
|
+
:event: :call
|
354
|
+
:defined_class: SingletonMethod
|
355
|
+
:method_id: added_method
|
356
|
+
:path: spec/fixtures/hook/singleton_method.rb
|
357
|
+
:lineno: 44
|
358
|
+
:static: false
|
359
|
+
:parameters: []
|
360
|
+
:receiver:
|
361
|
+
:class: SingletonMethod
|
362
|
+
:value: Singleton Method fixture
|
363
|
+
- :id: 2
|
364
|
+
:event: :call
|
365
|
+
:defined_class: AddMethod
|
366
|
+
:method_id: _added_method
|
367
|
+
:path: spec/fixtures/hook/singleton_method.rb
|
368
|
+
:lineno: 50
|
369
|
+
:static: false
|
370
|
+
:parameters: []
|
371
|
+
:receiver:
|
372
|
+
:class: SingletonMethod
|
373
|
+
:value: Singleton Method fixture
|
374
|
+
- :id: 3
|
375
|
+
:event: :return
|
376
|
+
:parent_id: 2
|
377
|
+
:return_value:
|
378
|
+
:class: String
|
379
|
+
:value: defined by including a module
|
380
|
+
- :id: 4
|
381
|
+
:event: :return
|
382
|
+
:parent_id: 1
|
383
|
+
:return_value:
|
384
|
+
:class: String
|
385
|
+
:value: defined by including a module
|
386
|
+
YAML
|
387
|
+
|
388
|
+
load 'spec/fixtures/hook/singleton_method.rb'
|
389
|
+
setup = -> { SingletonMethod.new.do_include }
|
390
|
+
test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml, setup: setup do |s|
|
391
|
+
expect(s.added_method).to eq('defined by including a module')
|
327
392
|
end
|
328
393
|
end
|
329
394
|
|
395
|
+
it "doesn't hook a singleton method defined for an instance" do
|
396
|
+
# Ideally, Ruby would fire a TracePoint event when a singleton
|
397
|
+
# class gets created by defining a method on an instance. It
|
398
|
+
# currently doesn't, though, so there's no way for us to hook such
|
399
|
+
# a method.
|
400
|
+
#
|
401
|
+
# This example will fail if Ruby's behavior changes at some point
|
402
|
+
# in the future.
|
403
|
+
events_yaml = <<~YAML
|
404
|
+
--- []
|
405
|
+
YAML
|
406
|
+
|
407
|
+
load 'spec/fixtures/hook/singleton_method.rb'
|
408
|
+
setup = -> { SingletonMethod.new_with_instance_method }
|
409
|
+
test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml, setup: setup do |s|
|
410
|
+
expect(s.say_instance_defined).to eq('defined for an instance')
|
411
|
+
end
|
412
|
+
end
|
413
|
+
|
330
414
|
it 'Reports exceptions' do
|
331
415
|
events_yaml = <<~YAML
|
332
416
|
---
|