appmap 0.34.4 → 0.34.5

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: 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