oggy-looksee 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,14 @@
1
+ == 0.1.0 2009-08-20
2
+
3
+ * Add methods undefined with Module#undef_method. Blue by default.
4
+
5
+ == 0.0.2 2009-07-28
6
+
7
+ * Added #grep to filter methods shown: lp(object).grep(/pattern/)
8
+ * Fix #1: Play nice with Wirble.
9
+ * Fix #3: Don't die when examining immediate objects (fixnum, symbol,
10
+ true, false, nil)
11
+
1
12
  == 0.0.1 2009-07-05
2
13
 
3
14
  * Hi.
data/Manifest.txt CHANGED
@@ -9,10 +9,13 @@ ext/looksee/node-1.9.h
9
9
  lib/looksee.rb
10
10
  lib/looksee/shortcuts.rb
11
11
  lib/looksee/version.rb
12
+ lib/looksee/wirble_compatibility.rb
13
+ looksee.gemspec
12
14
  script/console
13
15
  script/destroy
14
16
  script/generate
15
17
  spec/looksee_spec.rb
16
18
  spec/spec_helper.rb
19
+ spec/wirble_compatibility_spec.rb
17
20
  tasks/extconf.rake
18
21
  tasks/extconf/looksee.rake
data/README.rdoc CHANGED
@@ -65,7 +65,8 @@ This defines a method +lp+ ("lookup path") which lets you do:
65
65
  frozen? nil? untaint
66
66
 
67
67
  It'll also color the methods according to whether they're public,
68
- protected, private, or overridden. So pretty. You gotta try it.
68
+ protected, private, undefined (using Module#undef_method), or
69
+ overridden. So pretty. You gotta try it.
69
70
 
70
71
  By default, it shows public and protected methods. Add private ones
71
72
  like so:
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  require './lib/looksee/version'
7
7
 
8
8
  Hoe.plugin :newgem
9
- Hoe.plugin :cucumberfeatures
9
+ Hoe.plugin :email # from seattlerb - configure via ~/.hoerc
10
10
 
11
11
  $hoe = Hoe.spec 'looksee' do
12
12
  self.developer 'George Ogata', 'george.ogata@gmail.com'
@@ -36,7 +36,7 @@ VALUE Looksee_internal_superclass(VALUE self, VALUE internal_class) {
36
36
  * object's birth class.
37
37
  */
38
38
  VALUE Looksee_internal_class(VALUE self, VALUE object) {
39
- return RBASIC(object)->klass;
39
+ return CLASS_OF(object);
40
40
  }
41
41
 
42
42
  /*
@@ -115,6 +115,23 @@ VALUE Looksee_internal_private_instance_methods(VALUE self, VALUE klass) {
115
115
  return internal_instance_methods(klass, NOEX_PRIVATE);
116
116
  }
117
117
 
118
+ static int add_method_if_undefined(ID method_name, NODE *body, VALUE *names) {
119
+ /* Module#undef_method sets body->nd_body to NULL. */
120
+ if (body && !body->nd_body)
121
+ rb_ary_push(*names, ID2SYM(method_name));
122
+ return ST_CONTINUE;
123
+ }
124
+
125
+ /*
126
+ * Return the list of undefined instance methods (as Symbols) of the
127
+ * given internal class.
128
+ */
129
+ VALUE Looksee_internal_undefined_instance_methods(VALUE self, VALUE klass) {
130
+ VALUE names = rb_ary_new();
131
+ st_foreach(RCLASS_M_TBL(klass), add_method_if_undefined, (st_data_t)&names);
132
+ return names;
133
+ }
134
+
118
135
  void Init_looksee(void) {
119
136
  VALUE mLooksee = rb_define_module("Looksee");
120
137
  rb_define_singleton_method(mLooksee, "internal_superclass", Looksee_internal_superclass, 1);
@@ -123,4 +140,5 @@ void Init_looksee(void) {
123
140
  rb_define_singleton_method(mLooksee, "internal_public_instance_methods", Looksee_internal_public_instance_methods, 1);
124
141
  rb_define_singleton_method(mLooksee, "internal_protected_instance_methods", Looksee_internal_protected_instance_methods, 1);
125
142
  rb_define_singleton_method(mLooksee, "internal_private_instance_methods", Looksee_internal_private_instance_methods, 1);
143
+ rb_define_singleton_method(mLooksee, "internal_undefined_instance_methods", Looksee_internal_undefined_instance_methods, 1);
126
144
  }
data/lib/looksee.rb CHANGED
@@ -32,8 +32,8 @@ require "looksee/version"
32
32
  #
33
33
  # +lp+ returns a LookupPath object, which has +inspect+ defined to
34
34
  # print things out pretty. By default, it shows public, protected,
35
- # and overridden methods. They're all colored, which makes showing
36
- # overridden methods not such a strange idea.
35
+ # undefined, and overridden methods. They're all colored, which makes
36
+ # showing overridden methods not such a strange idea.
37
37
  #
38
38
  # Some examples of the other shortcuts:
39
39
  #
@@ -66,9 +66,11 @@ module Looksee
66
66
  # * +:public+ - include public methods
67
67
  # * +:protected+ - include protected methods
68
68
  # * +:private+ - include private methods
69
+ # * +:undefined+ - include undefined methods (see Module#undef_method)
69
70
  # * +:overridden+ - include methods overridden by subclasses
70
71
  #
71
- # The default (if options is nil or omitted) is [:public].
72
+ # The default (if options is nil or omitted) is given by
73
+ # #default_lookup_path_options.
72
74
  #
73
75
  def lookup_path(object, *options)
74
76
  normalized_options = Looksee.default_lookup_path_options.dup
@@ -77,13 +79,14 @@ module Looksee
77
79
  normalized_options[option] = true
78
80
  end
79
81
  normalized_options.update(hash_options)
80
- LookupPath.new(object, normalized_options)
82
+ LookupPath.for(object, normalized_options)
81
83
  end
82
84
 
83
85
  #
84
86
  # The default options passed to lookup_path.
85
87
  #
86
- # Default: <tt>{:public => true, :protected => true, :overridden => true}</tt>
88
+ # Default: <tt>{:public => true, :protected => true, :undefined =>
89
+ # true, :overridden => true}</tt>
87
90
  #
88
91
  attr_accessor :default_lookup_path_options
89
92
 
@@ -104,6 +107,7 @@ module Looksee
104
107
  # * :public
105
108
  # * :protected
106
109
  # * :private
110
+ # * :undefined
107
111
  # * :overridden
108
112
  #
109
113
  # The values are format strings. They should all contain a single
@@ -116,6 +120,7 @@ module Looksee
116
120
  # :public => "\e[1;32m%s\e[0m",
117
121
  # :protected => "\e[1;33m%s\e[0m",
118
122
  # :private => "\e[1;31m%s\e[0m",
123
+ # :undefined => "\e[1;34m%s\e[0m",
119
124
  # :overridden => "\e[1;30m%s\e[0m",
120
125
  # }
121
126
  #
@@ -136,19 +141,24 @@ module Looksee
136
141
  end
137
142
  end
138
143
 
139
- self.default_lookup_path_options = {:public => true, :protected => true, :overridden => true}
144
+ self.default_lookup_path_options = {:public => true, :protected => true, :undefined => true, :overridden => true}
140
145
  self.default_width = 80
141
146
  self.styles = {
142
147
  :module => "\e[1;37m%s\e[0m",
143
148
  :public => "\e[1;32m%s\e[0m",
144
149
  :protected => "\e[1;33m%s\e[0m",
145
150
  :private => "\e[1;31m%s\e[0m",
151
+ :undefined => "\e[1;34m%s\e[0m",
146
152
  :overridden => "\e[1;30m%s\e[0m",
147
153
  }
148
154
 
149
155
  class LookupPath
150
156
  attr_reader :entries
151
157
 
158
+ def initialize(entries)
159
+ @entries = entries
160
+ end
161
+
152
162
  #
153
163
  # Create a LookupPath for the given object.
154
164
  #
@@ -158,25 +168,41 @@ module Looksee
158
168
  # :public
159
169
  # :protected
160
170
  # :private
171
+ # :undefined
161
172
  # :overridden
162
173
  #
163
- def initialize(object, options={})
164
- @entries = []
165
- seen = {}
166
- Looksee.lookup_modules(object).each do |mod|
167
- entry = Entry.new(mod, seen, options)
168
- entry.methods.each{|m| seen[m] = true}
169
- @entries << entry
174
+ def self.for(object, options={})
175
+ entries = entries_for(object, options)
176
+ new(entries)
177
+ end
178
+
179
+ #
180
+ # Return a new LookupPath which only contains names matching the
181
+ # given pattern.
182
+ #
183
+ def grep(pattern)
184
+ entries = self.entries.map do |entry|
185
+ entry.grep(pattern)
170
186
  end
187
+ self.class.new(entries)
171
188
  end
172
189
 
173
190
  def inspect(options={})
174
191
  options = normalize_inspect_options(options)
175
- entries.map{|e| e.inspect(options)}.join
192
+ entries.map{|e| e.inspect(options)}.join("\n")
176
193
  end
177
194
 
178
195
  private # -------------------------------------------------------
179
196
 
197
+ def self.entries_for(object, options)
198
+ seen = {}
199
+ Looksee.lookup_modules(object).map do |mod|
200
+ entry = Entry.for(mod, seen, options)
201
+ entry.methods.each{|m| seen[m] = true}
202
+ entry
203
+ end
204
+ end
205
+
180
206
  def normalize_inspect_options(options)
181
207
  options[:width] ||= ENV['COLUMNS'].to_i.nonzero? || Looksee.default_width
182
208
  options
@@ -189,21 +215,40 @@ module Looksee
189
215
  # information (public, private, etc.).
190
216
  #
191
217
  class Entry
192
- #
193
- # Don't call me, silly. I'm just part of a LookupPath.
194
- #
195
- def initialize(mod, seen, options)
218
+ def initialize(mod, methods=[], visibilities={})
196
219
  @module = mod
197
- @methods = []
198
- @visibilities = {}
199
- add_methods(Looksee.internal_public_instance_methods(mod).map{|sym| sym.to_s} , :public , seen) if options[:public ]
200
- add_methods(Looksee.internal_protected_instance_methods(mod).map{|sym| sym.to_s}, :protected, seen) if options[:protected]
201
- add_methods(Looksee.internal_private_instance_methods(mod).map{|sym| sym.to_s} , :private , seen) if options[:private ]
202
- @methods.sort!
220
+ @methods = methods
221
+ @visibilities = visibilities
222
+ end
223
+
224
+ def self.for(mod, seen, options)
225
+ entry = new(mod)
226
+ entry.initialize_for(seen, options)
227
+ entry
203
228
  end
204
229
 
205
230
  attr_reader :module, :methods
206
231
 
232
+ def initialize_for(seen, options)
233
+ add_methods(Looksee.internal_public_instance_methods(@module).map{|sym| sym.to_s} , :public , seen) if options[:public ]
234
+ add_methods(Looksee.internal_protected_instance_methods(@module).map{|sym| sym.to_s}, :protected, seen) if options[:protected]
235
+ add_methods(Looksee.internal_private_instance_methods(@module).map{|sym| sym.to_s} , :private , seen) if options[:private ]
236
+ add_methods(Looksee.internal_undefined_instance_methods(@module).map{|sym| sym.to_s}, :undefined, seen) if options[:undefined]
237
+ @methods.sort!
238
+ end
239
+
240
+ def grep(pattern)
241
+ methods = []
242
+ visibilities = {}
243
+ @methods.each do |name|
244
+ if name[pattern]
245
+ methods << name
246
+ visibilities[name] = @visibilities[name]
247
+ end
248
+ end
249
+ self.class.new(@module, methods, visibilities)
250
+ end
251
+
207
252
  #
208
253
  # Return the name of the class or module.
209
254
  #
@@ -219,7 +264,7 @@ module Looksee
219
264
 
220
265
  #
221
266
  # Yield each method along with its visibility (:public,
222
- # :private, :protected, or :overridden).
267
+ # :private, :protected, :undefined, or :overridden).
223
268
  #
224
269
  def each
225
270
  @methods.each do |name|
@@ -236,7 +281,8 @@ module Looksee
236
281
  # columns. Pass a :width option to control the output width.
237
282
  #
238
283
  def inspect(options={})
239
- styled_module_name << "\n" << Columnizer.columnize(styled_methods, options[:width])
284
+ string = styled_module_name << "\n" << Columnizer.columnize(styled_methods, options[:width])
285
+ string.chomp
240
286
  end
241
287
 
242
288
  private # -----------------------------------------------------
@@ -268,6 +314,8 @@ module Looksee
268
314
  # sequences.
269
315
  #
270
316
  def columnize(strings, width)
317
+ return '' if strings.empty?
318
+
271
319
  num_columns = 1
272
320
  layout = [strings]
273
321
  loop do
@@ -330,3 +378,5 @@ module Looksee
330
378
  end
331
379
  end
332
380
  end
381
+
382
+ require 'looksee/wirble_compatibility'
@@ -1,3 +1,3 @@
1
1
  module Looksee
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -0,0 +1,86 @@
1
+ module Looksee
2
+ module WirbleCompatibility
3
+ class << self
4
+ def wirble_loaded?
5
+ Object.const_defined?(:Wirble) &&
6
+ Wirble.is_a?(Module) &&
7
+ Wirble.respond_to?(:colorize)
8
+ end
9
+
10
+ def wirble_colorizing?
11
+ IRB::Irb.method_defined?(:non_color_output_value)
12
+ end
13
+
14
+ def hook_into_wirble_load
15
+ unless Object.const_defined?(:Wirble)
16
+ Object.const_set :Wirble, Module.new
17
+ end
18
+ Wirble.send :extend, WirbleLoadHook
19
+ end
20
+
21
+ def hook_into_wirble_colorize
22
+ class << Wirble
23
+ def colorize_with_looksee(*args)
24
+ # If this gets called twice, Wirble will fuck up the
25
+ # aliases. Disable colorizing first to reset them.
26
+ if WirbleCompatibility.hooked_into_irb_output_value?
27
+ Wirble::Colorize.disable
28
+ end
29
+ colorize_without_looksee(*args)
30
+ WirbleCompatibility.hook_into_irb_output_value
31
+ end
32
+
33
+ alias colorize_without_looksee colorize
34
+ alias colorize colorize_with_looksee
35
+ end
36
+ end
37
+
38
+ def hook_into_irb_output_value
39
+ IRB::Irb.class_eval do
40
+ def output_value_with_looksee
41
+ if @context.last_value.is_a?(Looksee::LookupPath)
42
+ non_color_output_value
43
+ else
44
+ output_value_without_looksee
45
+ end
46
+ end
47
+
48
+ alias output_value_without_looksee output_value
49
+ alias output_value output_value_with_looksee
50
+ end
51
+ end
52
+
53
+ def hooked_into_irb_output_value?
54
+ IRB::Irb.method_defined?(:output_value_with_looksee)
55
+ end
56
+
57
+ def init
58
+ #
59
+ # How wirble is used:
60
+ #
61
+ # * Wirble is required/loaded. Defines Wirble module, with methods like Wirble.colorize.
62
+ # * Wirble.init is called. Nothing interesting.
63
+ # * Wirble.colorize is called. Hooks into IRB::Irb.output_value via an alias.
64
+ #
65
+ if !wirble_loaded?
66
+ hook_into_wirble_load
67
+ elsif !wirble_colorizing?
68
+ hook_into_wirble_colorize
69
+ else
70
+ hook_into_irb_output_value
71
+ end
72
+ end
73
+ end
74
+
75
+ module WirbleLoadHook
76
+ def singleton_method_added(name)
77
+ if name == :colorize && !respond_to?(:colorize_with_looksee)
78
+ WirbleCompatibility.hook_into_wirble_colorize
79
+ end
80
+ super
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ Looksee::WirbleCompatibility.init
data/looksee.gemspec ADDED
@@ -0,0 +1,44 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{looksee}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["George Ogata"]
9
+ s.date = %q{2009-08-20}
10
+ s.description = %q{Looksee lets you examine the method lookup path of objects in ways not
11
+ possible in plain ruby.}
12
+ s.email = ["george.ogata@gmail.com"]
13
+ s.extensions = ["ext/looksee/extconf.rb"]
14
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
15
+ s.files = [".autotest", "History.txt", "Manifest.txt", "README.rdoc", "Rakefile", "ext/looksee/extconf.rb", "ext/looksee/looksee.c", "ext/looksee/node-1.9.h", "lib/looksee.rb", "lib/looksee/shortcuts.rb", "lib/looksee/version.rb", "lib/looksee/wirble_compatibility.rb", "looksee.gemspec", "script/console", "script/destroy", "script/generate", "spec/looksee_spec.rb", "spec/spec_helper.rb", "spec/wirble_compatibility_spec.rb", "tasks/extconf.rake", "tasks/extconf/looksee.rake"]
16
+ s.homepage = %q{http://github.com/oggy/looksee}
17
+ s.rdoc_options = ["--main", "README.rdoc"]
18
+ s.require_paths = ["lib", "ext/looksee"]
19
+ s.rubyforge_project = %q{looksee}
20
+ s.rubygems_version = %q{1.3.4}
21
+ s.summary = %q{Looksee lets you examine the method lookup path of objects in ways not possible in plain ruby.}
22
+
23
+ if s.respond_to? :specification_version then
24
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
25
+ s.specification_version = 3
26
+
27
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
28
+ s.add_development_dependency(%q<newgem>, [">= 1.5.2"])
29
+ s.add_development_dependency(%q<rspec>, [">= 1.2.7"])
30
+ s.add_development_dependency(%q<mocha>, [">= 0.9.5"])
31
+ s.add_development_dependency(%q<hoe>, [">= 2.3.2"])
32
+ else
33
+ s.add_dependency(%q<newgem>, [">= 1.5.2"])
34
+ s.add_dependency(%q<rspec>, [">= 1.2.7"])
35
+ s.add_dependency(%q<mocha>, [">= 0.9.5"])
36
+ s.add_dependency(%q<hoe>, [">= 2.3.2"])
37
+ end
38
+ else
39
+ s.add_dependency(%q<newgem>, [">= 1.5.2"])
40
+ s.add_dependency(%q<rspec>, [">= 1.2.7"])
41
+ s.add_dependency(%q<mocha>, [">= 0.9.5"])
42
+ s.add_dependency(%q<hoe>, [">= 2.3.2"])
43
+ end
44
+ end
data/spec/looksee_spec.rb CHANGED
@@ -50,7 +50,7 @@ describe Looksee do
50
50
  temporary_module :Mod1
51
51
  temporary_module :Mod2
52
52
  temporary_class :Base
53
- temporary_class :Derived, Base do
53
+ temporary_class :Derived, :superclass => Base do
54
54
  include Mod1
55
55
  include Mod2
56
56
  end
@@ -71,6 +71,10 @@ describe Looksee do
71
71
  result = filtered_lookup_modules(C)
72
72
  result.should == %w'#<Class:C> #<Class:Object> Class Module Object Kernel'
73
73
  end
74
+
75
+ it "should work for immediate objects" do
76
+ filtered_lookup_modules(1).first.should == 'Fixnum'
77
+ end
74
78
  end
75
79
 
76
80
  describe ".lookup_path" do
@@ -83,67 +87,26 @@ describe Looksee do
83
87
  it "should return a LookupPath object for the given object" do
84
88
  object = Object.new
85
89
  Looksee.stubs(:default_lookup_path_options).returns({})
86
- Looksee::LookupPath.expects(:new).with(object, {})
90
+ Looksee::LookupPath.expects(:for).with(object, {})
87
91
  lookup_path = Looksee.lookup_path(object)
88
92
  end
89
93
 
90
94
  it "should allow symbol arguments as shortcuts for true options" do
91
95
  object = Object.new
92
96
  Looksee.stubs(:default_lookup_path_options).returns({})
93
- Looksee::LookupPath.expects(:new).with(object, {:public => true, :overridden => true})
97
+ Looksee::LookupPath.expects(:for).with(object, {:public => true, :overridden => true})
94
98
  Looksee.lookup_path(object, :public, :overridden)
95
99
  end
96
100
 
97
101
  it "should merge the default options, with the symbols, and the options hash" do
98
102
  object = Object.new
99
103
  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})
104
+ Looksee::LookupPath.expects(:for).with(object, {:public => false, :protected => true, :private => false})
101
105
  Looksee.lookup_path(object, :protected, :private, :private => false)
102
106
  end
103
107
  end
104
108
 
105
109
  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
110
  def self.target_method(name)
148
111
  define_method(:target_method){name}
149
112
  end
@@ -151,38 +114,33 @@ describe Looksee do
151
114
  def self.it_should_list_methods_with_visibility(visibility)
152
115
  it "should return the list of #{visibility} instance methods defined directly on a class" do
153
116
  temporary_class :C
154
- remove_methods C
155
- define_methods C, visibility => [:one, :two]
117
+ replace_methods C, visibility => [:one, :two]
156
118
  Looksee.send(target_method, C).to_set.should == Set[:one, :two]
157
119
  end
158
120
 
159
121
  it "should return the list of #{visibility} instance methods defined directly on a module" do
160
122
  temporary_module :M
161
- remove_methods M
162
- define_methods M, visibility => [:one, :two]
123
+ replace_methods M, visibility => [:one, :two]
163
124
  Looksee.send(target_method, M).to_set.should == Set[:one, :two]
164
125
  end
165
126
 
166
127
  it "should return the list of #{visibility} instance methods defined directly on a singleton class" do
167
128
  temporary_class :C
168
129
  c = C.new
169
- remove_methods c.singleton_class
170
- define_methods c.singleton_class, visibility => [:one, :two]
130
+ replace_methods c.singleton_class, visibility => [:one, :two]
171
131
  Looksee.send(target_method, c.singleton_class).to_set.should == Set[:one, :two]
172
132
  end
173
133
 
174
134
  it "should return the list of #{visibility} instance methods defined directly on a class' singleton class" do
175
135
  temporary_class :C
176
- remove_methods C.singleton_class, :class_singleton => true
177
- define_methods C.singleton_class, visibility => [:one, :two]
136
+ replace_methods C.singleton_class, visibility => [:one, :two], :class_singleton => true
178
137
  Looksee.send(target_method, C.singleton_class).to_set.should == Set[:one, :two]
179
138
  end
180
139
 
181
140
  # Worth checking as ruby keeps undef'd methods in method tables.
182
141
  it "should not return undefined methods" do
183
142
  temporary_class :C
184
- remove_methods C
185
- define_methods C, visibility => [:removed]
143
+ replace_methods C, visibility => [:removed]
186
144
  C.send(:undef_method, :removed)
187
145
  Looksee.send(target_method, C).to_set.should == Set[]
188
146
  end
@@ -191,8 +149,7 @@ describe Looksee do
191
149
  def self.it_should_not_list_methods_with_visibility(visibility1, visibility2)
192
150
  it "should not return any #{visibility1} or #{visibility2} instance methods" do
193
151
  temporary_class :C
194
- remove_methods C
195
- define_methods C, {visibility1 => [:a], visibility2 => [:b]}
152
+ replace_methods C, {visibility1 => [:a], visibility2 => [:b]}
196
153
  Looksee.send(target_method, C).to_set.should == Set[]
197
154
  end
198
155
  end
@@ -214,35 +171,108 @@ describe Looksee do
214
171
  it_should_list_methods_with_visibility :private
215
172
  it_should_not_list_methods_with_visibility :public, :protected
216
173
  end
174
+
175
+ describe ".internal_undefined_instance_methods" do
176
+ it "should return the list of undefined instance methods directly on a class" do
177
+ temporary_class :C
178
+ C.send(:define_method, :f){}
179
+ C.send(:undef_method, :f)
180
+ Looksee.internal_undefined_instance_methods(C).should == [:f]
181
+ end
182
+
183
+ it "should return the list of undefined instance methods directly on a module" do
184
+ temporary_module :M
185
+ M.send(:define_method, :f){}
186
+ M.send(:undef_method, :f)
187
+ Looksee.internal_undefined_instance_methods(M).should == [:f]
188
+ end
189
+
190
+ it "should return the list of undefined instance methods directly on a singleton class" do
191
+ temporary_class :C
192
+ c = C.new
193
+ c.singleton_class.send(:define_method, :f){}
194
+ c.singleton_class.send(:undef_method, :f)
195
+ Looksee.internal_undefined_instance_methods(c.singleton_class).should == [:f]
196
+ end
197
+
198
+ it "should return the list of undefined instance methods directly on a class' singleton class" do
199
+ temporary_class :C
200
+ C.singleton_class.send(:define_method, :f){}
201
+ C.singleton_class.send(:undef_method, :f)
202
+ Looksee.internal_undefined_instance_methods(C.singleton_class).should == [:f]
203
+ end
204
+
205
+ it "should not return defined methods" do
206
+ temporary_class :C
207
+ C.send(:define_method, :f){}
208
+ Looksee.internal_undefined_instance_methods(C).should == []
209
+ end
210
+
211
+ it "should not return removed methods" do
212
+ temporary_class :C
213
+ C.send(:define_method, :f){}
214
+ C.send(:remove_method, :f)
215
+ Looksee.internal_undefined_instance_methods(C).should == []
216
+ end
217
+ end
217
218
  end
218
219
  end
219
220
 
220
221
  describe Looksee::LookupPath do
221
- before do
222
- Looksee.default_lookup_path_options = {}
223
- end
224
-
225
222
  include TemporaryClasses
226
223
 
224
+ def stub_methods(mod, public, protected, private, undefined)
225
+ Looksee.stubs(:internal_public_instance_methods ).with(mod).returns(public)
226
+ Looksee.stubs(:internal_protected_instance_methods).with(mod).returns(protected)
227
+ Looksee.stubs(:internal_private_instance_methods ).with(mod).returns(private)
228
+ Looksee.stubs(:internal_undefined_instance_methods).with(mod).returns(undefined)
229
+ end
230
+
227
231
  describe "#entries" do
228
232
  it "should contain an entry for each module in the object's lookup path" do
229
233
  object = Object.new
230
234
  temporary_class :C
231
235
  temporary_class :D
232
236
  Looksee.stubs(:lookup_modules).with(object).returns([C, D])
233
- Looksee::LookupPath.new(object).entries.map{|entry| entry.module_name}.should == %w'C D'
237
+ Looksee::LookupPath.for(object).entries.map{|entry| entry.module_name}.should == %w'C D'
238
+ end
239
+ end
240
+
241
+ describe "grep" do
242
+ it "should only include methods matching the given regexp" do
243
+ temporary_class :C
244
+ temporary_class :D
245
+ stub_methods(C, ['axbyc', 'xy'], [], [], [])
246
+ stub_methods(D, ['axbyc', 'xdy'], [], [], [])
247
+ object = Object.new
248
+ Looksee.stubs(:lookup_modules).with(object).returns([C, D])
249
+ lookup_path = Looksee::LookupPath.for(object, :public => true, :overridden => true).grep(/x.y/)
250
+ lookup_path.entries.map{|entry| entry.module_name}.should == %w'C D'
251
+ lookup_path.entries[0].methods.to_set.should == Set['axbyc']
252
+ lookup_path.entries[1].methods.to_set.should == Set['axbyc', 'xdy']
253
+ end
254
+
255
+ it "should only include methods including the given string" do
256
+ temporary_class :C
257
+ temporary_class :D
258
+ stub_methods(C, ['axxa', 'axa'], [], [], [])
259
+ stub_methods(D, ['bxxb', 'axxa'], [], [], [])
260
+ object = Object.new
261
+ Looksee.stubs(:lookup_modules).with(object).returns([C, D])
262
+ lookup_path = Looksee::LookupPath.for(object, :public => true, :overridden => true).grep('xx')
263
+ lookup_path.entries.map{|entry| entry.module_name}.should == %w'C D'
264
+ lookup_path.entries[0].methods.to_set.should == Set['axxa']
265
+ lookup_path.entries[1].methods.to_set.should == Set['axxa', 'bxxb']
234
266
  end
235
267
  end
236
268
 
237
269
  describe "#inspect" do
238
270
  before do
239
- Looksee.stubs(:styles).returns(Hash.new{'%s'})
271
+ Looksee.stubs(:default_lookup_path_options).returns({})
240
272
  end
241
273
 
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)
274
+ before do
275
+ Looksee.stubs(:styles).returns(Hash.new{'%s'})
246
276
  end
247
277
 
248
278
  describe "contents" do
@@ -253,13 +283,13 @@ describe Looksee::LookupPath do
253
283
  end
254
284
  @object = Object.new
255
285
  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'])
286
+ stub_methods(C, ['public1', 'public2'], ['protected1', 'protected2'], ['private1', 'private2'], ['undefined1', 'undefined2'])
287
+ stub_methods(M, ['public1', 'public2'], ['protected1', 'protected2'], ['private1', 'private2'], ['undefined1', 'undefined2'])
258
288
  end
259
289
 
260
290
  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
291
+ lookup_path = Looksee::LookupPath.for(@object, :public => true, :overridden => true)
292
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
263
293
  |C
264
294
  | public1 public2
265
295
  |M
@@ -268,8 +298,8 @@ describe Looksee::LookupPath do
268
298
  end
269
299
 
270
300
  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
301
+ lookup_path = Looksee::LookupPath.for(@object, :protected => true, :overridden => true)
302
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
273
303
  |C
274
304
  | protected1 protected2
275
305
  |M
@@ -278,8 +308,8 @@ describe Looksee::LookupPath do
278
308
  end
279
309
 
280
310
  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
311
+ lookup_path = Looksee::LookupPath.for(@object, :private => true, :overridden => true)
312
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
283
313
  |C
284
314
  | private1 private2
285
315
  |M
@@ -287,9 +317,19 @@ describe Looksee::LookupPath do
287
317
  EOS
288
318
  end
289
319
 
320
+ it "should show modules and undefined instance methods when only undefined methods are requested" do
321
+ lookup_path = Looksee::LookupPath.for(@object, :undefined => true, :overridden => true)
322
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
323
+ |C
324
+ | undefined1 undefined2
325
+ |M
326
+ | undefined1 undefined2
327
+ EOS
328
+ end
329
+
290
330
  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
331
+ lookup_path = Looksee::LookupPath.for(@object, :public => true, :private => true, :overridden => true)
332
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
293
333
  |C
294
334
  | private1 private2 public1 public2
295
335
  |M
@@ -299,9 +339,9 @@ describe Looksee::LookupPath do
299
339
 
300
340
  it "should show singleton classes as class names in brackets" do
301
341
  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
342
+ stub_methods(C.singleton_class, ['public1', 'public2'], [], [], [])
343
+ lookup_path = Looksee::LookupPath.for(C, :public => true)
344
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
305
345
  |[C]
306
346
  | public1 public2
307
347
  EOS
@@ -309,13 +349,23 @@ describe Looksee::LookupPath do
309
349
 
310
350
  it "should handle singleton classes of singleton classes correctly" do
311
351
  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
352
+ stub_methods(C.singleton_class.singleton_class, ['public1', 'public2'], [], [], [])
353
+ lookup_path = Looksee::LookupPath.for(C.singleton_class, :public => true)
354
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
315
355
  |[[C]]
316
356
  | public1 public2
317
357
  EOS
318
358
  end
359
+
360
+ it "should not show any blank lines if a module has no methods" do
361
+ stub_methods(C, [], [], [], [])
362
+ lookup_path = Looksee::LookupPath.for(@object, :public => true, :overridden => true)
363
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
364
+ |C
365
+ |M
366
+ | public1 public2
367
+ EOS
368
+ end
319
369
  end
320
370
 
321
371
  describe "styles" do
@@ -325,6 +375,7 @@ describe Looksee::LookupPath do
325
375
  :public => "{%s}",
326
376
  :protected => "[%s]",
327
377
  :private => "<%s>",
378
+ :undefined => "~%s~",
328
379
  :overridden => "(%s)",
329
380
  }
330
381
  Looksee.stubs(:styles).returns(styles)
@@ -333,11 +384,11 @@ describe Looksee::LookupPath do
333
384
  it "should delimit each word with the configured delimiters" do
334
385
  temporary_class :C
335
386
  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
387
+ stub_methods(C, ['public'], ['protected'], ['private'], ['undefined'])
388
+ lookup_path = Looksee::LookupPath.for(Object.new, :public => true, :protected => true, :private => true, :undefined => true, :overridden => true)
389
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
339
390
  |\`C\'
340
- | <private> [protected] {public}
391
+ | <private> [protected] {public} ~undefined~
341
392
  EOS
342
393
  end
343
394
  end
@@ -346,9 +397,9 @@ describe Looksee::LookupPath do
346
397
  it "should wrap method lists at the configured number of columns, sorting vertically first, and aligning into a grid" do
347
398
  temporary_class :C
348
399
  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
400
+ stub_methods(C, %w'aa b c dd ee f g hh i', [], [], [])
401
+ lookup_path = Looksee::LookupPath.for(Object.new, :public => true)
402
+ lookup_path.inspect(:width => 20).should == <<-EOS.demargin.chomp
352
403
  |C
353
404
  | aa c ee g i
354
405
  | b dd f hh
@@ -359,10 +410,10 @@ describe Looksee::LookupPath do
359
410
  temporary_class :A
360
411
  temporary_class :B
361
412
  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
413
+ stub_methods(A, ['a', 'long_long_long_long_name'], [], [], [])
414
+ stub_methods(B, ['long_long_long', 'short'], [], [], [])
415
+ lookup_path = Looksee::LookupPath.for(Object.new, :public => true)
416
+ lookup_path.inspect.should == <<-EOS.demargin.chomp
366
417
  |A
367
418
  | a long_long_long_long_name
368
419
  |B
data/spec/spec_helper.rb CHANGED
@@ -2,6 +2,7 @@ require 'spec'
2
2
  require 'mocha'
3
3
  require 'looksee'
4
4
 
5
+ require 'rbconfig'
5
6
  require 'set'
6
7
 
7
8
  Spec::Runner.configure do |config|
@@ -59,8 +60,8 @@ module TemporaryClasses
59
60
  #
60
61
  # Create a temporary class with the given name and superclass.
61
62
  #
62
- def temporary_class(name, superclass=Object, &block)
63
- klass = Class.new(superclass)
63
+ def temporary_class(name, options={}, &block)
64
+ klass = Class.new(options[:superclass] || Object)
64
65
  Object.const_set(name, klass)
65
66
  klass.class_eval(&block) if block
66
67
  @temporary_modules << klass
@@ -77,4 +78,62 @@ module TemporaryClasses
77
78
  @temporary_modules << mod
78
79
  mod
79
80
  end
81
+
82
+ #
83
+ # Remove all methods defined exactly on the given module.
84
+ #
85
+ # As Ruby's reflection on singleton classes of classes isn't quite
86
+ # adequate, you need to provide a :class_singleton option when such
87
+ # a class is given.
88
+ #
89
+ def remove_methods(mod, opts={})
90
+ names = all_instance_methods(mod)
91
+
92
+ # all_instance_methods can't get just the methods on a class
93
+ # singleton class. Filter out superclass methods here.
94
+ if opts[:class_singleton]
95
+ klass = ObjectSpace.each_object(mod){|klass| break klass}
96
+ names -= all_instance_methods(klass.superclass.singleton_class)
97
+ end
98
+
99
+ names.sort_by{|name| name.in?([:remove_method, :send]) ? 1 : 0}.flatten
100
+ names.each do |name|
101
+ mod.send :remove_method, name
102
+ end
103
+ end
104
+
105
+ #
106
+ # Replace the methods of the given module with those named.
107
+ #
108
+ # +methods+ is a hash of visibilities to names.
109
+ #
110
+ # As Ruby's reflection on singleton classes of classes isn't quite
111
+ # adequate, you need to provide a :class_singleton option when such
112
+ # a class is given.
113
+ #
114
+ # e.g.:
115
+ #
116
+ # replace_methods MyClass, :public => [:a, :b]
117
+ #
118
+ def replace_methods(mod, options={})
119
+ remove_methods(mod, options)
120
+ mod.module_eval do
121
+ [:public, :protected, :private].each do |visibility|
122
+ Array(options[visibility]).each do |name|
123
+ define_method(name){}
124
+ send visibility, name
125
+ end
126
+ end
127
+ end
128
+ end
129
+
130
+ private # ---------------------------------------------------------
131
+
132
+ def all_instance_methods(mod)
133
+ names =
134
+ mod.public_instance_methods(false) +
135
+ mod.protected_instance_methods(false) +
136
+ mod.private_instance_methods(false)
137
+ names.map{|name| name.to_sym} # they're strings in ruby <1.9
138
+ end
80
139
  end
@@ -0,0 +1,119 @@
1
+ require 'spec_helper'
2
+ gem 'wirble' # die if wirble unavailable
3
+
4
+ describe Looksee::WirbleCompatibility do
5
+ describe "when looksee is loaded" do
6
+ #
7
+ # Run the given ruby string, and return the standard output.
8
+ #
9
+ def init_irb_with(code)
10
+ code = <<-EOS.demargin.gsub(/\n/, ';')
11
+ |#{code}
12
+ |#{stubbing_code}
13
+ |lp Object.new
14
+ EOS
15
+ irb = File.join Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'].sub(/ruby/, 'irb')
16
+ lib_dir = File.expand_path('lib')
17
+ # irb hangs when using readline without a tty
18
+ output = IO.popen("#{irb} -f --noreadline --noprompt --noverbose -I#{lib_dir} 2>&1", 'r+') do |io|
19
+ io.puts code
20
+ io.flush
21
+ io.close_write
22
+ io.read
23
+ end
24
+ end
25
+
26
+ def stubbing_code
27
+ <<-EOS.demargin
28
+ |C = Class.new
29
+ |
30
+ |Looksee.styles = Hash.new{'%s'}
31
+ |Looksee.styles[:public] = "\\e[1;32m%s\\e[0m"
32
+ |
33
+ |def Looksee.lookup_modules(object)
34
+ | [C]
35
+ |end
36
+ |def Looksee.internal_public_instance_methods(mod)
37
+ | [:a]
38
+ |end
39
+ |def Looksee.internal_protected_instance_methods(mod)
40
+ | []
41
+ |end
42
+ |def Looksee.internal_private_instance_methods(mod)
43
+ | []
44
+ |end
45
+ EOS
46
+ end
47
+
48
+ it "should work if wirble is not loaded" do
49
+ output = init_irb_with(<<-EOS.demargin)
50
+ |require 'irb'
51
+ |require 'looksee/shortcuts'
52
+ |require 'wirble'
53
+ |Wirble.init
54
+ |Wirble.colorize
55
+ EOS
56
+ output.should == <<-EOS.demargin
57
+ |C
58
+ | \e[1;32ma\e[0m
59
+ EOS
60
+ end
61
+
62
+ it "should work if wirble is loaded, but not initialized" do
63
+ output = init_irb_with(<<-EOS.demargin)
64
+ |require 'irb'
65
+ |require 'wirble'
66
+ |require 'looksee/shortcuts'
67
+ |Wirble.init
68
+ |Wirble.colorize
69
+ EOS
70
+ output.should == <<-EOS.demargin
71
+ |C
72
+ | \e[1;32ma\e[0m
73
+ EOS
74
+ end
75
+
76
+ it "should work if wirble is loaded and initialized, but colorizing is off" do
77
+ output = init_irb_with(<<-EOS.demargin)
78
+ |require 'irb'
79
+ |require 'wirble'
80
+ |Wirble.init
81
+ |require 'looksee/shortcuts'
82
+ |Wirble.colorize
83
+ EOS
84
+ output.should == <<-EOS.demargin
85
+ |C
86
+ | \e[1;32ma\e[0m
87
+ EOS
88
+ end
89
+
90
+ it "should work if wirble is loaded, initialized, and colorizing is on" do
91
+ output = init_irb_with(<<-EOS.demargin)
92
+ |require 'irb'
93
+ |require 'wirble'
94
+ |Wirble.init
95
+ |Wirble.colorize
96
+ |require 'looksee/shortcuts'
97
+ EOS
98
+ output.should == <<-EOS.demargin
99
+ |C
100
+ | \e[1;32ma\e[0m
101
+ EOS
102
+ end
103
+
104
+ it "should work if wirble colorizing is enabled twice" do
105
+ output = init_irb_with(<<-EOS.demargin)
106
+ |require 'irb'
107
+ |require 'looksee/shortcuts'
108
+ |require 'wirble'
109
+ |Wirble.init
110
+ |Wirble.colorize
111
+ |Wirble.colorize
112
+ EOS
113
+ output.should == <<-EOS.demargin
114
+ |C
115
+ | \e[1;32ma\e[0m
116
+ EOS
117
+ end
118
+ end
119
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oggy-looksee
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - George Ogata
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-07-05 00:00:00 -07:00
12
+ date: 2009-08-20 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 1.5.1
23
+ version: 1.5.2
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
@@ -74,15 +74,19 @@ files:
74
74
  - lib/looksee.rb
75
75
  - lib/looksee/shortcuts.rb
76
76
  - lib/looksee/version.rb
77
+ - lib/looksee/wirble_compatibility.rb
78
+ - looksee.gemspec
77
79
  - script/console
78
80
  - script/destroy
79
81
  - script/generate
80
82
  - spec/looksee_spec.rb
81
83
  - spec/spec_helper.rb
84
+ - spec/wirble_compatibility_spec.rb
82
85
  - tasks/extconf.rake
83
86
  - tasks/extconf/looksee.rake
84
87
  has_rdoc: false
85
88
  homepage: http://github.com/oggy/looksee
89
+ licenses:
86
90
  post_install_message:
87
91
  rdoc_options:
88
92
  - --main
@@ -105,7 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
109
  requirements: []
106
110
 
107
111
  rubyforge_project: looksee
108
- rubygems_version: 1.2.0
112
+ rubygems_version: 1.3.5
109
113
  signing_key:
110
114
  specification_version: 3
111
115
  summary: Looksee lets you examine the method lookup path of objects in ways not possible in plain ruby.