appmap 0.77.3 → 0.77.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6889544929fdf05360b3e528847aaa1e3c4a423c580b982d5ecbb3403a44f6aa
4
- data.tar.gz: dad6b337385f4700b0974bf727329afa2bcd4c33f5a17bd951fd9516608a5798
3
+ metadata.gz: 4a3775abc9bff6d949fa377e2da5121414500735c37d9c5b9d6f03a7a2ae31a7
4
+ data.tar.gz: 7254e2e5c3f25f431cdb25e8d0512d55a155afd8b64647b255b3a8223b663f3f
5
5
  SHA512:
6
- metadata.gz: 197d249f8aa3ca22766168688f3b31e3b9cdf4c16d20227012d77dc5d6d1eb16b7814af8e605b51a5dc5a60ef4861f8cfd3f7089878ff88b046fac59a15b52a3
7
- data.tar.gz: 0e74d7f95043ea588f0a0ef9aca170765db7475c6009d6471ce2299885b59aca25eb4d87bb72cd7045a2a743b6b8c95fbb6348786d99cb938a269c73189a5234
6
+ metadata.gz: cf54fd237e40750eff85fb4723651a747ba6e69214ea04acae1a95964a09e4a800ea1de31dbc4f7dec7d26c585ef7fb9167a3e97eb36e9b0c14003240696d6d6
7
+ data.tar.gz: 81f413c5e21379b145a99f9882db9faea3d27e75ef56df7a8a928ce0b74f141c26025300cf0449fbf00128eb4991d99c94316f4fb522c5bd25042fb6ad2cfff3
data/.travis.yml CHANGED
@@ -33,13 +33,6 @@ before_install:
33
33
  && nvm use --lts \
34
34
  && npm i -g yarn
35
35
 
36
- jobs:
37
- include:
38
- - stage: test
39
- env:
40
- # GEM_ALTERNATIVE_NAME only needed for deployment
41
- - GEM_ALTERNATIVE_NAME=''
42
-
43
36
  before_deploy:
44
37
  - |
45
38
  npm i -g \
data/CHANGELOG.md CHANGED
@@ -1,3 +1,10 @@
1
+ ## [0.77.4](https://github.com/applandinc/appmap-ruby/compare/v0.77.3...v0.77.4) (2022-04-04)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Update Rails request handler to the new hook architecture ([595b39a](https://github.com/applandinc/appmap-ruby/commit/595b39abb030c1dcf85c83e4717c25d4c5177d4d))
7
+
1
8
  ## [0.77.3](https://github.com/applandinc/appmap-ruby/compare/v0.77.2...v0.77.3) (2022-03-29)
2
9
 
3
10
 
@@ -1,18 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'appmap/event'
4
+ require 'appmap/hook/method'
4
5
 
5
6
  module AppMap
6
7
  module Handler
7
- module Function
8
- class << self
9
- def handle_call(defined_class, hook_method, receiver, args)
10
- AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
11
- end
8
+ # Base handler class, will emit method call and return events.
9
+ class Function < Hook::Method
10
+ def handle_call(receiver, args)
11
+ AppMap::Event::MethodCall.build_from_invocation(defined_class, hook_method, receiver, args)
12
+ end
12
13
 
13
- def handle_return(call_event_id, elapsed, return_value, exception)
14
- AppMap::Event::MethodReturn.build_from_invocation(call_event_id, return_value, exception, elapsed: elapsed)
15
- end
14
+ def handle_return(call_event_id, elapsed, return_value, exception)
15
+ AppMap::Event::MethodReturn.build_from_invocation(call_event_id, return_value, exception, elapsed: elapsed)
16
16
  end
17
17
  end
18
18
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'appmap/event'
4
+ require 'appmap/hook/method'
4
5
  require 'appmap/util'
5
6
  require 'rack'
6
7
 
@@ -81,29 +82,29 @@ module AppMap
81
82
  end
82
83
  end
83
84
 
84
- class NetHTTP
85
- class << self
86
- def copy_headers(obj)
87
- {}.tap do |headers|
88
- obj.each_header do |key, value|
89
- key = key.split('-').map(&:capitalize).join('-')
90
- headers[key] = value
91
- end
85
+ # Handler class for HTTP requests.
86
+ # Emits HTTP request events instead of method calls.
87
+ class NetHTTP < Hook::Method
88
+ def self.copy_headers(obj)
89
+ {}.tap do |headers|
90
+ obj.each_header do |key, value|
91
+ key = key.split('-').map(&:capitalize).join('-')
92
+ headers[key] = value
92
93
  end
93
94
  end
95
+ end
94
96
 
95
- def handle_call(defined_class, hook_method, receiver, args)
96
- # request will call itself again in a start block if it's not already started.
97
- return unless receiver.started?
97
+ def handle_call(receiver, args)
98
+ # request will call itself again in a start block if it's not already started.
99
+ return unless receiver.started?
98
100
 
99
- http = receiver
100
- request = args.first
101
- HTTPClientRequest.new(http, request)
102
- end
101
+ http = receiver
102
+ request = args.first
103
+ HTTPClientRequest.new(http, request)
104
+ end
103
105
 
104
- def handle_return(call_event_id, elapsed, return_value, exception)
105
- HTTPClientResponse.new(return_value, call_event_id, elapsed)
106
- end
106
+ def handle_return(call_event_id, elapsed, return_value, exception)
107
+ HTTPClientResponse.new(return_value, call_event_id, elapsed)
107
108
  end
108
109
  end
109
110
  end
@@ -100,15 +100,14 @@ module AppMap
100
100
 
101
101
  protected
102
102
 
103
- def before_hook(receiver, defined_class, _) # args
103
+ def before_hook(receiver, *)
104
104
  call_event = HTTPServerRequest.new(receiver.request)
105
105
  # http_server_request events are i/o and do not require a package name.
106
106
  AppMap.tracing.record_event call_event, defined_class: defined_class, method: hook_method
107
- [ call_event, TIME_NOW.call ]
107
+ call_event
108
108
  end
109
109
 
110
- def after_hook(receiver, call_event, start_time, _, _) # return_value, exception
111
- elapsed = TIME_NOW.call - start_time
110
+ def after_hook(receiver, call_event, elapsed, *)
112
111
  return_event = HTTPServerResponse.new receiver.response, call_event.id, elapsed
113
112
  AppMap.tracing.record_event return_event
114
113
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'appmap/handler/function'
3
4
  require 'appmap/event'
4
5
 
5
6
  module AppMap
@@ -9,7 +10,7 @@ module AppMap
9
10
  LOG = (ENV['APPMAP_TEMPLATE_DEBUG'] == 'true' || ENV['DEBUG'] == 'true')
10
11
 
11
12
  # All the code which is touched by the AppMap is recorded in the classMap.
12
- # This duck-typed 'method' is used to represent a view template as a package,
13
+ # This duck-typed 'method' is used to represent a view template as a package,
13
14
  # class, and method in the classMap.
14
15
  # The class name is generated from the template path. The package name is
15
16
  # 'app/views', and the method name is 'render'. The source location of the method
@@ -41,31 +42,31 @@ module AppMap
41
42
  def package
42
43
  'app/views'
43
44
  end
44
-
45
+
45
46
  def name
46
47
  'render'
47
48
  end
48
-
49
+
49
50
  def source_location
50
51
  path
51
52
  end
52
-
53
+
53
54
  def static
54
55
  true
55
56
  end
56
-
57
+
57
58
  def comment
58
59
  nil
59
60
  end
60
-
61
+
61
62
  def labels
62
63
  [ 'mvc.template' ]
63
64
  end
64
65
  end
65
-
66
+
66
67
  # TemplateCall is a type of function call which is specialized to view template rendering. Since
67
68
  # there isn't really a perfect method in Rails to hook, this one is synthesized from the available
68
- # information.
69
+ # information.
69
70
  class TemplateCall < AppMap::Event::MethodEvent
70
71
  # This is basically the +self+ parameter.
71
72
  attr_reader :render_instance
@@ -75,19 +76,19 @@ module AppMap
75
76
  attr_accessor :ready
76
77
 
77
78
  alias ready? ready
78
-
79
+
79
80
  def initialize(render_instance)
80
81
  super :call
81
-
82
+
82
83
  AppMap::Event::MethodEvent.build_from_invocation(:call, event: self)
83
84
  @ready = false
84
85
  @render_instance = render_instance
85
86
  end
86
-
87
+
87
88
  def static?
88
89
  true
89
90
  end
90
-
91
+
91
92
  def to_h
92
93
  super.tap do |h|
93
94
  h[:defined_class] = path ? path.parameterize.underscore : 'inline_template'
@@ -103,71 +104,76 @@ module AppMap
103
104
  end.compact
104
105
  end
105
106
  end
106
-
107
+
107
108
  TEMPLATE_RENDERER = 'appmap.handler.rails.template.renderer'
108
109
 
109
110
  # Hooks the ActionView::Resolver methods +find_all+, +find_all_anywhere+. The resolver is used
110
111
  # during template rendering to lookup the template file path from parameters such as the
111
112
  # template name, prefix, and partial (boolean).
112
- class ResolverHandler
113
- class << self
114
- # Handled as a normal function call.
115
- def handle_call(defined_class, hook_method, receiver, args)
116
- name, prefix, partial = args
117
- warn "Resolver: #{{ name: name, prefix: prefix, partial: partial }}" if LOG
118
-
119
- AppMap::Handler::Function.handle_call(defined_class, hook_method, receiver, args)
120
- end
113
+ class ResolverHandler < AppMap::Handler::Function
114
+ def handle_call(receiver, args)
115
+ name, prefix, partial = args
116
+ warn "Resolver: #{{ name: name, prefix: prefix, partial: partial }}" if LOG
121
117
 
122
- # When the resolver returns, look to see if there is template rendering underway.
123
- # If so, populate the template path. In all cases, add a TemplateMethod so that the
124
- # template will be recorded in the classMap.
125
- def handle_return(call_event_id, elapsed, return_value, exception)
126
- renderer = Array(Thread.current[TEMPLATE_RENDERER]).last
127
- path_obj = Array(return_value).first
128
-
129
- warn "Resolver return: #{path_obj}" if LOG
130
-
131
- if path_obj
132
- path = if path_obj.respond_to?(:identifier) && path_obj.inspect.index('#<')
133
- path_obj.identifier
134
- else
135
- path_obj.inspect
136
- end
137
- path = path[Dir.pwd.length + 1..-1] if path.index(Dir.pwd) == 0
138
- AppMap.tracing.record_method(TemplateMethod.new(path))
139
- renderer.path ||= path if renderer
140
- end
118
+ super
119
+ end
141
120
 
142
- AppMap::Handler::Function.handle_return(call_event_id, elapsed, return_value, exception)
143
- end
121
+ # When the resolver returns, look to see if there is template rendering underway.
122
+ # If so, populate the template path. In all cases, add a TemplateMethod so that the
123
+ # template will be recorded in the classMap.
124
+ def handle_return(call_event_id, elapsed, return_value, exception)
125
+ renderer = Array(Thread.current[TEMPLATE_RENDERER]).last
126
+ path_obj = Array(return_value).first
127
+
128
+ warn "Resolver return: #{path_obj}" if LOG
129
+
130
+ record_template_path renderer, path_obj
131
+
132
+ super
133
+ end
134
+
135
+ def record_template_path(renderer, path_obj)
136
+ return unless path_obj
137
+
138
+ path = path_from_obj path_obj
139
+ AppMap.tracing.record_method(TemplateMethod.new(path))
140
+ renderer.path ||= path if renderer
141
+ end
142
+
143
+ def path_from_obj(path_obj)
144
+ path =
145
+ if path_obj.respond_to?(:identifier) && path_obj.inspect.index('#<')
146
+ path_obj.identifier
147
+ else
148
+ path_obj.inspect
149
+ end
150
+ path = path[Dir.pwd.length + 1..] if path.index(Dir.pwd) == 0
151
+ path
144
152
  end
145
153
  end
146
154
 
147
155
  # Hooks the ActionView::Renderer method +render+. This method is used by Rails to perform
148
156
  # template rendering. The TemplateCall event which is emitted by this handler has a
149
- # +path+ parameter, which is nil until it's filled in by a ResolverHandler.
150
- class RenderHandler
151
- class << self
152
- def handle_call(defined_class, hook_method, receiver, args)
153
- # context, options
154
- _, options = args
155
-
156
- warn "Renderer: #{options}" if LOG
157
-
158
- TemplateCall.new(receiver).tap do |call|
159
- Thread.current[TEMPLATE_RENDERER] ||= []
160
- Thread.current[TEMPLATE_RENDERER] << call
161
- end
162
- end
163
-
164
- def handle_return(call_event_id, elapsed, return_value, exception)
165
- template_call = Array(Thread.current[TEMPLATE_RENDERER]).pop
166
- template_call.ready = true
157
+ # +path+ parameter, which is nil until it's filled in by a ResolverHandler.
158
+ class RenderHandler < AppMap::Hook::Method
159
+ def handle_call(receiver, args)
160
+ # context, options
161
+ _, options = args
162
+
163
+ warn "Renderer: #{options}" if LOG
167
164
 
168
- AppMap::Event::MethodReturnIgnoreValue.build_from_invocation(call_event_id, elapsed: elapsed)
165
+ TemplateCall.new(receiver).tap do |call|
166
+ Thread.current[TEMPLATE_RENDERER] ||= []
167
+ Thread.current[TEMPLATE_RENDERER] << call
169
168
  end
170
169
  end
170
+
171
+ def handle_return(call_event_id, elapsed, _return_value, _exception)
172
+ template_call = Array(Thread.current[TEMPLATE_RENDERER]).pop
173
+ template_call.ready = true
174
+
175
+ AppMap::Event::MethodReturnIgnoreValue.build_from_invocation(call_event_id, elapsed: elapsed)
176
+ end
171
177
  end
172
178
  end
173
179
  end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ def ruby2_keywords(*); end unless respond_to?(:ruby2_keywords, true)
4
+
5
+ module AppMap
6
+ class Hook
7
+ # Delegation methods for Ruby 2.
8
+ # cf. https://eregon.me/blog/2019/11/10/the-delegation-challenge-of-ruby27.html
9
+ class Method
10
+ ruby2_keywords def call(receiver, *args, &block)
11
+ return do_call(receiver, *args, &block) unless trace?
12
+
13
+ call_event = with_disabled_hook { before_hook receiver, *args }
14
+ trace_call call_event, receiver, *args, &block
15
+ end
16
+
17
+ protected
18
+
19
+ def before_hook(receiver, *args)
20
+ call_event = handle_call(receiver, args)
21
+ if call_event
22
+ AppMap.tracing.record_event \
23
+ call_event,
24
+ package: hook_package,
25
+ defined_class: defined_class,
26
+ method: hook_method
27
+ end
28
+ call_event
29
+ end
30
+
31
+ ruby2_keywords def do_call(receiver, *args, &block)
32
+ hook_method.bind(receiver).call(*args, &block)
33
+ end
34
+
35
+ ruby2_keywords def trace_call(call_event, receiver, *args, &block)
36
+ start_time = gettime
37
+ begin
38
+ return_value = do_call(receiver, *args, &block)
39
+ rescue # rubocop:disable Style/RescueStandardError
40
+ exception = $ERROR_INFO
41
+ raise
42
+ ensure
43
+ with_disabled_hook { after_hook receiver, call_event, gettime - start_time, return_value, exception } \
44
+ if call_event
45
+ end
46
+ end
47
+
48
+ def hook_method_def
49
+ this = self
50
+ proc { |*args, &block| this.call self, *args, &block }.tap do |hook|
51
+ hook.ruby2_keywords if hook.respond_to? :ruby2_keywords
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AppMap
4
+ class Hook
5
+ # Delegation methods for Ruby 3.
6
+ class Method
7
+ def call(receiver, *args, **kwargs, &block)
8
+ return do_call(receiver, *args, **kwargs, &block) unless trace?
9
+
10
+ call_event = with_disabled_hook { before_hook receiver, *args, **kwargs }
11
+ trace_call call_event, receiver, *args, **kwargs, &block
12
+ end
13
+
14
+ protected
15
+
16
+ def before_hook(receiver, *args, **kwargs)
17
+ args = [*args, kwargs] if !kwargs.empty? || keyrest?
18
+ call_event = handle_call(receiver, args)
19
+ if call_event
20
+ AppMap.tracing.record_event \
21
+ call_event,
22
+ package: hook_package,
23
+ defined_class: defined_class,
24
+ method: hook_method
25
+ end
26
+ call_event
27
+ end
28
+
29
+ def keyrest?
30
+ @keyrest ||= parameters.map(&:last).include? :keyrest
31
+ end
32
+
33
+ def do_call(receiver, *args, **kwargs, &block)
34
+ hook_method.bind_call(receiver, *args, **kwargs, &block)
35
+ end
36
+
37
+ def trace_call(call_event, receiver, *args, **kwargs, &block)
38
+ start_time = gettime
39
+ begin
40
+ return_value = do_call(receiver, *args, **kwargs, &block)
41
+ rescue # rubocop:disable Style/RescueStandardError
42
+ exception = $ERROR_INFO
43
+ raise
44
+ ensure
45
+ with_disabled_hook { after_hook receiver, call_event, gettime - start_time, return_value, exception } \
46
+ if call_event
47
+ end
48
+ end
49
+
50
+ def hook_method_def
51
+ this = self
52
+ proc { |*args, **kwargs, &block| this.call self, *args, **kwargs, &block }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,17 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AppMap
4
- NEW_RUBY = Util.ruby_minor_version >= 2.7
5
- if NEW_RUBY && !Proc.instance_methods.include?(:ruby2_keywords)
6
- warn "Ruby is #{RUBY_VERSION}, but Procs don't respond to #ruby2_keywords"
7
- end
8
-
9
4
  class Hook
10
5
  SIGNATURES = {}
11
-
12
6
  LOOKUP_SIGNATURE = lambda do |id|
13
7
  method = super(id)
14
-
8
+
15
9
  signature = SIGNATURES[[ method.owner, method.name ]]
16
10
  if signature
17
11
  method.singleton_class.module_eval do
@@ -20,36 +14,24 @@ module AppMap
20
14
  end
21
15
  end
22
16
  end
23
-
17
+
24
18
  method
25
19
  end
26
-
27
- class Method
28
- attr_reader :hook_package, :hook_class, :hook_method
29
20
 
30
- # +method_display_name+ may be nil if name resolution gets
31
- # deferred until runtime (e.g. for a singleton method on an
32
- # embedded Struct).
33
- attr_reader :method_display_name
21
+ # Single hooked method.
22
+ # Call #activate to override the original.
23
+ class Method
24
+ attr_reader :hook_package, :hook_class, :hook_method, :parameters, :arity
34
25
 
35
26
  HOOK_DISABLE_KEY = 'AppMap::Hook.disable'
36
27
  private_constant :HOOK_DISABLE_KEY
37
28
 
38
- # Grab the definition of Time.now here, to avoid interfering
39
- # with the method we're hooking.
40
- TIME_NOW = Time.method(:now)
41
- private_constant :TIME_NOW
42
-
43
- ARRAY_OF_EMPTY_HASH = [{}.freeze].freeze
44
-
45
29
  def initialize(hook_package, hook_class, hook_method)
46
30
  @hook_package = hook_package
47
31
  @hook_class = hook_class
48
32
  @hook_method = hook_method
49
-
50
- # Get the class for the method, if it's known.
51
- @defined_class, method_symbol = Hook.qualify_method_name(@hook_method)
52
- @method_display_name = [@defined_class, method_symbol, @hook_method.name].join if @defined_class
33
+ @parameters = hook_method.parameters
34
+ @arity = hook_method.arity
53
35
  end
54
36
 
55
37
  def activate
@@ -62,64 +44,6 @@ module AppMap
62
44
  warn "AppMap: Hooking #{msg} at line #{(hook_method.source_location || []).join(':')}"
63
45
  end
64
46
 
65
- defined_class = @defined_class
66
- hook_package = self.hook_package
67
- hook_method = self.hook_method
68
- before_hook = self.method(:before_hook)
69
- after_hook = self.method(:after_hook)
70
- with_disabled_hook = self.method(:with_disabled_hook)
71
-
72
- is_array_containing_empty_hash = ->(obj) {
73
- obj.is_a?(Array) && obj.length == 1 && obj[0].is_a?(Hash) && obj[0].size == 0
74
- }
75
-
76
- call_instance_method = lambda do |receiver, args, &block|
77
- # https://github.com/applandinc/appmap-ruby/issues/153
78
- if NEW_RUBY && is_array_containing_empty_hash.(args) && hook_method.arity == 1
79
- hook_method.bind_call(receiver, {}, &block)
80
- else
81
- if NEW_RUBY
82
- hook_method.bind_call(receiver, *args, &block)
83
- else
84
- hook_method.bind(receiver).call(*args, &block)
85
- end
86
- end
87
- end
88
-
89
- hook_method_def = Proc.new do |*args, &block|
90
- # We may not have gotten the class for the method during
91
- # initialization (e.g. for a singleton method on an embedded
92
- # struct), so make sure we have it now.
93
- defined_class, = Hook.qualify_method_name(hook_method) unless defined_class
94
-
95
- reentrant = Thread.current[HOOK_DISABLE_KEY]
96
- disabled_by_shallow_flag = \
97
- -> { hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package }
98
-
99
- enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
100
-
101
- enabled = false if %i[instance_eval instance_exec].member?(hook_method.name) && args.empty?
102
-
103
- return call_instance_method.call(self, args, &block) unless enabled
104
-
105
- call_event, start_time = with_disabled_hook.call do
106
- before_hook.call(self, defined_class, args)
107
- end
108
- return_value = nil
109
- exception = nil
110
- begin
111
- return_value = call_instance_method.call(self, args, &block)
112
- rescue
113
- exception = $ERROR_INFO
114
- raise
115
- ensure
116
- with_disabled_hook.call do
117
- after_hook.call(self, call_event, start_time, return_value, exception) if call_event
118
- end
119
- end
120
- end
121
- hook_method_def = hook_method_def.ruby2_keywords if hook_method_def.respond_to?(:ruby2_keywords)
122
-
123
47
  hook_method_parameters = hook_method.parameters.dup.freeze
124
48
  SIGNATURES[[ hook_class, hook_method.name ]] = hook_method_parameters
125
49
 
@@ -140,28 +64,42 @@ module AppMap
140
64
 
141
65
  protected
142
66
 
143
- def before_hook(receiver, defined_class, args)
144
- call_event = hook_package.handler_class.handle_call(defined_class, hook_method, receiver, args)
145
- AppMap.tracing.record_event(call_event, package: hook_package, defined_class: defined_class, method: hook_method) if call_event
146
- [ call_event, TIME_NOW.call ]
67
+ def gettime
68
+ Process.clock_gettime Process::CLOCK_MONOTONIC
69
+ end
70
+
71
+ def trace?
72
+ return false unless AppMap.tracing.enabled?
73
+ return false if Thread.current[HOOK_DISABLE_KEY]
74
+ return false if hook_package&.shallow? && AppMap.tracing.last_package_for_current_thread == hook_package
75
+
76
+ true
147
77
  end
148
78
 
149
- def after_hook(_receiver, call_event, start_time, return_value, exception)
150
- elapsed = TIME_NOW.call - start_time
151
- return_event = hook_package.handler_class.handle_return(call_event.id, elapsed, return_value, exception)
79
+ def method_display_name
80
+ return @method_display_name if @method_display_name
81
+
82
+ return @method_display_name = [defined_class, method_symbol, hook_method.name].join if defined_class
83
+
84
+ "#{hook_method.name} (class resolution deferred)"
85
+ end
86
+
87
+ def defined_class
88
+ @defined_class ||= Hook.qualify_method_name(hook_method)&.first
89
+ end
90
+
91
+ def after_hook(_receiver, call_event, elapsed, return_value, exception)
92
+ return_event = handle_return(call_event.id, elapsed, return_value, exception)
152
93
  AppMap.tracing.record_event(return_event) if return_event
153
- nil
154
94
  end
155
95
 
156
- def with_disabled_hook(&function)
96
+ def with_disabled_hook
157
97
  # Don't record functions, such as to_s and inspect, that might be called
158
98
  # by the fn. Otherwise there can be a stack overflow.
159
99
  Thread.current[HOOK_DISABLE_KEY] = true
160
- begin
161
- function.call
162
- ensure
163
- Thread.current[HOOK_DISABLE_KEY] = false
164
- end
100
+ yield
101
+ ensure
102
+ Thread.current[HOOK_DISABLE_KEY] = false
165
103
  end
166
104
  end
167
105
  end
@@ -189,3 +127,9 @@ unless ENV['APPMAP_NO_PATCH_MODULE'] == 'true'
189
127
  prepend AppMap::ModuleMethods
190
128
  end
191
129
  end
130
+
131
+ if RUBY_VERSION < '3'
132
+ require 'appmap/hook/method/ruby2'
133
+ else
134
+ require 'appmap/hook/method/ruby3'
135
+ end
data/lib/appmap/hook.rb CHANGED
@@ -118,7 +118,7 @@ module AppMap
118
118
  cls, method = entry
119
119
  return false if config.never_hook?(cls, method)
120
120
 
121
- Hook::Method.new(hook.package, cls, method).activate
121
+ hook.package.handler_class.new(hook.package, cls, method).activate
122
122
  end
123
123
 
124
124
  methods = []
@@ -209,7 +209,7 @@ module AppMap
209
209
  package = config.lookup_package(hook_cls, method)
210
210
  next unless package
211
211
 
212
- Hook::Method.new(package, hook_cls, method).activate
212
+ package.handler_class.new(package, hook_cls, method).activate
213
213
  end
214
214
  end
215
215
 
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.77.3'
6
+ VERSION = '0.77.4'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.5.1'
9
9
 
data/spec/hook_spec.rb CHANGED
@@ -115,7 +115,7 @@ describe 'AppMap class Hooking', docker: false do
115
115
  require 'appmap/hook/method'
116
116
  package = config.lookup_package(hook_cls, method)
117
117
  expect(package).to be
118
- hook_method = AppMap::Hook::Method.new(package, hook_cls, method)
118
+ hook_method = AppMap::Handler::Function.new(package, hook_cls, method)
119
119
  hook_method.activate
120
120
 
121
121
  tracer = AppMap.tracing.trace
@@ -1188,7 +1188,7 @@ describe 'AppMap class Hooking', docker: false do
1188
1188
  require 'appmap/hook/method'
1189
1189
 
1190
1190
  pkg = AppMap::Config::Package.new('fixtures/hook/prependend_override')
1191
- AppMap::Hook::Method.new(pkg, PrependedClass, PrependedClass.public_instance_method(:say_hello)).activate
1191
+ AppMap::Handler::Function.new(pkg, PrependedClass, PrependedClass.public_instance_method(:say_hello)).activate
1192
1192
 
1193
1193
  tracer = AppMap.tracing.trace
1194
1194
  AppMap::Event.reset_id_counter
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.77.3
4
+ version: 0.77.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Gilpin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-03-29 00:00:00.000000000 Z
11
+ date: 2022-04-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -383,6 +383,8 @@ files:
383
383
  - lib/appmap/handler/rails/template.rb
384
384
  - lib/appmap/hook.rb
385
385
  - lib/appmap/hook/method.rb
386
+ - lib/appmap/hook/method/ruby2.rb
387
+ - lib/appmap/hook/method/ruby3.rb
386
388
  - lib/appmap/metadata.rb
387
389
  - lib/appmap/middleware/remote_recording.rb
388
390
  - lib/appmap/minitest.rb