2018-03-04 10:09:32 -05:00
|
|
|
require_relative '../spec_helper'
|
|
|
|
require_relative '../fixtures/class'
|
|
|
|
require_relative 'fixtures/metaclass'
|
2017-05-07 08:04:49 -04:00
|
|
|
|
|
|
|
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
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { class << 1; self; end }.should raise_error(TypeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "raises a TypeError for symbols" do
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { class << :symbol; self; end }.should raise_error(TypeError)
|
2017-05-07 08:04:49 -04:00
|
|
|
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
|
2019-07-27 06:40:09 -04:00
|
|
|
-> { CONST }.should raise_error(NameError)
|
2017-05-07 08:04:49 -04:00
|
|
|
end
|
|
|
|
|
|
|
|
it "cannot be accessed via object::CONST" do
|
2019-07-27 06:40:09 -04:00
|
|
|
-> do
|
2017-05-07 08:04:49 -04:00
|
|
|
@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
|
|
|
|
|
2019-07-27 06:40:09 -04:00
|
|
|
-> do
|
2017-05-07 08:04:49 -04:00
|
|
|
@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
|
|
|
|
|
2019-07-27 06:40:09 -04:00
|
|
|
-> do
|
2017-05-07 08:04:49 -04:00
|
|
|
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
|