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.
@@ -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
@@ -0,0 +1,3 @@
1
+ module Looksee
2
+ VERSION = '0.0.1'
3
+ end
@@ -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"
@@ -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)
@@ -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