introspection 0.0.1
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/.gitignore +1 -0
- data/Gemfile +3 -0
- data/README.md +7 -0
- data/Rakefile +11 -0
- data/introspection.gemspec +28 -0
- data/lib/introspection.rb +4 -0
- data/lib/introspection/assertions.rb +17 -0
- data/lib/introspection/method.rb +37 -0
- data/lib/introspection/receivers.rb +40 -0
- data/lib/introspection/snapshot.rb +35 -0
- data/lib/introspection/version.rb +3 -0
- data/samples/class.rb +122 -0
- data/samples/instance.rb +110 -0
- data/samples/module.rb +27 -0
- data/test/class_snapshot_test.rb +87 -0
- data/test/instance_snapshot_test.rb +122 -0
- data/test/module_snapshot_test.rb +40 -0
- data/test/snapshot_test.rb +50 -0
- data/test/test_helper.rb +30 -0
- metadata +120 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Gemfile.lock
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Dynamic inspection of the hierarchy of method definitions on a Ruby object.
|
2
|
+
|
3
|
+
Motivations :-
|
4
|
+
|
5
|
+
* Extract code that may be generally useful from Mocha.
|
6
|
+
* Learn more about the Ruby's Object, Class & Module and the method receiver chain.
|
7
|
+
* Detect undesirable changes to classes made by other libraries.
|
data/Rakefile
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib/', __FILE__)
|
3
|
+
$LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
require "introspection/version"
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "introspection"
|
9
|
+
s.version = Introspection::VERSION
|
10
|
+
s.platform = Gem::Platform::RUBY
|
11
|
+
s.authors = ["James Mead"]
|
12
|
+
s.email = ["james@floehopper.org"]
|
13
|
+
s.homepage = "http://jamesmead.org"
|
14
|
+
s.summary = %q{Dynamic inspection of the hierarchy of method definitions on a Ruby object.}
|
15
|
+
s.description = %q{}
|
16
|
+
|
17
|
+
s.required_rubygems_version = ">= 1.3.6"
|
18
|
+
s.rubyforge_project = "introspection"
|
19
|
+
|
20
|
+
s.files = `git ls-files`.split("\n")
|
21
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
22
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
|
25
|
+
s.add_dependency "metaid", "~> 1.0"
|
26
|
+
|
27
|
+
s.add_development_dependency "rake"
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Introspection
|
2
|
+
module Assertions
|
3
|
+
def assert_snapshot_changed(object)
|
4
|
+
before = Snapshot.new(object)
|
5
|
+
yield
|
6
|
+
after = Snapshot.new(object)
|
7
|
+
assert before.changed?(after), "Snapshot has not changed."
|
8
|
+
end
|
9
|
+
|
10
|
+
def assert_snapshot_unchanged(object)
|
11
|
+
before = Snapshot.new(object)
|
12
|
+
yield
|
13
|
+
after = Snapshot.new(object)
|
14
|
+
assert !before.changed?(after), "Snapshot has changed: #{before.diff(after).inspect}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
3
|
+
module Introspection
|
4
|
+
|
5
|
+
class Method
|
6
|
+
|
7
|
+
extend Forwardable
|
8
|
+
def_delegators :@method, :owner
|
9
|
+
|
10
|
+
attr_reader :method, :visibility
|
11
|
+
|
12
|
+
def initialize(method, visibility = :public)
|
13
|
+
@method, @visibility = method, visibility
|
14
|
+
end
|
15
|
+
|
16
|
+
def ==(other)
|
17
|
+
(owner == other.owner) && (name == other.name) && (visibility == other.visibility)
|
18
|
+
end
|
19
|
+
|
20
|
+
def eql?(other)
|
21
|
+
(self.class === other) && (self == other)
|
22
|
+
end
|
23
|
+
|
24
|
+
def hash
|
25
|
+
[owner, name, visibility].hash
|
26
|
+
end
|
27
|
+
|
28
|
+
def name
|
29
|
+
@method.name.to_sym
|
30
|
+
end
|
31
|
+
|
32
|
+
def inspect
|
33
|
+
"#{owner}##{name} (#{visibility})"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "metaid"
|
2
|
+
|
3
|
+
module Introspection
|
4
|
+
|
5
|
+
module Receivers
|
6
|
+
|
7
|
+
class NullMetaclass
|
8
|
+
def ancestors
|
9
|
+
Array.new
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class NullReceiver
|
14
|
+
def metaclass
|
15
|
+
NullMetaclass.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def receivers
|
19
|
+
Array.new
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def superklass
|
24
|
+
respond_to?(:superclass) ? superclass : NullReceiver.new
|
25
|
+
end
|
26
|
+
|
27
|
+
def local_receivers
|
28
|
+
[metaclass] + metaclass.ancestors - superklass.metaclass.ancestors
|
29
|
+
end
|
30
|
+
|
31
|
+
def receivers
|
32
|
+
local_receivers + superklass.receivers
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Object
|
39
|
+
include Introspection::Receivers
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "introspection/receivers"
|
2
|
+
require "metaid"
|
3
|
+
|
4
|
+
module Introspection
|
5
|
+
|
6
|
+
class Snapshot
|
7
|
+
attr_reader :methods
|
8
|
+
|
9
|
+
def initialize(object)
|
10
|
+
@methods = object.receivers.map do |receiver|
|
11
|
+
[:public, :protected, :private].map do |visibility|
|
12
|
+
query_method = "#{visibility}_instance_methods"
|
13
|
+
receiver.send(query_method, false).map do |method|
|
14
|
+
unbound_method = receiver.instance_method(method)
|
15
|
+
if unbound_method.owner.equal?(receiver)
|
16
|
+
Method.new(unbound_method, visibility)
|
17
|
+
end
|
18
|
+
end.compact
|
19
|
+
end
|
20
|
+
end.flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
def diff(other)
|
24
|
+
{
|
25
|
+
:added => other.methods - methods,
|
26
|
+
:removed => methods - other.methods
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def changed?(other)
|
31
|
+
diff(other).values.flatten.any?
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/samples/class.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
module Kernel
|
2
|
+
def foo
|
3
|
+
puts "Kernel#foo"
|
4
|
+
super
|
5
|
+
rescue NoMethodError
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Object
|
10
|
+
def foo
|
11
|
+
puts "Object#foo"
|
12
|
+
super
|
13
|
+
rescue NoMethodError
|
14
|
+
end
|
15
|
+
def self.foo
|
16
|
+
puts "Object.foo"
|
17
|
+
super
|
18
|
+
rescue NoMethodError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Module
|
23
|
+
def foo
|
24
|
+
puts "Module#foo"
|
25
|
+
super
|
26
|
+
rescue NoMethodError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Class
|
31
|
+
def foo
|
32
|
+
puts "Class#foo"
|
33
|
+
super
|
34
|
+
rescue NoMethodError
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
module GreatUncle
|
39
|
+
def foo
|
40
|
+
puts "GreatUncle#foo"
|
41
|
+
super
|
42
|
+
rescue NoMethodError
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
module GreatAunt
|
47
|
+
def foo
|
48
|
+
puts "GreatAunt#foo"
|
49
|
+
super
|
50
|
+
rescue NoMethodError
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
class Grandaddy
|
55
|
+
def self.foo
|
56
|
+
puts "Grandaddy.foo"
|
57
|
+
super
|
58
|
+
rescue NoMethodError
|
59
|
+
end
|
60
|
+
extend GreatUncle, GreatAunt
|
61
|
+
end
|
62
|
+
|
63
|
+
module Uncle
|
64
|
+
def foo
|
65
|
+
puts "Uncle#foo"
|
66
|
+
super
|
67
|
+
rescue NoMethodError
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
module Aunt
|
72
|
+
def foo
|
73
|
+
puts "Aunt#foo"
|
74
|
+
super
|
75
|
+
rescue NoMethodError
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Daddy < Grandaddy
|
80
|
+
def self.foo
|
81
|
+
puts "Daddy.foo"
|
82
|
+
super
|
83
|
+
rescue NoMethodError
|
84
|
+
end
|
85
|
+
extend Uncle, Aunt
|
86
|
+
end
|
87
|
+
|
88
|
+
module Brother
|
89
|
+
def foo
|
90
|
+
puts "Brother#foo"
|
91
|
+
super
|
92
|
+
rescue NoMethodError
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module HalfSister
|
97
|
+
def foo
|
98
|
+
puts "HalfSister#foo"
|
99
|
+
super
|
100
|
+
rescue NoMethodError
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
module Sister
|
105
|
+
def foo
|
106
|
+
puts "Sister#foo"
|
107
|
+
super
|
108
|
+
rescue NoMethodError
|
109
|
+
end
|
110
|
+
include HalfSister
|
111
|
+
end
|
112
|
+
|
113
|
+
class Sonny < Daddy
|
114
|
+
def self.foo
|
115
|
+
puts "Sonny.foo"
|
116
|
+
super
|
117
|
+
rescue NoMethodError
|
118
|
+
end
|
119
|
+
extend Brother, Sister
|
120
|
+
end
|
121
|
+
|
122
|
+
Sonny.foo
|
data/samples/instance.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
module Kernel
|
2
|
+
def foo
|
3
|
+
puts "Kernel#foo"
|
4
|
+
super
|
5
|
+
rescue NoMethodError
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Object
|
10
|
+
def foo
|
11
|
+
puts "Object#foo"
|
12
|
+
super
|
13
|
+
rescue NoMethodError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module GreatUncle
|
18
|
+
def foo
|
19
|
+
puts "GreatUncle#foo"
|
20
|
+
super
|
21
|
+
rescue NoMethodError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module GreatAunt
|
26
|
+
def foo
|
27
|
+
puts "GreatAunt#foo"
|
28
|
+
super
|
29
|
+
rescue NoMethodError
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Grandaddy
|
34
|
+
def foo
|
35
|
+
puts "Grandaddy#foo"
|
36
|
+
super
|
37
|
+
rescue NoMethodError
|
38
|
+
end
|
39
|
+
include GreatUncle, GreatAunt
|
40
|
+
end
|
41
|
+
|
42
|
+
module Uncle
|
43
|
+
def foo
|
44
|
+
puts "Uncle#foo"
|
45
|
+
super
|
46
|
+
rescue NoMethodError
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module Aunt
|
51
|
+
def foo
|
52
|
+
puts "Aunt#foo"
|
53
|
+
super
|
54
|
+
rescue NoMethodError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Daddy < Grandaddy
|
59
|
+
def foo
|
60
|
+
puts "Daddy#foo"
|
61
|
+
super
|
62
|
+
rescue NoMethodError
|
63
|
+
end
|
64
|
+
include Uncle, Aunt
|
65
|
+
end
|
66
|
+
|
67
|
+
module Brother
|
68
|
+
def foo
|
69
|
+
puts "Brother#foo"
|
70
|
+
super
|
71
|
+
rescue NoMethodError
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
module HalfSister
|
76
|
+
def foo
|
77
|
+
puts "HalfSister#foo"
|
78
|
+
super
|
79
|
+
rescue NoMethodError
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
module Sister
|
84
|
+
def foo
|
85
|
+
puts "Sister#foo"
|
86
|
+
super
|
87
|
+
rescue NoMethodError
|
88
|
+
end
|
89
|
+
include HalfSister
|
90
|
+
end
|
91
|
+
|
92
|
+
class Sonny < Daddy
|
93
|
+
def foo
|
94
|
+
puts "Sonny#foo"
|
95
|
+
super
|
96
|
+
rescue NoMethodError
|
97
|
+
end
|
98
|
+
include Brother, Sister
|
99
|
+
end
|
100
|
+
|
101
|
+
sonny = Sonny.new
|
102
|
+
class << sonny
|
103
|
+
def foo
|
104
|
+
puts "sonny#foo"
|
105
|
+
super
|
106
|
+
rescue NoMethodError
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
sonny.foo
|
data/samples/module.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
module Grandaddy
|
2
|
+
def foo
|
3
|
+
puts "Grandaddy#foo"
|
4
|
+
super
|
5
|
+
rescue NoMethodError
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module Daddy
|
10
|
+
def foo
|
11
|
+
puts "Daddy#foo"
|
12
|
+
super
|
13
|
+
rescue NoMethodError
|
14
|
+
end
|
15
|
+
include Grandaddy
|
16
|
+
end
|
17
|
+
|
18
|
+
module Sonny
|
19
|
+
def self.foo
|
20
|
+
puts "Sonny.foo"
|
21
|
+
super
|
22
|
+
rescue NoMethodError
|
23
|
+
end
|
24
|
+
extend Daddy
|
25
|
+
end
|
26
|
+
|
27
|
+
Sonny.foo
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ClassSnapshotTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_detect_class_method_on_class
|
6
|
+
for_all_method_visibilities do |visibility|
|
7
|
+
klass = Class.new
|
8
|
+
klass.metaclass.send(:define_method, :foo) {}
|
9
|
+
klass.metaclass.send(visibility, :foo)
|
10
|
+
assert_method_exists(klass, klass.metaclass, :foo, visibility)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_detect_class_method_on_superclass
|
15
|
+
for_all_method_visibilities do |visibility|
|
16
|
+
superklass = Class.new
|
17
|
+
superklass.metaclass.send(:define_method, :foo) {}
|
18
|
+
superklass.metaclass.send(visibility, :foo)
|
19
|
+
klass = Class.new(superklass)
|
20
|
+
assert_method_exists(klass, superklass.metaclass, :foo, visibility)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_detect_class_method_on_superclass_of_superclass
|
25
|
+
for_all_method_visibilities do |visibility|
|
26
|
+
superduperklass = Class.new
|
27
|
+
superduperklass.metaclass.send(:define_method, :foo) {}
|
28
|
+
superduperklass.metaclass.send(visibility, :foo)
|
29
|
+
klass = Class.new(Class.new(superduperklass))
|
30
|
+
assert_method_exists(klass, superduperklass.metaclass, :foo, visibility)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_detect_method_on_module_included_as_class_method_into_class
|
35
|
+
for_all_method_visibilities do |visibility|
|
36
|
+
mod = Module.new
|
37
|
+
mod.send(:define_method, :foo) {}
|
38
|
+
mod.send(visibility, :foo)
|
39
|
+
klass = Class.new do
|
40
|
+
extend mod
|
41
|
+
end
|
42
|
+
assert_method_exists(klass, mod, :foo, visibility)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_detect_method_on_module_included_as_class_method_into_superclass
|
47
|
+
for_all_method_visibilities do |visibility|
|
48
|
+
mod = Module.new
|
49
|
+
mod.send(:define_method, :foo) {}
|
50
|
+
mod.send(visibility, :foo)
|
51
|
+
superklass = Class.new do
|
52
|
+
extend mod
|
53
|
+
end
|
54
|
+
klass = Class.new(superklass)
|
55
|
+
assert_method_exists(klass, mod, :foo, visibility)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_detect_method_on_module_included_as_class_method_into_superclass_of_superclass
|
60
|
+
for_all_method_visibilities do |visibility|
|
61
|
+
mod = Module.new
|
62
|
+
mod.send(:define_method, :foo) {}
|
63
|
+
mod.send(visibility, :foo)
|
64
|
+
superduperklass = Class.new do
|
65
|
+
extend mod
|
66
|
+
end
|
67
|
+
klass = Class.new(Class.new(superduperklass))
|
68
|
+
assert_method_exists(klass, mod, :foo, visibility)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_detect_method_on_module_included_into_module_included_as_class_method_into_class
|
73
|
+
for_all_method_visibilities do |visibility|
|
74
|
+
mod = Module.new
|
75
|
+
mod.send(:define_method, :foo) {}
|
76
|
+
mod.send(visibility, :foo)
|
77
|
+
supermod = Module.new do
|
78
|
+
include mod
|
79
|
+
end
|
80
|
+
klass = Class.new do
|
81
|
+
extend supermod
|
82
|
+
end
|
83
|
+
assert_method_exists(klass, mod, :foo, visibility)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class InstanceSnapshotTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_detect_instance_method_on_singleton_class
|
6
|
+
for_all_method_visibilities do |visibility|
|
7
|
+
instance = Class.new.new
|
8
|
+
instance.metaclass.send(:define_method, :foo) {}
|
9
|
+
instance.metaclass.send(visibility, :foo)
|
10
|
+
assert_method_exists(instance, instance.metaclass, :foo, visibility)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_detect_instance_method_on_module_included_into_singleton_class
|
15
|
+
for_all_method_visibilities do |visibility|
|
16
|
+
mod = Module.new
|
17
|
+
mod.send(:define_method, :foo) {}
|
18
|
+
mod.send(visibility, :foo)
|
19
|
+
instance = Class.new.new
|
20
|
+
instance.extend(mod)
|
21
|
+
assert_method_exists(instance, mod, :foo, visibility)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_detect_instance_method_on_module_included_module_included_into_singleton_class
|
26
|
+
for_all_method_visibilities do |visibility|
|
27
|
+
supermod = Module.new
|
28
|
+
supermod.send(:define_method, :foo) {}
|
29
|
+
supermod.send(visibility, :foo)
|
30
|
+
mod = Module.new
|
31
|
+
mod.send(:include, supermod)
|
32
|
+
instance = Class.new.new
|
33
|
+
instance.extend(mod)
|
34
|
+
assert_method_exists(instance, supermod, :foo, visibility)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_detect_instance_method_on_class
|
39
|
+
for_all_method_visibilities do |visibility|
|
40
|
+
klass = Class.new
|
41
|
+
klass.send(:define_method, :foo) {}
|
42
|
+
klass.send(visibility, :foo)
|
43
|
+
instance = klass.new
|
44
|
+
assert_method_exists(instance, klass, :foo, visibility)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_detect_instance_method_on_superclass
|
49
|
+
for_all_method_visibilities do |visibility|
|
50
|
+
superklass = Class.new
|
51
|
+
superklass.send(:define_method, :foo) {}
|
52
|
+
superklass.send(visibility, :foo)
|
53
|
+
instance = Class.new(superklass).new
|
54
|
+
assert_method_exists(instance, superklass, :foo, visibility)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_detect_instance_method_on_superclass_of_superclass
|
59
|
+
for_all_method_visibilities do |visibility|
|
60
|
+
superduperklass = Class.new
|
61
|
+
superduperklass.send(:define_method, :foo) {}
|
62
|
+
superduperklass.send(visibility, :foo)
|
63
|
+
instance = Class.new(Class.new(superduperklass)).new
|
64
|
+
assert_method_exists(instance, superduperklass, :foo, visibility)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_detect_instance_method_on_module_included_into_class
|
69
|
+
for_all_method_visibilities do |visibility|
|
70
|
+
mod = Module.new
|
71
|
+
mod.send(:define_method, :foo) {}
|
72
|
+
mod.send(visibility, :foo)
|
73
|
+
instance = Class.new do
|
74
|
+
include mod
|
75
|
+
end.new
|
76
|
+
assert_method_exists(instance, mod, :foo, visibility)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_detect_instance_method_on_module_included_into_superclass
|
81
|
+
for_all_method_visibilities do |visibility|
|
82
|
+
mod = Module.new
|
83
|
+
mod.send(:define_method, :foo) {}
|
84
|
+
mod.send(visibility, :foo)
|
85
|
+
superklass = Class.new do
|
86
|
+
include mod
|
87
|
+
end
|
88
|
+
instance = Class.new(superklass).new
|
89
|
+
assert_method_exists(instance, mod, :foo, visibility)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_detect_instance_method_on_module_included_into_superclass_of_superclass
|
94
|
+
for_all_method_visibilities do |visibility|
|
95
|
+
mod = Module.new
|
96
|
+
mod.send(:define_method, :foo) {}
|
97
|
+
mod.send(visibility, :foo)
|
98
|
+
superduperklass = Class.new do
|
99
|
+
include mod
|
100
|
+
end
|
101
|
+
instance = Class.new(Class.new(superduperklass)).new
|
102
|
+
assert_method_exists(instance, mod, :foo, visibility)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_detect_instance_method_on_module_included_into_module_included_into_class
|
107
|
+
for_all_method_visibilities do |visibility|
|
108
|
+
mod = Module.new
|
109
|
+
mod.send(:define_method, :foo) {}
|
110
|
+
mod.send(visibility, :foo)
|
111
|
+
supermod = Module.new do
|
112
|
+
include mod
|
113
|
+
end
|
114
|
+
klass = Class.new do
|
115
|
+
include supermod
|
116
|
+
end
|
117
|
+
instance = klass.new
|
118
|
+
assert_method_exists(instance, mod, :foo, visibility)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class ModuleSnapshotTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def test_detect_module_method_on_module
|
6
|
+
for_all_method_visibilities do |visibility|
|
7
|
+
mod = Module.new
|
8
|
+
mod.metaclass.send(:define_method, :foo) {}
|
9
|
+
mod.metaclass.send(visibility, :foo)
|
10
|
+
assert_method_exists(mod, mod.metaclass, :foo, visibility)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_detect_method_on_module_included_as_module_method_on_module
|
15
|
+
for_all_method_visibilities do |visibility|
|
16
|
+
supermod = Module.new
|
17
|
+
supermod.send(:define_method, :foo) {}
|
18
|
+
supermod.send(visibility, :foo)
|
19
|
+
mod = Module.new do
|
20
|
+
extend supermod
|
21
|
+
end
|
22
|
+
assert_method_exists(mod, supermod, :foo, visibility)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_detect_method_on_module_included_into_module_included_as_module_method_on_module
|
27
|
+
for_all_method_visibilities do |visibility|
|
28
|
+
superdupermod = Module.new
|
29
|
+
superdupermod.send(:define_method, :foo) {}
|
30
|
+
superdupermod.send(visibility, :foo)
|
31
|
+
supermod = Module.new do
|
32
|
+
include superdupermod
|
33
|
+
end
|
34
|
+
mod = Module.new do
|
35
|
+
extend supermod
|
36
|
+
end
|
37
|
+
assert_method_exists(mod, superdupermod, :foo, visibility)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class SnapshotTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include Introspection
|
6
|
+
|
7
|
+
def test_should_report_methods_added
|
8
|
+
instance = Class.new.new
|
9
|
+
before = Snapshot.new(instance)
|
10
|
+
instance.metaclass.send(:define_method, :foo) {}
|
11
|
+
after = Snapshot.new(instance)
|
12
|
+
diff = before.diff(after)
|
13
|
+
assert_equal 1, diff[:added].length
|
14
|
+
assert_equal instance.metaclass, diff[:added][0].owner
|
15
|
+
assert_equal :foo, diff[:added][0].name
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_should_report_methods_removed
|
19
|
+
instance = Class.new.new
|
20
|
+
instance.metaclass.send(:define_method, :foo) {}
|
21
|
+
before = Snapshot.new(instance)
|
22
|
+
instance.metaclass.send(:remove_method, :foo)
|
23
|
+
after = Snapshot.new(instance)
|
24
|
+
diff = before.diff(after)
|
25
|
+
assert_equal 1, diff[:removed].length
|
26
|
+
assert_equal instance.metaclass, diff[:removed][0].owner
|
27
|
+
assert_equal :foo, diff[:removed][0].name
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_should_indicate_snapshot_has_changed_when_method_is_added
|
31
|
+
instance = Class.new.new
|
32
|
+
assert_snapshot_changed(instance) do
|
33
|
+
instance.metaclass.send(:define_method, :foo) {}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def test_should_indicate_snapshot_has_changed_when_method_is_removed
|
38
|
+
instance = Class.new.new
|
39
|
+
instance.metaclass.send(:define_method, :foo) {}
|
40
|
+
assert_snapshot_changed(instance) do
|
41
|
+
instance.metaclass.send(:remove_method, :foo)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_should_indicate_snapshot_has_not_changed_when_method_no_methods_are_added_or_removed
|
46
|
+
instance = Class.new.new
|
47
|
+
assert_snapshot_unchanged(instance) {}
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
|
4
|
+
require "introspection"
|
5
|
+
require "test/unit"
|
6
|
+
|
7
|
+
module Introspection
|
8
|
+
module TestHelper
|
9
|
+
def for_all_method_visibilities(&block)
|
10
|
+
[:public, :protected, :private].each do |visibility|
|
11
|
+
block.call(visibility)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
module LocalAssertions
|
16
|
+
def assert_method_exists(object, owner, method_name, visibility)
|
17
|
+
snapshot = Snapshot.new(object)
|
18
|
+
method = owner.instance_method(method_name)
|
19
|
+
expected_method = Method.new(method, visibility)
|
20
|
+
methods_for_name = snapshot.methods.select { |m| m.name == method_name }
|
21
|
+
assert_equal [expected_method], methods_for_name
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Test::Unit::TestCase
|
27
|
+
include Introspection::TestHelper
|
28
|
+
include Introspection::LocalAssertions
|
29
|
+
include Introspection::Assertions
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: introspection
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- James Mead
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2011-07-13 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: metaid
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 15
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: "1.0"
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rake
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
description: ""
|
51
|
+
email:
|
52
|
+
- james@floehopper.org
|
53
|
+
executables: []
|
54
|
+
|
55
|
+
extensions: []
|
56
|
+
|
57
|
+
extra_rdoc_files: []
|
58
|
+
|
59
|
+
files:
|
60
|
+
- .gitignore
|
61
|
+
- Gemfile
|
62
|
+
- README.md
|
63
|
+
- Rakefile
|
64
|
+
- introspection.gemspec
|
65
|
+
- lib/introspection.rb
|
66
|
+
- lib/introspection/assertions.rb
|
67
|
+
- lib/introspection/method.rb
|
68
|
+
- lib/introspection/receivers.rb
|
69
|
+
- lib/introspection/snapshot.rb
|
70
|
+
- lib/introspection/version.rb
|
71
|
+
- samples/class.rb
|
72
|
+
- samples/instance.rb
|
73
|
+
- samples/module.rb
|
74
|
+
- test/class_snapshot_test.rb
|
75
|
+
- test/instance_snapshot_test.rb
|
76
|
+
- test/module_snapshot_test.rb
|
77
|
+
- test/snapshot_test.rb
|
78
|
+
- test/test_helper.rb
|
79
|
+
has_rdoc: true
|
80
|
+
homepage: http://jamesmead.org
|
81
|
+
licenses: []
|
82
|
+
|
83
|
+
post_install_message:
|
84
|
+
rdoc_options: []
|
85
|
+
|
86
|
+
require_paths:
|
87
|
+
- lib
|
88
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 3
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
none: false
|
99
|
+
requirements:
|
100
|
+
- - ">="
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
hash: 23
|
103
|
+
segments:
|
104
|
+
- 1
|
105
|
+
- 3
|
106
|
+
- 6
|
107
|
+
version: 1.3.6
|
108
|
+
requirements: []
|
109
|
+
|
110
|
+
rubyforge_project: introspection
|
111
|
+
rubygems_version: 1.6.2
|
112
|
+
signing_key:
|
113
|
+
specification_version: 3
|
114
|
+
summary: Dynamic inspection of the hierarchy of method definitions on a Ruby object.
|
115
|
+
test_files:
|
116
|
+
- test/class_snapshot_test.rb
|
117
|
+
- test/instance_snapshot_test.rb
|
118
|
+
- test/module_snapshot_test.rb
|
119
|
+
- test/snapshot_test.rb
|
120
|
+
- test/test_helper.rb
|