forked from ruby/spec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
metaclass_spec.rb
143 lines (116 loc) · 3.45 KB
/
metaclass_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
require_relative '../spec_helper'
require_relative '../fixtures/class'
require_relative 'fixtures/metaclass'
describe "self in a metaclass body (class << obj)" do
it "is TrueClass for true" do
class << true; self; end.should == TrueClass
end
it "is FalseClass for false" do
class << false; self; end.should == FalseClass
end
it "is NilClass for nil" do
class << nil; self; end.should == NilClass
end
it "raises a TypeError for numbers" do
lambda { class << 1; self; end }.should raise_error(TypeError)
end
it "raises a TypeError for symbols" do
lambda { class << :symbol; self; end }.should raise_error(TypeError)
end
it "is a singleton Class instance" do
cls = class << mock('x'); self; end
cls.is_a?(Class).should == true
cls.should_not equal(Object)
end
end
describe "A constant on a metaclass" do
before :each do
@object = Object.new
class << @object
CONST = self
end
end
it "can be accessed after the metaclass body is reopened" do
class << @object
CONST.should == self
end
end
it "can be accessed via self::CONST" do
class << @object
self::CONST.should == self
end
end
it "can be accessed via const_get" do
class << @object
const_get(:CONST).should == self
end
end
it "is not defined on the object's class" do
@object.class.const_defined?(:CONST).should be_false
end
it "is not defined in the metaclass opener's scope" do
class << @object
CONST
end
lambda { CONST }.should raise_error(NameError)
end
it "cannot be accessed via object::CONST" do
lambda do
@object::CONST
end.should raise_error(TypeError)
end
it "raises a NameError for anonymous_module::CONST" do
@object = Class.new
class << @object
CONST = 100
end
lambda do
@object::CONST
end.should raise_error(NameError)
end
it "appears in the metaclass constant list" do
constants = class << @object; constants; end
constants.should include(:CONST)
end
it "does not appear in the object's class constant list" do
@object.class.constants.should_not include(:CONST)
end
it "is not preserved when the object is duped" do
@object = @object.dup
lambda do
class << @object; CONST; end
end.should raise_error(NameError)
end
it "is preserved when the object is cloned" do
@object = @object.clone
class << @object
CONST.should_not be_nil
end
end
end
describe "calling methods on the metaclass" do
it "calls a method on the metaclass" do
MetaClassSpecs::A.cheese.should == 'edam'
MetaClassSpecs::B.cheese.should == 'stilton'
end
it "calls a method on the instance's metaclass" do
b = MetaClassSpecs::B.new
b_meta = MetaClassSpecs.metaclass_of b
b_meta.send(:define_method, :cheese) {'cheshire'}
b.cheese.should == 'cheshire'
end
it "calls a method in deeper chains of metaclasses" do
b = MetaClassSpecs::B.new
b_meta = MetaClassSpecs.metaclass_of b
b_meta_meta = MetaClassSpecs.metaclass_of b_meta
b_meta_meta.send(:define_method, :cheese) {'gouda'}
b_meta.cheese.should == 'gouda'
b_meta_meta_meta = MetaClassSpecs.metaclass_of b_meta_meta
b_meta_meta_meta.send(:define_method, :cheese) {'wensleydale'}
b_meta_meta.cheese.should == 'wensleydale'
end
it "calls a method defined on the metaclass of the metaclass" do
d_meta = MetaClassSpecs::D.singleton_class
d_meta.ham.should == 'iberico'
end
end