looksee 3.0.0-universal-java-1.8

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 (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