matthew-method_lister 0.2.0
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/README.markdown +177 -0
- data/lib/method_lister/color_display.rb +77 -0
- data/lib/method_lister/find_result.rb +50 -0
- data/lib/method_lister/finder.rb +107 -0
- data/lib/method_lister/ruby_ext.rb +24 -0
- data/lib/method_lister/simple_display.rb +55 -0
- data/lib/method_lister.rb +7 -0
- data/spec/color_display_spec.rb +37 -0
- data/spec/find_result_spec.rb +162 -0
- data/spec/finder_spec.rb +146 -0
- data/spec/helpers/matchers/list_methods.rb +41 -0
- data/spec/helpers/object_mother/find_result.rb +3 -0
- data/spec/helpers/object_mother/find_scenario.rb +45 -0
- data/spec/rcov.opts +6 -0
- data/spec/ruby_ext_spec.rb +55 -0
- data/spec/scenarios/class_with_inheritance.rb +17 -0
- data/spec/scenarios/class_with_inheritance_and_modules.rb +25 -0
- data/spec/scenarios/eigenclass.rb +18 -0
- data/spec/scenarios/eigenclass_with_modules.rb +26 -0
- data/spec/scenarios/filters_results_without_methods.rb +20 -0
- data/spec/scenarios/mixed_visibility_methods.rb +36 -0
- data/spec/scenarios/object_without_eigenclass.rb +4 -0
- data/spec/scenarios/overloaded_methods.rb +35 -0
- data/spec/scenarios/overloaded_methods_with_modules_mixed_in.rb +26 -0
- data/spec/scenarios/private_methods.rb +14 -0
- data/spec/scenarios/single_class.rb +11 -0
- data/spec/scenarios/single_class_with_module_mixed_in.rb +19 -0
- data/spec/simple_display_spec.rb +38 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +14 -0
- metadata +86 -0
data/README.markdown
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
About
|
|
2
|
+
=====
|
|
3
|
+
|
|
4
|
+
Method Lister is used to query objects and discover which ancestor implements
|
|
5
|
+
which methods. It's quite common to have a lot of mixins and several classes
|
|
6
|
+
in an object's class hierarchy, especially in a Rails application. To help
|
|
7
|
+
with this Method Lister adds the ability to find out in which classes/modules
|
|
8
|
+
the methods on an object are implemented.
|
|
9
|
+
|
|
10
|
+
Method Lister adds 3 methods to all objects in the system: `ls`, `grep`, and
|
|
11
|
+
`which`. Since these names are sometimes taken you can also use `mls`,
|
|
12
|
+
`mgrep`, and `mwhich`.
|
|
13
|
+
|
|
14
|
+
Method Lister is intended to be used from IRB or during debugging.
|
|
15
|
+
|
|
16
|
+
Install
|
|
17
|
+
=======
|
|
18
|
+
|
|
19
|
+
Get the gem:
|
|
20
|
+
|
|
21
|
+
# Add GitHub as a Gem Source (only have to do this once)
|
|
22
|
+
gem sources -a http://gems.github.com
|
|
23
|
+
|
|
24
|
+
# Install the gem
|
|
25
|
+
sudo gem install matthew-method_lister
|
|
26
|
+
|
|
27
|
+
Open up `~/.irbrc` and add these lines:
|
|
28
|
+
|
|
29
|
+
require 'rubygems'
|
|
30
|
+
require 'method_lister'
|
|
31
|
+
|
|
32
|
+
Usage
|
|
33
|
+
=====
|
|
34
|
+
|
|
35
|
+
`ls` or `mls`
|
|
36
|
+
-------------
|
|
37
|
+
|
|
38
|
+
The `ls` command will list all methods an object responds to, organized by the
|
|
39
|
+
module or class which provides the implementation. For example (results may
|
|
40
|
+
vary, depending on what you have loaded):
|
|
41
|
+
|
|
42
|
+
>> [].ls
|
|
43
|
+
========== Module Kernel ==========
|
|
44
|
+
PUBLIC: inspect clone method public_methods instance_variable_defined?
|
|
45
|
+
equal? which freeze grep mgrep methods respond_to? dup instance_variables
|
|
46
|
+
__id__ object_id eql? require id singleton_methods send taint frozen?
|
|
47
|
+
instance_variable_get __send__ instance_of? mls to_a type mwhich
|
|
48
|
+
protected_methods instance_eval == display === ls instance_variable_set
|
|
49
|
+
kind_of? extend to_s gem hash pretty_inspect class tainted? =~
|
|
50
|
+
private_methods nil? untaint is_a?
|
|
51
|
+
|
|
52
|
+
========== Module PP::ObjectMixin ==========
|
|
53
|
+
PUBLIC: pretty_print_instance_variables pretty_print_inspect pretty_print
|
|
54
|
+
pretty_print_cycle
|
|
55
|
+
|
|
56
|
+
========== Module Enumerable ==========
|
|
57
|
+
PUBLIC: find_all sort_by collect include? detect max sort partition any?
|
|
58
|
+
to_a reject zip find min member? entries inject all? select
|
|
59
|
+
each_with_index grep map
|
|
60
|
+
|
|
61
|
+
========== Class Array ==========
|
|
62
|
+
PUBLIC: delete_if & map! empty? indexes rindex reject last to_s assoc
|
|
63
|
+
reverse! sort each include? values_at slice * sort! each_index fetch +
|
|
64
|
+
clear pretty_print concat size join flatten! shift - eql? reverse to_ary
|
|
65
|
+
insert [] indices nitems inspect rassoc replace compact! []= | collect
|
|
66
|
+
push delete_at << frozen? reverse_each flatten hash collect! uniq! select
|
|
67
|
+
first to_a fill index reject! zip pack unshift compact transpose <=>
|
|
68
|
+
pretty_print_cycle at == slice! length uniq delete pop map
|
|
69
|
+
|
|
70
|
+
You can show protected and private methods too by passing in "true":
|
|
71
|
+
|
|
72
|
+
>> [].ls true
|
|
73
|
+
========== Module Kernel ==========
|
|
74
|
+
PUBLIC: inspect clone method public_methods instance_variable_defined?
|
|
75
|
+
equal? which freeze grep mgrep methods respond_to? dup instance_variables
|
|
76
|
+
__id__ object_id eql? require id singleton_methods send taint frozen?
|
|
77
|
+
instance_variable_get __send__ instance_of? mls to_a type mwhich
|
|
78
|
+
protected_methods instance_eval == display === ls instance_variable_set
|
|
79
|
+
kind_of? extend to_s gem hash pretty_inspect class tainted? =~
|
|
80
|
+
private_methods nil? untaint is_a?
|
|
81
|
+
|
|
82
|
+
PRIVATE: select global_variables readline warn singleton_method_added gsub
|
|
83
|
+
exit! method_missing exec abort load chomp! remove_instance_variable print
|
|
84
|
+
eval proc untrace_var srand Integer local_variables
|
|
85
|
+
singleton_method_removed readlines raise chop getc gem_original_require
|
|
86
|
+
system at_exit putc set_trace_func rand test lambda Float p
|
|
87
|
+
initialize_copy singleton_method_undefined chomp fail callcc sub! syscall
|
|
88
|
+
sleep iterator? catch autoload puts ` pp String sprintf split caller gsub!
|
|
89
|
+
open block_given? throw URI gets trap sub loop Array fork format exit
|
|
90
|
+
chop! printf binding autoload? scan trace_var
|
|
91
|
+
|
|
92
|
+
========== Module PP::ObjectMixin ==========
|
|
93
|
+
PUBLIC: pretty_print_instance_variables pretty_print_inspect pretty_print
|
|
94
|
+
pretty_print_cycle
|
|
95
|
+
|
|
96
|
+
========== Class Object ==========
|
|
97
|
+
PRIVATE: initialize timeout irb_binding
|
|
98
|
+
|
|
99
|
+
========== Module Enumerable ==========
|
|
100
|
+
PUBLIC: find_all sort_by collect include? detect max sort partition any?
|
|
101
|
+
to_a reject zip find min member? entries inject all? select
|
|
102
|
+
each_with_index grep map
|
|
103
|
+
|
|
104
|
+
========== Class Array ==========
|
|
105
|
+
PUBLIC: delete_if & map! empty? indexes rindex reject last to_s assoc
|
|
106
|
+
reverse! sort each include? values_at slice * sort! each_index fetch +
|
|
107
|
+
clear pretty_print concat size join flatten! shift - eql? reverse to_ary
|
|
108
|
+
insert [] indices nitems inspect rassoc replace compact! []= | collect
|
|
109
|
+
push delete_at << frozen? reverse_each flatten hash collect! uniq! select
|
|
110
|
+
first to_a fill index reject! zip pack unshift compact transpose <=>
|
|
111
|
+
pretty_print_cycle at == slice! length uniq delete pop map
|
|
112
|
+
|
|
113
|
+
PRIVATE: initialize_copy initialize
|
|
114
|
+
|
|
115
|
+
`grep` or `mgrep`
|
|
116
|
+
-----------------
|
|
117
|
+
|
|
118
|
+
The `grep` command takes a regular expression and only returns methods which
|
|
119
|
+
match the given regex. In this example we'll use `mgrep` since on Array
|
|
120
|
+
objects `grep` is already taken:
|
|
121
|
+
|
|
122
|
+
>> [].mgrep /f/
|
|
123
|
+
========== Module Kernel ==========
|
|
124
|
+
PUBLIC: instance_variable_defined? freeze frozen? instance_of? kind_of?
|
|
125
|
+
|
|
126
|
+
========== Module Enumerable ==========
|
|
127
|
+
PUBLIC: find_all find
|
|
128
|
+
|
|
129
|
+
========== Class Array ==========
|
|
130
|
+
PUBLIC: delete_if fetch flatten! shift frozen? flatten first fill unshift
|
|
131
|
+
|
|
132
|
+
Similar to `ls` you can pass in an extra argument of "true" to see protected
|
|
133
|
+
and private methods:
|
|
134
|
+
|
|
135
|
+
>> [].mgrep /f/, true
|
|
136
|
+
========== Module Kernel ==========
|
|
137
|
+
PUBLIC: instance_variable_defined? freeze frozen? instance_of? kind_of?
|
|
138
|
+
|
|
139
|
+
PRIVATE: method_missing set_trace_func singleton_method_undefined fail
|
|
140
|
+
sprintf fork format printf
|
|
141
|
+
|
|
142
|
+
========== Module Enumerable ==========
|
|
143
|
+
PUBLIC: find_all find
|
|
144
|
+
|
|
145
|
+
========== Class Array ==========
|
|
146
|
+
PUBLIC: delete_if fetch flatten! shift frozen? flatten first fill unshift
|
|
147
|
+
|
|
148
|
+
Note that `method_missing` is always considered a match, since it could always
|
|
149
|
+
potentially execute.
|
|
150
|
+
|
|
151
|
+
`which` or `mwhich`
|
|
152
|
+
-------------------
|
|
153
|
+
|
|
154
|
+
The `which` command is for finding which classes or modules implement the
|
|
155
|
+
method you're seeking. You can pass the method name in as a string or symbol.
|
|
156
|
+
|
|
157
|
+
>> [].which :to_a
|
|
158
|
+
========== Module Kernel ==========
|
|
159
|
+
PUBLIC: to_a
|
|
160
|
+
|
|
161
|
+
========== Module Enumerable ==========
|
|
162
|
+
PUBLIC: to_a
|
|
163
|
+
|
|
164
|
+
========== Class Array ==========
|
|
165
|
+
PUBLIC: to_a
|
|
166
|
+
|
|
167
|
+
Logically the `which` command is the same as `grep(/^your_method$/)` and so
|
|
168
|
+
the same comments apply about `method_missing` and the optional parameter to
|
|
169
|
+
see protected/private methods.
|
|
170
|
+
|
|
171
|
+
License
|
|
172
|
+
=======
|
|
173
|
+
|
|
174
|
+
Copyright 2008, Matthew O'Connor All rights reserved.
|
|
175
|
+
|
|
176
|
+
This program is free software; you can redistribute it and/or modify it under
|
|
177
|
+
the same terms as Ruby 1.8.7 itself.
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module MethodLister
|
|
2
|
+
class ColorDisplay < SimpleDisplay
|
|
3
|
+
def initialize
|
|
4
|
+
@ansi = AnsiEscape.new
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def location_description(result)
|
|
10
|
+
color(super, :yellow_fg, :bold)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def color_method_overloaded_from_kernel(source, method)
|
|
14
|
+
if source != Kernel and Kernel.instance_methods.member? method
|
|
15
|
+
color(method, :green_fg)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def color_method_missing(source, method)
|
|
20
|
+
color(method, :red_fg) if method == "method_missing"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def color_method_array_primative(source, method)
|
|
24
|
+
exempt_sources = [Kernel, Fixnum, Hash, String, Enumerable, Array]
|
|
25
|
+
unless exempt_sources.member? source
|
|
26
|
+
primatives = %w{[] []= each <<}
|
|
27
|
+
color(method, :magenta_fg) if primatives.member? method
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def process_method(result, method)
|
|
32
|
+
color_method(result.object, method)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def color_method(source, method)
|
|
36
|
+
coloring_methods.each do |coloring_method|
|
|
37
|
+
colored_method = self.send(coloring_method, source, method)
|
|
38
|
+
return colored_method if colored_method
|
|
39
|
+
end
|
|
40
|
+
method
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def coloring_methods
|
|
44
|
+
private_methods.select {|method| method =~ /^color_method_/}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def color(string, *colors)
|
|
48
|
+
output_is_to_tty? ? @ansi.color_string(string, *colors) : string
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def output_is_to_tty?
|
|
52
|
+
$stdout.tty?
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class AnsiEscape
|
|
57
|
+
Colors = {
|
|
58
|
+
:none => 0, :black_fg => 30, :red_fg => 31, :green_fg => 32,
|
|
59
|
+
:yellow_fg => 33, :blue_fg => 34, :magenta_fg => 35, :cyan_fg => 36,
|
|
60
|
+
:white_fg => 37, :black_bg => 40, :red_bg => 41, :green_bg => 42,
|
|
61
|
+
:yellow_bg => 43, :blue_bg => 44, :magenta_bg => 45, :cyan_bg => 46,
|
|
62
|
+
:white_bg => 47, :bold => 1, :underline => 4, :blink => 5,
|
|
63
|
+
:reverse => 7, :invisible => 8
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
def color_string(string, *colors)
|
|
67
|
+
color_code(*colors) + string + color_code(:none)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
def color_code(*colors)
|
|
73
|
+
ansi_codes = colors.map {|c| Colors[c]}.compact.join(";")
|
|
74
|
+
"\e[#{ansi_codes}m" unless ansi_codes.empty?
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module MethodLister
|
|
2
|
+
class FindResult
|
|
3
|
+
attr_reader :object
|
|
4
|
+
VISIBILITIES = [:public, :protected, :private]
|
|
5
|
+
|
|
6
|
+
def initialize(options={})
|
|
7
|
+
@object = options[:object] || raise(ArgumentError, "No :object given.")
|
|
8
|
+
@methods = Hash.new
|
|
9
|
+
VISIBILITIES.each do |visibility|
|
|
10
|
+
@methods[visibility] = options[visibility] || Array.new
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def methods(visibility)
|
|
15
|
+
if visibility == :all
|
|
16
|
+
VISIBILITIES.inject(Array.new) { |result, viz| result + methods(viz) }
|
|
17
|
+
elsif VISIBILITIES.include? visibility
|
|
18
|
+
@methods[visibility]
|
|
19
|
+
else
|
|
20
|
+
raise ArgumentError, "Unknown visibility #{visibility.inspect}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def has_methods?(visibility=:all)
|
|
25
|
+
!methods(visibility).empty?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def narrow_to_methods_matching!(rx)
|
|
29
|
+
VISIBILITIES.each do |visibility|
|
|
30
|
+
@methods[visibility] = @methods[visibility].select do |method|
|
|
31
|
+
method =~ rx || method == "method_missing"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
self
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def ==(other)
|
|
38
|
+
object.eql?(other.object) &&
|
|
39
|
+
methods(:all).sort == other.methods(:all).sort
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def inspect
|
|
43
|
+
repr = "object=#{object.inspect}"
|
|
44
|
+
VISIBILITIES.each do |visibility|
|
|
45
|
+
repr += " #{visibility}=#{methods(visibility).sort.inspect}"
|
|
46
|
+
end
|
|
47
|
+
"<#{repr}>"
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
module MethodLister
|
|
2
|
+
class Finder
|
|
3
|
+
def find_all(object)
|
|
4
|
+
@results, @seen = Array.new, Hash.new
|
|
5
|
+
record_methods_for_eigenclass(object)
|
|
6
|
+
search_class_hierarchy(object.class)
|
|
7
|
+
@results
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def ls(object)
|
|
11
|
+
find_all(object).select { |results| results.has_methods? }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def grep(rx, object)
|
|
15
|
+
ls(object).map do |result|
|
|
16
|
+
result.narrow_to_methods_matching!(rx)
|
|
17
|
+
end.select { |result| result.has_methods? }
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def which(method, object)
|
|
21
|
+
grep(/^#{Regexp.escape(method.to_s)}$/, object)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def search_class_hierarchy(klass)
|
|
27
|
+
while klass
|
|
28
|
+
scan :class, klass
|
|
29
|
+
klass = klass.superclass
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def scan(type, klass_or_module)
|
|
34
|
+
unless @seen.has_key? klass_or_module
|
|
35
|
+
@seen[klass_or_module] = true
|
|
36
|
+
record_methods_for klass_or_module
|
|
37
|
+
scan_modules(type, klass_or_module)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def scan_modules(type, klass_or_module)
|
|
42
|
+
modules_for(type, klass_or_module).each do |a_module|
|
|
43
|
+
scan :module, a_module
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def record_methods_for(klass_or_module)
|
|
48
|
+
record(
|
|
49
|
+
:object => klass_or_module,
|
|
50
|
+
:public => klass_or_module.public_instance_methods(false),
|
|
51
|
+
:protected => klass_or_module.protected_instance_methods(false),
|
|
52
|
+
:private => klass_or_module.private_instance_methods(false)
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def record(result_options)
|
|
57
|
+
@results << FindResult.new(result_options)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def modules_for(obj_type, klass_or_module)
|
|
61
|
+
case obj_type
|
|
62
|
+
when :module
|
|
63
|
+
klass_or_module.included_modules
|
|
64
|
+
when :class
|
|
65
|
+
if superclass = klass_or_module.superclass
|
|
66
|
+
klass_or_module.included_modules - superclass.included_modules
|
|
67
|
+
else
|
|
68
|
+
klass_or_module.included_modules
|
|
69
|
+
end
|
|
70
|
+
when :eigenclass
|
|
71
|
+
eigenclass = get_eigenclass(klass_or_module)
|
|
72
|
+
eigenclass.included_modules - klass_or_module.class.included_modules
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def record_methods_for_eigenclass(object)
|
|
77
|
+
@seen[object] = true
|
|
78
|
+
return unless eigenclass = get_eigenclass(object)
|
|
79
|
+
|
|
80
|
+
# This complication arises because the methods used to do reflection on
|
|
81
|
+
# eigenclasses will return methods from the object's class. For public
|
|
82
|
+
# and protected methods we can determine where the method came from, but
|
|
83
|
+
# for private methods we have to do a heuristic guess. c.f. scenario
|
|
84
|
+
# "mixed_visibility_methods.rb"
|
|
85
|
+
singleton_methods = object.singleton_methods(false)
|
|
86
|
+
public_methods = eigenclass.public_instance_methods(false)
|
|
87
|
+
protected_methods = eigenclass.protected_instance_methods(false)
|
|
88
|
+
private_methods = eigenclass.private_instance_methods(false)
|
|
89
|
+
ancestor_privates = eigenclass.ancestors.map do |ancestor|
|
|
90
|
+
ancestor.private_instance_methods(false)
|
|
91
|
+
end.flatten.uniq
|
|
92
|
+
|
|
93
|
+
record(:object => object,
|
|
94
|
+
:public => singleton_methods & public_methods,
|
|
95
|
+
:protected => singleton_methods & protected_methods,
|
|
96
|
+
:private => private_methods - ancestor_privates)
|
|
97
|
+
|
|
98
|
+
scan_modules(:eigenclass, object)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def get_eigenclass(object)
|
|
102
|
+
class << object; self; end
|
|
103
|
+
rescue TypeError
|
|
104
|
+
nil
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Kernel
|
|
2
|
+
def mls(show_non_public=false,
|
|
3
|
+
displayer=MethodLister::ColorDisplay.new,
|
|
4
|
+
finder=MethodLister::Finder.new)
|
|
5
|
+
displayer.display finder.ls(self), show_non_public
|
|
6
|
+
end
|
|
7
|
+
alias :ls :mls
|
|
8
|
+
|
|
9
|
+
def mgrep(regex,
|
|
10
|
+
show_non_public=false,
|
|
11
|
+
displayer=MethodLister::ColorDisplay.new,
|
|
12
|
+
finder=MethodLister::Finder.new)
|
|
13
|
+
displayer.display finder.grep(regex, self), show_non_public
|
|
14
|
+
end
|
|
15
|
+
alias :grep :mgrep
|
|
16
|
+
|
|
17
|
+
def mwhich(method,
|
|
18
|
+
show_non_public=false,
|
|
19
|
+
displayer=MethodLister::ColorDisplay.new,
|
|
20
|
+
finder=MethodLister::Finder.new)
|
|
21
|
+
displayer.display finder.which(method, self), show_non_public
|
|
22
|
+
end
|
|
23
|
+
alias :which :mwhich
|
|
24
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module MethodLister
|
|
2
|
+
class SimpleDisplay
|
|
3
|
+
def display(findings, show_non_public=false)
|
|
4
|
+
findings.reverse.each do |result|
|
|
5
|
+
list = method_list(result, show_non_public)
|
|
6
|
+
if !list.empty?
|
|
7
|
+
puts header(result)
|
|
8
|
+
puts list
|
|
9
|
+
puts seperator(result)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
nil
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
def header(result)
|
|
18
|
+
"========== #{location_description(result)} =========="
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def method_list(result, show_non_public)
|
|
22
|
+
FindResult::VISIBILITIES.map do |visibility|
|
|
23
|
+
(visibility == :public || show_non_public) ?
|
|
24
|
+
method_set(result, visibility) :
|
|
25
|
+
nil
|
|
26
|
+
end.compact.join("\n\n")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def method_set(result, visibility)
|
|
30
|
+
if result.has_methods?(visibility)
|
|
31
|
+
"#{visibility.to_s.upcase}: " +
|
|
32
|
+
result.methods(visibility).map {|method|
|
|
33
|
+
process_method(result, method)
|
|
34
|
+
}.join(" ")
|
|
35
|
+
else
|
|
36
|
+
nil
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def process_method(result, method)
|
|
41
|
+
method
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def seperator(result)
|
|
45
|
+
""
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def location_description(result)
|
|
49
|
+
object = result.object
|
|
50
|
+
return "Class #{object}" if object.kind_of? Class
|
|
51
|
+
return "Module #{object}" if object.kind_of? Module
|
|
52
|
+
return "Eigenclass"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
|
|
2
|
+
|
|
3
|
+
describe MethodLister::ColorDisplay do
|
|
4
|
+
describe "#display" do
|
|
5
|
+
before do
|
|
6
|
+
@results = [
|
|
7
|
+
result(Object.new, :public => ["foo"]),
|
|
8
|
+
result(Array, :public => ["bar"] + Array.public_instance_methods(false)),
|
|
9
|
+
result(Kernel, :public => ["baz"] + Kernel.public_instance_methods(false)),
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
@displayer = MethodLister::ColorDisplay.new
|
|
13
|
+
|
|
14
|
+
@output = ""
|
|
15
|
+
stub(@displayer).puts do |*args|
|
|
16
|
+
@output += args.map {|arg| arg.to_s}.join("") + "\n"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "attempts to write out the relevant information" do
|
|
21
|
+
@displayer.display @results
|
|
22
|
+
@output.should =~ /Module Kernel.*.*Class Array.*bar.*Eigenclass.*foo/m
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "does not add ANSI codes if output is not a tty" do
|
|
26
|
+
mock(@displayer).output_is_to_tty? { false }.times(any_times)
|
|
27
|
+
@displayer.display @results
|
|
28
|
+
@output.should_not =~ /\e\[0m;/m
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "does add ANSI codes if output is a tty" do
|
|
32
|
+
mock(@displayer).output_is_to_tty? { true }.times(any_times)
|
|
33
|
+
@displayer.display @results
|
|
34
|
+
@output.should =~ /\e\[0m/m
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/spec_helper")
|
|
2
|
+
|
|
3
|
+
describe MethodLister::FindResult do
|
|
4
|
+
before do
|
|
5
|
+
names = %w{foo bar baz}
|
|
6
|
+
@public = names.map {|m| "public_#{m}"}
|
|
7
|
+
@protected = names.map {|m| "protected_#{m}"}
|
|
8
|
+
@private = names.map {|m| "private_#{m}"}
|
|
9
|
+
@all = @public + @protected + @private
|
|
10
|
+
|
|
11
|
+
@object = Object.new
|
|
12
|
+
@empty_result = MethodLister::FindResult.new(:object => @object)
|
|
13
|
+
@only_public = MethodLister::FindResult.new(:object => @object,
|
|
14
|
+
:public => @public)
|
|
15
|
+
@only_protected = MethodLister::FindResult.new(:object => @object,
|
|
16
|
+
:protected => @protected)
|
|
17
|
+
@only_private = MethodLister::FindResult.new(:object => @object,
|
|
18
|
+
:private => @private)
|
|
19
|
+
@mixed_result = MethodLister::FindResult.new(:object => @object,
|
|
20
|
+
:public => @public,
|
|
21
|
+
:protected => @protected,
|
|
22
|
+
:private => @private)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe "#object" do
|
|
26
|
+
it "requires at least an associated object" do
|
|
27
|
+
lambda do
|
|
28
|
+
MethodLister::FindResult.new(:public => @public)
|
|
29
|
+
end.should raise_error(ArgumentError)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "knows its associated object" do
|
|
33
|
+
@empty_result.object.should == @object
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
describe "#methods" do
|
|
38
|
+
it "knows all its methods" do
|
|
39
|
+
@empty_result.methods(:all).should be_empty
|
|
40
|
+
@only_public.methods(:all).should == @public
|
|
41
|
+
@only_protected.methods(:all).should == @protected
|
|
42
|
+
@only_private.methods(:all).should == @private
|
|
43
|
+
@mixed_result.methods(:all).should == @all
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "knows its public methods" do
|
|
47
|
+
@empty_result.methods(:public).should be_empty
|
|
48
|
+
@only_public.methods(:public).should == @public
|
|
49
|
+
@only_protected.methods(:public).should be_empty
|
|
50
|
+
@only_private.methods(:public).should be_empty
|
|
51
|
+
@mixed_result.methods(:public).should == @public
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "knows its protected methods" do
|
|
55
|
+
@empty_result.methods(:protected).should be_empty
|
|
56
|
+
@only_public.methods(:protected).should be_empty
|
|
57
|
+
@only_protected.methods(:protected).should == @protected
|
|
58
|
+
@only_private.methods(:protected).should be_empty
|
|
59
|
+
@mixed_result.methods(:protected).should == @protected
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "knows its private methods" do
|
|
63
|
+
@empty_result.methods(:private).should be_empty
|
|
64
|
+
@only_public.methods(:private).should be_empty
|
|
65
|
+
@only_protected.methods(:private).should be_empty
|
|
66
|
+
@only_private.methods(:private).should == @private
|
|
67
|
+
@mixed_result.methods(:private).should == @private
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "raises an exception if given an unknown visibility" do
|
|
71
|
+
lambda do
|
|
72
|
+
@empty_result.methods(:foobar)
|
|
73
|
+
end.should raise_error(ArgumentError)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
describe "#has_methods?" do
|
|
78
|
+
it "knows if it has methods of any visibility" do
|
|
79
|
+
@empty_result.should_not have_methods
|
|
80
|
+
@only_public.should have_methods
|
|
81
|
+
@only_protected.should have_methods
|
|
82
|
+
@only_private.should have_methods
|
|
83
|
+
@mixed_result.should have_methods
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "knows if it has methods of visibility :all" do
|
|
87
|
+
@empty_result.should_not have_methods(:all)
|
|
88
|
+
@only_public.should have_methods(:all)
|
|
89
|
+
@only_protected.should have_methods(:all)
|
|
90
|
+
@only_private.should have_methods(:all)
|
|
91
|
+
@mixed_result.should have_methods(:all)
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
it "knows if it has methods of visibility :public" do
|
|
95
|
+
@empty_result.should_not have_methods(:public)
|
|
96
|
+
@only_public.should have_methods(:public)
|
|
97
|
+
@only_protected.should_not have_methods(:public)
|
|
98
|
+
@only_private.should_not have_methods(:public)
|
|
99
|
+
@mixed_result.should have_methods(:public)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it "knows if it has methods of visibility :protected" do
|
|
103
|
+
@empty_result.should_not have_methods(:protected)
|
|
104
|
+
@only_public.should_not have_methods(:protected)
|
|
105
|
+
@only_protected.should have_methods(:protected)
|
|
106
|
+
@only_private.should_not have_methods(:protected)
|
|
107
|
+
@mixed_result.should have_methods(:protected)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
it "knows if it has methods of visibility :private" do
|
|
111
|
+
@empty_result.should_not have_methods(:private)
|
|
112
|
+
@only_public.should_not have_methods(:private)
|
|
113
|
+
@only_protected.should_not have_methods(:private)
|
|
114
|
+
@only_private.should have_methods(:private)
|
|
115
|
+
@mixed_result.should have_methods(:private)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
describe "#narrow_to_methods_matching!" do
|
|
120
|
+
it "removes all methods not matching the given regex" do
|
|
121
|
+
rx = /foo/
|
|
122
|
+
@mixed_result.methods(:all).select {|meth| meth !~ rx}.should_not be_empty
|
|
123
|
+
@mixed_result.narrow_to_methods_matching!(rx)
|
|
124
|
+
@mixed_result.methods(:all).select {|meth| meth !~ rx}.should be_empty
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
it "never removes method_missing" do
|
|
128
|
+
@private << "method_missing"
|
|
129
|
+
rx = /foo/
|
|
130
|
+
@mixed_result.methods(:all).select {|meth| meth !~ rx}.should_not be_empty
|
|
131
|
+
@mixed_result.narrow_to_methods_matching!(rx)
|
|
132
|
+
@mixed_result.methods(:all).select {|meth| meth !~ rx}.should be_include("method_missing")
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
describe "#==" do
|
|
137
|
+
it "is equal if both have the same methods and object" do
|
|
138
|
+
@mixed_result.should == MethodLister::FindResult.new(
|
|
139
|
+
:object => @mixed_result.object,
|
|
140
|
+
:public => @mixed_result.methods(:public),
|
|
141
|
+
:protected => @mixed_result.methods(:protected),
|
|
142
|
+
:private => @mixed_result.methods(:private)
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
it "is not equal if both have same methods but different objects" do
|
|
147
|
+
@mixed_result.should_not == MethodLister::FindResult.new(
|
|
148
|
+
:object => Object.new,
|
|
149
|
+
:public => @mixed_result.methods(:public),
|
|
150
|
+
:protected => @mixed_result.methods(:protected),
|
|
151
|
+
:private => @mixed_result.methods(:private)
|
|
152
|
+
)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "is not equal if both have the same object but different methods" do
|
|
156
|
+
@mixed_result.should_not == MethodLister::FindResult.new(
|
|
157
|
+
:object => @mixed_result.object,
|
|
158
|
+
:public => ["something_else"]
|
|
159
|
+
)
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|