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