method_info 0.0.0 → 0.0.1
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.
- 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