looksee 0.2.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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