appmap 0.34.0 → 0.35.0
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 -1
- data/.rbenv-gemsets +1 -0
- data/CHANGELOG.md +31 -0
- data/README.md +17 -2
- data/Rakefile +10 -3
- data/appmap.gemspec +5 -0
- data/ext/appmap/appmap.c +95 -0
- data/ext/appmap/extconf.rb +6 -0
- data/lib/appmap.rb +4 -1
- data/lib/appmap/class_map.rb +5 -6
- data/lib/appmap/config.rb +2 -2
- data/lib/appmap/cucumber.rb +19 -2
- data/lib/appmap/event.rb +28 -10
- data/lib/appmap/hook.rb +13 -20
- data/lib/appmap/hook/method.rb +59 -27
- data/lib/appmap/rails/request_handler.rb +88 -0
- data/lib/appmap/rails/sql_handler.rb +13 -10
- data/lib/appmap/railtie.rb +3 -5
- data/lib/appmap/rspec.rb +10 -0
- data/lib/appmap/trace.rb +9 -7
- data/lib/appmap/util.rb +18 -1
- data/lib/appmap/version.rb +1 -1
- data/spec/abstract_controller4_base_spec.rb +1 -1
- data/spec/abstract_controller_base_spec.rb +6 -0
- data/spec/fixtures/hook/instance_method.rb +4 -0
- data/spec/fixtures/hook/singleton_method.rb +21 -12
- data/spec/hook_spec.rb +85 -8
- metadata +50 -4
- data/lib/appmap/rails/action_handler.rb +0 -91
data/spec/hook_spec.rb
CHANGED
@@ -22,7 +22,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
22
22
|
while tracer.event?
|
23
23
|
events << tracer.next_event.to_h
|
24
24
|
end
|
25
|
-
end.map(&AppMap::Util.method(:sanitize_event))
|
25
|
+
end.map(&AppMap::Util.method(:sanitize_event))
|
26
26
|
end
|
27
27
|
|
28
28
|
def invoke_test_file(file, setup: nil, &block)
|
@@ -50,7 +50,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
50
50
|
def test_hook_behavior(file, events_yaml, setup: nil, &block)
|
51
51
|
config, tracer = invoke_test_file(file, setup: setup, &block)
|
52
52
|
|
53
|
-
events = collect_events(tracer)
|
53
|
+
events = collect_events(tracer).to_yaml
|
54
54
|
|
55
55
|
expect(Diffy::Diff.new(events_yaml, events).to_s).to eq('')
|
56
56
|
|
@@ -342,7 +342,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
342
342
|
:defined_class: SingletonMethod
|
343
343
|
:method_id: added_method
|
344
344
|
:path: spec/fixtures/hook/singleton_method.rb
|
345
|
-
:lineno:
|
345
|
+
:lineno: 21
|
346
346
|
:static: false
|
347
347
|
:parameters: []
|
348
348
|
:receiver:
|
@@ -350,10 +350,10 @@ describe 'AppMap class Hooking', docker: false do
|
|
350
350
|
:value: Singleton Method fixture
|
351
351
|
- :id: 2
|
352
352
|
:event: :call
|
353
|
-
:defined_class: AddMethod
|
353
|
+
:defined_class: SingletonMethod::AddMethod
|
354
354
|
:method_id: _added_method
|
355
355
|
:path: spec/fixtures/hook/singleton_method.rb
|
356
|
-
:lineno:
|
356
|
+
:lineno: 27
|
357
357
|
:static: false
|
358
358
|
:parameters: []
|
359
359
|
:receiver:
|
@@ -395,10 +395,44 @@ describe 'AppMap class Hooking', docker: false do
|
|
395
395
|
load 'spec/fixtures/hook/singleton_method.rb'
|
396
396
|
setup = -> { SingletonMethod.new_with_instance_method }
|
397
397
|
test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml, setup: setup do |s|
|
398
|
+
# Make sure we're testing the right thing
|
399
|
+
say_instance_defined = s.method(:say_instance_defined)
|
400
|
+
expect(say_instance_defined.owner.to_s).to start_with('#<Class:#<SingletonMethod:')
|
401
|
+
|
402
|
+
# Verify the native extension works as expected
|
403
|
+
expect(AppMap::Hook.singleton_method_owner_name(say_instance_defined)).to eq('SingletonMethod')
|
404
|
+
|
398
405
|
expect(s.say_instance_defined).to eq('defined for an instance')
|
399
406
|
end
|
400
407
|
end
|
401
408
|
|
409
|
+
it 'hooks a singleton method on an embedded struct' do
|
410
|
+
events_yaml = <<~YAML
|
411
|
+
---
|
412
|
+
- :id: 1
|
413
|
+
:event: :call
|
414
|
+
:defined_class: SingletonMethod::STRUCT_TEST
|
415
|
+
:method_id: say_struct_singleton
|
416
|
+
:path: spec/fixtures/hook/singleton_method.rb
|
417
|
+
:lineno: 52
|
418
|
+
:static: true
|
419
|
+
:parameters: []
|
420
|
+
:receiver:
|
421
|
+
:class: Class
|
422
|
+
:value: SingletonMethod::STRUCT_TEST
|
423
|
+
- :id: 2
|
424
|
+
:event: :return
|
425
|
+
:parent_id: 1
|
426
|
+
:return_value:
|
427
|
+
:class: String
|
428
|
+
:value: singleton for a struct
|
429
|
+
YAML
|
430
|
+
|
431
|
+
test_hook_behavior 'spec/fixtures/hook/singleton_method.rb', events_yaml do
|
432
|
+
expect(SingletonMethod::STRUCT_TEST.say_struct_singleton).to eq('singleton for a struct')
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
402
436
|
it 'Reports exceptions' do
|
403
437
|
events_yaml = <<~YAML
|
404
438
|
---
|
@@ -466,7 +500,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
466
500
|
:event: :call
|
467
501
|
:defined_class: ActiveSupport::SecurityUtils
|
468
502
|
:method_id: secure_compare
|
469
|
-
:path:
|
503
|
+
:path: lib/active_support/security_utils.rb
|
470
504
|
:lineno: 26
|
471
505
|
:static: true
|
472
506
|
:parameters:
|
@@ -564,7 +598,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
564
598
|
:children:
|
565
599
|
- :name: secure_compare
|
566
600
|
:type: function
|
567
|
-
:location:
|
601
|
+
:location: lib/active_support/security_utils.rb:26
|
568
602
|
:static: true
|
569
603
|
:labels:
|
570
604
|
- security
|
@@ -590,7 +624,7 @@ describe 'AppMap class Hooking', docker: false do
|
|
590
624
|
config, tracer = invoke_test_file 'spec/fixtures/hook/compare.rb' do
|
591
625
|
expect(Compare.compare('string', 'string')).to be_truthy
|
592
626
|
end
|
593
|
-
cm = AppMap::ClassMap.build_from_methods(
|
627
|
+
cm = AppMap::Util.sanitize_paths(AppMap::ClassMap.build_from_methods(tracer.event_methods))
|
594
628
|
entry = cm[1][:children][0][:children][0][:children][0]
|
595
629
|
# Sanity check, make sure we got the right one
|
596
630
|
expect(entry[:name]).to eq('secure_compare')
|
@@ -599,4 +633,47 @@ describe 'AppMap class Hooking', docker: false do
|
|
599
633
|
expect(Diffy::Diff.new(classmap_yaml, cm.to_yaml).to_s).to eq('')
|
600
634
|
end
|
601
635
|
end
|
636
|
+
|
637
|
+
it "doesn't cause expectations on Time.now to fail" do
|
638
|
+
events_yaml = <<~YAML
|
639
|
+
---
|
640
|
+
- :id: 1
|
641
|
+
:event: :call
|
642
|
+
:defined_class: InstanceMethod
|
643
|
+
:method_id: say_the_time
|
644
|
+
:path: spec/fixtures/hook/instance_method.rb
|
645
|
+
:lineno: 24
|
646
|
+
:static: false
|
647
|
+
:parameters: []
|
648
|
+
:receiver:
|
649
|
+
:class: InstanceMethod
|
650
|
+
:value: Instance Method fixture
|
651
|
+
- :id: 2
|
652
|
+
:event: :return
|
653
|
+
:parent_id: 1
|
654
|
+
:return_value:
|
655
|
+
:class: String
|
656
|
+
:value: '2020-01-01 00:00:00 +0000'
|
657
|
+
YAML
|
658
|
+
test_hook_behavior 'spec/fixtures/hook/instance_method.rb', events_yaml do
|
659
|
+
require 'timecop'
|
660
|
+
begin
|
661
|
+
tz = ENV['TZ']
|
662
|
+
ENV['TZ'] = 'UTC'
|
663
|
+
Timecop.freeze(Time.utc('2020-01-01')) do
|
664
|
+
expect(Time).to receive(:now).exactly(3).times.and_call_original
|
665
|
+
expect(InstanceMethod.new.say_the_time).to be
|
666
|
+
end
|
667
|
+
ensure
|
668
|
+
ENV['TZ'] = tz
|
669
|
+
end
|
670
|
+
end
|
671
|
+
end
|
672
|
+
|
673
|
+
it "preserves the arity of hooked methods" do
|
674
|
+
invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
|
675
|
+
expect(InstanceMethod.instance_method(:say_echo).arity).to be(1)
|
676
|
+
expect(InstanceMethod.new.method(:say_echo).arity).to be(1)
|
677
|
+
end
|
678
|
+
end
|
602
679
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appmap
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.35.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kevin Gilpin
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -164,6 +164,20 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: rake-compiler
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - ">="
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - ">="
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
167
181
|
- !ruby/object:Gem::Dependency
|
168
182
|
name: climate_control
|
169
183
|
requirement: !ruby/object:Gem::Requirement
|
@@ -248,16 +262,46 @@ dependencies:
|
|
248
262
|
- - "~>"
|
249
263
|
- !ruby/object:Gem::Version
|
250
264
|
version: '4.0'
|
265
|
+
- !ruby/object:Gem::Dependency
|
266
|
+
name: timecop
|
267
|
+
requirement: !ruby/object:Gem::Requirement
|
268
|
+
requirements:
|
269
|
+
- - ">="
|
270
|
+
- !ruby/object:Gem::Version
|
271
|
+
version: '0'
|
272
|
+
type: :development
|
273
|
+
prerelease: false
|
274
|
+
version_requirements: !ruby/object:Gem::Requirement
|
275
|
+
requirements:
|
276
|
+
- - ">="
|
277
|
+
- !ruby/object:Gem::Version
|
278
|
+
version: '0'
|
279
|
+
- !ruby/object:Gem::Dependency
|
280
|
+
name: hashie
|
281
|
+
requirement: !ruby/object:Gem::Requirement
|
282
|
+
requirements:
|
283
|
+
- - ">="
|
284
|
+
- !ruby/object:Gem::Version
|
285
|
+
version: '0'
|
286
|
+
type: :development
|
287
|
+
prerelease: false
|
288
|
+
version_requirements: !ruby/object:Gem::Requirement
|
289
|
+
requirements:
|
290
|
+
- - ">="
|
291
|
+
- !ruby/object:Gem::Version
|
292
|
+
version: '0'
|
251
293
|
description:
|
252
294
|
email:
|
253
295
|
- kgilpin@gmail.com
|
254
296
|
executables:
|
255
297
|
- appmap
|
256
|
-
extensions:
|
298
|
+
extensions:
|
299
|
+
- ext/appmap/extconf.rb
|
257
300
|
extra_rdoc_files: []
|
258
301
|
files:
|
259
302
|
- ".dockerignore"
|
260
303
|
- ".gitignore"
|
304
|
+
- ".rbenv-gemsets"
|
261
305
|
- ".rubocop.yml"
|
262
306
|
- ".ruby-version"
|
263
307
|
- ".travis.yml"
|
@@ -277,6 +321,8 @@ files:
|
|
277
321
|
- examples/mock_webapp/lib/mock_webapp/request.rb
|
278
322
|
- examples/mock_webapp/lib/mock_webapp/user.rb
|
279
323
|
- exe/appmap
|
324
|
+
- ext/appmap/appmap.c
|
325
|
+
- ext/appmap/extconf.rb
|
280
326
|
- lib/appmap.rb
|
281
327
|
- lib/appmap/algorithm/prune_class_map.rb
|
282
328
|
- lib/appmap/algorithm/stats.rb
|
@@ -292,7 +338,7 @@ files:
|
|
292
338
|
- lib/appmap/middleware/remote_recording.rb
|
293
339
|
- lib/appmap/minitest.rb
|
294
340
|
- lib/appmap/open.rb
|
295
|
-
- lib/appmap/rails/
|
341
|
+
- lib/appmap/rails/request_handler.rb
|
296
342
|
- lib/appmap/rails/sql_handler.rb
|
297
343
|
- lib/appmap/railtie.rb
|
298
344
|
- lib/appmap/record.rb
|
@@ -1,91 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'appmap/event'
|
4
|
-
|
5
|
-
module AppMap
|
6
|
-
module Rails
|
7
|
-
module ActionHandler
|
8
|
-
Context = Struct.new(:id, :start_time)
|
9
|
-
|
10
|
-
module ContextKey
|
11
|
-
def context_key
|
12
|
-
"#{HTTPServerRequest.name}#call"
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class HTTPServerRequest
|
17
|
-
include ContextKey
|
18
|
-
|
19
|
-
class Call < AppMap::Event::MethodCall
|
20
|
-
attr_accessor :payload
|
21
|
-
|
22
|
-
def initialize(payload)
|
23
|
-
super AppMap::Event.next_id_counter, :call, Thread.current.object_id
|
24
|
-
|
25
|
-
self.payload = payload
|
26
|
-
end
|
27
|
-
|
28
|
-
def to_h
|
29
|
-
super.tap do |h|
|
30
|
-
h[:http_server_request] = {
|
31
|
-
request_method: payload[:method],
|
32
|
-
path_info: payload[:path]
|
33
|
-
}
|
34
|
-
|
35
|
-
params = payload[:params]
|
36
|
-
h[:message] = params.keys.map do |key|
|
37
|
-
val = params[key]
|
38
|
-
{
|
39
|
-
name: key,
|
40
|
-
class: val.class.name,
|
41
|
-
value: self.class.display_string(val),
|
42
|
-
object_id: val.__id__
|
43
|
-
}
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def call(_, started, finished, _, payload) # (name, started, finished, unique_id, payload)
|
50
|
-
event = Call.new(payload)
|
51
|
-
Thread.current[context_key] = Context.new(event.id, Time.now)
|
52
|
-
AppMap.tracing.record_event(event)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class HTTPServerResponse
|
57
|
-
include ContextKey
|
58
|
-
|
59
|
-
class Call < AppMap::Event::MethodReturnIgnoreValue
|
60
|
-
attr_accessor :payload
|
61
|
-
|
62
|
-
def initialize(payload, parent_id, elapsed)
|
63
|
-
super AppMap::Event.next_id_counter, :return, Thread.current.object_id
|
64
|
-
|
65
|
-
self.payload = payload
|
66
|
-
self.parent_id = parent_id
|
67
|
-
self.elapsed = elapsed
|
68
|
-
end
|
69
|
-
|
70
|
-
def to_h
|
71
|
-
super.tap do |h|
|
72
|
-
h[:http_server_response] = {
|
73
|
-
status: payload[:status]
|
74
|
-
}
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
def call(_, started, finished, _, payload) # (name, started, finished, unique_id, payload)
|
80
|
-
return unless Thread.current[context_key]
|
81
|
-
|
82
|
-
context = Thread.current[context_key]
|
83
|
-
Thread.current[context_key] = nil
|
84
|
-
|
85
|
-
event = Call.new(payload, context.id, Time.now - context.start_time)
|
86
|
-
AppMap.tracing.record_event(event)
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|