looksee 3.0.0-universal-java-1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +66 -0
  3. data/LICENSE +22 -0
  4. data/README.markdown +175 -0
  5. data/Rakefile +13 -0
  6. data/ext/extconf.rb +19 -0
  7. data/ext/mri/1.9.2/debug.h +36 -0
  8. data/ext/mri/1.9.2/id.h +170 -0
  9. data/ext/mri/1.9.2/method.h +103 -0
  10. data/ext/mri/1.9.2/node.h +483 -0
  11. data/ext/mri/1.9.2/thread_pthread.h +27 -0
  12. data/ext/mri/1.9.2/vm_core.h +707 -0
  13. data/ext/mri/1.9.2/vm_opts.h +51 -0
  14. data/ext/mri/1.9.3/atomic.h +56 -0
  15. data/ext/mri/1.9.3/debug.h +41 -0
  16. data/ext/mri/1.9.3/id.h +175 -0
  17. data/ext/mri/1.9.3/internal.h +227 -0
  18. data/ext/mri/1.9.3/internal_falcon.h +248 -0
  19. data/ext/mri/1.9.3/method.h +105 -0
  20. data/ext/mri/1.9.3/node.h +503 -0
  21. data/ext/mri/1.9.3/thread_pthread.h +51 -0
  22. data/ext/mri/1.9.3/vm_core.h +755 -0
  23. data/ext/mri/1.9.3/vm_opts.h +51 -0
  24. data/ext/mri/2.0.0/internal.h +378 -0
  25. data/ext/mri/2.0.0/method.h +138 -0
  26. data/ext/mri/2.1.0/internal.h +889 -0
  27. data/ext/mri/2.1.0/method.h +142 -0
  28. data/ext/mri/2.2.0/internal.h +1182 -0
  29. data/ext/mri/2.2.0/method.h +141 -0
  30. data/ext/mri/env-1.8.h +27 -0
  31. data/ext/mri/eval_c-1.8.h +27 -0
  32. data/ext/mri/mri.c +309 -0
  33. data/ext/mri/node-1.9.h +35 -0
  34. data/ext/rbx/rbx.c +13 -0
  35. data/lib/looksee.rb +2 -0
  36. data/lib/looksee/JRuby.jar +0 -0
  37. data/lib/looksee/adapter.rb +8 -0
  38. data/lib/looksee/adapter/base.rb +105 -0
  39. data/lib/looksee/adapter/rubinius.rb +84 -0
  40. data/lib/looksee/clean.rb +169 -0
  41. data/lib/looksee/columnizer.rb +73 -0
  42. data/lib/looksee/core_ext.rb +48 -0
  43. data/lib/looksee/editor.rb +64 -0
  44. data/lib/looksee/help.rb +54 -0
  45. data/lib/looksee/inspector.rb +70 -0
  46. data/lib/looksee/lookup_path.rb +95 -0
  47. data/lib/looksee/rbx.bundle +0 -0
  48. data/lib/looksee/version.rb +11 -0
  49. data/spec/looksee/adapter_spec.rb +588 -0
  50. data/spec/looksee/clean_spec.rb +41 -0
  51. data/spec/looksee/columnizer_spec.rb +52 -0
  52. data/spec/looksee/core_ext_spec.rb +17 -0
  53. data/spec/looksee/editor_spec.rb +107 -0
  54. data/spec/looksee/inspector_spec.rb +179 -0
  55. data/spec/looksee/lookup_path_spec.rb +87 -0
  56. data/spec/spec_helper.rb +29 -0
  57. data/spec/support/core_ext.rb +25 -0
  58. data/spec/support/temporary_classes.rb +78 -0
  59. data/spec/support/test_adapter.rb +83 -0
  60. metadata +116 -0
@@ -0,0 +1,48 @@
1
+ module Looksee
2
+ module ObjectMixin
3
+ #
4
+ # Define #ls as a shortcut for Looksee[self, *args].
5
+ #
6
+ # This is defined via method_missing to be less intrusive. pry 0.10, e.g.,
7
+ # relies on Object#ls not existing.
8
+ #
9
+ def method_missing(name, *args)
10
+ if name == :ls
11
+ Looksee[self, *args]
12
+ else
13
+ super
14
+ end
15
+ end
16
+
17
+ def respond_to?(name, include_private=false)
18
+ super || name == :ls
19
+ end
20
+
21
+ def self.rename(name) # :nodoc:
22
+ name = name[:ls] if name.is_a?(Hash)
23
+ alias_method name, :ls
24
+ remove_method :ls
25
+ end
26
+ end
27
+
28
+ #
29
+ # Rename the #ls method, added to every object. Example:
30
+ #
31
+ # rename :_ls
32
+ #
33
+ # This renames Looksee's #ls method to #_ls.
34
+ #
35
+ # For backward compatibility, the old-style invocation is also
36
+ # supported. Please note this is deprecated.
37
+ #
38
+ # rename :ls => :_ls
39
+ #
40
+ def self.rename(name)
41
+ ObjectMixin.rename(name)
42
+ end
43
+
44
+ name = ENV['LOOKSEE_METHOD'] and
45
+ rename name
46
+
47
+ Object.send :include, ObjectMixin
48
+ end
@@ -0,0 +1,64 @@
1
+ require 'shellwords'
2
+
3
+ module Looksee
4
+ class Editor
5
+ def initialize(command)
6
+ @command = command.dup
7
+ infer_arguments
8
+ end
9
+
10
+ attr_reader :command
11
+
12
+ #
13
+ # Run the editor command for the +method_name+ of +object+.
14
+ #
15
+ def edit(object, method_name)
16
+ method = LookupPath.new(object).find(method_name.to_s) or
17
+ raise NoMethodError, "no method `#{method_name}' in lookup path of #{object.class} instance"
18
+ file, line = Looksee.adapter.source_location(method)
19
+ if !file
20
+ raise NoSourceLocationError, "no source location for #{method.owner}##{method.name}"
21
+ elsif !File.exist?(file)
22
+ raise NoSourceFileError, "cannot find source file: #{file}"
23
+ else
24
+ run(file, line)
25
+ end
26
+ end
27
+
28
+ #
29
+ # Run the editor command for the given file and line.
30
+ #
31
+ def run(file, line)
32
+ system *command_for(file, line)
33
+ end
34
+
35
+ #
36
+ # Return the editor command for the given file and line.
37
+ #
38
+ # This is an array of the command with its arguments.
39
+ #
40
+ def command_for(file, line)
41
+ line = line.to_s
42
+ words = Shellwords.shellwords(command)
43
+ words.map! do |word|
44
+ word.gsub!(/%f/, file)
45
+ word.gsub!(/%l/, line)
46
+ word.gsub!(/%%/, '%')
47
+ word
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def infer_arguments
54
+ return if command =~ /%[fl]/
55
+
56
+ case command[/\S+/]
57
+ when /\A(?:g?vim?|.*macs|pico|nano)\z/
58
+ command << " +%l %f"
59
+ when 'mate'
60
+ command << " -l%l %f"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -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) or Looksee[object, *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.ls.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,70 @@
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
+ #
15
+ # Print the method lookup path of self. See the README for details.
16
+ #
17
+ def inspect
18
+ lookup_path.entries.reverse.map do |entry|
19
+ inspect_entry(entry)
20
+ end.join("\n")
21
+ end
22
+
23
+ #
24
+ # Open an editor at the named method's definition.
25
+ #
26
+ # Uses Looksee.editor to determine the editor command to run.
27
+ #
28
+ # Only works for methods for which file and line numbers are
29
+ # accessible.
30
+ #
31
+ def edit(name)
32
+ Editor.new(Looksee.editor).edit(lookup_path.object, name)
33
+ end
34
+
35
+ private
36
+
37
+ def inspect_entry(entry)
38
+ string = styled_module_name(entry) << "\n"
39
+ string << Columnizer.columnize(styled_methods(entry), @width)
40
+ string.chomp
41
+ end
42
+
43
+ def styled_module_name(entry)
44
+ Looksee.styles[:module] % Looksee.adapter.describe_module(entry.module)
45
+ end
46
+
47
+ def styled_methods(entry)
48
+ pattern = filter_pattern
49
+ show_overridden = @visibilities.include?(:overridden)
50
+ entry.map do |name, visibility|
51
+ next if !selected?(name, visibility)
52
+ style = entry.overridden?(name) ? :overridden : visibility
53
+ next if style == :overridden && !show_overridden
54
+ Looksee.styles[style] % name
55
+ end.compact
56
+ end
57
+
58
+ def filter_pattern
59
+ strings = filters.grep(String)
60
+ regexps = filters.grep(Regexp)
61
+ string_patterns = strings.map{|s| Regexp.escape(s)}
62
+ regexp_patterns = regexps.map{|s| s.source}
63
+ /#{(string_patterns + regexp_patterns).join('|')}/
64
+ end
65
+
66
+ def selected?(name, visibility)
67
+ @visibilities.include?(visibility) && name =~ filter_pattern
68
+ end
69
+ end
70
+ 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 Looksee.adapter.real_module(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
@@ -0,0 +1,11 @@
1
+ module Looksee
2
+ VERSION = [3, 0, 0]
3
+
4
+ class << VERSION
5
+ include Comparable
6
+
7
+ def to_s
8
+ join('.')
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,588 @@
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
+ map { |mod| @adapter.describe_module(mod) }.
19
+ select{ |description| deterministic_module?(description) }
20
+ end
21
+
22
+ #
23
+ # Return true if the given module name is of a module we can test
24
+ # for.
25
+ #
26
+ # This excludes ruby version dependent modules, and modules tossed
27
+ # into the hierarchy by testing frameworks.
28
+ #
29
+ def deterministic_module?(description)
30
+ junk_patterns = [
31
+ # pollution from testing libraries
32
+ 'Mocha', 'Spec',
33
+ # RSpec adds this under ruby 1.8.6
34
+ 'InstanceExecHelper',
35
+ # RSpec 2
36
+ 'RSpec::',
37
+ # only in ruby 1.9
38
+ 'BasicObject',
39
+ # something pulls this in under ruby 1.9
40
+ 'PP',
41
+ # our own pollution,
42
+ 'Looksee::ObjectMixin',
43
+ ]
44
+ pattern = /\b(#{junk_patterns.join('|')})\b/
45
+ description !~ pattern
46
+ end
47
+
48
+ it "should contain an entry for each module in the object's lookup path" do
49
+ temporary_module :Mod1
50
+ temporary_module :Mod2
51
+ temporary_class :Base
52
+ temporary_class :Derived, :superclass => Base do
53
+ include Mod1
54
+ include Mod2
55
+ end
56
+ filtered_lookup_modules(Derived.new) ==
57
+ ['Derived', 'Mod2', 'Mod1', 'Base', 'Object', 'Kernel']
58
+ end
59
+
60
+ it "should contain an entry for the object's singleton class if it exists" do
61
+ object = Object.new
62
+ object.singleton_class
63
+
64
+ filtered_lookup_modules(object).should ==
65
+ ['[Object instance]', 'Object', 'Kernel']
66
+ end
67
+
68
+ it "should contain entries for singleton classes of all ancestors for class objects" do
69
+ temporary_class :C
70
+ filtered_lookup_modules(C).should ==
71
+ ['[C]', '[Object]', 'Class', 'Module', 'Object', 'Kernel']
72
+ end
73
+
74
+ it "should work for immediate objects" do
75
+ filtered_lookup_modules(1).first.should == 'Fixnum'
76
+ end
77
+ end
78
+
79
+ describe "internal instance methods:" do
80
+ def self.target_method(name)
81
+ define_method(:target_method){name}
82
+ end
83
+
84
+ def self.it_should_list_methods_with_visibility(visibility)
85
+ it "should return the list of #{visibility} instance methods defined directly on a class" do
86
+ temporary_class :C
87
+ add_methods C, visibility => [:one, :two]
88
+ @adapter.send(target_method, C).to_set.should == Set[:one, :two]
89
+ end
90
+
91
+ it "should return the list of #{visibility} instance methods defined directly on a module" do
92
+ temporary_module :M
93
+ add_methods M, visibility => [:one, :two]
94
+ @adapter.send(target_method, M).to_set.should == Set[:one, :two]
95
+ end
96
+
97
+ it "should return the list of #{visibility} instance methods defined directly on a singleton class" do
98
+ temporary_class :C
99
+ c = C.new
100
+ add_methods c.singleton_class, visibility => [:one, :two]
101
+ @adapter.send(target_method, c.singleton_class).to_set.should == Set[:one, :two]
102
+ end
103
+
104
+ it "should return the list of #{visibility} instance methods defined directly on a class' singleton class" do
105
+ temporary_class :C
106
+ add_methods C.singleton_class, visibility => [:one, :two], :class_singleton => true
107
+ @adapter.send(target_method, C.singleton_class).to_set.should == Set[:one, :two]
108
+ end
109
+
110
+ it "should not return undefined methods" do
111
+ temporary_class :C
112
+ add_methods C, visibility => [:removed]
113
+ C.send(:undef_method, :removed)
114
+ @adapter.send(target_method, C).to_set.should == Set[]
115
+ end
116
+
117
+ if RUBY_VERSION >= '2'
118
+ it "should return methods only for origin classes" do
119
+ temporary_class :C
120
+ add_methods C, visibility => :one
121
+ temporary_module :M
122
+ add_methods M, visibility => :one
123
+ C.send(:prepend, M)
124
+ @adapter.send(target_method, C).should be_empty
125
+
126
+ origin = @adapter.lookup_modules(C.new).
127
+ find { |mod| @adapter.describe_module(mod) == 'C (origin)' }
128
+ @adapter.send(target_method, origin).should_not be_empty
129
+ end
130
+ end
131
+ end
132
+
133
+ def self.it_should_not_list_methods_with_visibility(visibility1, visibility2)
134
+ it "should not return any #{visibility1} or #{visibility2} instance methods" do
135
+ temporary_class :C
136
+ add_methods C, {visibility1 => [:a], visibility2 => [:b]}
137
+ @adapter.send(target_method, C).to_set.should == Set[]
138
+ end
139
+ end
140
+
141
+ describe ".internal_public_instance_methods" do
142
+ target_method :internal_public_instance_methods
143
+ it_should_list_methods_with_visibility :public
144
+ it_should_not_list_methods_with_visibility :private, :protected
145
+ end
146
+
147
+ describe ".internal_protected_instance_methods" do
148
+ target_method :internal_protected_instance_methods
149
+ it_should_list_methods_with_visibility :protected
150
+ it_should_not_list_methods_with_visibility :public, :private
151
+ end
152
+
153
+ describe ".internal_private_instance_methods" do
154
+ target_method :internal_private_instance_methods
155
+ it_should_list_methods_with_visibility :private
156
+ it_should_not_list_methods_with_visibility :public, :protected
157
+ end
158
+
159
+ describe ".internal_undefined_instance_methods" do
160
+ it "should return the list of undefined instance methods directly on a class" do
161
+ temporary_class :C
162
+ C.send(:define_method, :f){}
163
+ C.send(:undef_method, :f)
164
+ @adapter.internal_undefined_instance_methods(C).should == [:f]
165
+ end
166
+
167
+ it "should return the list of undefined instance methods directly on a module" do
168
+ temporary_module :M
169
+ M.send(:define_method, :f){}
170
+ M.send(:undef_method, :f)
171
+ @adapter.internal_undefined_instance_methods(M).should == [:f]
172
+ end
173
+
174
+ it "should return the list of undefined instance methods directly on a singleton class" do
175
+ temporary_class :C
176
+ c = C.new
177
+ c.singleton_class.send(:define_method, :f){}
178
+ c.singleton_class.send(:undef_method, :f)
179
+ @adapter.internal_undefined_instance_methods(c.singleton_class).should == [:f]
180
+ end
181
+
182
+ it "should return the list of undefined instance methods directly on a class' singleton class" do
183
+ temporary_class :C
184
+ C.singleton_class.send(:define_method, :f){}
185
+ C.singleton_class.send(:undef_method, :f)
186
+ @adapter.internal_undefined_instance_methods(C.singleton_class).should == [:f]
187
+ end
188
+
189
+ it "should not return defined methods" do
190
+ temporary_class :C
191
+ C.send(:define_method, :f){}
192
+ @adapter.internal_undefined_instance_methods(C).should == []
193
+ end
194
+
195
+ it "should not return removed methods" do
196
+ temporary_class :C
197
+ C.send(:define_method, :f){}
198
+ C.send(:remove_method, :f)
199
+ @adapter.internal_undefined_instance_methods(C).should == []
200
+ end
201
+
202
+ it "should handle the MRI allocator being undefined (e.g. Struct)" do
203
+ struct_singleton_class = (class << Struct; self; end)
204
+ @adapter.internal_undefined_instance_methods(struct_singleton_class).should == []
205
+ end
206
+
207
+ if RUBY_VERSION >= '2'
208
+ it "should return an empty list for non-origin classes" do
209
+ temporary_class :C
210
+ C.send(:define_method, :f){}
211
+ C.send(:undef_method, :f)
212
+ temporary_module :M
213
+ M.send(:define_method, :f){}
214
+ M.send(:undef_method, :f)
215
+ C.send(:prepend, M)
216
+
217
+ @adapter.internal_undefined_instance_methods(C).should be_empty
218
+
219
+ origin = @adapter.lookup_modules(C.new).
220
+ find { |mod| @adapter.describe_module(mod) == 'C (origin)' }
221
+ @adapter.internal_undefined_instance_methods(origin).should_not be_empty
222
+ end
223
+ end
224
+ end
225
+ end
226
+
227
+ describe "#singleton_class?" do
228
+ it "should return true if the object is a singleton class of an object" do
229
+ object = (class << Object.new; self; end)
230
+ @adapter.singleton_class?(object).should == true
231
+ end
232
+
233
+ it "should return true if the object is a singleton class of a class" do
234
+ object = (class << Class.new; self; end)
235
+ @adapter.singleton_class?(object).should == true
236
+ end
237
+
238
+ it "should return true if the object is a singleton class of a singleton class" do
239
+ object = (class << (class << Class.new; self; end); self; end)
240
+ @adapter.singleton_class?(object).should == true
241
+ end
242
+
243
+ it "should return false if the object is just a class" do
244
+ object = Class.new
245
+ @adapter.singleton_class?(object).should == false
246
+ end
247
+
248
+ it "should return false if the object is just a module" do
249
+ object = Module.new
250
+ @adapter.singleton_class?(object).should == false
251
+ end
252
+
253
+ it "should return false if the object is just an object" do
254
+ object = Object.new
255
+ @adapter.singleton_class?(object).should == false
256
+ end
257
+
258
+ it "should return false if the object is TrueClass" do
259
+ @adapter.singleton_class?(TrueClass).should == false
260
+ end
261
+
262
+ it "should return false if the object is FalseClass" do
263
+ @adapter.singleton_class?(FalseClass).should == false
264
+ end
265
+
266
+ it "should return false if the object is NilClass" do
267
+ @adapter.singleton_class?(NilClass).should == false
268
+ end
269
+ end
270
+
271
+ describe "singleton_instance" do
272
+ it "should return the instance of the given singleton class" do
273
+ object = Object.new
274
+ @adapter.singleton_instance((class << object; self; end)).should equal(object)
275
+ end
276
+
277
+ it "should return the instance of the given class singleton class" do
278
+ klass = Class.new
279
+ @adapter.singleton_instance((class << klass; self; end)).should equal(klass)
280
+ end
281
+
282
+ it "should return the instance of the given module singleton class" do
283
+ mod = Module.new
284
+ @adapter.singleton_instance((class << mod; self; end)).should equal(mod)
285
+ end
286
+
287
+ it "should raise a TypeError if the given object is just a class" do
288
+ lambda do
289
+ @adapter.singleton_instance(Class.new)
290
+ end.should raise_error(TypeError)
291
+ end
292
+
293
+ it "should raise a TypeError if the given object is just a module" do
294
+ lambda do
295
+ @adapter.singleton_instance(Module.new)
296
+ end.should raise_error(TypeError)
297
+ end
298
+
299
+ it "should raise a TypeError if the given object is just a object" do
300
+ lambda do
301
+ @adapter.singleton_instance(Object.new)
302
+ end.should raise_error(TypeError)
303
+ end
304
+ end
305
+
306
+ describe "#module_name" do
307
+ it "should return the fully-qualified name of the given module" do
308
+ ::M = Module.new
309
+ M::N = Module.new
310
+ begin
311
+ @adapter.module_name(M::N).should == 'M::N'
312
+ ensure
313
+ Object.send :remove_const, :M
314
+ end
315
+ end
316
+
317
+ it "should return the fully-qualified name of the given class" do
318
+ ::M = Module.new
319
+ M::C = Class.new
320
+ begin
321
+ @adapter.module_name(M::C).should == 'M::C'
322
+ ensure
323
+ Object.send :remove_const, :M
324
+ end
325
+ end
326
+
327
+ it "should not be affected by overridding the module's #to_s or #name" do
328
+ begin
329
+ ::M = Module.new
330
+ ::M::C = Class.new do
331
+ def name
332
+ 'overridden'
333
+ end
334
+ def to_s
335
+ 'overridden'
336
+ end
337
+ end
338
+ @adapter.describe_module(M::C).should == 'M::C'
339
+ ensure
340
+ Object.send :remove_const, :M
341
+ end
342
+ end
343
+
344
+ it "should return an empty string for unnamed modules" do
345
+ @adapter.module_name(Module.new).should == ''
346
+ end
347
+
348
+ it "should return an empty string for unnamed classes" do
349
+ @adapter.module_name(Class.new).should == ''
350
+ end
351
+
352
+ it "should return an empty string for singleton classes" do
353
+ object = Object.new
354
+ @adapter.module_name((class << object; self; end)).should == ''
355
+ end
356
+
357
+ it "should raise a TypeError if the argumeent is not a module" do
358
+ lambda do
359
+ @adapter.module_name(Object.new)
360
+ end.should raise_error(TypeError)
361
+ end
362
+ end
363
+
364
+ describe "#describe_module" do
365
+ it "should return the fully-qualified name of a module" do
366
+ begin
367
+ ::M = Module.new
368
+ ::M::N = Module.new
369
+ @adapter.describe_module(::M::N).should == 'M::N'
370
+ ensure
371
+ Object.send :remove_const, :M
372
+ end
373
+ end
374
+
375
+ it "should not be affected by overridding the module's #to_s or #name" do
376
+ begin
377
+ ::M = Module.new
378
+ ::M::C = Class.new do
379
+ def name
380
+ 'overridden'
381
+ end
382
+ def to_s
383
+ 'overridden'
384
+ end
385
+ end
386
+ @adapter.describe_module(::M::C).should == 'M::C'
387
+ ensure
388
+ Object.send :remove_const, :M
389
+ end
390
+ end
391
+
392
+ describe "for an unnamed class" do
393
+ it "should describe the object as 'unnamed Class'" do
394
+ @adapter.describe_module(Class.new).should == 'unnamed Class'
395
+ end
396
+ end
397
+
398
+ describe "for an unnamed module" do
399
+ it "should describe the object as 'unnamed Module'" do
400
+ @adapter.describe_module(Module.new).should == 'unnamed Module'
401
+ end
402
+ end
403
+
404
+ describe "for an included class of an unnamed module" do
405
+ it "should describe the object as 'unnamed Module'" do
406
+ klass = Class.new do
407
+ include Module.new
408
+ end
409
+ mod = @adapter.lookup_modules(klass.new)[1]
410
+ @adapter.describe_module(mod).should == 'unnamed Module'
411
+ end
412
+ end
413
+
414
+ describe "for a singleton class of an object" do
415
+ it "should describe the object in brackets" do
416
+ begin
417
+ ::M = Module.new
418
+ ::M::C = Class.new
419
+ object = M::C.new
420
+ @adapter.describe_module(object.singleton_class).should == '[M::C instance]'
421
+ ensure
422
+ Object.send :remove_const, :M
423
+ end
424
+ end
425
+ end
426
+
427
+ describe "for a singleton class of a named class" do
428
+ it "should return the class name in brackets" do
429
+ begin
430
+ ::M = Module.new
431
+ ::M::C = Class.new
432
+ @adapter.describe_module(M::C.singleton_class).should == '[M::C]'
433
+ ensure
434
+ Object.send :remove_const, :M
435
+ end
436
+ end
437
+ end
438
+
439
+ describe "for a singleton class of an unnamed class" do
440
+ it "should describe the object as '[unnamed Class]'" do
441
+ klass = Class.new
442
+ @adapter.describe_module(klass.singleton_class).should == '[unnamed Class]'
443
+ end
444
+ end
445
+
446
+ describe "for a singleton class of an unnamed module" do
447
+ it "should describe the object as '[unnamed Module]'" do
448
+ mod = Module.new
449
+ @adapter.describe_module(mod.singleton_class).should == '[unnamed Module]'
450
+ end
451
+ end
452
+
453
+ describe "for a singleton class of a singleton class" do
454
+ it "should return the object's class name in two pairs of brackets" do
455
+ begin
456
+ ::M = Module.new
457
+ ::M::C = Class.new
458
+ klass = M::C.singleton_class.singleton_class
459
+ @adapter.describe_module(klass).should == '[[M::C]]'
460
+ ensure
461
+ Object.send :remove_const, :M
462
+ end
463
+ end
464
+ end
465
+ end
466
+
467
+ describe "#source_location" do
468
+ def load_source(source)
469
+ @tmp = "#{ROOT}/spec/tmp"
470
+ # rbx 1.2.3 caches the file content by file name - ensure file names are different.
471
+ @source_path = "#@tmp/c#{__id__}.rb"
472
+ FileUtils.mkdir_p @tmp
473
+ open(@source_path, 'w') { |f| f.print source }
474
+ load @source_path
475
+ @source_path
476
+ end
477
+
478
+ after do
479
+ FileUtils.rm_rf @tmp if @tmp
480
+ Object.send(:remove_const, :C) if Object.const_defined?(:C)
481
+ end
482
+
483
+ it "should return the file and line number the given method was defined on" do
484
+ path = load_source <<-EOS.demargin
485
+ |class C
486
+ | def f
487
+ | end
488
+ |end
489
+ EOS
490
+ method = C.instance_method(:f)
491
+ @adapter.source_location(method).should == [path, 2]
492
+ end
493
+
494
+ it "should work for methods defined via a block (MRI BMETHOD)" do
495
+ path = load_source <<-EOS.demargin
496
+ |class C
497
+ | define_method :f do
498
+ | end
499
+ |end
500
+ EOS
501
+ method = C.instance_method(:f)
502
+ @adapter.source_location(method).should == [path, 2]
503
+ end
504
+
505
+ it "should work for methods defined via a proc (MRI BMETHOD)" do
506
+ path = load_source <<-EOS.demargin
507
+ |class C
508
+ | f = lambda do
509
+ | end
510
+ | define_method :f, f
511
+ |end
512
+ EOS
513
+ method = C.instance_method(:f)
514
+ @adapter.source_location(method).should == [path, 2]
515
+ end
516
+
517
+ it "should work for methods defined via a UnboundMethod (MRI DMETHOD)" do
518
+ path = load_source <<-EOS.demargin
519
+ |class C
520
+ | def f
521
+ | end
522
+ | define_method :g, instance_method(:f)
523
+ |end
524
+ EOS
525
+ method = C.instance_method(:g)
526
+ @adapter.source_location(method).should == [path, 2]
527
+ end
528
+
529
+ it "should work for methods defined via a BoundMethod (MRI DMETHOD)" do
530
+ path = load_source <<-EOS.demargin
531
+ |class C
532
+ | def f
533
+ | end
534
+ | define_method :g, new.method(:f)
535
+ |end
536
+ EOS
537
+ method = C.instance_method(:g)
538
+ @adapter.source_location(method).should == [path, 2]
539
+ end
540
+
541
+ it "should work for methods whose visibility is overridden in a subclass (MRI ZSUPER)" do
542
+ path = load_source <<-EOS.demargin
543
+ |class C
544
+ | def f
545
+ | end
546
+ |end
547
+ |class D < C
548
+ | private :f
549
+ |end
550
+ EOS
551
+ begin
552
+ method = D.instance_method(:f)
553
+ @adapter.source_location(method).should == [path, 2]
554
+ ensure
555
+ Object.send(:remove_const, :D)
556
+ end
557
+ end
558
+
559
+ it "should work for aliases (MRI FBODY)" do
560
+ path = load_source <<-EOS.demargin
561
+ |class C
562
+ | def f
563
+ | end
564
+ | alias g f
565
+ |end
566
+ EOS
567
+ method = C.instance_method(:g)
568
+ @adapter.source_location(method).should == [path, 2]
569
+ end
570
+
571
+ it "should return nil for primitive methods (MRI CBODY)" do
572
+ method = String.instance_method(:bytesize)
573
+ @adapter.source_location(method).should == nil
574
+ end
575
+
576
+ it "should raise a TypeError if the argument is not an UnboundMethod" do
577
+ path = load_source <<-EOS.demargin
578
+ |class C
579
+ | def f
580
+ | end
581
+ |end
582
+ EOS
583
+ lambda do
584
+ @adapter.source_location(nil)
585
+ end.should raise_error(TypeError)
586
+ end
587
+ end
588
+ end