appmap 0.34.4 → 0.34.5

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: a3a509a94faa2a85ff11702ae55ab3df975f04416d93d169b0a24989fd520b9d
4
- data.tar.gz: 3895b9c788413f85738ba487e717bf48e4dfc2e8c0792f30c6f9f6ba7dc5e9c8
3
+ metadata.gz: 3b4318e1ef7e025a8f616022cefd323784e7b8ee8ccf06b1b7167b9937e860df
4
+ data.tar.gz: c6a743f2c7f4ebe8cd58f7ae971b5dae15791bc808dab9d4dcf87f4971986818
5
5
  SHA512:
6
- metadata.gz: 07fe201395cad27e731402b5b2ff0efd653056dad16b9301657034df0b6e7fd3a205768ef3656874f55109edfc4d788d752f8bc73f4ff6874c815104547ce281
7
- data.tar.gz: cbbd1551c352519954e9ddc9dd27279d6d232663fb67fd5ed4369a603a382f140593aa195aa08f35a57b0a14d38bef07bfef0aa7bd942d660a865fb3682d500d
6
+ metadata.gz: 9f271a71bb8bffa7004d054ae91646c9c93a476b15cbe80c459a9cafcb13ab0169be6c8a2db9b8cc993c3bc767deef4c9c0ca4befe6bd6881cf2d93c7b692b64
7
+ data.tar.gz: 3b6c5067a325fa2a0aed71b5ed92179196061cd6cfa015f907a25dab798e757c2cd53efbd028513df1f22cc2e2b8aa9345f8d19aa27018eccfbee4cd39766ece
@@ -1,3 +1,6 @@
1
+ # v0.34.5
2
+ * Ensure that hooking a method doesn't change its arity.
3
+
1
4
  # v0.34.4
2
5
  * Make sure `AppMap:Rails::SQLExaminer::ActiveRecordExaminer.server_version` only calls
3
6
  `ActiveRecord::Base.connection.database_version` if it's available.
data/README.md CHANGED
@@ -57,6 +57,15 @@ end
57
57
 
58
58
  Then install with `bundle`.
59
59
 
60
+ **Railtie**
61
+
62
+ If you are using Ruby on Rails, require the railtie after Rails is loaded.
63
+
64
+ ```
65
+ # application.rb is a good place to do this, along with all the other railties.
66
+ require 'appmap/railtie'
67
+ ```
68
+
60
69
  # Configuration
61
70
 
62
71
  When you run your program, the `appmap` gem reads configuration settings from `appmap.yml`. Here's a sample configuration
@@ -8,7 +8,12 @@
8
8
  (!SPECIAL_CONST_P(obj) && \
9
9
  (BUILTIN_TYPE(obj) == T_CLASS || BUILTIN_TYPE(obj) == T_MODULE))
10
10
 
11
- static VALUE singleton_method_owner_name(VALUE klass, VALUE method)
11
+ #define ARITIES_KEY "__arities__"
12
+
13
+ VALUE am_AppMapHook;
14
+
15
+ static VALUE
16
+ singleton_method_owner_name(VALUE klass, VALUE method)
12
17
  {
13
18
  VALUE owner = rb_funcall(method, rb_intern("owner"), 0);
14
19
  VALUE attached = rb_ivar_get(owner, rb_intern("__attached__"));
@@ -27,10 +32,64 @@ static VALUE singleton_method_owner_name(VALUE klass, VALUE method)
27
32
  // #to_s on the method's owner and hope for the best.
28
33
  return rb_funcall(owner, rb_intern("to_s"), 0);
29
34
  }
30
-
35
+
36
+
37
+ static VALUE
38
+ am_define_method_with_arity(VALUE mod, VALUE name, VALUE arity, VALUE proc)
39
+ {
40
+ VALUE arities_key = rb_intern(ARITIES_KEY);
41
+ VALUE arities = rb_ivar_get(mod, arities_key);
42
+
43
+ if (arities == Qundef || NIL_P(arities)) {
44
+ arities = rb_hash_new();
45
+ rb_ivar_set(mod, arities_key, arities);
46
+ }
47
+ rb_hash_aset(arities, name, arity);
48
+
49
+ return rb_funcall(mod, rb_intern("define_method"), 2, name, proc);
50
+ }
51
+
52
+ static VALUE
53
+ am_get_method_arity(VALUE method, VALUE orig_arity_method)
54
+ {
55
+ VALUE owner = rb_funcall(method, rb_intern("owner"), 0);
56
+ VALUE arities = rb_ivar_get(owner, rb_intern(ARITIES_KEY));
57
+ VALUE name = rb_funcall(method, rb_intern("name"), 0);
58
+ VALUE arity = Qnil;
59
+ // See if we saved an arity for the method.
60
+ if (!NIL_P(arities)) {
61
+ arity = rb_hash_aref(arities, name);
62
+ }
63
+ // Didn't find one, call the original method.
64
+ if (NIL_P(arity)) {
65
+ VALUE bound_method = rb_funcall(orig_arity_method, rb_intern("bind"), 1, method);
66
+ arity = rb_funcall(bound_method, rb_intern("call"), 0);
67
+ }
68
+
69
+ return arity;
70
+ }
71
+
72
+ static VALUE
73
+ am_unbound_method_arity(VALUE method)
74
+ {
75
+ VALUE orig_unbound_method_arity = rb_ivar_get(am_AppMapHook, rb_intern("@unbound_method_arity"));
76
+ return am_get_method_arity(method, orig_unbound_method_arity);
77
+ }
78
+
79
+ static VALUE
80
+ am_method_arity(VALUE method)
81
+ {
82
+ VALUE orig_method_arity = rb_ivar_get(am_AppMapHook, rb_intern("@method_arity"));
83
+ return am_get_method_arity(method, orig_method_arity);
84
+ }
85
+
31
86
  void Init_appmap() {
32
87
  VALUE appmap = rb_define_module("AppMap");
33
- VALUE hook = rb_define_class_under(appmap, "Hook", rb_cObject);
88
+ am_AppMapHook = rb_define_class_under(appmap, "Hook", rb_cObject);
89
+
90
+ rb_define_singleton_method(am_AppMapHook, "singleton_method_owner_name", singleton_method_owner_name, 1);
34
91
 
35
- rb_define_singleton_method(hook, "singleton_method_owner_name", singleton_method_owner_name, 1);
92
+ rb_define_method(rb_cModule, "define_method_with_arity", am_define_method_with_arity, 3);
93
+ rb_define_method(rb_cUnboundMethod, "arity", am_unbound_method_arity, 0);
94
+ rb_define_method(rb_cMethod, "arity", am_method_arity, 0);
36
95
  }
@@ -6,6 +6,9 @@ module AppMap
6
6
  class Hook
7
7
  LOG = (ENV['DEBUG'] == 'true')
8
8
 
9
+ @unbound_method_arity = ::UnboundMethod.instance_method(:arity)
10
+ @method_arity = ::Method.instance_method(:arity)
11
+
9
12
  class << self
10
13
  def lock_builtins
11
14
  return if @builtins_hooked
@@ -30,7 +30,7 @@ module AppMap
30
30
  msg = if method_display_name
31
31
  "#{method_display_name}"
32
32
  else
33
- "#{hook_method.name} (class resolution deferrred)"
33
+ "#{hook_method.name} (class resolution deferred)"
34
34
  end
35
35
  warn "AppMap: Hooking " + msg
36
36
  end
@@ -41,36 +41,39 @@ module AppMap
41
41
  after_hook = self.method(:after_hook)
42
42
  with_disabled_hook = self.method(:with_disabled_hook)
43
43
 
44
- hook_class.define_method hook_method.name do |*args, &block|
45
- instance_method = hook_method.bind(self).to_proc
44
+ hook_method_def = nil
45
+ hook_class.instance_eval do
46
+ hook_method_def = Proc.new do |*args, &block|
47
+ instance_method = hook_method.bind(self).to_proc
46
48
 
47
- # We may not have gotten the class for the method during
48
- # initialization (e.g. for a singleton method on an embedded
49
- # struct), so make sure we have it now.
50
- defined_class,_ = Hook.qualify_method_name(hook_method) unless defined_class
49
+ # We may not have gotten the class for the method during
50
+ # initialization (e.g. for a singleton method on an embedded
51
+ # struct), so make sure we have it now.
52
+ defined_class,_ = Hook.qualify_method_name(hook_method) unless defined_class
51
53
 
52
- hook_disabled = Thread.current[HOOK_DISABLE_KEY]
53
- enabled = true if !hook_disabled && AppMap.tracing.enabled?
54
- return instance_method.call(*args, &block) unless enabled
54
+ hook_disabled = Thread.current[HOOK_DISABLE_KEY]
55
+ enabled = true if !hook_disabled && AppMap.tracing.enabled?
56
+ return instance_method.call(*args, &block) unless enabled
55
57
 
56
- call_event, start_time = with_disabled_hook.() do
57
- before_hook.(self, defined_class, args)
58
- end
59
- return_value = nil
60
- exception = nil
61
- begin
62
- return_value = instance_method.(*args, &block)
63
- rescue
64
- exception = $ERROR_INFO
65
- raise
66
- ensure
67
- with_disabled_hook.() do
68
- after_hook.(call_event, start_time, return_value, exception)
58
+ call_event, start_time = with_disabled_hook.() do
59
+ before_hook.(self, defined_class, args)
60
+ end
61
+ return_value = nil
62
+ exception = nil
63
+ begin
64
+ return_value = instance_method.(*args, &block)
65
+ rescue
66
+ exception = $ERROR_INFO
67
+ raise
68
+ ensure
69
+ with_disabled_hook.() do
70
+ after_hook.(call_event, start_time, return_value, exception)
71
+ end
69
72
  end
70
73
  end
71
74
  end
75
+ hook_class.define_method_with_arity(hook_method.name, hook_method.arity, hook_method_def)
72
76
  end
73
-
74
77
  protected
75
78
 
76
79
  def before_hook(receiver, defined_class, args)
@@ -43,7 +43,7 @@ module AppMap
43
43
  require 'hashie'
44
44
  h.extend(Hashie::Extensions::DeepLocate)
45
45
  keys = %i(path location)
46
- entries = h.deep_locate ->(k,v,o) {
46
+ h.deep_locate ->(k,v,o) {
47
47
  next unless keys.include?(k)
48
48
 
49
49
  fix = ->(v) {v.gsub(%r{#{Gem.dir}/gems/.*(?=lib)}, '')}
@@ -3,7 +3,7 @@
3
3
  module AppMap
4
4
  URL = 'https://github.com/applandinc/appmap-ruby'
5
5
 
6
- VERSION = '0.34.4'
6
+ VERSION = '0.34.5'
7
7
 
8
8
  APPMAP_FORMAT_VERSION = '1.2'
9
9
  end
@@ -669,4 +669,11 @@ describe 'AppMap class Hooking', docker: false do
669
669
  end
670
670
  end
671
671
  end
672
+
673
+ it "preserves the arity of hooked methods" do
674
+ invoke_test_file 'spec/fixtures/hook/instance_method.rb' do
675
+ expect(InstanceMethod.instance_method(:say_echo).arity).to be(1)
676
+ expect(InstanceMethod.new.method(:say_echo).arity).to be(1)
677
+ end
678
+ end
672
679
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: appmap
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.34.4
4
+ version: 0.34.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kevin Gilpin
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-09-21 00:00:00.000000000 Z
11
+ date: 2020-09-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport