method_locator 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 +5 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +142 -0
- data/Rakefile +1 -0
- data/lib/method_locator.rb +43 -0
- data/lib/method_locator/version.rb +3 -0
- data/method_locator.gemspec +23 -0
- data/spec/method_locator/method_locator_spec.rb +114 -0
- data/spec/spec_helper.rb +8 -0
- metadata +70 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011 Ryan LeCompte
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# Method Locator
|
2
|
+
|
3
|
+
Method Locator is a Ruby gem that allows you to easily determine the method lookup path of a particular
|
4
|
+
object / class / module, as well as find all places (represented as UnboundMethod instances) where
|
5
|
+
a callable method is defined on an object. This is very useful in an environment such as Rails where
|
6
|
+
you are unsure where a method may be defined or overridden. Note that by default, Ruby tends to hide
|
7
|
+
singleton classes when you invoke Class#ancestors. The new Object#method_lookup_path does not hide
|
8
|
+
these singleton classes and will return them, so that you get a true representation of how Ruby
|
9
|
+
performs lookups for methods.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Method Locator is available as a RubyGem:
|
14
|
+
|
15
|
+
gem install method_locator
|
16
|
+
|
17
|
+
## Examples
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
module M1
|
21
|
+
def foo
|
22
|
+
puts "foo from M1"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module M2
|
27
|
+
def foo
|
28
|
+
puts "foo from M2"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module M3
|
33
|
+
def blah
|
34
|
+
puts "blah from M3"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module M4
|
39
|
+
def self.hello
|
40
|
+
puts "hello from M4"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class A
|
45
|
+
def foo
|
46
|
+
puts "foo from A"
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.blah
|
50
|
+
puts "blah from A"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class B < A
|
55
|
+
include M1
|
56
|
+
include M2
|
57
|
+
|
58
|
+
def foo
|
59
|
+
puts "foo from B"
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.blah
|
63
|
+
puts "blah from B"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
B.extend(M3)
|
68
|
+
|
69
|
+
b = B.new
|
70
|
+
|
71
|
+
def b.foo
|
72
|
+
puts "foo from b's singleton class!"
|
73
|
+
end
|
74
|
+
|
75
|
+
puts "all method owners of b#foo"
|
76
|
+
b.methods_for(:foo).each { |m| puts "#{m.name}, owner: #{m.owner}" }
|
77
|
+
|
78
|
+
puts "all method owners of B.blah"
|
79
|
+
B.methods_for(:blah).each { |m| puts "#{m.name}, owner: #{m.owner}" }
|
80
|
+
|
81
|
+
puts "all method owners of B.new"
|
82
|
+
B.methods_for(:new).each { |m| puts "#{m.name}, owner: #{m.owner}" }
|
83
|
+
|
84
|
+
puts "all method owners of M4.hello"
|
85
|
+
M4.methods_for(:hello).each { |m| puts "#{m.name}, owner: #{m.owner}" }
|
86
|
+
|
87
|
+
puts "method lookup path for b"
|
88
|
+
p b.method_lookup_path
|
89
|
+
|
90
|
+
puts "method lookup path for B"
|
91
|
+
p B.method_lookup_path
|
92
|
+
|
93
|
+
puts "method lookup path for M2"
|
94
|
+
p M2.method_lookup_path
|
95
|
+
|
96
|
+
|
97
|
+
# OUTPUT
|
98
|
+
|
99
|
+
all method owners of b#foo
|
100
|
+
foo, owner: #<Class:#<B:0x007fcc74031d38>>
|
101
|
+
foo, owner: B
|
102
|
+
foo, owner: M2
|
103
|
+
foo, owner: M1
|
104
|
+
foo, owner: A
|
105
|
+
|
106
|
+
all method owners of B.blah
|
107
|
+
blah, owner: #<Class:B>
|
108
|
+
blah, owner: M3
|
109
|
+
blah, owner: #<Class:A>
|
110
|
+
|
111
|
+
all method owners of B.new
|
112
|
+
new, owner: Class
|
113
|
+
|
114
|
+
all method owners of M4.hello
|
115
|
+
hello, owner: #<Class:M4>
|
116
|
+
|
117
|
+
method lookup path for b
|
118
|
+
[#<Class:#<B:0x007fcc74031d38>>, B, M2, M1, A, Object, MethodLocator, Kernel, BasicObject]
|
119
|
+
|
120
|
+
method lookup path for B
|
121
|
+
[#<Class:B>, M3, #<Class:A>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, MethodLocator, Kernel, BasicObject]
|
122
|
+
|
123
|
+
method lookup path for M2
|
124
|
+
[#<Class:M2>, Module, Object, MethodLocator, Kernel, BasicObject]
|
125
|
+
```
|
126
|
+
|
127
|
+
## Note on Patches/Pull Requests
|
128
|
+
|
129
|
+
* Fork the project.
|
130
|
+
* Make your feature addition or bug fix.
|
131
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
132
|
+
* Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
133
|
+
* Send me a pull request. Bonus points for topic branches.
|
134
|
+
|
135
|
+
## Authors
|
136
|
+
|
137
|
+
* Ryan LeCompte
|
138
|
+
|
139
|
+
## Copyright
|
140
|
+
|
141
|
+
Copyright (c) 2011 Ryan LeCompte. See LICENSE for
|
142
|
+
further details.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "method_locator/version"
|
2
|
+
|
3
|
+
# MethodLocator finds all method definitions in an object instance, class, or
|
4
|
+
# module ancestor chain. This code will make more sense if you understand Ruby's
|
5
|
+
# object model and method lookup path. A great explanation of this can be found in:
|
6
|
+
# "Ruby's Eigenclasses Demystified" - http://blog.madebydna.com/all/code/2011/06/24/eigenclasses-demystified.html
|
7
|
+
|
8
|
+
module MethodLocator
|
9
|
+
def methods_for(meth)
|
10
|
+
method_lookup_path.each_with_object([]) do |clazz, matches|
|
11
|
+
matches << clazz.instance_method(meth) if instance_methods_for(clazz).include?(meth)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_lookup_path
|
16
|
+
module_or_regular_object? ? nonclass_lookup_path : class_lookup_path
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def module_or_regular_object?
|
22
|
+
self.class == Module || !is_a?(Class)
|
23
|
+
end
|
24
|
+
|
25
|
+
def nonclass_lookup_path
|
26
|
+
self.class.ancestors.unshift(singleton_class)
|
27
|
+
end
|
28
|
+
|
29
|
+
def class_lookup_path
|
30
|
+
lookup_path = ancestors.grep(Class).map(&:singleton_class) + Class.ancestors
|
31
|
+
# reverse is used here since included_modules contains all modules defined in
|
32
|
+
# the current class as well as in its ancestors.
|
33
|
+
lookup_path.reverse.map do |clazz|
|
34
|
+
[clazz.included_modules, clazz]
|
35
|
+
end.flatten.uniq.reverse
|
36
|
+
end
|
37
|
+
|
38
|
+
def instance_methods_for(clazz)
|
39
|
+
clazz.instance_methods(false) + clazz.private_instance_methods(false)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
Object.send(:include, MethodLocator)
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "method_locator/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "method_locator"
|
7
|
+
s.version = MethodLocator::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Ryan LeCompte"]
|
10
|
+
s.email = ["lecompte@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/ryanlecompte/method_locator"
|
12
|
+
s.summary = %q{Method Locator provides a way to traverse an object's method lookup path to find all places where a method may be defined.}
|
13
|
+
s.description = %q{Method Locator provides a way to traverse an object's method lookup path to find all places where a method may be defined.}
|
14
|
+
|
15
|
+
s.rubyforge_project = "method_locator"
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- spec/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.add_development_dependency("rspec", "~> 2.5.0")
|
23
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe MethodLocator do
|
5
|
+
|
6
|
+
describe "#methods_for" do
|
7
|
+
it "returns proper methods for regular object instances" do
|
8
|
+
instance = B.new
|
9
|
+
|
10
|
+
def instance.foo
|
11
|
+
end
|
12
|
+
|
13
|
+
instance.methods_for(:foo).map(&:owner).should == [instance.singleton_class,
|
14
|
+
B,
|
15
|
+
M2,
|
16
|
+
M1,
|
17
|
+
A]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "returns proper methods for classes" do
|
21
|
+
B.methods_for(:blah).map(&:owner).should == [B.singleton_class, M3, A.singleton_class]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns proper methods for modules" do
|
25
|
+
M4.methods_for(:hello).map(&:owner).should == [M4.singleton_class]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#method_lookup_path" do
|
30
|
+
it "returns proper path for regular object instances" do
|
31
|
+
instance = B.new
|
32
|
+
method_lookup_path_for(instance).should == [instance.singleton_class,
|
33
|
+
B,
|
34
|
+
M2,
|
35
|
+
M1,
|
36
|
+
A,
|
37
|
+
Object,
|
38
|
+
MethodLocator,
|
39
|
+
Kernel,
|
40
|
+
BasicObject]
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns proper path for classes" do
|
44
|
+
method_lookup_path_for(B).should == [B.singleton_class,
|
45
|
+
M3,
|
46
|
+
A.singleton_class,
|
47
|
+
Object.singleton_class,
|
48
|
+
BasicObject.singleton_class,
|
49
|
+
Class,
|
50
|
+
Module,
|
51
|
+
Object,
|
52
|
+
MethodLocator,
|
53
|
+
Kernel,
|
54
|
+
BasicObject]
|
55
|
+
end
|
56
|
+
|
57
|
+
it "returns proper path for modules" do
|
58
|
+
method_lookup_path_for(M2).should == [M2.singleton_class,
|
59
|
+
Module,
|
60
|
+
Object,
|
61
|
+
MethodLocator,
|
62
|
+
Kernel,
|
63
|
+
BasicObject]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def method_lookup_path_for(obj)
|
68
|
+
allowed_items = [obj, M1, M2, M3, M4, A, B, Class, Module, Object, MethodLocator, Kernel, BasicObject]
|
69
|
+
allowed_items = allowed_items + allowed_items.map(&:singleton_class)
|
70
|
+
obj.method_lookup_path.keep_if { |c| allowed_items.include?(c) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
module M1
|
76
|
+
def foo
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
module M2
|
81
|
+
def foo
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
module M3
|
86
|
+
def blah
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
module M4
|
91
|
+
def self.hello
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
class A
|
96
|
+
def foo
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.blah
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
class B < A
|
104
|
+
include M1
|
105
|
+
include M2
|
106
|
+
|
107
|
+
def foo
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.blah
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
B.extend(M3)
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
require 'rspec'
|
4
|
+
require 'method_locator'
|
5
|
+
|
6
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
7
|
+
RSpec.configure do |config|
|
8
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: method_locator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan LeCompte
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-09-21 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: &70159058806000 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.5.0
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70159058806000
|
25
|
+
description: Method Locator provides a way to traverse an object's method lookup path
|
26
|
+
to find all places where a method may be defined.
|
27
|
+
email:
|
28
|
+
- lecompte@gmail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- .gitignore
|
34
|
+
- Gemfile
|
35
|
+
- LICENSE
|
36
|
+
- README.md
|
37
|
+
- Rakefile
|
38
|
+
- lib/method_locator.rb
|
39
|
+
- lib/method_locator/version.rb
|
40
|
+
- method_locator.gemspec
|
41
|
+
- spec/method_locator/method_locator_spec.rb
|
42
|
+
- spec/spec_helper.rb
|
43
|
+
homepage: http://github.com/ryanlecompte/method_locator
|
44
|
+
licenses: []
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options: []
|
47
|
+
require_paths:
|
48
|
+
- lib
|
49
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
requirements: []
|
62
|
+
rubyforge_project: method_locator
|
63
|
+
rubygems_version: 1.8.10
|
64
|
+
signing_key:
|
65
|
+
specification_version: 3
|
66
|
+
summary: Method Locator provides a way to traverse an object's method lookup path
|
67
|
+
to find all places where a method may be defined.
|
68
|
+
test_files:
|
69
|
+
- spec/method_locator/method_locator_spec.rb
|
70
|
+
- spec/spec_helper.rb
|