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