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.
- checksums.yaml +7 -0
- data/CHANGELOG +66 -0
- data/LICENSE +22 -0
- data/README.markdown +175 -0
- data/Rakefile +13 -0
- data/ext/extconf.rb +19 -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/1.9.3/atomic.h +56 -0
- data/ext/mri/1.9.3/debug.h +41 -0
- data/ext/mri/1.9.3/id.h +175 -0
- data/ext/mri/1.9.3/internal.h +227 -0
- data/ext/mri/1.9.3/internal_falcon.h +248 -0
- data/ext/mri/1.9.3/method.h +105 -0
- data/ext/mri/1.9.3/node.h +503 -0
- data/ext/mri/1.9.3/thread_pthread.h +51 -0
- data/ext/mri/1.9.3/vm_core.h +755 -0
- data/ext/mri/1.9.3/vm_opts.h +51 -0
- data/ext/mri/2.0.0/internal.h +378 -0
- data/ext/mri/2.0.0/method.h +138 -0
- data/ext/mri/2.1.0/internal.h +889 -0
- data/ext/mri/2.1.0/method.h +142 -0
- data/ext/mri/2.2.0/internal.h +1182 -0
- data/ext/mri/2.2.0/method.h +141 -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 +309 -0
- data/ext/mri/node-1.9.h +35 -0
- data/ext/rbx/rbx.c +13 -0
- data/lib/looksee.rb +2 -0
- data/lib/looksee/JRuby.jar +0 -0
- data/lib/looksee/adapter.rb +8 -0
- data/lib/looksee/adapter/base.rb +105 -0
- data/lib/looksee/adapter/rubinius.rb +84 -0
- data/lib/looksee/clean.rb +169 -0
- data/lib/looksee/columnizer.rb +73 -0
- data/lib/looksee/core_ext.rb +48 -0
- data/lib/looksee/editor.rb +64 -0
- data/lib/looksee/help.rb +54 -0
- data/lib/looksee/inspector.rb +70 -0
- data/lib/looksee/lookup_path.rb +95 -0
- data/lib/looksee/rbx.bundle +0 -0
- data/lib/looksee/version.rb +11 -0
- data/spec/looksee/adapter_spec.rb +588 -0
- data/spec/looksee/clean_spec.rb +41 -0
- data/spec/looksee/columnizer_spec.rb +52 -0
- data/spec/looksee/core_ext_spec.rb +17 -0
- data/spec/looksee/editor_spec.rb +107 -0
- data/spec/looksee/inspector_spec.rb +179 -0
- data/spec/looksee/lookup_path_spec.rb +87 -0
- data/spec/spec_helper.rb +29 -0
- data/spec/support/core_ext.rb +25 -0
- data/spec/support/temporary_classes.rb +78 -0
- data/spec/support/test_adapter.rb +83 -0
- 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
|
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) 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,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
|