appmap 0.72.4 → 0.74.0

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: df8cfae5642145ffdf039e46a97b4dc2cf748e35658611237e4f8c86fcfdeb4b
4
- data.tar.gz: af8324a534ba90df3ba87467581936e3ce3109e67fcc1952fd3e25912b003be6
3
+ metadata.gz: eb314cb1f41f3735c773670cd8082bbc8cedb98d9b01c6c71a0c6a2d2b094da5
4
+ data.tar.gz: fb4f94b03bb6c50465d22db22f5cc1d25bfb95e3638543986306ca63ce5603e7
5
5
  SHA512:
6
- metadata.gz: c8428c0343bb9a856d96fa332ca6217c9cabb518df36e48562f5855714ef6291c2b2ea7a14f7e8b5ea2a4a01ae3e1ac685a95e7889cca705ab451989c83f226b
7
- data.tar.gz: ea54394ee1feb68949a4b92764585a1e170ac35d0fd0b2fb5ef744a0499831f23d1e179f85110b21a7dec029ca0a4cb7258ad64f081fa5273423e6fe178bbbcb
6
+ metadata.gz: 2da7b411bf29e42b6c81dad1c897021e67e85b4770037543508ea2562a6132d3e8c7d2fe8574971231c06f3ebaaa328264d9db461363f3f59166922d8327060f
7
+ data.tar.gz: 248770c6d301895621c72d6f46c6e6e1c18abaa368356c7c9deebd480e91ce8bb456374c04bd4547ae7423976d8911f1e627c137d2cffdb6801db50bef55a229
data/CHANGELOG.md CHANGED
@@ -1,3 +1,38 @@
1
+ # [0.74.0](https://github.com/applandinc/appmap-ruby/compare/v0.73.0...v0.74.0) (2022-03-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Apply special case hook handling to Kernel and instance_eval ([25823ff](https://github.com/applandinc/appmap-ruby/commit/25823ff0fb86beff3edc64da251a125ee198ef40))
7
+ * Only apply a method hook to a class that defines the method ([ede2236](https://github.com/applandinc/appmap-ruby/commit/ede22364bfcbf324e8db3aa6d64d5b032f36ace2))
8
+ * Optimize/improve string-ification of values ([c9b6cdb](https://github.com/applandinc/appmap-ruby/commit/c9b6cdb72dfc55cc3a166eda470eba19093e9090))
9
+
10
+
11
+ ### Features
12
+
13
+ * Improve hook performance by using bind_call ([e09fce9](https://github.com/applandinc/appmap-ruby/commit/e09fce9f5b3c0b18bc3b81083c1523df6a6932db))
14
+ * Label system.exec, string.pack, string.html_safe ([963c6dd](https://github.com/applandinc/appmap-ruby/commit/963c6ddfa0f607ad219ae8829cfb383b0d5988d0))
15
+ * Log initiation of each builtin hook ([902a736](https://github.com/applandinc/appmap-ruby/commit/902a7360d17c6b49de97f34643c733e8c47c294d))
16
+
17
+ # [0.73.0](https://github.com/applandinc/appmap-ruby/compare/v0.72.5...v0.73.0) (2022-03-07)
18
+
19
+
20
+ ### Bug Fixes
21
+
22
+ * Remove GC before test case execution, because it's slow ([d38695e](https://github.com/applandinc/appmap-ruby/commit/d38695ed9425a5363e48f2b7bdd5dc3853a827bf))
23
+
24
+
25
+ ### Features
26
+
27
+ * Use bind_call when its available ([60d4fb5](https://github.com/applandinc/appmap-ruby/commit/60d4fb5919974d977722ee730b141ae398cbe927))
28
+
29
+ ## [0.72.5](https://github.com/applandinc/appmap-ruby/compare/v0.72.4...v0.72.5) (2022-02-17)
30
+
31
+
32
+ ### Bug Fixes
33
+
34
+ * Override method accessors to provide the correct signature ([#223](https://github.com/applandinc/appmap-ruby/issues/223)) ([936bba4](https://github.com/applandinc/appmap-ruby/commit/936bba470c5360ee313e0b3e45a65d83acd0b53d))
35
+
1
36
  ## [0.72.4](https://github.com/applandinc/appmap-ruby/compare/v0.72.3...v0.72.4) (2022-02-17)
2
37
 
3
38
 
data/LICENSE.txt CHANGED
@@ -1,6 +1,18 @@
1
- The MIT License (MIT)
1
+ The Software is provided to you by the Licensor under the License, as defined below, subject to the following condition.
2
2
 
3
- Copyright (c) 2018 Kevin Gilpin
3
+ Without limiting other conditions in the License, the grant of rights under the License will not include,
4
+ and the License does not grant to you, the right to Sell the Software.
5
+
6
+ For purposes of the foregoing, “Sell” means practicing any or all of the rights granted to you under the License to provide to third parties,
7
+ for a fee or other consideration (including without limitation fees for hosting or consulting/ support services related to the Software),
8
+ a product or service whose value derives, entirely or substantially, from the functionality of the Software.
9
+ Any license notice or attribution required by the License must also include this Commons Clause License Condition notice.
10
+
11
+ Software: AppMap agent for Ruby
12
+
13
+ License: MIT License
14
+
15
+ Copyright 2022, AppLand Inc
4
16
 
5
17
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
18
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,13 @@
1
+ - methods:
2
+ - Open3#capture2
3
+ - Open3#capture2e
4
+ - Open3#capture3
5
+ - Open3#pipeline
6
+ - Open3#pipeline_r
7
+ - Open3#pipeline_rw
8
+ - Open3#pipeline_start
9
+ - Open3#pipeline_w
10
+ - Open3#popen2
11
+ - Open3#popen2e
12
+ - Open3#popen3
13
+ label: system.exec
@@ -0,0 +1,39 @@
1
+ - methods:
2
+ - Marshal#load
3
+ - Marshal#restore
4
+ require_name: ruby
5
+ label: deserialize.unsafe
6
+ - method: Marshal#dump
7
+ require_name: ruby
8
+ label: serialize
9
+ - method: String#pack
10
+ require_name: ruby
11
+ label: string.pack
12
+ - methods:
13
+ - String#unpack
14
+ - String#unpack1
15
+ require_name: ruby
16
+ label: string.unpack
17
+ #- methods:
18
+ # TODO: eval does not happen in the right context, and therefore any new constants
19
+ # which are defined are placed on the wrong module/class.
20
+ # - Kernel#eval
21
+ # - Binding#eval
22
+ # - BasicObject#instance_eval
23
+ # These methods cannot be hooked as far as I can tell.
24
+ # Why? When calling one of these functions, the context at the point of
25
+ # definition is used. It's not possible to bind class_eval to a new context.
26
+ # - Module#class_eval
27
+ # - Module#module_eval
28
+ # require_name: ruby
29
+ # label: lang.eval
30
+ - methods:
31
+ - IO#popen
32
+ - Kernel#exec
33
+ - Kernel#spawn
34
+ - Kernel#syscall
35
+ - Kernel#system
36
+ - Process#exec
37
+ - Process#spawn
38
+ require_name: ruby
39
+ label: system.exec
@@ -51,7 +51,7 @@ module AppMap
51
51
  appmap['metadata'] = update_metadata(scenario, appmap['metadata'])
52
52
  scenario_filename = AppMap::Util.scenario_filename(appmap['metadata']['name'])
53
53
 
54
- AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, scenario_filename), JSON.generate(appmap))
54
+ AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, scenario_filename), appmap)
55
55
  end
56
56
 
57
57
  def enabled?
data/lib/appmap/event.rb CHANGED
@@ -20,7 +20,9 @@ module AppMap
20
20
  MethodEventStruct = Struct.new(:id, :event, :thread_id)
21
21
 
22
22
  class MethodEvent < MethodEventStruct
23
- LIMIT = 100
23
+ MAX_ARRAY_ENUMERATION = 10
24
+ MAX_HASH_ENUMERATION = 10
25
+ MAX_STRING_LENGTH = 100
24
26
 
25
27
  class << self
26
28
  def build_from_invocation(event_type, event:)
@@ -48,14 +50,14 @@ module AppMap
48
50
  end
49
51
 
50
52
  start = Time.now
51
- value_string = custom_display_string(value) || default_display_string(value)
53
+ value_string, final = custom_display_string(value) || default_display_string(value)
52
54
 
53
55
  if @times
54
56
  elapsed = Time.now - start
55
57
  @times[best_class_name(value)] += elapsed
56
58
  end
57
59
 
58
- encode_display_string(value_string)
60
+ final ? value_string : encode_display_string(value_string)
59
61
  end
60
62
 
61
63
  def object_properties(hash_like)
@@ -80,21 +82,33 @@ module AppMap
80
82
  end
81
83
 
82
84
  def encode_display_string(value)
83
- (value||'')[0...LIMIT].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
85
+ (value||'')[0...MAX_STRING_LENGTH].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
84
86
  end
85
87
 
86
88
  def custom_display_string(value)
87
89
  case value
88
90
  when NilClass, TrueClass, FalseClass, Numeric, Time, Date
89
- value.to_s
91
+ [ value.to_s, true ]
92
+ when Symbol
93
+ [ ":#{value}", true ]
90
94
  when String
91
- value[0...LIMIT].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
95
+ result = value[0...MAX_STRING_LENGTH].encode('utf-8', invalid: :replace, undef: :replace, replace: '_')
96
+ result << " (...#{value.length - MAX_STRING_LENGTH} more characters)" if value.length > MAX_STRING_LENGTH
97
+ [ result, true ]
98
+ when Array
99
+ result = value[0...MAX_ARRAY_ENUMERATION].map{|v| display_string(v)}.join(', ')
100
+ result << " (...#{value.length - MAX_ARRAY_ENUMERATION} more items)" if value.length > MAX_ARRAY_ENUMERATION
101
+ [ [ '[', result, ']' ].join, true ]
102
+ when Hash
103
+ result = value.keys[0...MAX_HASH_ENUMERATION].map{|key| "#{display_string(key)}=>#{display_string(value[key])}"}.join(', ')
104
+ result << " (...#{value.size - MAX_HASH_ENUMERATION} more entries)" if value.size > MAX_HASH_ENUMERATION
105
+ [ [ '{', result, '}' ].join, true ]
92
106
  when File
93
- "#{value.class}[path=#{value.path}]"
107
+ [ "#{value.class}[path=#{value.path}]", true ]
94
108
  when Net::HTTP
95
- "#{value.class}[#{value.address}:#{value.port}]"
109
+ [ "#{value.class}[#{value.address}:#{value.port}]", true ]
96
110
  when Net::HTTPGenericRequest
97
- "#{value.class}[#{value.method} #{value.path}]"
111
+ [ "#{value.class}[#{value.method} #{value.path}]", true ]
98
112
  end
99
113
  rescue StandardError
100
114
  nil
@@ -11,3 +11,7 @@
11
11
  label: mvc.template.resolver
12
12
  handler_class: AppMap::Handler::Rails::Template::ResolverHandler
13
13
  require_name: action_view
14
+ - methods:
15
+ - ActionView::Helpers::SanitizeHelper#sanitize
16
+ label: string.html_safe
17
+ require_name: action_view
@@ -7,6 +7,23 @@ module AppMap
7
7
  end
8
8
 
9
9
  class Hook
10
+ SIGNATURES = {}
11
+
12
+ LOOKUP_SIGNATURE = lambda do |id|
13
+ method = super(id)
14
+
15
+ signature = SIGNATURES[[ method.owner, method.name ]]
16
+ if signature
17
+ method.singleton_class.module_eval do
18
+ define_method(:parameters) do
19
+ signature
20
+ end
21
+ end
22
+ end
23
+
24
+ method
25
+ end
26
+
10
27
  class Method
11
28
  attr_reader :hook_package, :hook_class, :hook_method
12
29
 
@@ -52,22 +69,24 @@ module AppMap
52
69
  after_hook = self.method(:after_hook)
53
70
  with_disabled_hook = self.method(:with_disabled_hook)
54
71
 
55
- hook_method_def = Proc.new do |*args, &block|
56
- instance_method = hook_method.bind(self).to_proc
57
-
58
- is_array_containing_empty_hash = ->(obj) {
59
- obj.is_a?(Array) && obj.length == 1 && obj[0].is_a?(Hash) && obj[0].size == 0
60
- }
61
-
62
- call_instance_method = -> {
63
- # https://github.com/applandinc/appmap-ruby/issues/153
64
- if NEW_RUBY && is_array_containing_empty_hash.(args) && hook_method.arity == 1
65
- instance_method.call({}, &block)
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)
66
83
  else
67
- instance_method.call(*args, &block)
84
+ hook_method.bind(receiver).call(*args, &block)
68
85
  end
69
- }
86
+ end
87
+ end
70
88
 
89
+ hook_method_def = Proc.new do |*args, &block|
71
90
  # We may not have gotten the class for the method during
72
91
  # initialization (e.g. for a singleton method on an embedded
73
92
  # struct), so make sure we have it now.
@@ -79,7 +98,9 @@ module AppMap
79
98
 
80
99
  enabled = true if AppMap.tracing.enabled? && !reentrant && !disabled_by_shallow_flag.call
81
100
 
82
- return call_instance_method.call unless enabled
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
83
104
 
84
105
  call_event, start_time = with_disabled_hook.call do
85
106
  before_hook.call(self, defined_class, args)
@@ -87,7 +108,7 @@ module AppMap
87
108
  return_value = nil
88
109
  exception = nil
89
110
  begin
90
- return_value = call_instance_method.call
111
+ return_value = call_instance_method.call(self, args, &block)
91
112
  rescue
92
113
  exception = $ERROR_INFO
93
114
  raise
@@ -100,11 +121,19 @@ module AppMap
100
121
  hook_method_def = hook_method_def.ruby2_keywords if hook_method_def.respond_to?(:ruby2_keywords)
101
122
 
102
123
  hook_method_parameters = hook_method.parameters.dup.freeze
103
- hook_class.ancestors.first.tap do |cls|
104
- cls.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def)
105
- redefined_method = cls.instance_method(hook_method.name)
106
- redefined_method.singleton_class.module_eval do
107
- define_method(:parameters) { hook_method_parameters }
124
+ SIGNATURES[[ hook_class, hook_method.name ]] = hook_method_parameters
125
+
126
+ # irb(main):001:0> Kernel.public_instance_method(:system)
127
+ # (irb):1:in `public_instance_method': method `system' for module `Kernel' is private (NameError)
128
+ if hook_class == Kernel
129
+ hook_class.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def)
130
+ else
131
+ hook_class.ancestors.find { |cls| cls.method_defined?(hook_method.name, false) }.tap do |cls|
132
+ if cls
133
+ cls.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def)
134
+ else
135
+ warn "#{hook_method.name} not found on #{hook_class}"
136
+ end
108
137
  end
109
138
  end
110
139
  end
@@ -136,4 +165,27 @@ module AppMap
136
165
  end
137
166
  end
138
167
  end
168
+
169
+ module ObjectMethods
170
+ define_method(:method, AppMap::Hook::LOOKUP_SIGNATURE)
171
+ define_method(:public_method, AppMap::Hook::LOOKUP_SIGNATURE)
172
+ define_method(:singleton_method, AppMap::Hook::LOOKUP_SIGNATURE)
173
+ end
174
+
175
+ module ModuleMethods
176
+ define_method(:instance_method, AppMap::Hook::LOOKUP_SIGNATURE)
177
+ define_method(:public_instance_method, AppMap::Hook::LOOKUP_SIGNATURE)
178
+ end
179
+ end
180
+
181
+ unless ENV['APPMAP_NO_PATCH_OBJECT'] == 'true'
182
+ class Object
183
+ prepend AppMap::ObjectMethods
184
+ end
185
+ end
186
+
187
+ unless ENV['APPMAP_NO_PATCH_MODULE'] == 'true'
188
+ class Module
189
+ prepend AppMap::ModuleMethods
190
+ end
139
191
  end
data/lib/appmap/hook.rb CHANGED
@@ -105,6 +105,9 @@ module AppMap
105
105
 
106
106
  Array(hook.method_names).each do |method_name|
107
107
  method_name = method_name.to_sym
108
+
109
+ warn "AppMap: Initiating hook for builtin #{class_name} #{method_name}" if LOG
110
+
108
111
  base_cls = Util.class_from_string(class_name, must: false)
109
112
  next unless base_cls
110
113
 
@@ -116,6 +119,11 @@ module AppMap
116
119
  end
117
120
 
118
121
  methods = []
122
+ # irb(main):001:0> Kernel.public_instance_method(:system)
123
+ # (irb):1:in `public_instance_method': method `system' for module `Kernel' is private (NameError)
124
+ if base_cls == Kernel
125
+ methods << [ base_cls, base_cls.instance_method(method_name) ] rescue nil
126
+ end
119
127
  methods << [ base_cls, base_cls.public_instance_method(method_name) ] rescue nil
120
128
  methods << [ base_cls, base_cls.protected_instance_method(method_name) ] rescue nil
121
129
  if base_cls.respond_to?(:singleton_class)
@@ -120,7 +120,7 @@ module AppMap
120
120
  }.compact
121
121
  fname = AppMap::Util.scenario_filename(name)
122
122
 
123
- AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
123
+ AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, fname), appmap)
124
124
  end
125
125
 
126
126
  def enabled?
@@ -142,7 +142,6 @@ if AppMap::Minitest.enabled?
142
142
  alias run_without_hook run
143
143
 
144
144
  def run
145
- GC.start
146
145
  AppMap::Minitest.begin_test self, name
147
146
  begin
148
147
  run_without_hook
data/lib/appmap/record.rb CHANGED
@@ -23,5 +23,5 @@ at_exit do
23
23
  'classMap' => AppMap.class_map(tracer.event_methods),
24
24
  'events' => events
25
25
  }
26
- AppMap::Util.write_appmap('appmap.json', JSON.generate(appmap))
26
+ AppMap::Util.write_appmap('appmap.json', appmap)
27
27
  end
data/lib/appmap/rspec.rb CHANGED
@@ -87,7 +87,6 @@ module AppMap
87
87
  end
88
88
 
89
89
  warn "Starting recording of example #{example}@#{source_location}" if AppMap::RSpec::LOG
90
- GC.start
91
90
  @trace = AppMap.tracing.trace
92
91
  @webdriver_port = webdriver_port.()
93
92
  end
@@ -206,7 +205,7 @@ module AppMap
206
205
  }.compact
207
206
  fname = AppMap::Util.scenario_filename(name)
208
207
 
209
- AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, fname), JSON.generate(appmap))
208
+ AppMap::Util.write_appmap(File.join(APPMAP_OUTPUT_DIR, fname), appmap)
210
209
  end
211
210
 
212
211
  def enabled?
data/lib/appmap/util.rb CHANGED
@@ -171,7 +171,7 @@ module AppMap
171
171
  mode = File::RDWR | File::CREAT | File::EXCL
172
172
  ::Dir::Tmpname.create([ 'appmap_', '.json' ]) do |tmpname|
173
173
  tempfile = File.open(tmpname, mode)
174
- tempfile.write(appmap)
174
+ tempfile.write(JSON.generate(appmap))
175
175
  tempfile.close
176
176
  # Atomically move the tempfile into place.
177
177
  FileUtils.mv tempfile.path, filename
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.72.4'
6
+ VERSION = '0.74.0'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.5.1'
9
9
 
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'appmap/event'
5
+
6
+ include AppMap
7
+
8
+ describe 'display_string' do
9
+ def display_string(value)
10
+ Event::MethodEvent.display_string value
11
+ end
12
+
13
+ def compare_display_string(value, expected)
14
+ expect(display_string(value)).to eq(expected)
15
+ end
16
+
17
+ context 'for a' do
18
+ it 'String' do
19
+ compare_display_string 'foo', 'foo'
20
+ end
21
+ it 'long String' do
22
+ compare_display_string 'foo' * 100, 'foo' * 33 + 'f (...200 more characters)'
23
+ end
24
+ it 'Array' do
25
+ compare_display_string([ 1, 'my', :bar, [ 2, 3 ], { 4 => 5 } ], '[1, my, :bar, [2, 3], {4=>5}]')
26
+ end
27
+ it 'large Array' do
28
+ compare_display_string 50.times.map { |i| i }, '[0, 1, 2, 3, 4, 5, 6, 7, 8, 9 (...40 more items)]'
29
+ end
30
+ it 'large Hash' do
31
+ compare_display_string(50.times.map { |i| [ i*2, i*2+1] }.to_h, '{0=>1, 2=>3, 4=>5, 6=>7, 8=>9, 10=>11, 12=>13, 14=>15, 16=>17, 18=>19 (...40 more entries)}')
32
+ end
33
+ it 'Hash' do
34
+ compare_display_string({ 1 => 2, 'my' => 'neighbor', bar: :baz, ary: [ 1, 2 ]}, '{1=>2, my=>neighbor, :bar=>:baz, :ary=>[1, 2]}')
35
+ end
36
+ it 'big Hash' do
37
+ compare_display_string(50.times.map { |i| [ i*2, i*2+1] }.to_h, '{0=>1, 2=>3, 4=>5, 6=>7, 8=>9, 10=>11, 12=>13, 14=>15, 16=>17, 18=>19 (...40 more entries)}')
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,8 @@
1
+
2
+ class ReportParameters
3
+ def to_s; self.class.name; end
4
+
5
+ def report_parameters(*args, kw1:, kw2: 'kw2', **kws)
6
+ method(:report_parameters).parameters
7
+ end
8
+ end
@@ -47,7 +47,6 @@ RSpec.configure do |config|
47
47
 
48
48
  config.before(:suite) do
49
49
  DatabaseCleaner.strategy = :transaction
50
- DatabaseCleaner.clean_with(:truncation)
51
50
  end
52
51
 
53
52
  config.around :each do |example|
@@ -47,7 +47,6 @@ RSpec.configure do |config|
47
47
 
48
48
  config.before(:suite) do
49
49
  DatabaseCleaner.strategy = :transaction
50
- DatabaseCleaner.clean_with(:truncation)
51
50
  end
52
51
 
53
52
  config.around :each do |example|
data/spec/hook_spec.rb CHANGED
@@ -225,6 +225,12 @@ describe 'AppMap class Hooking', docker: false do
225
225
  :value: default
226
226
  YAML
227
227
  test_hook_behavior 'spec/fixtures/hook/instance_method.rb', events_yaml do
228
+ expect(InstanceMethod.instance_method(:say_default).parameters).to eq([])
229
+ expect(InstanceMethod.public_instance_method(:say_default).parameters).to eq([])
230
+ expect(InstanceMethod.new.method(:say_default).parameters).to eq([])
231
+ expect(InstanceMethod.new.public_method(:say_default).parameters).to eq([])
232
+ expect { InstanceMethod.new.singleton_method(:say_default) }.to raise_error(NameError)
233
+
228
234
  expect(InstanceMethod.new.say_default).to eq('default')
229
235
  end
230
236
  end
@@ -315,7 +321,22 @@ describe 'AppMap class Hooking', docker: false do
315
321
  :class: String
316
322
  :value: protected
317
323
  YAML
324
+ parameters = []
318
325
  test_hook_behavior 'spec/fixtures/hook/protected_method.rb', events_yaml do
326
+ expect(ProtectedMethod.singleton_method(:call_protected).parameters).to eq(parameters)
327
+ expect(ProtectedMethod.instance_method(:call_protected).parameters).to eq(parameters)
328
+ expect(ProtectedMethod.public_instance_method(:call_protected).parameters).to eq(parameters)
329
+ expect(ProtectedMethod.new.method(:call_protected).parameters).to eq(parameters)
330
+ expect(ProtectedMethod.new.public_method(:call_protected).parameters).to eq(parameters)
331
+ expect { ProtectedMethod.new.singleton_method(:call_protected) }.to raise_error(NameError)
332
+
333
+ expect(ProtectedMethod.singleton_method(:protected_method).parameters).to eq(parameters)
334
+ expect(ProtectedMethod.instance_method(:protected_method).parameters).to eq(parameters)
335
+ expect(ProtectedMethod.public_instance_method(:protected_method).parameters).to eq(parameters)
336
+ expect(ProtectedMethod.new.method(:protected_method).parameters).to eq(parameters)
337
+ expect(ProtectedMethod.new.public_method(:protected_method).parameters).to eq(parameters)
338
+ expect { ProtectedMethod.new.singleton_method(:protected_method) }.to raise_error(NameError)
339
+
319
340
  expect(ProtectedMethod.new.call_protected).to eq('protected')
320
341
  end
321
342
  end
@@ -358,6 +379,7 @@ describe 'AppMap class Hooking', docker: false do
358
379
  :class: String
359
380
  :value: self.protected
360
381
  YAML
382
+ parameters = []
361
383
  test_hook_behavior 'spec/fixtures/hook/protected_method.rb', events_yaml do
362
384
  expect(ProtectedMethod.call_protected).to eq('self.protected')
363
385
  end
@@ -1100,6 +1122,51 @@ describe 'AppMap class Hooking', docker: false do
1100
1122
  end
1101
1123
  end
1102
1124
 
1125
+ it 'preserves the sighnature of hooked methods' do
1126
+ parameters = []
1127
+ events = <<~YAML
1128
+ ---
1129
+ - :id: 1
1130
+ :event: :call
1131
+ :defined_class: ReportParameters
1132
+ :method_id: report_parameters
1133
+ :path: spec/fixtures/hook/report_parameters.rb
1134
+ :lineno: 5
1135
+ :static: false
1136
+ :parameters:
1137
+ - :name: :args
1138
+ :class: Array
1139
+ :value: "[foo]"
1140
+ :kind: :rest
1141
+ - :name: :kw1
1142
+ :class: String
1143
+ :value: kw1
1144
+ :kind: :keyreq
1145
+ - :name: :kw2
1146
+ :class: NilClass
1147
+ :value: null
1148
+ :kind: :key
1149
+ - :name: :kws
1150
+ :class: Hash
1151
+ :value: "{}"
1152
+ :kind: :keyrest
1153
+ :receiver:
1154
+ :class: ReportParameters
1155
+ :value: ReportParameters
1156
+ - :id: 2
1157
+ :event: :return
1158
+ :parent_id: 1
1159
+ :return_value:
1160
+ :class: Array
1161
+ :value: "[[:rest, :args], [:keyreq, :kw1], [:key, :kw2], [:keyrest, :kws]]"
1162
+ YAML
1163
+ parameters = [[:rest, :args], [:keyreq, :kw1], [:key, :kw2], [:keyrest, :kws]]
1164
+ test_hook_behavior 'spec/fixtures/hook/report_parameters.rb', events do
1165
+ expect(ReportParameters.instance_method(:report_parameters).parameters).to eq(parameters)
1166
+ expect(ReportParameters.new.report_parameters('foo', kw1: 'kw1')).to eq(parameters)
1167
+ end
1168
+ end
1169
+
1103
1170
  describe 'kwargs handling' do
1104
1171
  if ruby_2?
1105
1172
  # https://github.com/applandinc/appmap-ruby/issues/153
@@ -78,7 +78,7 @@ describe 'Rails' do
78
78
  'name' => 'params',
79
79
  'class' => 'ActiveSupport::HashWithIndifferentAccess',
80
80
  'object_id' => Integer,
81
- 'value' => '{"login"=>"alice"}',
81
+ 'value' => '{login=>alice}',
82
82
  'kind' => 'req'
83
83
  ),
84
84
  'receiver' => anything
@@ -127,7 +127,7 @@ describe 'Net::HTTP handler' do
127
127
  :message:
128
128
  - :name: ary
129
129
  :class: Array
130
- :value: '["1", "2"]'
130
+ :value: "[1, 2]"
131
131
  EVENT
132
132
  end
133
133
 
data/test/gem_test.rb CHANGED
@@ -24,7 +24,7 @@ class GemTest < Minitest::Test
24
24
  appmap_file = 'tmp/appmap/minitest/Parser_parser.appmap.json'
25
25
  appmap = JSON.parse(File.read(appmap_file))
26
26
  events = appmap['events']
27
- assert_equal 2, events.size
27
+ assert_equal 6, events.size
28
28
  assert_equal 'call', events.first['event']
29
29
  assert_equal 'default_parser', events.first['method_id']
30
30
  assert_match /\lib\/parser\/base\.rb$/, events.first['path']
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.72.4
4
+ version: 0.74.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: 2022-02-17 00:00:00.000000000 Z
11
+ date: 2022-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -339,10 +339,11 @@ files:
339
339
  - lib/appmap/agent.rb
340
340
  - lib/appmap/builtin_hooks/json.yml
341
341
  - lib/appmap/builtin_hooks/logger.yml
342
- - lib/appmap/builtin_hooks/marshal.yml
343
342
  - lib/appmap/builtin_hooks/net/http.yml
343
+ - lib/appmap/builtin_hooks/open3.yml
344
344
  - lib/appmap/builtin_hooks/openssl.yml
345
345
  - lib/appmap/builtin_hooks/psych.yml
346
+ - lib/appmap/builtin_hooks/ruby.yml
346
347
  - lib/appmap/class_map.rb
347
348
  - lib/appmap/command/agent_setup/init.rb
348
349
  - lib/appmap/command/agent_setup/status.rb
@@ -409,6 +410,7 @@ files:
409
410
  - spec/config_spec.rb
410
411
  - spec/depends/api_spec.rb
411
412
  - spec/depends/spec_helper.rb
413
+ - spec/display_string_spec.rb
412
414
  - spec/fixtures/config/incomplete_config.yml
413
415
  - spec/fixtures/config/invalid_config.yml
414
416
  - spec/fixtures/config/invalid_yaml_config.yml
@@ -447,6 +449,7 @@ files:
447
449
  - spec/fixtures/hook/pkg_a/a.rb
448
450
  - spec/fixtures/hook/prepended_override.rb
449
451
  - spec/fixtures/hook/protected_method.rb
452
+ - spec/fixtures/hook/report_parameters.rb
450
453
  - spec/fixtures/hook/revoke_api_key.appmap.json
451
454
  - spec/fixtures/hook/singleton_method.rb
452
455
  - spec/fixtures/hook/spec/api_spec.rb
@@ -1,8 +0,0 @@
1
- - methods:
2
- - Marshal#load
3
- - Marshal#restore
4
- require_name: ruby
5
- label: deserialize.unsafe
6
- - method: Marshal#dump
7
- require_name: ruby
8
- label: serialize