looksee 1.0.0-universal-java-1.6
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.
- data/CHANGELOG +14 -0
- data/LICENSE +22 -0
- data/README.markdown +161 -0
- data/Rakefile +10 -0
- data/ext/extconf.rb +9 -0
- data/ext/mri/1.9.2/debug.h +36 -0
- data/ext/mri/1.9.2/id.h +170 -0
- data/ext/mri/1.9.2/method.h +103 -0
- data/ext/mri/1.9.2/node.h +483 -0
- data/ext/mri/1.9.2/thread_pthread.h +27 -0
- data/ext/mri/1.9.2/vm_core.h +707 -0
- data/ext/mri/1.9.2/vm_opts.h +51 -0
- data/ext/mri/env-1.8.h +27 -0
- data/ext/mri/eval_c-1.8.h +27 -0
- data/ext/mri/mri.c +269 -0
- data/ext/mri/node-1.9.h +35 -0
- data/ext/rbx/rbx.c +13 -0
- data/lib/looksee.rb +5 -0
- data/lib/looksee/adapter.rb +10 -0
- data/lib/looksee/adapter/base.rb +100 -0
- data/lib/looksee/adapter/rubinius.rb +73 -0
- data/lib/looksee/clean.rb +122 -0
- data/lib/looksee/columnizer.rb +73 -0
- data/lib/looksee/core_ext.rb +59 -0
- data/lib/looksee/editor.rb +58 -0
- data/lib/looksee/help.rb +54 -0
- data/lib/looksee/inspector.rb +55 -0
- data/lib/looksee/jruby.jar +0 -0
- data/lib/looksee/lookup_path.rb +95 -0
- data/lib/looksee/rbx.bundle +0 -0
- data/lib/looksee/shortcuts.rb +3 -0
- data/lib/looksee/version.rb +11 -0
- data/lib/looksee/wirble_compatibility.rb +86 -0
- data/spec/adapter_spec.rb +546 -0
- data/spec/columnizer_spec.rb +52 -0
- data/spec/core_ext_spec.rb +41 -0
- data/spec/editor_spec.rb +128 -0
- data/spec/inspector_spec.rb +178 -0
- data/spec/lookup_path_spec.rb +84 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/support/core_ext.rb +25 -0
- data/spec/support/temporary_classes.rb +102 -0
- data/spec/support/test_adapter.rb +72 -0
- data/spec/wirble_compatibility_spec.rb +116 -0
- metadata +158 -0
data/lib/looksee/help.rb
ADDED
@@ -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
|
Binary file
|
@@ -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
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module Looksee
|
2
|
+
module WirbleCompatibility
|
3
|
+
class << self
|
4
|
+
def wirble_loaded?
|
5
|
+
Object.const_defined?(:Wirble) &&
|
6
|
+
Wirble.is_a?(Module) &&
|
7
|
+
Wirble.respond_to?(:colorize)
|
8
|
+
end
|
9
|
+
|
10
|
+
def wirble_colorizing?
|
11
|
+
require 'irb'
|
12
|
+
IRB::Irb.method_defined?(:non_color_output_value)
|
13
|
+
end
|
14
|
+
|
15
|
+
def hook_into_wirble_load
|
16
|
+
unless Object.const_defined?(:Wirble)
|
17
|
+
Object.const_set :Wirble, Module.new
|
18
|
+
end
|
19
|
+
Wirble.send :extend, WirbleLoadHook
|
20
|
+
end
|
21
|
+
|
22
|
+
def hook_into_wirble_colorize
|
23
|
+
class << Wirble
|
24
|
+
def colorize_with_looksee(*args)
|
25
|
+
# If this gets called twice, Wirble will fuck up the
|
26
|
+
# aliases. Disable colorizing first to reset them.
|
27
|
+
if WirbleCompatibility.hooked_into_irb_output_value?
|
28
|
+
Wirble::Colorize.disable
|
29
|
+
end
|
30
|
+
colorize_without_looksee(*args)
|
31
|
+
WirbleCompatibility.hook_into_irb_output_value
|
32
|
+
end
|
33
|
+
|
34
|
+
alias colorize_without_looksee colorize
|
35
|
+
alias colorize colorize_with_looksee
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def hook_into_irb_output_value
|
40
|
+
IRB::Irb.class_eval do
|
41
|
+
def output_value_with_looksee
|
42
|
+
case @context.last_value
|
43
|
+
when Looksee::Inspector, Looksee::Help
|
44
|
+
non_color_output_value
|
45
|
+
else
|
46
|
+
output_value_without_looksee
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
alias output_value_without_looksee output_value
|
51
|
+
alias output_value output_value_with_looksee
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def hooked_into_irb_output_value?
|
56
|
+
IRB::Irb.method_defined?(:output_value_with_looksee)
|
57
|
+
end
|
58
|
+
|
59
|
+
def init
|
60
|
+
#
|
61
|
+
# How wirble is used:
|
62
|
+
#
|
63
|
+
# * Wirble is required/loaded. Defines Wirble module, with methods like Wirble.colorize.
|
64
|
+
# * Wirble.init is called. Nothing interesting.
|
65
|
+
# * Wirble.colorize is called. Hooks into IRB::Irb.output_value via an alias.
|
66
|
+
#
|
67
|
+
if !wirble_loaded?
|
68
|
+
hook_into_wirble_load
|
69
|
+
elsif !wirble_colorizing?
|
70
|
+
hook_into_wirble_colorize
|
71
|
+
else
|
72
|
+
hook_into_irb_output_value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
module WirbleLoadHook
|
78
|
+
def singleton_method_added(name)
|
79
|
+
if name == :colorize && !respond_to?(:colorize_with_looksee)
|
80
|
+
WirbleCompatibility.hook_into_wirble_colorize
|
81
|
+
end
|
82
|
+
super
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -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
|