diff options
Diffstat (limited to 'spec/ruby/language/metaclass_spec.rb')
-rw-r--r-- | spec/ruby/language/metaclass_spec.rb | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/spec/ruby/language/metaclass_spec.rb b/spec/ruby/language/metaclass_spec.rb new file mode 100644 index 0000000000..b3bcd9ef18 --- /dev/null +++ b/spec/ruby/language/metaclass_spec.rb @@ -0,0 +1,143 @@ +require File.expand_path('../../spec_helper', __FILE__) +require File.expand_path('../../fixtures/class', __FILE__) +require File.expand_path('../fixtures/metaclass', __FILE__) + +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 |