method_locator 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ .idea/*
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "http://rubygems.org"
2
+ gemspec
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.
@@ -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.
@@ -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,3 @@
1
+ module MethodLocator
2
+ VERSION = "0.0.1"
3
+ end
@@ -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)
@@ -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