require File.expand_path(File.dirname(__FILE__) + '/spec_helper') describe "YARD::Handlers::Ruby::#{LEGACY_PARSER ? "Legacy::" : ""}ClassHandler" do before(:all) { parse_file :class_handler_001, __FILE__ } it "should parse a class block with docstring" do P("A").docstring.should == "Docstring" end it "should handle complex class names" do P("A::B::C").should_not == nil end it "should handle the subclassing syntax" do P("A::B::C").superclass.should == P(:String) P("A::X").superclass.should == Registry.at("A::B::C") end it "should interpret class << self as a class level block" do P("A.classmethod1").should_not == nil end it "should interpret class << ClassName as a class level block in ClassName's namespace" do P("A::B::C.Hello").should be_instance_of(CodeObjects::MethodObject) end it "should make visibility public when parsing a block" do P("A::B::C#method1").visibility.should == :public end it "should set superclass type to :class if it is a Proxy" do P("A::B::C").superclass.type.should == :class end it "should look for a superclass before creating the class if it shares the same name" do P('B::A').superclass.should == P('A') end it "should handle class definitions in the form ::ClassName" do Registry.at("MyRootClass").should_not be_nil end it "should handle superclass as a constant-style method (camping style < R /path/)" do P('Test1').superclass.should == P(:R) P('Test2').superclass.should == P(:R) P('Test6').superclass.should == P(:NotDelegateClass) end it "should handle superclass with OStruct.new or Struct.new syntax (superclass should be OStruct/Struct)" do P('Test3').superclass.should == P(:Struct) P('Test4').superclass.should == P(:OStruct) end it "should handle DelegateClass(CLASSNAME) superclass syntax" do P('Test5').superclass.should == P(:Array) end it "should handle a superclass of the same name in the form ::ClassName" do P('Q::Logger').superclass.should == P(:Logger) P('Q::Foo').superclass.should_not == P('Q::Logger') end ["CallMethod('test')", "VSD^#}}", 'not.aclass', 'self'].each do |klass| it "should raise an UndocumentableError for invalid class '#{klass}'" do with_parser(:ruby18) { undoc_error "class #{klass}; end" } end end ['@@INVALID', 'hi', '$MYCLASS', 'AnotherClass.new'].each do |klass| it "should raise an UndocumentableError for invalid superclass '#{klass}' but it should create the class." do YARD::CodeObjects::ClassObject.should_receive(:new).with(Registry.root, 'A') with_parser(:ruby18) { undoc_error "class A < #{klass}; end" } Registry.at('A').superclass.should == P(:Object) end end ['not.aclass', 'self', 'AnotherClass.new'].each do |klass| it "should raise an UndocumentableError if the constant class reference 'class << SomeConstant' does not point to a valid class name" do with_parser(:ruby18) do undoc_error <<-eof CONST = #{klass} class << CONST; end eof end Registry.at(klass).should be_nil end end it "should document 'class << SomeConstant' by using SomeConstant's value as a reference to the real class name" do Registry.at('String.classmethod').should_not be_nil end it "should allow class << SomeRubyClass to create the class if it does not exist" do Registry.at('Symbol.toString').should_not be_nil end it "should document 'class Exception' without running into superclass issues" do Parser::SourceParser.parse_string <<-eof class Exception end eof Registry.at(:Exception).should_not be_nil end it "should document 'class RT < XX::RT' with proper superclass even if XX::RT is a proxy" do Registry.at(:RT).should_not be_nil Registry.at(:RT).superclass.should == P('XX::RT') end it "should not overwrite docstring with an empty one" do Registry.at(:Zebra).docstring.should == "Docstring 2" end it "should turn 'class Const < Struct.new(:sym)' into class Const with attr :sym" do obj = Registry.at("Point") obj.should be_kind_of(CodeObjects::ClassObject) attrs = obj.attributes[:instance] [:x, :y, :z].each do |key| attrs.should have_key(key) attrs[key][:read].should_not be_nil attrs[key][:write].should_not be_nil end end it "should turn 'class Const < Struct.new('Name', :sym)' into class Const with attr :sym" do obj = Registry.at("AnotherPoint") obj.should be_kind_of(CodeObjects::ClassObject) attrs = obj.attributes[:instance] [:a, :b, :c].each do |key| attrs.should have_key(key) attrs[key][:read].should_not be_nil attrs[key][:write].should_not be_nil end Registry.at("XPoint").should be_nil end it "should create a Struct::Name class when class Const < Struct.new('Name', :sym) is found" do obj = Registry.at("Struct::XPoint") obj.should_not be_nil end it "should attach attribtues to the generated Struct::Name class when Struct.new('Name') is used" do obj = Registry.at("Struct::XPoint") attrs = obj.attributes[:instance] [:a, :b, :c].each do |key| attrs.should have_key(key) attrs[key][:read].should_not be_nil attrs[key][:write].should_not be_nil end end it "should use @attr to set attribute descriptions on Struct subclasses" do obj = Registry.at("DoccedStruct#input") obj.docstring.should == "the input stream" end it "should use @attr to set attribute types on Struct subclasses" do obj = Registry.at("DoccedStruct#someproc") obj.should_not be_nil obj.tag(:return).should_not be_nil obj.tag(:return).types.should == ["Proc", "#call"] end it "should default types unspecified by @attr to Object on Struct subclasses" do obj = Registry.at("DoccedStruct#mode") obj.should_not be_nil obj.tag(:return).should_not be_nil obj.tag(:return).types.should == ["Object"] end it "should create parameters for writers of Struct subclass's attributes" do obj = Registry.at("DoccedStruct#input=") obj.tags(:param).size.should == 1 obj.tag(:param).types.should == ["IO"] end ["SemiDoccedStruct", "NotAStruct"].each do |struct| describe("Attributes on a " + (struct == "NotAStruct" ? "class" : "struct")) do it "defines both readers and writers when @attr is used on Structs" do obj = Registry.at(struct) attrs = obj.attributes[:instance] attrs[:first][:read].should_not be_nil attrs[:first][:write].should_not be_nil end it "defines only a reader when only @attr_reader is used on Structs" do obj = Registry.at(struct) attrs = obj.attributes[:instance] attrs[:second][:read].should_not be_nil attrs[:second][:write].should be_nil end it "defines only a writer when only @attr_writer is used on Structs" do obj = Registry.at(struct) attrs = obj.attributes[:instance] attrs[:third][:read].should be_nil attrs[:third][:write].should_not be_nil end it "defines a reader with correct return types when @attr_reader is used on Structs" do obj = Registry.at("#{struct}#second") obj.tag(:return).types.should == ["Fixnum"] end it "defines a writer with correct parameter types when @attr_writer is used on Structs" do obj = Registry.at("#{struct}#third=") obj.tag(:param).types.should == ["Array"] end it "defines a reader and a writer when both @attr_reader and @attr_writer are used" do obj = Registry.at(struct) attrs = obj.attributes[:instance] attrs[:fourth][:read].should_not be_nil attrs[:fourth][:write].should_not be_nil end it "uses @attr_reader for the getter when both @attr_reader and @attr_writer are given" do obj = Registry.at("#{struct}#fourth") obj.tag(:return).types.should == ["#read"] end it "uses @attr_writer for the setter when both @attr_reader and @attr_writer are given" do obj = Registry.at("#{struct}#fourth=") obj.tag(:param).types.should == ["IO"] end it "extracts text from @attr_reader" do Registry.at("#{struct}#fourth").docstring.should == "returns a proc that reads" end it "extracts text from @attr_writer" do Registry.at("#{struct}#fourth=").docstring.should == "sets the proc that writes stuff" end end end it "should inherit from a regular struct" do Registry.at('RegularStruct').superclass.should == P(:Struct) Registry.at('RegularStruct2').superclass.should == P(:Struct) end it "should handle inheritance from 'self'" do Registry.at('Outer1::Inner1').superclass.should == Registry.at('Outer1') end end