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 +1 -0
- data/README.rdoc +6 -2
- data/VERSION +1 -1
- data/lib/method_info.rb +41 -3
- data/spec/method_info_spec.rb +51 -2
- data/spec/spec_helper.rb +0 -1
- metadata +1 -1
data/.gitignore
CHANGED
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
|
-
|
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.
|
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
|
-
|
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
|
data/spec/method_info_spec.rb
CHANGED
@@ -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
|
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
|
-
|
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