summaryrefslogtreecommitdiff
path: root/spec/ruby/language/metaclass_spec.rb
blob: fc8306797759f4d6383267cec2e33248122d0c16 (plain)
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
    -> { class << 1; self; end }.should raise_error(TypeError)
  end

  it "raises a TypeError for symbols" do
    -> { 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
    -> { CONST }.should raise_error(NameError)
  end

  it "cannot be accessed via object::CONST" do
    -> 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

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

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