appmap 0.72.4 → 0.74.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 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