looksee 0.2.1 → 1.0.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.
Files changed (57) hide show
  1. data/CHANGELOG +14 -0
  2. data/LICENSE +22 -0
  3. data/README.markdown +161 -0
  4. data/Rakefile +9 -36
  5. data/ext/extconf.rb +9 -0
  6. data/ext/mri/1.9.2/debug.h +36 -0
  7. data/ext/mri/1.9.2/id.h +170 -0
  8. data/ext/mri/1.9.2/method.h +103 -0
  9. data/ext/mri/1.9.2/node.h +483 -0
  10. data/ext/mri/1.9.2/thread_pthread.h +27 -0
  11. data/ext/mri/1.9.2/vm_core.h +707 -0
  12. data/ext/mri/1.9.2/vm_opts.h +51 -0
  13. data/ext/mri/env-1.8.h +27 -0
  14. data/ext/mri/eval_c-1.8.h +27 -0
  15. data/ext/mri/mri.c +269 -0
  16. data/ext/{looksee → mri}/node-1.9.h +0 -0
  17. data/ext/rbx/rbx.c +13 -0
  18. data/lib/looksee.rb +4 -385
  19. data/lib/looksee/adapter.rb +10 -0
  20. data/lib/looksee/adapter/base.rb +100 -0
  21. data/lib/looksee/adapter/rubinius.rb +73 -0
  22. data/lib/looksee/clean.rb +122 -0
  23. data/lib/looksee/columnizer.rb +73 -0
  24. data/lib/looksee/core_ext.rb +59 -0
  25. data/lib/looksee/editor.rb +58 -0
  26. data/lib/looksee/help.rb +54 -0
  27. data/lib/looksee/inspector.rb +55 -0
  28. data/lib/looksee/lookup_path.rb +95 -0
  29. data/lib/looksee/rbx.bundle +0 -0
  30. data/lib/looksee/shortcuts.rb +1 -105
  31. data/lib/looksee/version.rb +9 -1
  32. data/lib/looksee/wirble_compatibility.rb +2 -3
  33. data/spec/adapter_spec.rb +546 -0
  34. data/spec/columnizer_spec.rb +52 -0
  35. data/spec/core_ext_spec.rb +41 -0
  36. data/spec/editor_spec.rb +128 -0
  37. data/spec/inspector_spec.rb +178 -0
  38. data/spec/lookup_path_spec.rb +84 -0
  39. data/spec/spec_helper.rb +13 -127
  40. data/spec/support/core_ext.rb +25 -0
  41. data/spec/support/temporary_classes.rb +102 -0
  42. data/spec/support/test_adapter.rb +72 -0
  43. data/spec/wirble_compatibility_spec.rb +20 -23
  44. metadata +91 -57
  45. data/.autotest +0 -9
  46. data/History.txt +0 -22
  47. data/Manifest.txt +0 -21
  48. data/README.rdoc +0 -129
  49. data/ext/looksee/extconf.rb +0 -6
  50. data/ext/looksee/looksee.c +0 -144
  51. data/looksee.gemspec +0 -44
  52. data/script/console +0 -7
  53. data/script/destroy +0 -14
  54. data/script/generate +0 -14
  55. data/spec/looksee_spec.rb +0 -425
  56. data/tasks/extconf.rake +0 -13
  57. data/tasks/extconf/looksee.rake +0 -43
@@ -0,0 +1,54 @@
1
+ module Looksee
2
+ class Help
3
+ def inspect
4
+ <<-EOS.gsub(/^ *\|/, '')
5
+ |== Looksee Quick Reference
6
+ |
7
+ | object.ls(*specifiers)
8
+ | Print the methods of \`object\'.
9
+ |
10
+ | Available specifiers:
11
+ |
12
+ | :public :private :overridden
13
+ | :protected :undefined
14
+ | Print methods with these visibilities.
15
+ |
16
+ | :nopublic :noprivate :nooverridden
17
+ | :noprotected :noundefined
18
+ | Do not print methods with these visibilities.
19
+ |
20
+ | "string"
21
+ | Print methods containing this string.
22
+ |
23
+ | /regexp/
24
+ | Print methods matching this regexp.
25
+ |
26
+ | Styles:
27
+ |
28
+ | #{Looksee.styles[:module] % 'Module'}
29
+ | #{Looksee.styles[:public] % 'public'} }
30
+ | #{Looksee.styles[:protected] % 'protected'} } like a traffic light!
31
+ | #{Looksee.styles[:private] % 'private'} }
32
+ | #{Looksee.styles[:undefined] % 'undefined'} ] like a ghost!
33
+ | #{Looksee.styles[:overridden] % 'overridden'} ] like a shadow!
34
+ |
35
+ | Customize with Looksee.styles:
36
+ |
37
+ | Looksee.styles = {
38
+ | :module => '**%s**',
39
+ | :private => '(%s)',
40
+ | ...
41
+ | }
42
+ |
43
+ | object.edit(method)
44
+ |
45
+ | Jump to the source of the given method. Set your editor
46
+ | with Looksee.editor or the LOOKSEE_EDITOR environment
47
+ | variable. "%f" expands to the file name, "%l" to the line
48
+ | number. Example:
49
+ |
50
+ | Looksee.editor = "emacs -nw +%f %l"
51
+ EOS
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,55 @@
1
+ module Looksee
2
+ class Inspector
3
+ def initialize(lookup_path, options={})
4
+ @lookup_path = lookup_path
5
+ @visibilities = (vs = options[:visibilities]) ? vs.to_set : Set[]
6
+ @filters = (fs = options[:filters]) ? fs.to_set : Set[]
7
+ @width = options[:width] || ENV['COLUMNS'].to_i.nonzero? || Looksee.default_width
8
+ end
9
+
10
+ attr_reader :lookup_path
11
+ attr_reader :visibilities
12
+ attr_reader :filters
13
+
14
+ def inspect
15
+ lookup_path.entries.reverse.map do |entry|
16
+ inspect_entry(entry)
17
+ end.join("\n")
18
+ end
19
+
20
+ private
21
+
22
+ def inspect_entry(entry)
23
+ string = styled_module_name(entry) << "\n"
24
+ string << Columnizer.columnize(styled_methods(entry), @width)
25
+ string.chomp
26
+ end
27
+
28
+ def styled_module_name(entry)
29
+ Looksee.styles[:module] % Looksee.adapter.describe_module(entry.module)
30
+ end
31
+
32
+ def styled_methods(entry)
33
+ pattern = filter_pattern
34
+ show_overridden = @visibilities.include?(:overridden)
35
+ entry.map do |name, visibility|
36
+ next if !selected?(name, visibility)
37
+ style = entry.overridden?(name) ? :overridden : visibility
38
+ next if style == :overridden && !show_overridden
39
+ Looksee.styles[style] % name
40
+ end.compact
41
+ end
42
+
43
+ def filter_pattern
44
+ strings = filters.grep(String)
45
+ regexps = filters.grep(Regexp)
46
+ string_patterns = strings.map{|s| Regexp.escape(s)}
47
+ regexp_patterns = regexps.map{|s| s.source}
48
+ /#{(string_patterns + regexp_patterns).join('|')}/
49
+ end
50
+
51
+ def selected?(name, visibility)
52
+ @visibilities.include?(visibility) && name =~ filter_pattern
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,95 @@
1
+ module Looksee
2
+ #
3
+ # Represents the method lookup path of an object, as a list of
4
+ # Entries.
5
+ #
6
+ class LookupPath
7
+ def initialize(object)
8
+ @object = object
9
+ @entries = create_entries
10
+ end
11
+
12
+ #
13
+ # The object this lookup path represents.
14
+ #
15
+ attr_reader :object
16
+
17
+ #
18
+ # List of Entry objects, each one representing a Module in the
19
+ # lookup path.
20
+ #
21
+ attr_reader :entries
22
+
23
+ def find(name)
24
+ entries.each do |entry|
25
+ visibility = entry.methods[name] or
26
+ next
27
+
28
+ if visibility == :undefined
29
+ return nil
30
+ else
31
+ return entry.module.instance_method(name)
32
+ end
33
+ end
34
+ nil
35
+ end
36
+
37
+ #
38
+ # Return a string showing the object's lookup path.
39
+ #
40
+ def inspect(options={})
41
+ Inspector.new(self, options).inspect
42
+ end
43
+
44
+ private # -------------------------------------------------------
45
+
46
+ def create_entries
47
+ seen = Set.new
48
+ Looksee.adapter.lookup_modules(object).map do |mod|
49
+ entry = Entry.new(mod, seen)
50
+ seen += entry.methods.keys
51
+ entry
52
+ end
53
+ end
54
+
55
+ #
56
+ # An entry in the LookupPath.
57
+ #
58
+ class Entry
59
+ def initialize(mod, overridden)
60
+ @module = mod
61
+ @methods = find_methods
62
+ @overridden = overridden
63
+ end
64
+
65
+ attr_reader :module, :methods
66
+
67
+ def overridden?(name)
68
+ @overridden.include?(name.to_s)
69
+ end
70
+
71
+ #
72
+ # Yield each method in alphabetical order along with its
73
+ # visibility (:public, :private, :protected, :undefined, or
74
+ # :overridden).
75
+ #
76
+ def each(&block)
77
+ @methods.sort.each(&block)
78
+ end
79
+
80
+ include Enumerable
81
+
82
+ private # -----------------------------------------------------
83
+
84
+ def find_methods
85
+ methods = {}
86
+ [:public, :protected, :private, :undefined].each do |visibility|
87
+ Looksee.adapter.send("internal_#{visibility}_instance_methods", @module).each do |method|
88
+ methods[method.to_s] = visibility
89
+ end
90
+ end
91
+ methods
92
+ end
93
+ end
94
+ end
95
+ end
Binary file
@@ -1,107 +1,3 @@
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
1
  require 'looksee'
10
2
 
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
55
-
56
- module Looksee
57
- #
58
- # Show a quick reference.
59
- #
60
- def self.help
61
- Help.new
62
- end
63
-
64
- class Help
65
- def inspect
66
- <<-EOS.gsub(/^ *\|/, '')
67
- |== Looksee Quick Reference
68
- |
69
- | lp(object)
70
- | object.lookup_path
71
- | Print the method lookup path of \`object\'
72
- |
73
- | lpi(klass)
74
- | Print the method lookup path of an instance of \`klass\'.
75
- |
76
- |Add .grep(/pattern/) to restrict the methods listed:
77
- |
78
- | lp(object).grep(/foo/)
79
- |
80
- |== Visibilities
81
- |
82
- |Methods are printed according to their visibility:
83
- |
84
- |#{style_info}
85
- |
86
- |Pass options to specify which visibilities to show:
87
- |
88
- | lp(object, :private => true, :overridden => false)
89
- | lp(object, :private , :overridden => false) # shortcut
90
- EOS
91
- end
92
-
93
- def style_info
94
- max_width = 0
95
- styles = [:public, :protected, :private, :undefined, :overridden]
96
- data = styles.map do |name|
97
- display_style = Looksee.styles[name] % name
98
- display_length = display_style.length
99
- max_width = display_length if display_length > max_width
100
- on = Looksee.default_lookup_path_options[name] ? 'on' : 'off'
101
- [display_style, on]
102
- end.map do |display_style, on|
103
- " * #{display_style.ljust(max_width)} (#{on} by default)"
104
- end.join("\n")
105
- end
106
- end
107
- end
3
+ warn "'looksee/shortcuts' is deprecated; please require 'looksee' instead."
@@ -1,3 +1,11 @@
1
1
  module Looksee
2
- VERSION = '0.2.1'
2
+ VERSION = [1, 0, 0]
3
+
4
+ class << VERSION
5
+ include Comparable
6
+
7
+ def to_s
8
+ join('.')
9
+ end
10
+ end
3
11
  end
@@ -8,6 +8,7 @@ module Looksee
8
8
  end
9
9
 
10
10
  def wirble_colorizing?
11
+ require 'irb'
11
12
  IRB::Irb.method_defined?(:non_color_output_value)
12
13
  end
13
14
 
@@ -39,7 +40,7 @@ module Looksee
39
40
  IRB::Irb.class_eval do
40
41
  def output_value_with_looksee
41
42
  case @context.last_value
42
- when Looksee::LookupPath, Looksee::Help
43
+ when Looksee::Inspector, Looksee::Help
43
44
  non_color_output_value
44
45
  else
45
46
  output_value_without_looksee
@@ -83,5 +84,3 @@ module Looksee
83
84
  end
84
85
  end
85
86
  end
86
-
87
- Looksee::WirbleCompatibility.init
@@ -0,0 +1,546 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Looksee.adapter" do
4
+ include TemporaryClasses
5
+
6
+ before do
7
+ @adapter = NATIVE_ADAPTER
8
+ end
9
+
10
+ describe "#lookup_modules" do
11
+ #
12
+ # Filter out modules which are hard to test against, and returns
13
+ # the list of module names. #inspect strings are used for names
14
+ # of singleton classes, since they have no name.
15
+ #
16
+ def filtered_lookup_modules(object)
17
+ result = @adapter.lookup_modules(object)
18
+ result.select{ |mod| deterministic_module?(mod) }
19
+ end
20
+
21
+ #
22
+ # Return true if the given module name is of a module we can test
23
+ # for.
24
+ #
25
+ # This excludes ruby version dependent modules, and modules tossed
26
+ # into the hierarchy by testing frameworks.
27
+ #
28
+ def deterministic_module?(mod)
29
+ junk_patterns = [
30
+ # pollution from testing libraries
31
+ 'Mocha', 'Spec',
32
+ # RSpec adds this under ruby 1.8.6
33
+ 'InstanceExecHelper',
34
+ # RSpec 2
35
+ 'RSpec::',
36
+ # only in ruby 1.9
37
+ 'BasicObject',
38
+ # something pulls this in under ruby 1.9
39
+ 'PP',
40
+ # our own pollution,
41
+ 'Looksee::Object',
42
+ ]
43
+ pattern = /\A(#{junk_patterns.join('|')})/
44
+
45
+ # Singleton classes of junk are junk.
46
+ if Looksee.ruby_engine == 'rbx'
47
+ # Rubinius singleton class #inspect strings aren't formatted
48
+ # like the others.
49
+ while mod.respond_to?(:__metaclass_object__) && (object = mod.__metaclass_object__).is_a?(Class)
50
+ mod = object
51
+ end
52
+ mod.name !~ pattern
53
+ else
54
+ name = mod.to_s
55
+ while name =~ /\A#<Class:(.*)>\z/
56
+ name = $1
57
+ end
58
+ name !~ pattern
59
+ end
60
+ end
61
+
62
+ it "should contain an entry for each module in the object's lookup path" do
63
+ temporary_module :Mod1
64
+ temporary_module :Mod2
65
+ temporary_class :Base
66
+ temporary_class :Derived, :superclass => Base do
67
+ include Mod1
68
+ include Mod2
69
+ end
70
+ filtered_lookup_modules(Derived.new) == [Derived, Mod2, Mod1, Base, Object, Kernel]
71
+ end
72
+
73
+ it "should contain an entry for the object's singleton class if it exists" do
74
+ object = Object.new
75
+ object.singleton_class
76
+
77
+ filtered_lookup_modules(object).should == [object.singleton_class, Object, Kernel]
78
+ end
79
+
80
+ it "should contain entries for singleton classes of all ancestors for class objects" do
81
+ temporary_class :C
82
+ filtered_lookup_modules(C).should == [C.singleton_class, Object.singleton_class, Class, Module, Object, Kernel]
83
+ end
84
+
85
+ it "should work for immediate objects" do
86
+ filtered_lookup_modules(1).first.should == Fixnum
87
+ end
88
+ end
89
+
90
+ describe "internal instance methods:" do
91
+ def self.target_method(name)
92
+ define_method(:target_method){name}
93
+ end
94
+
95
+ def self.it_should_list_methods_with_visibility(visibility)
96
+ it "should return the list of #{visibility} instance methods defined directly on a class" do
97
+ temporary_class :C
98
+ replace_methods C, visibility => [:one, :two]
99
+ @adapter.send(target_method, C).to_set.should == Set[:one, :two]
100
+ end
101
+
102
+ it "should return the list of #{visibility} instance methods defined directly on a module" do
103
+ temporary_module :M
104
+ replace_methods M, visibility => [:one, :two]
105
+ @adapter.send(target_method, M).to_set.should == Set[:one, :two]
106
+ end
107
+
108
+ it "should return the list of #{visibility} instance methods defined directly on a singleton class" do
109
+ temporary_class :C
110
+ c = C.new
111
+ replace_methods c.singleton_class, visibility => [:one, :two]
112
+ @adapter.send(target_method, c.singleton_class).to_set.should == Set[:one, :two]
113
+ end
114
+
115
+ it "should return the list of #{visibility} instance methods defined directly on a class' singleton class" do
116
+ temporary_class :C
117
+ replace_methods C.singleton_class, visibility => [:one, :two], :class_singleton => true
118
+ @adapter.send(target_method, C.singleton_class).to_set.should == Set[:one, :two]
119
+ end
120
+
121
+ # Worth checking as ruby keeps undef'd methods in method tables.
122
+ it "should not return undefined methods" do
123
+ temporary_class :C
124
+ replace_methods C, visibility => [:removed]
125
+ C.send(:undef_method, :removed)
126
+ @adapter.send(target_method, C).to_set.should == Set[]
127
+ end
128
+ end
129
+
130
+ def self.it_should_not_list_methods_with_visibility(visibility1, visibility2)
131
+ it "should not return any #{visibility1} or #{visibility2} instance methods" do
132
+ temporary_class :C
133
+ replace_methods C, {visibility1 => [:a], visibility2 => [:b]}
134
+ @adapter.send(target_method, C).to_set.should == Set[]
135
+ end
136
+ end
137
+
138
+ describe ".internal_public_instance_methods" do
139
+ target_method :internal_public_instance_methods
140
+ it_should_list_methods_with_visibility :public
141
+ it_should_not_list_methods_with_visibility :private, :protected
142
+ end
143
+
144
+ describe ".internal_protected_instance_methods" do
145
+ target_method :internal_protected_instance_methods
146
+ it_should_list_methods_with_visibility :protected
147
+ it_should_not_list_methods_with_visibility :public, :private
148
+ end
149
+
150
+ describe ".internal_private_instance_methods" do
151
+ target_method :internal_private_instance_methods
152
+ it_should_list_methods_with_visibility :private
153
+ it_should_not_list_methods_with_visibility :public, :protected
154
+ end
155
+
156
+ describe ".internal_undefined_instance_methods" do
157
+ it "should return the list of undefined instance methods directly on a class" do
158
+ temporary_class :C
159
+ C.send(:define_method, :f){}
160
+ C.send(:undef_method, :f)
161
+ @adapter.internal_undefined_instance_methods(C).should == [:f]
162
+ end
163
+
164
+ it "should return the list of undefined instance methods directly on a module" do
165
+ temporary_module :M
166
+ M.send(:define_method, :f){}
167
+ M.send(:undef_method, :f)
168
+ @adapter.internal_undefined_instance_methods(M).should == [:f]
169
+ end
170
+
171
+ it "should return the list of undefined instance methods directly on a singleton class" do
172
+ temporary_class :C
173
+ c = C.new
174
+ c.singleton_class.send(:define_method, :f){}
175
+ c.singleton_class.send(:undef_method, :f)
176
+ @adapter.internal_undefined_instance_methods(c.singleton_class).should == [:f]
177
+ end
178
+
179
+ it "should return the list of undefined instance methods directly on a class' singleton class" do
180
+ temporary_class :C
181
+ C.singleton_class.send(:define_method, :f){}
182
+ C.singleton_class.send(:undef_method, :f)
183
+ @adapter.internal_undefined_instance_methods(C.singleton_class).should == [:f]
184
+ end
185
+
186
+ it "should not return defined methods" do
187
+ temporary_class :C
188
+ C.send(:define_method, :f){}
189
+ @adapter.internal_undefined_instance_methods(C).should == []
190
+ end
191
+
192
+ it "should not return removed methods" do
193
+ temporary_class :C
194
+ C.send(:define_method, :f){}
195
+ C.send(:remove_method, :f)
196
+ @adapter.internal_undefined_instance_methods(C).should == []
197
+ end
198
+ end
199
+ end
200
+
201
+ describe "#singleton_class?" do
202
+ it "should return true if the object is a singleton class of an object" do
203
+ object = (class << Object.new; self; end)
204
+ @adapter.singleton_class?(object).should be_true
205
+ end
206
+
207
+ it "should return true if the object is a singleton class of a class" do
208
+ object = (class << Class.new; self; end)
209
+ @adapter.singleton_class?(object).should be_true
210
+ end
211
+
212
+ it "should return true if the object is a singleton class of a singleton class" do
213
+ object = (class << (class << Class.new; self; end); self; end)
214
+ @adapter.singleton_class?(object).should be_true
215
+ end
216
+
217
+ it "should return false if the object is just a class" do
218
+ object = Class.new
219
+ @adapter.singleton_class?(object).should be_false
220
+ end
221
+
222
+ it "should return false if the object is just a module" do
223
+ object = Module.new
224
+ @adapter.singleton_class?(object).should be_false
225
+ end
226
+
227
+ it "should return false if the object is just an object" do
228
+ object = Object.new
229
+ @adapter.singleton_class?(object).should be_false
230
+ end
231
+
232
+ it "should return false if the object is TrueClass" do
233
+ @adapter.singleton_class?(TrueClass).should be_false
234
+ end
235
+
236
+ it "should return false if the object is FalseClass" do
237
+ @adapter.singleton_class?(FalseClass).should be_false
238
+ end
239
+
240
+ it "should return false if the object is NilClass" do
241
+ @adapter.singleton_class?(NilClass).should be_false
242
+ end
243
+ end
244
+
245
+ describe "singleton_instance" do
246
+ it "should return the instance of the given singleton class" do
247
+ object = Object.new
248
+ @adapter.singleton_instance((class << object; self; end)).should equal(object)
249
+ end
250
+
251
+ it "should return the instance of the given class singleton class" do
252
+ klass = Class.new
253
+ @adapter.singleton_instance((class << klass; self; end)).should equal(klass)
254
+ end
255
+
256
+ it "should return the instance of the given module singleton class" do
257
+ mod = Module.new
258
+ @adapter.singleton_instance((class << mod; self; end)).should equal(mod)
259
+ end
260
+
261
+ it "should raise a TypeError if the given object is just a class" do
262
+ lambda do
263
+ @adapter.singleton_instance(Class.new)
264
+ end.should raise_error(TypeError)
265
+ end
266
+
267
+ it "should raise a TypeError if the given object is just a module" do
268
+ lambda do
269
+ @adapter.singleton_instance(Module.new)
270
+ end.should raise_error(TypeError)
271
+ end
272
+
273
+ it "should raise a TypeError if the given object is just a object" do
274
+ lambda do
275
+ @adapter.singleton_instance(Object.new)
276
+ end.should raise_error(TypeError)
277
+ end
278
+ end
279
+
280
+ describe "#module_name" do
281
+ it "should return the fully-qualified name of the given module" do
282
+ ::M = Module.new
283
+ M::N = Module.new
284
+ begin
285
+ @adapter.module_name(M::N).should == 'M::N'
286
+ ensure
287
+ Object.send :remove_const, :M
288
+ end
289
+ end
290
+
291
+ it "should return the fully-qualified name of the given class" do
292
+ ::M = Module.new
293
+ M::C = Class.new
294
+ begin
295
+ @adapter.module_name(M::C).should == 'M::C'
296
+ ensure
297
+ Object.send :remove_const, :M
298
+ end
299
+ end
300
+
301
+ it "should not be affected by overridding the module's #to_s or #name" do
302
+ begin
303
+ ::M = Module.new
304
+ ::M::C = Class.new do
305
+ def name
306
+ 'overridden'
307
+ end
308
+ def to_s
309
+ 'overridden'
310
+ end
311
+ end
312
+ @adapter.describe_module(M::C).should == 'M::C'
313
+ ensure
314
+ Object.send :remove_const, :M
315
+ end
316
+ end
317
+
318
+ it "should return an empty string for unnamed modules" do
319
+ @adapter.module_name(Module.new).should == ''
320
+ end
321
+
322
+ it "should return an empty string for unnamed classes" do
323
+ @adapter.module_name(Class.new).should == ''
324
+ end
325
+
326
+ it "should return an empty string for singleton classes" do
327
+ object = Object.new
328
+ @adapter.module_name((class << object; self; end)).should == ''
329
+ end
330
+
331
+ it "should raise a TypeError if the argumeent is not a module" do
332
+ lambda do
333
+ @adapter.module_name(Object.new)
334
+ end.should raise_error(TypeError)
335
+ end
336
+ end
337
+
338
+ describe "#describe_module" do
339
+ it "should return the fully-qualified name of a module" do
340
+ begin
341
+ ::M = Module.new
342
+ ::M::N = Module.new
343
+ @adapter.describe_module(::M::N).should == 'M::N'
344
+ ensure
345
+ Object.send :remove_const, :M
346
+ end
347
+ end
348
+
349
+ it "should not be affected by overridding the module's #to_s or #name" do
350
+ begin
351
+ ::M = Module.new
352
+ ::M::C = Class.new do
353
+ def name
354
+ 'overridden'
355
+ end
356
+ def to_s
357
+ 'overridden'
358
+ end
359
+ end
360
+ @adapter.describe_module(::M::C).should == 'M::C'
361
+ ensure
362
+ Object.send :remove_const, :M
363
+ end
364
+ end
365
+
366
+ describe "for an unnamed class" do
367
+ it "should describe the object as 'unnamed Class'" do
368
+ @adapter.describe_module(Class.new).should == 'unnamed Class'
369
+ end
370
+ end
371
+
372
+ describe "for an unnamed module" do
373
+ it "should describe the object as 'unnamed Module'" do
374
+ @adapter.describe_module(Module.new).should == 'unnamed Module'
375
+ end
376
+ end
377
+
378
+ describe "for a singleton class of an object" do
379
+ it "should describe the object in brackets" do
380
+ begin
381
+ ::M = Module.new
382
+ ::M::C = Class.new
383
+ object = M::C.new
384
+ @adapter.describe_module(object.singleton_class).should == '[M::C instance]'
385
+ ensure
386
+ Object.send :remove_const, :M
387
+ end
388
+ end
389
+ end
390
+
391
+ describe "for a singleton class of a named class" do
392
+ it "should return the class name in brackets" do
393
+ begin
394
+ ::M = Module.new
395
+ ::M::C = Class.new
396
+ @adapter.describe_module(M::C.singleton_class).should == '[M::C]'
397
+ ensure
398
+ Object.send :remove_const, :M
399
+ end
400
+ end
401
+ end
402
+
403
+ describe "for a singleton class of an unnamed class" do
404
+ it "should describe the object as '[unnamed Class]'" do
405
+ klass = Class.new
406
+ @adapter.describe_module(klass.singleton_class).should == '[unnamed Class]'
407
+ end
408
+ end
409
+
410
+ describe "for a singleton class of an unnamed module" do
411
+ it "should describe the object as '[unnamed Module]'" do
412
+ mod = Module.new
413
+ @adapter.describe_module(mod.singleton_class).should == '[unnamed Module]'
414
+ end
415
+ end
416
+
417
+ describe "for a singleton class of a singleton class" do
418
+ it "should return the object's class name in two pairs of brackets" do
419
+ begin
420
+ ::M = Module.new
421
+ ::M::C = Class.new
422
+ klass = M::C.singleton_class.singleton_class
423
+ @adapter.describe_module(klass).should == '[[M::C]]'
424
+ ensure
425
+ Object.send :remove_const, :M
426
+ end
427
+ end
428
+ end
429
+ end
430
+
431
+ describe "#source_location" do
432
+ def load_source(source)
433
+ @tmp = "#{ROOT}/spec/tmp"
434
+ @source_path = "#@tmp/c.rb"
435
+ FileUtils.mkdir_p @tmp
436
+ open(@source_path, 'w') { |f| f.print source }
437
+ load @source_path
438
+ @source_path
439
+ end
440
+
441
+ after do
442
+ FileUtils.rm_rf @tmp
443
+ Object.send(:remove_const, :C) if Object.const_defined?(:C)
444
+ end
445
+
446
+ it "should return the file and line number the given method was defined on" do
447
+ path = load_source <<-EOS.demargin
448
+ |class C
449
+ | def f
450
+ | end
451
+ |end
452
+ EOS
453
+ method = C.instance_method(:f)
454
+ @adapter.source_location(method).should == [path, 2]
455
+ end
456
+
457
+ it "should work for methods defined via a block (MRI BMETHOD)" do
458
+ path = load_source <<-EOS.demargin
459
+ |class C
460
+ | define_method :f do
461
+ | end
462
+ |end
463
+ EOS
464
+ method = C.instance_method(:f)
465
+ @adapter.source_location(method).should == [path, 2]
466
+ end
467
+
468
+ it "should work for methods defined via a proc (MRI BMETHOD)" do
469
+ path = load_source <<-EOS.demargin
470
+ |class C
471
+ | f = lambda do
472
+ | end
473
+ | define_method :f, f
474
+ |end
475
+ EOS
476
+ method = C.instance_method(:f)
477
+ @adapter.source_location(method).should == [path, 2]
478
+ end
479
+
480
+ it "should work for methods defined via a UnboundMethod (MRI DMETHOD)" do
481
+ path = load_source <<-EOS.demargin
482
+ |class C
483
+ | def f
484
+ | end
485
+ | define_method :g, instance_method(:f)
486
+ |end
487
+ EOS
488
+ method = C.instance_method(:g)
489
+ @adapter.source_location(method).should == [path, 2]
490
+ end
491
+
492
+ it "should work for methods defined via a BoundMethod (MRI DMETHOD)" do
493
+ path = load_source <<-EOS.demargin
494
+ |class C
495
+ | def f
496
+ | end
497
+ | define_method :g, new.method(:f)
498
+ |end
499
+ EOS
500
+ method = C.instance_method(:g)
501
+ @adapter.source_location(method).should == [path, 2]
502
+ end
503
+
504
+ it "should work for methods whose visibility is overridden in a subclass (MRI ZSUPER)" do
505
+ path = load_source <<-EOS.demargin
506
+ |class C
507
+ | def f
508
+ | end
509
+ |end
510
+ |class D < C
511
+ | private :f
512
+ |end
513
+ EOS
514
+ begin
515
+ method = D.instance_method(:f)
516
+ @adapter.source_location(method).should == [path, 2]
517
+ ensure
518
+ Object.send(:remove_const, :D)
519
+ end
520
+ end
521
+
522
+ it "should work for aliases (MRI FBODY)" do
523
+ path = load_source <<-EOS.demargin
524
+ |class C
525
+ | def f
526
+ | end
527
+ | alias g f
528
+ |end
529
+ EOS
530
+ method = C.instance_method(:g)
531
+ @adapter.source_location(method).should == [path, 2]
532
+ end
533
+
534
+ it "should raise a TypeError if the argument is not an UnboundMethod" do
535
+ path = load_source <<-EOS.demargin
536
+ |class C
537
+ | def f
538
+ | end
539
+ |end
540
+ EOS
541
+ lambda do
542
+ @adapter.source_location(nil)
543
+ end.should raise_error(TypeError)
544
+ end
545
+ end
546
+ end