looksee 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/.autotest +9 -0
- data/History.txt +3 -0
- data/Manifest.txt +18 -0
- data/README.rdoc +118 -0
- data/Rakefile +38 -0
- data/ext/looksee/extconf.rb +6 -0
- data/ext/looksee/looksee.c +126 -0
- data/ext/looksee/node-1.9.h +35 -0
- data/lib/looksee.rb +332 -0
- data/lib/looksee/shortcuts.rb +54 -0
- data/lib/looksee/version.rb +3 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/looksee_spec.rb +374 -0
- data/spec/spec_helper.rb +80 -0
- data/tasks/extconf.rake +13 -0
- data/tasks/extconf/looksee.rake +43 -0
- metadata +192 -0
@@ -0,0 +1,54 @@
|
|
1
|
+
=begin
|
2
|
+
|
3
|
+
Include this to pollute the standard ruby modules with handy aliases.
|
4
|
+
Perfect for your .irbrc, or for punching into your program to work out
|
5
|
+
what that +flazbot+ variable can do.
|
6
|
+
|
7
|
+
=end
|
8
|
+
|
9
|
+
require 'looksee'
|
10
|
+
|
11
|
+
class Object
|
12
|
+
private # ---------------------------------------------------------
|
13
|
+
|
14
|
+
#
|
15
|
+
# Alias for Looksee.lookup_path.
|
16
|
+
#
|
17
|
+
# (Added by Looksee.)
|
18
|
+
#
|
19
|
+
def lp(*args)
|
20
|
+
Looksee.lookup_path(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# Run Looksee.lookup_path on an instance of the given class.
|
25
|
+
#
|
26
|
+
# (Added by Looksee.)
|
27
|
+
#
|
28
|
+
def lpi(klass, *args)
|
29
|
+
Looksee.lookup_path(klass.allocate, *args)
|
30
|
+
end
|
31
|
+
|
32
|
+
public # ----------------------------------------------------------
|
33
|
+
|
34
|
+
#
|
35
|
+
# Call Looksee.lookup_path on this object.
|
36
|
+
#
|
37
|
+
# (Added by Looksee.)
|
38
|
+
#
|
39
|
+
def lookup_path(*args)
|
40
|
+
Looksee.lookup_path(self, *args)
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Dump the lookup path to standard output, and return self.
|
45
|
+
#
|
46
|
+
# Good for stuffing in a call chain.
|
47
|
+
#
|
48
|
+
# (Added by Looksee.)
|
49
|
+
#
|
50
|
+
def dump_lookup_path(*args)
|
51
|
+
p lookup_path(*args)
|
52
|
+
self
|
53
|
+
end
|
54
|
+
end
|
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/looksee.rb'}"
|
9
|
+
puts "Loading looksee gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
@@ -0,0 +1,374 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Looksee do
|
4
|
+
include TemporaryClasses
|
5
|
+
|
6
|
+
describe ".lookup_modules" do
|
7
|
+
#
|
8
|
+
# Wrapper for the method under test.
|
9
|
+
#
|
10
|
+
# Filter out modules which are hard to test against, and returns
|
11
|
+
# the list of module names. #inspect strings are used for names
|
12
|
+
# of singleton classes, since they have no name.
|
13
|
+
#
|
14
|
+
def filtered_lookup_modules(object)
|
15
|
+
result = Looksee.lookup_modules(object)
|
16
|
+
# Singleton classes have no name ('' in <1.9, nil in 1.9+). Use
|
17
|
+
# the inspect string instead.
|
18
|
+
names = result.map{|mod| mod.name.to_s.empty? ? mod.inspect : mod.name}
|
19
|
+
names.select{|name| deterministic_module_name?(name)}
|
20
|
+
end
|
21
|
+
|
22
|
+
#
|
23
|
+
# Return true if the given module name is of a module we can test
|
24
|
+
# for.
|
25
|
+
#
|
26
|
+
# This excludes ruby version dependent modules, and modules tossed
|
27
|
+
# into the hierarchy by testing frameworks.
|
28
|
+
#
|
29
|
+
def deterministic_module_name?(name)
|
30
|
+
junk_patterns = [
|
31
|
+
# pollution from testing libraries
|
32
|
+
'Mocha', 'Spec',
|
33
|
+
# RSpec adds this under ruby 1.8.6
|
34
|
+
'InstanceExecHelper',
|
35
|
+
# only in ruby 1.9
|
36
|
+
'BasicObject',
|
37
|
+
# something pulls this in under ruby 1.9
|
38
|
+
'PP',
|
39
|
+
]
|
40
|
+
|
41
|
+
# Singleton classes of junk are junk.
|
42
|
+
while name =~ /\A#<Class:(.*)>\z/
|
43
|
+
name = $1
|
44
|
+
end
|
45
|
+
|
46
|
+
name !~ /\A(#{junk_patterns.join('|')})/
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should contain an entry for each module in the object's lookup path" do
|
50
|
+
temporary_module :Mod1
|
51
|
+
temporary_module :Mod2
|
52
|
+
temporary_class :Base
|
53
|
+
temporary_class :Derived, Base do
|
54
|
+
include Mod1
|
55
|
+
include Mod2
|
56
|
+
end
|
57
|
+
filtered_lookup_modules(Derived.new) == %w'Derived Mod2 Mod1 Base Object Kernel'
|
58
|
+
end
|
59
|
+
|
60
|
+
it "contain an entry for the object's singleton class if it exists" do
|
61
|
+
object = Object.new
|
62
|
+
object.singleton_class
|
63
|
+
|
64
|
+
result = filtered_lookup_modules(object)
|
65
|
+
result.shift.should =~ /\A#<Class:\#<Object:0x[\da-f]+>>\z/
|
66
|
+
result.should == %w"Object Kernel"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should contain entries for singleton classes of all ancestors for class objects" do
|
70
|
+
temporary_class :C
|
71
|
+
result = filtered_lookup_modules(C)
|
72
|
+
result.should == %w'#<Class:C> #<Class:Object> Class Module Object Kernel'
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe ".lookup_path" do
|
77
|
+
it "should return a LookupPath object" do
|
78
|
+
object = Object.new
|
79
|
+
lookup_path = Looksee.lookup_path(object)
|
80
|
+
lookup_path.should be_a(Looksee::LookupPath)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should return a LookupPath object for the given object" do
|
84
|
+
object = Object.new
|
85
|
+
Looksee.stubs(:default_lookup_path_options).returns({})
|
86
|
+
Looksee::LookupPath.expects(:new).with(object, {})
|
87
|
+
lookup_path = Looksee.lookup_path(object)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should allow symbol arguments as shortcuts for true options" do
|
91
|
+
object = Object.new
|
92
|
+
Looksee.stubs(:default_lookup_path_options).returns({})
|
93
|
+
Looksee::LookupPath.expects(:new).with(object, {:public => true, :overridden => true})
|
94
|
+
Looksee.lookup_path(object, :public, :overridden)
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should merge the default options, with the symbols, and the options hash" do
|
98
|
+
object = Object.new
|
99
|
+
Looksee.stubs(:default_lookup_path_options).returns({:public => false, :protected => false, :private => false})
|
100
|
+
Looksee::LookupPath.expects(:new).with(object, {:public => false, :protected => true, :private => false})
|
101
|
+
Looksee.lookup_path(object, :protected, :private, :private => false)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "internal instance methods:" do
|
106
|
+
#
|
107
|
+
# Remove all methods defined exactly on the given module. As Ruby's
|
108
|
+
# reflection on singleton classes of classes isn't quite adequate,
|
109
|
+
# you need to provide a :class_singleton option when such a class is
|
110
|
+
# given.
|
111
|
+
#
|
112
|
+
def remove_methods(mod, opts={})
|
113
|
+
names = all_instance_methods(mod)
|
114
|
+
|
115
|
+
# all_instance_methods can't get just the methods on a class
|
116
|
+
# singleton class. Filter out superclass methods here.
|
117
|
+
if opts[:class_singleton]
|
118
|
+
klass = ObjectSpace.each_object(mod){|klass| break klass}
|
119
|
+
names -= all_instance_methods(klass.superclass.singleton_class)
|
120
|
+
end
|
121
|
+
|
122
|
+
names.sort_by{|name| name.in?([:remove_method, :send]) ? 1 : 0}.flatten
|
123
|
+
names.each do |name|
|
124
|
+
mod.send :remove_method, name
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def define_methods(mod, opts)
|
129
|
+
mod.module_eval do
|
130
|
+
[:public, :protected, :private].each do |visibility|
|
131
|
+
Array(opts[visibility]).each do |name|
|
132
|
+
define_method(name){}
|
133
|
+
send visibility, name
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def all_instance_methods(mod)
|
140
|
+
names =
|
141
|
+
mod.public_instance_methods(false) +
|
142
|
+
mod.protected_instance_methods(false) +
|
143
|
+
mod.private_instance_methods(false)
|
144
|
+
names.map{|name| name.to_sym} # they're strings in ruby <1.9
|
145
|
+
end
|
146
|
+
|
147
|
+
def self.target_method(name)
|
148
|
+
define_method(:target_method){name}
|
149
|
+
end
|
150
|
+
|
151
|
+
def self.it_should_list_methods_with_visibility(visibility)
|
152
|
+
it "should return the list of #{visibility} instance methods defined directly on a class" do
|
153
|
+
temporary_class :C
|
154
|
+
remove_methods C
|
155
|
+
define_methods C, visibility => [:one, :two]
|
156
|
+
Looksee.send(target_method, C).to_set.should == Set[:one, :two]
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should return the list of #{visibility} instance methods defined directly on a module" do
|
160
|
+
temporary_module :M
|
161
|
+
remove_methods M
|
162
|
+
define_methods M, visibility => [:one, :two]
|
163
|
+
Looksee.send(target_method, M).to_set.should == Set[:one, :two]
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should return the list of #{visibility} instance methods defined directly on a singleton class" do
|
167
|
+
temporary_class :C
|
168
|
+
c = C.new
|
169
|
+
remove_methods c.singleton_class
|
170
|
+
define_methods c.singleton_class, visibility => [:one, :two]
|
171
|
+
Looksee.send(target_method, c.singleton_class).to_set.should == Set[:one, :two]
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should return the list of #{visibility} instance methods defined directly on a class' singleton class" do
|
175
|
+
temporary_class :C
|
176
|
+
remove_methods C.singleton_class, :class_singleton => true
|
177
|
+
define_methods C.singleton_class, visibility => [:one, :two]
|
178
|
+
Looksee.send(target_method, C.singleton_class).to_set.should == Set[:one, :two]
|
179
|
+
end
|
180
|
+
|
181
|
+
# Worth checking as ruby keeps undef'd methods in method tables.
|
182
|
+
it "should not return undefined methods" do
|
183
|
+
temporary_class :C
|
184
|
+
remove_methods C
|
185
|
+
define_methods C, visibility => [:removed]
|
186
|
+
C.send(:undef_method, :removed)
|
187
|
+
Looksee.send(target_method, C).to_set.should == Set[]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.it_should_not_list_methods_with_visibility(visibility1, visibility2)
|
192
|
+
it "should not return any #{visibility1} or #{visibility2} instance methods" do
|
193
|
+
temporary_class :C
|
194
|
+
remove_methods C
|
195
|
+
define_methods C, {visibility1 => [:a], visibility2 => [:b]}
|
196
|
+
Looksee.send(target_method, C).to_set.should == Set[]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe ".internal_public_instance_methods" do
|
201
|
+
target_method :internal_public_instance_methods
|
202
|
+
it_should_list_methods_with_visibility :public
|
203
|
+
it_should_not_list_methods_with_visibility :private, :protected
|
204
|
+
end
|
205
|
+
|
206
|
+
describe ".internal_protected_instance_methods" do
|
207
|
+
target_method :internal_protected_instance_methods
|
208
|
+
it_should_list_methods_with_visibility :protected
|
209
|
+
it_should_not_list_methods_with_visibility :public, :private
|
210
|
+
end
|
211
|
+
|
212
|
+
describe ".internal_private_instance_methods" do
|
213
|
+
target_method :internal_private_instance_methods
|
214
|
+
it_should_list_methods_with_visibility :private
|
215
|
+
it_should_not_list_methods_with_visibility :public, :protected
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe Looksee::LookupPath do
|
221
|
+
before do
|
222
|
+
Looksee.default_lookup_path_options = {}
|
223
|
+
end
|
224
|
+
|
225
|
+
include TemporaryClasses
|
226
|
+
|
227
|
+
describe "#entries" do
|
228
|
+
it "should contain an entry for each module in the object's lookup path" do
|
229
|
+
object = Object.new
|
230
|
+
temporary_class :C
|
231
|
+
temporary_class :D
|
232
|
+
Looksee.stubs(:lookup_modules).with(object).returns([C, D])
|
233
|
+
Looksee::LookupPath.new(object).entries.map{|entry| entry.module_name}.should == %w'C D'
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe "#inspect" do
|
238
|
+
before do
|
239
|
+
Looksee.stubs(:styles).returns(Hash.new{'%s'})
|
240
|
+
end
|
241
|
+
|
242
|
+
def stub_methods(mod, public, protected, private)
|
243
|
+
Looksee.stubs(:internal_public_instance_methods ).with(mod).returns(public)
|
244
|
+
Looksee.stubs(:internal_protected_instance_methods).with(mod).returns(protected)
|
245
|
+
Looksee.stubs(:internal_private_instance_methods ).with(mod).returns(private)
|
246
|
+
end
|
247
|
+
|
248
|
+
describe "contents" do
|
249
|
+
before do
|
250
|
+
temporary_module :M
|
251
|
+
temporary_class :C do
|
252
|
+
include M
|
253
|
+
end
|
254
|
+
@object = Object.new
|
255
|
+
Looksee.stubs(:lookup_modules).with(@object).returns([C, M])
|
256
|
+
stub_methods(C, ['public1', 'public2'], ['protected1', 'protected2'], ['private1', 'private2'])
|
257
|
+
stub_methods(M, ['public1', 'public2'], ['protected1', 'protected2'], ['private1', 'private2'])
|
258
|
+
end
|
259
|
+
|
260
|
+
it "should show only public instance methods when only public methods are requested" do
|
261
|
+
lookup_path = Looksee::LookupPath.new(@object, :public => true, :overridden => true)
|
262
|
+
lookup_path.inspect.should == <<-EOS.demargin
|
263
|
+
|C
|
264
|
+
| public1 public2
|
265
|
+
|M
|
266
|
+
| public1 public2
|
267
|
+
EOS
|
268
|
+
end
|
269
|
+
|
270
|
+
it "should show modules and protected instance methods when only protected methods are requested" do
|
271
|
+
lookup_path = Looksee::LookupPath.new(@object, :protected => true, :overridden => true)
|
272
|
+
lookup_path.inspect.should == <<-EOS.demargin
|
273
|
+
|C
|
274
|
+
| protected1 protected2
|
275
|
+
|M
|
276
|
+
| protected1 protected2
|
277
|
+
EOS
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should show modules and private instance methods when only private methods are requested" do
|
281
|
+
lookup_path = Looksee::LookupPath.new(@object, :private => true, :overridden => true)
|
282
|
+
lookup_path.inspect.should == <<-EOS.demargin
|
283
|
+
|C
|
284
|
+
| private1 private2
|
285
|
+
|M
|
286
|
+
| private1 private2
|
287
|
+
EOS
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should show modules with public and private instance methods when only public and private methods are requested" do
|
291
|
+
lookup_path = Looksee::LookupPath.new(@object, :public => true, :private => true, :overridden => true)
|
292
|
+
lookup_path.inspect.should == <<-EOS.demargin
|
293
|
+
|C
|
294
|
+
| private1 private2 public1 public2
|
295
|
+
|M
|
296
|
+
| private1 private2 public1 public2
|
297
|
+
EOS
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should show singleton classes as class names in brackets" do
|
301
|
+
Looksee.stubs(:lookup_modules).with(C).returns([C.singleton_class])
|
302
|
+
stub_methods(C.singleton_class, ['public1', 'public2'], [], [])
|
303
|
+
lookup_path = Looksee::LookupPath.new(C, :public => true)
|
304
|
+
lookup_path.inspect.should == <<-EOS.demargin
|
305
|
+
|[C]
|
306
|
+
| public1 public2
|
307
|
+
EOS
|
308
|
+
end
|
309
|
+
|
310
|
+
it "should handle singleton classes of singleton classes correctly" do
|
311
|
+
Looksee.stubs(:lookup_modules).with(C.singleton_class).returns([C.singleton_class.singleton_class])
|
312
|
+
stub_methods(C.singleton_class.singleton_class, ['public1', 'public2'], [], [])
|
313
|
+
lookup_path = Looksee::LookupPath.new(C.singleton_class, :public => true)
|
314
|
+
lookup_path.inspect.should == <<-EOS.demargin
|
315
|
+
|[[C]]
|
316
|
+
| public1 public2
|
317
|
+
EOS
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe "styles" do
|
322
|
+
before do
|
323
|
+
styles = {
|
324
|
+
:module => "`%s'",
|
325
|
+
:public => "{%s}",
|
326
|
+
:protected => "[%s]",
|
327
|
+
:private => "<%s>",
|
328
|
+
:overridden => "(%s)",
|
329
|
+
}
|
330
|
+
Looksee.stubs(:styles).returns(styles)
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should delimit each word with the configured delimiters" do
|
334
|
+
temporary_class :C
|
335
|
+
Looksee.stubs(:lookup_modules).returns([C])
|
336
|
+
stub_methods(C, ['public'], ['protected'], ['private'])
|
337
|
+
lookup_path = Looksee::LookupPath.new(Object.new, :public => true, :protected => true, :private => true, :overridden => true)
|
338
|
+
lookup_path.inspect.should == <<-EOS.demargin
|
339
|
+
|\`C\'
|
340
|
+
| <private> [protected] {public}
|
341
|
+
EOS
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
describe "layout" do
|
346
|
+
it "should wrap method lists at the configured number of columns, sorting vertically first, and aligning into a grid" do
|
347
|
+
temporary_class :C
|
348
|
+
Looksee.stubs(:lookup_modules).returns([C])
|
349
|
+
stub_methods(C, %w'aa b c dd ee f g hh i', [], [])
|
350
|
+
lookup_path = Looksee::LookupPath.new(Object.new, :public => true)
|
351
|
+
lookup_path.inspect(:width => 20).should == <<-EOS.demargin
|
352
|
+
|C
|
353
|
+
| aa c ee g i
|
354
|
+
| b dd f hh
|
355
|
+
EOS
|
356
|
+
end
|
357
|
+
|
358
|
+
it "should lay the methods of each module out independently" do
|
359
|
+
temporary_class :A
|
360
|
+
temporary_class :B
|
361
|
+
Looksee.stubs(:lookup_modules).returns([A, B])
|
362
|
+
stub_methods(A, ['a', 'long_long_long_long_name'], [], [])
|
363
|
+
stub_methods(B, ['long_long_long', 'short'], [], [])
|
364
|
+
lookup_path = Looksee::LookupPath.new(Object.new, :public => true)
|
365
|
+
lookup_path.inspect.should == <<-EOS.demargin
|
366
|
+
|A
|
367
|
+
| a long_long_long_long_name
|
368
|
+
|B
|
369
|
+
| long_long_long short
|
370
|
+
EOS
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|