appmap 0.77.3 → 0.77.4

Sign up to get free protection for your applications and to get access to all the features.
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