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 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
|
# frozen_string_literal: true
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
RSpec.describe "YARD::Handlers::Ruby::#{LEGACY_PARSER ? "Legacy::" : ""}ClassHandler" do
before(:all) { parse_file :class_handler_001, __FILE__ }
it "parses a class block with docstring" do
expect(P("A").docstring).to eq "Docstring"
end
it "handles complex class names" do
expect(P("A::B::C")).not_to eq nil
end
it "handles the subclassing syntax" do
expect(P("A::B::C").superclass).to eq P(:String)
expect(P("A::X").superclass).to eq Registry.at("A::B::C")
end
it "interprets class << self as a class level block" do
expect(P("A.classmethod1")).not_to eq nil
end
it "interprets class << ClassName as a class level block in ClassName's namespace" do
expect(P("A::B::C.Hello")).to be_instance_of(CodeObjects::MethodObject)
end
it "makes visibility public when parsing a block" do
expect(P("A::B::C#method1").visibility).to eq :public
end
it "sets superclass type to :class if it is a Proxy" do
expect(P("A::B::C").superclass.type).to eq :class
end
it "looks for a superclass before creating the class if it shares the same name" do
expect(P('B::A').superclass).to eq P('A')
end
it "handles class definitions in the form ::ClassName" do
expect(Registry.at("MyRootClass")).not_to be nil
end
it "handles superclass as a constant-style method (camping style < R /path/)" do
expect(P('Test1').superclass).to eq P(:R)
expect(P('Test2').superclass).to eq P(:R)
expect(P('Test6').superclass).to eq P(:NotDelegateClass)
end
it "handles superclass with OStruct.new or Struct.new syntax (superclass should be OStruct/Struct)" do
expect(P('Test3').superclass).to eq P(:Struct)
expect(P('Test4').superclass).to eq P(:OStruct)
end
it "handles DelegateClass(CLASSNAME) superclass syntax" do
expect(P('Test5').superclass).to eq P(:Array)
end
it "handles a superclass of the same name in the form ::ClassName" do
expect(P('Q::Logger').superclass).to eq P(:Logger)
expect(P('Q::Foo').superclass).not_to eq P('Q::Logger')
end
["CallMethod('test')", "VSD^#}}", 'not.aclass', 'self'].each do |klass|
it "raises 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 "raises an UndocumentableError for invalid superclass '#{klass}' but it should create the class." do
expect(YARD::CodeObjects::ClassObject).to receive(:new).with(Registry.root, 'A')
with_parser(:ruby18) { undoc_error "class A < #{klass}; end" }
expect(Registry.at('A').superclass).to eq P(:Object)
end
end
['not.aclass', 'self', 'AnotherClass.new'].each do |klass|
it "raises 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
expect(Registry.at(klass)).to be nil
end
end
it "documents 'class << SomeConstant' by using SomeConstant's value as a reference to the real class name" do
expect(Registry.at('String.classmethod')).not_to be nil
end
it "allows class << SomeRubyClass to create the class if it does not exist" do
expect(Registry.at('Symbol.toString')).not_to be nil
end
it "documents 'class Exception' without running into superclass issues" do
Parser::SourceParser.parse_string <<-eof
class Exception
end
eof
expect(Registry.at(:Exception)).not_to be nil
end
it "documents 'class RT < XX::RT' with proper superclass even if XX::RT is a proxy" do
expect(Registry.at(:RT)).not_to be nil
expect(Registry.at(:RT).superclass).to eq P('XX::RT')
end
it "does not overwrite docstring with an empty one" do
expect(Registry.at(:Zebra).docstring).to eq "Docstring 2"
end
it "turns 'class Const < Struct.new(:sym)' into class Const with attr :sym" do
obj = Registry.at("Point")
expect(obj).to be_kind_of(CodeObjects::ClassObject)
attrs = obj.attributes[:instance]
[:x, :y, :z].each do |key|
expect(attrs).to have_key(key)
expect(attrs[key][:read]).not_to be nil
expect(attrs[key][:write]).not_to be nil
end
end
it "turns 'class Const < Struct.new('Name', :sym)' into class Const with attr :sym" do
obj = Registry.at("AnotherPoint")
expect(obj).to be_kind_of(CodeObjects::ClassObject)
attrs = obj.attributes[:instance]
[:a, :b, :c].each do |key|
expect(attrs).to have_key(key)
expect(attrs[key][:read]).not_to be nil
expect(attrs[key][:write]).not_to be nil
end
expect(Registry.at("XPoint")).to be nil
end
it "creates a Struct::Name class when class Const < Struct.new('Name', :sym) is found" do
obj = Registry.at("Struct::XPoint")
expect(obj).not_to be nil
end
it "attaches 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|
expect(attrs).to have_key(key)
expect(attrs[key][:read]).not_to be nil
expect(attrs[key][:write]).not_to be nil
end
end
it "uses @attr to set attribute descriptions on Struct subclasses" do
obj = Registry.at("DoccedStruct#input")
expect(obj.docstring).to eq "the input stream"
end
it "uses @attr to set attribute types on Struct subclasses" do
obj = Registry.at("DoccedStruct#someproc")
expect(obj).not_to be nil
expect(obj.tag(:return)).not_to be nil
expect(obj.tag(:return).types).to eq ["Proc", "#call"]
end
it "defaults types unspecified by @attr to Object on Struct subclasses" do
obj = Registry.at("DoccedStruct#mode")
expect(obj).not_to be nil
expect(obj.tag(:return)).not_to be nil
expect(obj.tag(:return).types).to eq ["Object"]
end
it "creates parameters for writers of Struct subclass's attributes" do
obj = Registry.at("DoccedStruct#input=")
expect(obj.tags(:param).size).to eq 1
expect(obj.tag(:param).types).to eq ["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]
expect(attrs[:first][:read]).not_to be nil
expect(attrs[:first][:write]).not_to 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]
expect(attrs[:second][:read]).not_to be nil
expect(attrs[:second][:write]).to 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]
expect(attrs[:third][:read]).to be nil
expect(attrs[:third][:write]).not_to be nil
end
it "defines a reader with correct return types when @attr_reader is used on Structs" do
obj = Registry.at("#{struct}#second")
expect(obj.tag(:return).types).to eq ["Fixnum"]
end
it "defines a writer with correct parameter types when @attr_writer is used on Structs" do
obj = Registry.at("#{struct}#third=")
expect(obj.tag(:param).types).to eq ["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]
expect(attrs[:fourth][:read]).not_to be nil
expect(attrs[:fourth][:write]).not_to 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")
expect(obj.tag(:return).types).to eq ["#read"]
end
it "uses @attr_writer for the setter when both @attr_reader and @attr_writer are given" do
obj = Registry.at("#{struct}#fourth=")
expect(obj.tag(:param).types).to eq ["IO"]
end
it "extracts text from @attr_reader" do
expect(Registry.at("#{struct}#fourth").docstring).to eq "returns a proc that reads"
end
it "extracts text from @attr_writer" do
expect(Registry.at("#{struct}#fourth=").docstring).to eq "sets the proc that writes stuff"
end
end
end
it "inherits from a regular struct" do
expect(Registry.at('RegularStruct').superclass).to eq P(:Struct)
expect(Registry.at('RegularStruct2').superclass).to eq P(:Struct)
end
it "handles inheritance from 'self'" do
expect(Registry.at('Outer1::Inner1').superclass).to eq Registry.at('Outer1')
end
end
|