method_info 0.0.0 → 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -19,3 +19,4 @@ rdoc
19
19
  pkg
20
20
 
21
21
  ## PROJECT::SPECIFIC
22
+ method_info.gemspec
data/README.rdoc CHANGED
@@ -4,8 +4,12 @@ Provides info about methods that can be called on an object and where they are d
4
4
 
5
5
  Usage:
6
6
 
7
- require 'method_info'
8
- "abc".method_info
7
+ >> require 'method_info'
8
+ => true
9
+ >> "abc".method_info.ancestors
10
+ => [String, Enumerable, Comparable, Object, MethodInfoMethod, Kernel]
11
+ >> "abc".method_info.method_owner(:==)
12
+ => String
9
13
 
10
14
  == Note on Patches/Pull Requests
11
15
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.0
1
+ 0.0.1
data/lib/method_info.rb CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  module MethodInfoMethod
3
2
  def method_info
4
3
  MethodInfo.new(self)
@@ -25,18 +24,57 @@ class MethodInfo
25
24
  @ancestors += @object.class.ancestors
26
25
  end
27
26
 
27
+ # Returns the class or module where method is defined
28
28
  def method_owner(method)
29
29
  @object.method(method).owner
30
30
  end
31
31
 
32
+ # Returns the same value as :method_owner, but also check if the
33
+ # method is handled by a method_missing method in the chain. If it
34
+ # is :method_missing is returned, otherwise the error raised is
35
+ # reraised. This requires an invocation of the method which could
36
+ # cause side effects. Hence this method is considered to be
37
+ # dangerous.
32
38
  def method_owner!(method)
33
39
  method_owner(method)
34
40
  rescue NameError => e
35
41
  begin
36
- @object.send(method)
37
- method_owner(:method_missing)
42
+ @object.clone.send(method)
43
+ :method_missing
38
44
  rescue NoMethodError
39
45
  raise e
40
46
  end
41
47
  end
48
+
49
+ def method_map
50
+ @method_map = Hash.new
51
+ current_ancestors = ancestors
52
+ @method_map['__ancestors'] = current_ancestors
53
+ current_ancestors.each do |ancestor|
54
+ @method_map[ancestor] = []
55
+ end
56
+
57
+ @object.methods.each do |method|
58
+ @method_map[method_owner(method)] << method
59
+ end
60
+ @method_map
61
+ end
62
+
63
+ def to_s
64
+ map = method_map
65
+
66
+ result = ""
67
+ map['__ancestors'].each do |ancestor|
68
+ break if ancestor == Object
69
+ next if map[ancestor].empty?
70
+ result +=
71
+ "=== #{ancestor} ===\n" +
72
+ map[ancestor].sort.join(", ") +
73
+ "\n"
74
+ end
75
+ index_of_object = map['__ancestors'].index(Object)
76
+ result +=
77
+ "=== #{map['__ancestors'][index_of_object..-1].join(", ")} ==="
78
+ result
79
+ end
42
80
  end
@@ -50,6 +50,13 @@ describe MethodInfo do
50
50
  def duplicate_instance_method
51
51
  :abstract_duplicate_instance_method
52
52
  end
53
+
54
+ def method_missing(method)
55
+ if method == :missing_method_handled_at_abstract
56
+ return :missing_method_handled_at_abstract
57
+ end
58
+ super
59
+ end
53
60
  end
54
61
 
55
62
  class ConcreteMethodOwnerDummy < AbstractMethodOwnerDummy
@@ -83,6 +90,10 @@ describe MethodInfo do
83
90
  lambda { ConcreteMethodOwnerDummy.new.method_info.method_owner(:poof) }.should raise_error(NameError)
84
91
  end
85
92
 
93
+ it "raises an error if the method is handled by :method_missing" do
94
+ lambda { ConcreteMethodOwnerDummy.new.method_info.method_owner(:missing_method_handled_at_concrete) }.should raise_error(NameError)
95
+ end
96
+
86
97
  describe "method_owner!" do
87
98
  it "is the class of the object for an instance_method" do
88
99
  ConcreteMethodOwnerDummy.new.method_info.method_owner!(:concrete_instance_method).should ==
@@ -98,9 +109,47 @@ describe MethodInfo do
98
109
  lambda { ConcreteMethodOwnerDummy.new.method_info.method_owner!(:poof) }.should raise_error(NameError)
99
110
  end
100
111
 
101
- it "is the first method missing method if there is one that handles the method" do
112
+ it "is :method_missing if the concrete class handles the method" do
102
113
  ConcreteMethodOwnerDummy.new.method_info.method_owner!(:missing_method_handled_at_concrete).should ==
103
- "ConcreteMethodOwnerDummy::method_missing"
114
+ :method_missing
115
+ end
116
+
117
+ it "is :method_missing if the abstract class handles the method" do
118
+ ConcreteMethodOwnerDummy.new.method_info.method_owner!(:missing_method_handled_at_abstract).should ==
119
+ :method_missing
120
+ end
121
+
122
+ it "is :method_missing if the object has a method_missing singleton method that handles the method" do
123
+ monkey = Object.new
124
+ def monkey.method_missing(method)
125
+ if method == :missing_method_handled_at_singleton_method
126
+ return :missing_method_handled_at_singleton_method
127
+ end
128
+ super
129
+ end
130
+ monkey.method_info.method_owner!(:missing_method_handled_at_singleton_method).should ==
131
+ :method_missing
132
+ end
133
+
134
+ it "does not modify an object whose method_missing has side effects" do
135
+ monkey = Object.new
136
+ monkey.instance_eval { @hair = "brown" }
137
+ def monkey.method_missing(method)
138
+ @hair = "blue"
139
+ end
140
+ monkey.method_info.method_owner!(:unknown_method)
141
+ monkey.instance_eval('@hair').should == "brown"
142
+ end
143
+
144
+ # Undesirable behaviour, but I don't think there is an easy way around it
145
+ it "will not protect an object's objects from method_missing side effects" do
146
+ monkey = Object.new
147
+ monkey.instance_eval { @limbs = [:arms, :legs] }
148
+ def monkey.method_missing(method)
149
+ @limbs.shift
150
+ end
151
+ monkey.method_info.method_owner!(:unknown_method)
152
+ monkey.instance_eval('@limbs').should == [:legs]
104
153
  end
105
154
 
106
155
  it "raises an error if the object does not respond to the method" do
data/spec/spec_helper.rb CHANGED
@@ -3,7 +3,6 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
  require 'method_info'
4
4
  require 'spec'
5
5
  require 'spec/autorun'
6
- require 'ruby-debug'
7
6
 
8
7
  Spec::Runner.configure do |config|
9
8
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: method_info
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0
4
+ version: 0.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom ten Thij