typedocs 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +19 -0
- data/.simplecov +11 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +232 -0
- data/Rakefile +2 -0
- data/bin/typedocs +64 -0
- data/lib/typedocs.rb +78 -0
- data/lib/typedocs/arguments_spec.rb +126 -0
- data/lib/typedocs/block_spec.rb +51 -0
- data/lib/typedocs/context.rb +38 -0
- data/lib/typedocs/dsl.rb +37 -0
- data/lib/typedocs/enable.rb +1 -0
- data/lib/typedocs/fallback.rb +6 -0
- data/lib/typedocs/fallback/impl.rb +19 -0
- data/lib/typedocs/grammer_printer.rb +45 -0
- data/lib/typedocs/method_spec.rb +88 -0
- data/lib/typedocs/multi_functional_interface.rb +10 -0
- data/lib/typedocs/parser.rb +28 -0
- data/lib/typedocs/parser/ast_builder.rb +70 -0
- data/lib/typedocs/parser/object_builder.rb +104 -0
- data/lib/typedocs/type_spec.rb +210 -0
- data/lib/typedocs/version.rb +3 -0
- data/spec/example_spec.rb +130 -0
- data/spec/fallback_spec.rb +28 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/typedef_spec.rb +76 -0
- data/spec/typedocs/parser/ast_builder_spec.rb +60 -0
- data/spec/typedocs/parser/object_builder_spec.rb +43 -0
- data/spec/typedocs_spec.rb +598 -0
- data/typedocs.gemspec +22 -0
- metadata +165 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'typedocs/fallback/impl'
|
2
|
+
|
3
|
+
describe TypedocsFallback do
|
4
|
+
it { should_not be_enabled }
|
5
|
+
it('do_anything should success'){ subject.do_anything }
|
6
|
+
it('do_nothing should success'){ subject.do_nothing }
|
7
|
+
describe TypedocsFallback::DSL do
|
8
|
+
subject do
|
9
|
+
c = Class.new
|
10
|
+
c.instance_eval { include TypedocsFallback::DSL }
|
11
|
+
c
|
12
|
+
end
|
13
|
+
describe '#tdoc' do
|
14
|
+
it 'should accepts any arguments' do
|
15
|
+
subject.instance_eval do
|
16
|
+
tdoc
|
17
|
+
tdoc "String"
|
18
|
+
tdoc "1", "aaa"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
it 'should return null object' do
|
22
|
+
subject.instance_eval do
|
23
|
+
tdoc('aaa').bbb().ccc(10, 20).ddd
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'Typedocs.typedef' do
|
4
|
+
before(:each) do
|
5
|
+
@ns = Module.new
|
6
|
+
::TypedocsSpecSandbox = @ns
|
7
|
+
end
|
8
|
+
after(:each) do
|
9
|
+
Object.instance_eval { remove_const :TypedocsSpecSandbox }
|
10
|
+
Typedocs.initialize!
|
11
|
+
end
|
12
|
+
describe 'spec itself' do
|
13
|
+
it('Object::TypeDocsSpecSandBox exists') do
|
14
|
+
::TypedocsSpecSandbox.should_not be_nil
|
15
|
+
::TypedocsSpecSandbox.name.should == 'TypedocsSpecSandbox'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'basic usage' do
|
20
|
+
before(:each) do
|
21
|
+
class @ns::A
|
22
|
+
include Typedocs::DSL
|
23
|
+
end
|
24
|
+
end
|
25
|
+
let(:klass) { @ns::A }
|
26
|
+
describe 'defining custom type @ConfigHash' do
|
27
|
+
before(:each) do
|
28
|
+
klass.class_eval do
|
29
|
+
tdoc.typedef :@ConfigHash, "{:hoge => Integer}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
it 'should referable from definede class' do
|
33
|
+
klass.class_eval {
|
34
|
+
tdoc "@ConfigHash"
|
35
|
+
def legal
|
36
|
+
{hoge: 10}
|
37
|
+
end
|
38
|
+
tdoc "@ConfigHash"
|
39
|
+
def illegal
|
40
|
+
{hoge: 'hoge'}
|
41
|
+
end
|
42
|
+
}
|
43
|
+
klass.new.legal.should == {hoge: 10}
|
44
|
+
expect { klass.new.illegal }.to raise_error Typedocs::RetValError
|
45
|
+
end
|
46
|
+
it 'should referable from inner type' do
|
47
|
+
expect {
|
48
|
+
class @ns::B
|
49
|
+
include Typedocs::DSL
|
50
|
+
tdoc "@ConfigHash"
|
51
|
+
def illegal; {}; end
|
52
|
+
end
|
53
|
+
Typedocs.context(@ns::B).defined_type!("@ConfigHash")
|
54
|
+
}.to raise_error Typedocs::NoSuchType
|
55
|
+
end
|
56
|
+
it 'should not referable from other type' do
|
57
|
+
class klass::InnerType
|
58
|
+
include Typedocs::DSL
|
59
|
+
tdoc "@ConfigHash"
|
60
|
+
def illegal; {}; end
|
61
|
+
end
|
62
|
+
expect { klass::InnerType.new.illegal }.to raise_error Typedocs::RetValError
|
63
|
+
end
|
64
|
+
it 'should not referable from inherited type' do
|
65
|
+
expect {
|
66
|
+
class @ns::C < klass
|
67
|
+
include Typedocs::DSL
|
68
|
+
tdoc "@ConfigHash"
|
69
|
+
def illegal; {}; end
|
70
|
+
end
|
71
|
+
Typedocs.context(@ns::C).defined_type!("@ConfigHash")
|
72
|
+
}.to raise_error Typedocs::NoSuchType
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Typedocs::Parser::ASTBuilder do
|
4
|
+
def v_h(val)
|
5
|
+
{value: val}
|
6
|
+
end
|
7
|
+
def type_name_h(name)
|
8
|
+
{type_name: v_h(name) }
|
9
|
+
end
|
10
|
+
def named_type_h(type_name)
|
11
|
+
{type: type_name_h(type_name)}
|
12
|
+
end
|
13
|
+
describe 'type' do
|
14
|
+
subject { super().type }
|
15
|
+
it { should parse('TypeName').as(type_name_h('TypeName')) }
|
16
|
+
it { should parse('@DefinedTypeName').as(defined_type_name: {value: 'DefinedTypeName'}) }
|
17
|
+
it { should parse('_').as(any: '_') }
|
18
|
+
it { should parse('nil').as(nil_value: 'nil') }
|
19
|
+
it { should parse('void').as(void: 'void') }
|
20
|
+
it { should parse(':symbol_value').as(symbol_value: {value: 'symbol_value'}) }
|
21
|
+
it { should parse('"string_value"').as(string_value: {value: 'string_value'})}
|
22
|
+
it { should parse("'string'") }
|
23
|
+
it { should parse('[TupleType1, TupleType2]').as(tuple: {types: [named_type_h('TupleType1'), named_type_h('TupleType2')]}) }
|
24
|
+
it { should parse('[ArrayType...]').as(array: named_type_h('ArrayType')) }
|
25
|
+
it { should parse('{KeyType => ValueType}').as(hash_t: {key_t: named_type_h('KeyType'), val_t: named_type_h('ValueType')}) }
|
26
|
+
it { should parse('{:a => B}').as(hash_v: {entries: {key_v: {symbol_value: {value: 'a'}}, val_t: named_type_h('B')}, anymore: nil}) }
|
27
|
+
it { should parse('{:a => Integer, "b" => String}').as(hash_v: {entries: [{key_v: {symbol_value: {value: 'a'}}, val_t: named_type_h('Integer')}, {key_v: {string_value: {value: 'b'}}, val_t: named_type_h('String')}], anymore: nil}) }
|
28
|
+
it { should parse('{:a => Integer, ...}').as(hash_v: {entries: {key_v: {symbol_value: {value: 'a'}}, val_t: named_type_h('Integer')}, anymore: ', ...'}) }
|
29
|
+
it { should parse('Type1 | Type2').as([type_name_h('Type1'), type_name_h('Type2')])}
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'arg_spec' do
|
33
|
+
subject { super().arg_spec }
|
34
|
+
it { should parse('name').as(name: v_h('name'), type: nil, attr: nil) }
|
35
|
+
it { should parse('name:String').as(name: v_h('name'), type: type_name_h('String'), attr: nil) }
|
36
|
+
it { should parse('name:String|nil') }
|
37
|
+
it { should parse('data:{key:String => value:String}') }
|
38
|
+
it { should parse('Integer').as(type: type_name_h('Integer'), attr: nil) }
|
39
|
+
it { should parse('?String').as(type: type_name_h('String'), attr: '?') }
|
40
|
+
it { should parse('*a:String').as(type: type_name_h('String'), name: {value: 'a'}, attr: '*') }
|
41
|
+
it { should parse('*a').as(name: {value: 'a'}, type: nil, attr: '*') }
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'method_spec' do
|
45
|
+
subject { super().method_spec }
|
46
|
+
let(:empty_arg_spec) { {arg_specs: [], block_spec: nil, return_spec: nil} }
|
47
|
+
it { should parse('').as(method_spec: empty_arg_spec) }
|
48
|
+
it { should parse('Integer').as(method_spec: {arg_specs: [], block_spec: nil, return_spec: {type: {type_name: v_h('Integer')}}}) }
|
49
|
+
it { should parse('_ -> _') }
|
50
|
+
it { should parse('_ ->') }
|
51
|
+
it { should parse('& ->').as(method_spec: empty_arg_spec.merge(block_spec: {attr: nil, name: nil})) }
|
52
|
+
it { should parse('?&b ->').as(method_spec: empty_arg_spec.merge(block_spec: {attr: '?', name: v_h('b')})) }
|
53
|
+
it { should parse('a -> b -> & ->') }
|
54
|
+
it { should parse('a -> b -> ?& ->') }
|
55
|
+
it { should parse('a -> b -> &callback ->') }
|
56
|
+
it { should parse('_ -> Integer || Integer') }
|
57
|
+
it { should parse('_ -> _ || _ ->') }
|
58
|
+
it { should parse('||').as(method_spec: [empty_arg_spec, empty_arg_spec]) }
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Typedocs::Parser::ObjectBuilder do
|
4
|
+
let(:klass) { Class.new }
|
5
|
+
let(:parser) { Typedocs::Parser::ASTBuilder.new }
|
6
|
+
let(:ts) { Typedocs::TypeSpec }
|
7
|
+
subject { Typedocs::Parser::ObjectBuilder.create_builder_for(klass) }
|
8
|
+
|
9
|
+
def t(parser_rule_name, src, expected_klass)
|
10
|
+
parser_rule = parser.public_send(parser_rule_name)
|
11
|
+
subject.apply(parser_rule.parse(src)).should be_kind_of(expected_klass)
|
12
|
+
end
|
13
|
+
def td(parser_rule_name, src, to_source)
|
14
|
+
parser_rule = parser.public_send(parser_rule_name)
|
15
|
+
obj = subject.apply(parser_rule.parse(src))
|
16
|
+
obj.should_not be_kind_of(Hash)
|
17
|
+
obj.to_source.should == to_source
|
18
|
+
end
|
19
|
+
describe 'transform types' do
|
20
|
+
it { t(:type, 'Integer', ts::TypeIsA) }
|
21
|
+
it { td(:type, '@X', '@X') }
|
22
|
+
it { t(:type, '_', ts::Any) }
|
23
|
+
it { t(:type, ':a', ts::Value) }
|
24
|
+
it { t(:type, '"str"', ts::Value) }
|
25
|
+
it { t(:type, '[A...]', ts::Array) }
|
26
|
+
it { t(:type, '[A, B]', ts::ArrayAsStruct) }
|
27
|
+
it { t(:type, '{A => B}', ts::HashType) }
|
28
|
+
describe 'hash_v' do
|
29
|
+
subject { super().apply(parser.type.parse('{:a => B}')) }
|
30
|
+
it { should be_kind_of(ts::HashValue) }
|
31
|
+
its(:to_source) { should == '{:a => B}' }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'transform root' do
|
36
|
+
it { td(:root, 'A|B', 'A|B') }
|
37
|
+
it { td(:root, 'name:String', 'name:String') }
|
38
|
+
it { td(:root, 'a -> b', 'a:_ -> b:_') }
|
39
|
+
it { td(:root, 'x:A->ret:B || a:A -> b:B -> ret:C', 'x:A -> ret:B || a:A -> b:B -> ret:C') }
|
40
|
+
it { td(:root, 'x:X -> ?y -> *Z ->', 'x:X -> ?y:_ -> *Z -> void') }
|
41
|
+
it { td(:root, 'a -> ?&b ->', 'a:_ -> ?&b -> void') }
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,598 @@
|
|
1
|
+
load File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe Typedocs::Parser do
|
4
|
+
def parse(src)
|
5
|
+
Typedocs::Parser.new.parse(::Object, src)
|
6
|
+
end
|
7
|
+
describe 'parsing single validation' do
|
8
|
+
def spec_for(src)
|
9
|
+
parse(src).retval_spec
|
10
|
+
end
|
11
|
+
describe 'is-a' do
|
12
|
+
subject { spec_for 'Numeric' }
|
13
|
+
it { should be_valid(1) }
|
14
|
+
it { should_not be_valid('string') }
|
15
|
+
end
|
16
|
+
describe 'is-a(absolute name)' do
|
17
|
+
subject { spec_for '::Numeric' }
|
18
|
+
it { should be_valid(1) }
|
19
|
+
it { should_not be_valid('string') }
|
20
|
+
end
|
21
|
+
describe 'is-a(nested name)' do
|
22
|
+
subject { spec_for '::Object::Numeric' }
|
23
|
+
it { should be_valid(1) }
|
24
|
+
it { should_not be_valid('string') }
|
25
|
+
end
|
26
|
+
describe 'dont care(implicit)' do
|
27
|
+
subject { spec_for '' }
|
28
|
+
it { should be_valid(1) }
|
29
|
+
it { should be_valid(nil) }
|
30
|
+
end
|
31
|
+
describe 'dont carre(explicit)' do
|
32
|
+
subject { spec_for '--' }
|
33
|
+
it { should be_valid(1) }
|
34
|
+
it { should be_valid(nil) }
|
35
|
+
end
|
36
|
+
describe 'anything' do
|
37
|
+
subject { spec_for '_' }
|
38
|
+
it { should be_valid(1) }
|
39
|
+
it { should be_valid(nil) }
|
40
|
+
end
|
41
|
+
describe 'Array(as struct)' do
|
42
|
+
subject { spec_for '[String, Symbol]' }
|
43
|
+
it { should be_valid(['hoge', :foo]) }
|
44
|
+
it { should_not be_valid(['hoge', 'hoge']) }
|
45
|
+
it { should_not be_valid([nil, :foo]) }
|
46
|
+
it { should_not be_valid(['hoge', :foo, :bar]) }
|
47
|
+
|
48
|
+
describe 'when empty' do
|
49
|
+
subject { spec_for '[]' }
|
50
|
+
it { should be_valid([]) }
|
51
|
+
it { should_not be_valid([nil]) }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
describe 'OR' do
|
55
|
+
subject { spec_for 'String | Symbol | Integer' }
|
56
|
+
it { should be_valid('s') }
|
57
|
+
it { should be_valid(:aaa) }
|
58
|
+
it { should be_valid(100) }
|
59
|
+
it { should_not be_valid(1.0) }
|
60
|
+
end
|
61
|
+
describe 'nil' do
|
62
|
+
subject { spec_for 'nil' }
|
63
|
+
it { should be_valid(nil) }
|
64
|
+
it { should_not be_valid(1) }
|
65
|
+
end
|
66
|
+
describe '[...]' do
|
67
|
+
subject { spec_for '[Integer...]' }
|
68
|
+
it { should be_valid([1,2,3]) }
|
69
|
+
it { should be_valid([]) }
|
70
|
+
it { should_not be_valid(['a']) }
|
71
|
+
end
|
72
|
+
describe 'Hash' do
|
73
|
+
describe 'hash_value' do
|
74
|
+
subject { spec_for '{:sym => Integer, "str" => String, \'str2\' => Symbol}' }
|
75
|
+
it { should be_valid({sym: 10, "str" => "string", "str2" => :a}) }
|
76
|
+
it { should_not be_valid({sym: 10, "str" => "string", "str2" => :a, a: 1}) }
|
77
|
+
it { should_not be_valid({sym: 10}) }
|
78
|
+
it { should_not be_valid(nil) }
|
79
|
+
end
|
80
|
+
shared_examples_for 'hash_type' do
|
81
|
+
it { should be_valid({}) }
|
82
|
+
it { should be_valid({'key' => 100}) }
|
83
|
+
it { should_not be_valid({'key' => 'value'}) }
|
84
|
+
it { should_not be_valid({:key => 100}) }
|
85
|
+
end
|
86
|
+
describe 'hash_type' do
|
87
|
+
subject { spec_for '{String => Integer}' }
|
88
|
+
it_should_behave_like 'hash_type'
|
89
|
+
end
|
90
|
+
describe 'named hash_type' do
|
91
|
+
subject { spec_for '{name:String => age:Integer}' }
|
92
|
+
it_should_behave_like 'hash_type'
|
93
|
+
end
|
94
|
+
# TODO: optional key
|
95
|
+
# TODO: error if key duplicated
|
96
|
+
end
|
97
|
+
# name:spec style
|
98
|
+
end
|
99
|
+
describe 'parsing method specification with block' do
|
100
|
+
describe 'Integer -> & -> String' do
|
101
|
+
subject { parse 'Integer -> & -> String' }
|
102
|
+
its(:block_spec) { should_not be_nil }
|
103
|
+
it { subject.block_spec.should_not be_valid(nil) }
|
104
|
+
it { subject.block_spec.should be_valid(lambda{}) }
|
105
|
+
end
|
106
|
+
describe 'Invalid syntax' do
|
107
|
+
it do
|
108
|
+
expect { parse 'Integer -> & -> Integer -> String' }.to raise_error
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
describe 'parsing complex arguments' do
|
113
|
+
def self.when_parsing(tdoc, &block)
|
114
|
+
describe "when parsing '#{tdoc}'" do
|
115
|
+
def self.its_arguments_should_accept(args)
|
116
|
+
it { method_spec.arguments_spec.should be_valid(args) }
|
117
|
+
end
|
118
|
+
def self.its_arguments_should_not_accept(args)
|
119
|
+
it { method_spec.arguments_spec.should_not be_valid(args) }
|
120
|
+
end
|
121
|
+
def self.its_arguments_should_accept_empty_only
|
122
|
+
its_arguments_should_accept []
|
123
|
+
its_arguments_should_not_accept [1]
|
124
|
+
end
|
125
|
+
def self.its_block_should_required
|
126
|
+
its(:block_spec) {
|
127
|
+
should be_valid(lambda{})
|
128
|
+
should_not be_valid(nil)
|
129
|
+
}
|
130
|
+
end
|
131
|
+
def self.its_block_should_optional
|
132
|
+
its(:block_spec) {
|
133
|
+
should be_valid(lambda{})
|
134
|
+
should be_valid(nil)
|
135
|
+
}
|
136
|
+
end
|
137
|
+
def self.its_block_should_none
|
138
|
+
its(:block_spec) {
|
139
|
+
should_not be_valid(lambda{})
|
140
|
+
should be_valid(nil)
|
141
|
+
}
|
142
|
+
end
|
143
|
+
def self.its_retval_should_accept(val)
|
144
|
+
its(:retval_spec) { should be_valid(val) }
|
145
|
+
end
|
146
|
+
def self.its_retval_should_not_accept(val)
|
147
|
+
its(:retval_spec) { should_not be_valid(val) }
|
148
|
+
end
|
149
|
+
def self.about_retval
|
150
|
+
describe 'retval' do
|
151
|
+
subject { method_spec.retval_spec }
|
152
|
+
def self.valid(val)
|
153
|
+
it { should be_valid(val) }
|
154
|
+
end
|
155
|
+
def self.invalid(val)
|
156
|
+
it { should_not be_valid(val) }
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
let(:method_spec) { parse(tdoc) }
|
161
|
+
subject { method_spec }
|
162
|
+
self.instance_eval &block
|
163
|
+
end
|
164
|
+
end
|
165
|
+
when_parsing 'Integer' do
|
166
|
+
its_arguments_should_accept_empty_only
|
167
|
+
|
168
|
+
its_block_should_none
|
169
|
+
|
170
|
+
its_retval_should_accept 1
|
171
|
+
its_retval_should_not_accept nil
|
172
|
+
end
|
173
|
+
when_parsing 'Integer -> Integer' do
|
174
|
+
its_arguments_should_accept [1]
|
175
|
+
its_arguments_should_not_accept []
|
176
|
+
|
177
|
+
its_block_should_none
|
178
|
+
|
179
|
+
its_retval_should_accept 1
|
180
|
+
its_retval_should_not_accept nil
|
181
|
+
end
|
182
|
+
when_parsing 'Integer|nil' do
|
183
|
+
its_arguments_should_accept_empty_only
|
184
|
+
|
185
|
+
its_block_should_none
|
186
|
+
|
187
|
+
its_retval_should_accept 1
|
188
|
+
its_retval_should_accept nil
|
189
|
+
its_retval_should_not_accept :a
|
190
|
+
end
|
191
|
+
when_parsing '& -> nil' do
|
192
|
+
its_arguments_should_accept_empty_only
|
193
|
+
its_block_should_required
|
194
|
+
its_retval_should_accept nil
|
195
|
+
end
|
196
|
+
when_parsing '?& -> nil' do
|
197
|
+
its_arguments_should_accept_empty_only
|
198
|
+
its_block_should_optional
|
199
|
+
its_retval_should_accept nil
|
200
|
+
end
|
201
|
+
when_parsing '*Integer -> nil' do
|
202
|
+
its_arguments_should_accept []
|
203
|
+
its_arguments_should_accept [1]
|
204
|
+
its_arguments_should_accept [1, 2]
|
205
|
+
its_arguments_should_not_accept [nil]
|
206
|
+
|
207
|
+
its_block_should_none
|
208
|
+
its_retval_should_accept nil
|
209
|
+
end
|
210
|
+
when_parsing '[Integer] -> nil' do
|
211
|
+
its_arguments_should_accept [[1]]
|
212
|
+
its_arguments_should_not_accept [[1, 2]]
|
213
|
+
end
|
214
|
+
when_parsing '[Integer, String] -> nil' do
|
215
|
+
its_arguments_should_accept [[1, 'foo']]
|
216
|
+
its_arguments_should_not_accept [[1, 2]]
|
217
|
+
end
|
218
|
+
when_parsing '[Integer...] -> nil' do
|
219
|
+
its_arguments_should_accept [[]]
|
220
|
+
its_arguments_should_accept [[1,2,3]]
|
221
|
+
its_arguments_should_not_accept [[nil]]
|
222
|
+
end
|
223
|
+
when_parsing '{ :i => Integer, ...} -> nil' do
|
224
|
+
its_arguments_should_accept [{i: 1}]
|
225
|
+
its_arguments_should_accept [{i: 1, j: 2}]
|
226
|
+
its_arguments_should_not_accept [{}]
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
describe Typedocs::MethodSpec do
|
232
|
+
def parse(src)
|
233
|
+
Typedocs::Parser.new.parse(::Object, src)
|
234
|
+
end
|
235
|
+
def ok(*args,&block)
|
236
|
+
case args.last
|
237
|
+
when Proc
|
238
|
+
subject.call_with_validate(block, *args[0..-2], &args.last)
|
239
|
+
else
|
240
|
+
subject.call_with_validate(block, *args)
|
241
|
+
end
|
242
|
+
# should not raise any exceptions
|
243
|
+
end
|
244
|
+
def ng_arg(*args, &block)
|
245
|
+
expect { ok(*args, &block) }.to raise_error Typedocs::ArgumentError
|
246
|
+
end
|
247
|
+
def ng_ret(*args, &block)
|
248
|
+
expect { ok(*args, &block) }.to raise_error Typedocs::RetValError
|
249
|
+
end
|
250
|
+
def ng_block(*args, &block)
|
251
|
+
expect { ok(*args, &block) }.to raise_error Typedocs::BlockError
|
252
|
+
end
|
253
|
+
describe 'validation' do
|
254
|
+
describe 'retval is Integer' do
|
255
|
+
subject { parse('Integer') }
|
256
|
+
it { ok { 1 } }
|
257
|
+
it { ng_ret { nil } }
|
258
|
+
it { ng_ret { '1' } }
|
259
|
+
end
|
260
|
+
describe 'retval is Integer or nil' do
|
261
|
+
subject { parse 'Integer|nil' }
|
262
|
+
it { ok { 1 } }
|
263
|
+
it { ok { nil } }
|
264
|
+
it { ng_ret { '1' } }
|
265
|
+
end
|
266
|
+
describe 'Integer -> Integer' do
|
267
|
+
subject { parse 'Integer -> Integer' }
|
268
|
+
it { ok(1) {|i| i} }
|
269
|
+
it { ng_arg(nil) { nil } }
|
270
|
+
it { ng_ret(1) { nil } }
|
271
|
+
end
|
272
|
+
describe 'Integer -> --(dont care)' do
|
273
|
+
subject { parse 'Integer -> --' }
|
274
|
+
it { ok(1) {1} }
|
275
|
+
it { ng_arg(nil) {1} }
|
276
|
+
end
|
277
|
+
describe 'Integer -> & -> String' do
|
278
|
+
subject { parse 'Integer -> & -> String' }
|
279
|
+
it { ok(1,lambda{|i|i.to_s}) {|&block| block.call 1} }
|
280
|
+
it { ng_block(1) {|&block| block.call 1} }
|
281
|
+
end
|
282
|
+
describe 'Integer -> ?& -> String' do
|
283
|
+
subject { parse 'Integer -> ?& -> String' }
|
284
|
+
it { ok(1,lambda{|i|i.to_s}) {|&block| block.call 'a'} }
|
285
|
+
it { ok(1) {|&block| 'a'} }
|
286
|
+
end
|
287
|
+
describe 'Integer -> Integer || String -> String' do
|
288
|
+
subject { parse 'Integer -> Integer || String -> String' }
|
289
|
+
it { ok(1) {|i| 2} }
|
290
|
+
it { ok('a') {|s| 'x'} }
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
describe Typedocs::TypeSpec::TypeIsA do
|
296
|
+
before do
|
297
|
+
::Object.module_eval do
|
298
|
+
module A
|
299
|
+
module B
|
300
|
+
class C
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
after do
|
307
|
+
::Object.module_eval { remove_const :A }
|
308
|
+
end
|
309
|
+
it do
|
310
|
+
Typedocs::TypeSpec::TypeIsA.new(A::B, 'C').should be_valid(A::B::C.new)
|
311
|
+
end
|
312
|
+
it do
|
313
|
+
Typedocs::TypeSpec::TypeIsA.new(A::B, '::A::B::C').should be_valid(A::B::C.new)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
module TypeSpecSpecSandbox
|
318
|
+
end
|
319
|
+
describe Typedocs::TypeSpec do
|
320
|
+
let(:sandbox) { TypeSpecSpecSandbox }
|
321
|
+
let(:ns) { Typedocs::TypeSpec }
|
322
|
+
describe '::Any' do
|
323
|
+
subject { ns::Any.new }
|
324
|
+
it { should be_valid(nil) }
|
325
|
+
its(:to_source) { should == '_' }
|
326
|
+
it { expect { subject.error_message_for(nil) }.to raise_error }
|
327
|
+
end
|
328
|
+
describe '::Void' do
|
329
|
+
subject { ns::Void.new }
|
330
|
+
it { should be_valid(nil) }
|
331
|
+
its(:to_source) { should == 'void' }
|
332
|
+
it { expect { subject.error_message_for(nil) }.to raise_error }
|
333
|
+
end
|
334
|
+
describe '::TypeIsA' do
|
335
|
+
shared_examples_for 'Integer only' do
|
336
|
+
it { should be_valid(1) }
|
337
|
+
it { should_not be_valid('1') }
|
338
|
+
it { should_not be_valid(nil) }
|
339
|
+
end
|
340
|
+
describe 'with absolute name' do
|
341
|
+
subject { ns::TypeIsA.new sandbox, '::Integer' }
|
342
|
+
its(:to_source) { should == "::Integer" }
|
343
|
+
it_behaves_like 'Integer only'
|
344
|
+
end
|
345
|
+
describe 'with relative name' do
|
346
|
+
subject { ns::TypeIsA.new sandbox, 'Integer' }
|
347
|
+
its(:to_source) { should == "Integer" }
|
348
|
+
it_behaves_like 'Integer only'
|
349
|
+
end
|
350
|
+
end
|
351
|
+
describe '::Nil' do
|
352
|
+
subject { ns::Nil.new }
|
353
|
+
it { should be_valid(nil) }
|
354
|
+
it { should_not be_valid(1) }
|
355
|
+
its(:to_source) { should == 'nil' }
|
356
|
+
it { subject.error_message_for(1).should == "1 should == nil" }
|
357
|
+
end
|
358
|
+
describe '::ArrayAsStruct' do
|
359
|
+
subject { ns::ArrayAsStruct.new([ns::TypeIsA.new(Object,'Integer'), ns::Nil.new]) }
|
360
|
+
it { should be_valid([1, nil]) }
|
361
|
+
it { should_not be_valid(1) }
|
362
|
+
it { should_not be_valid([nil, nil]) }
|
363
|
+
it { should_not be_valid([]) }
|
364
|
+
it { should_not be_valid([1, nil, 1]) }
|
365
|
+
its(:to_source) { should == '[Integer, nil]' }
|
366
|
+
end
|
367
|
+
describe '::Array' do
|
368
|
+
subject { ns::Array.new(ns::TypeIsA.new(Object, 'Integer')) }
|
369
|
+
it { should be_valid([]) }
|
370
|
+
it { should be_valid([1]) }
|
371
|
+
it { should be_valid([1,2,3]) }
|
372
|
+
it { should_not be_valid(1) }
|
373
|
+
it { should_not be_valid([nil]) }
|
374
|
+
it { should_not be_valid([1,nil,2]) }
|
375
|
+
its(:to_source) { should == 'Integer...' }
|
376
|
+
end
|
377
|
+
describe '::HashValue' do
|
378
|
+
describe 'not accept others' do
|
379
|
+
subject { ns::HashValue.new([[:foo, ns::TypeIsA.new(Object, 'Integer')], ['bar', ns::Nil.new]], false) }
|
380
|
+
it { should be_valid({foo: 1, 'bar' => nil}) }
|
381
|
+
it { should_not be_valid({}) }
|
382
|
+
it { should_not be_valid({foo: 1}) }
|
383
|
+
it { should_not be_valid({foo: 1, 'bar' => nil, baz: 99}) }
|
384
|
+
its(:to_source) { should == '{:foo => Integer, "bar" => nil}' }
|
385
|
+
end
|
386
|
+
describe 'accept others' do
|
387
|
+
subject { ns::HashValue.new([[:foo, ns::TypeIsA.new(Object, 'Integer')], ['bar', ns::Nil.new]], true) }
|
388
|
+
it { should be_valid({foo: 1, 'bar' => nil}) }
|
389
|
+
it { should_not be_valid({}) }
|
390
|
+
it { should_not be_valid({foo: 1}) }
|
391
|
+
it { should be_valid({foo: 1, 'bar' => nil, baz: 99}) }
|
392
|
+
its(:to_source) { should == '{:foo => Integer, "bar" => nil, ...}' }
|
393
|
+
end
|
394
|
+
end
|
395
|
+
describe '::HashType' do
|
396
|
+
subject { ns::HashType.new(ns::TypeIsA.new(Object, 'Integer'), ns::TypeIsA.new(Object, 'String')) }
|
397
|
+
it { should be_valid({1 => "a"}) }
|
398
|
+
it { should be_valid({}) }
|
399
|
+
it { should_not be_valid({1 => 1}) }
|
400
|
+
it { should_not be_valid({1 => "a", 2 => 2}) }
|
401
|
+
its(:to_source) { should == '{Integer => String}' }
|
402
|
+
end
|
403
|
+
describe '::Or' do
|
404
|
+
describe 'when empty' do
|
405
|
+
it { expect { ns::Or.new([]) }.to raise_error ::ArgumentError }
|
406
|
+
end
|
407
|
+
describe 'Integer|nil' do
|
408
|
+
subject { ns::Or.new([ns::TypeIsA.new(Object, 'Integer'), ns::Nil.new]) }
|
409
|
+
it { should be_valid(nil) }
|
410
|
+
it { should be_valid(1) }
|
411
|
+
it { should_not be_valid("str") }
|
412
|
+
its(:to_source) { should == "Integer|nil" }
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
class ValueEquals < Typedocs::TypeSpec
|
418
|
+
def initialize(val)
|
419
|
+
@val = val
|
420
|
+
end
|
421
|
+
def valid?(val)
|
422
|
+
val == @val
|
423
|
+
end
|
424
|
+
def inspect
|
425
|
+
"(== #{@val.inspect})"
|
426
|
+
end
|
427
|
+
end
|
428
|
+
describe 'ensure ValueEquals helper works' do
|
429
|
+
subject { ValueEquals }
|
430
|
+
it { subject.new(1).should be_valid(1) }
|
431
|
+
it { subject.new(1).should_not be_valid(2) }
|
432
|
+
end
|
433
|
+
describe Typedocs::ArgumentsSpec do
|
434
|
+
def val(v)
|
435
|
+
ValueEquals.new(v)
|
436
|
+
end
|
437
|
+
subject { Typedocs::ArgumentsSpec.new }
|
438
|
+
describe 'with ()' do
|
439
|
+
it { should be_valid([]) }
|
440
|
+
it { should_not be_valid([:a]) }
|
441
|
+
end
|
442
|
+
describe 'with (a)' do
|
443
|
+
before do
|
444
|
+
subject.add_required(val :a)
|
445
|
+
end
|
446
|
+
it { should be_valid([:a]) }
|
447
|
+
it { should_not be_valid([]) }
|
448
|
+
it { should_not be_valid([:a,:b]) }
|
449
|
+
end
|
450
|
+
describe 'with (?a)' do
|
451
|
+
before do
|
452
|
+
subject.add_optional(val :a)
|
453
|
+
end
|
454
|
+
it { should be_valid([:a]) }
|
455
|
+
it { should be_valid([]) }
|
456
|
+
it { should_not be_valid([:a,:b]) }
|
457
|
+
end
|
458
|
+
describe 'with (a, ?b, ?c)' do
|
459
|
+
before do
|
460
|
+
subject.add_required(val :a)
|
461
|
+
subject.add_optional(val :b)
|
462
|
+
subject.add_optional(val :c)
|
463
|
+
end
|
464
|
+
it { should be_valid([:a]) }
|
465
|
+
it { should be_valid([:a,:b]) }
|
466
|
+
it { should be_valid([:a,:b,:c]) }
|
467
|
+
it { should_not be_valid([:a,:b,:c,:d]) }
|
468
|
+
it { should_not be_valid([]) }
|
469
|
+
end
|
470
|
+
describe 'with (*a)' do
|
471
|
+
before do
|
472
|
+
subject.add_rest(val :a)
|
473
|
+
end
|
474
|
+
it { should be_valid([]) }
|
475
|
+
it { should be_valid([:a]) }
|
476
|
+
it { should be_valid([:a,:a,:a]) }
|
477
|
+
it { should_not be_valid([:a,:b]) }
|
478
|
+
end
|
479
|
+
describe 'with (a, *b)' do
|
480
|
+
before do
|
481
|
+
subject.add_required(val :a)
|
482
|
+
subject.add_rest(val :b)
|
483
|
+
end
|
484
|
+
it { should be_valid([:a]) }
|
485
|
+
it { should be_valid([:a,:b,:b]) }
|
486
|
+
it { should_not be_valid([:a,:b,:c]) }
|
487
|
+
it { should_not be_valid([]) }
|
488
|
+
end
|
489
|
+
describe 'with (*a, b)' do
|
490
|
+
before do
|
491
|
+
subject.add_rest(val :a)
|
492
|
+
subject.add_required(val :b)
|
493
|
+
end
|
494
|
+
it { should be_valid([:b]) }
|
495
|
+
it { should be_valid([:a, :b]) }
|
496
|
+
it { should be_valid([:a, :a, :b]) }
|
497
|
+
it { should_not be_valid([:a, :b, :b]) }
|
498
|
+
it { should_not be_valid([]) }
|
499
|
+
it { should_not be_valid([:c]) }
|
500
|
+
end
|
501
|
+
describe 'with (a, *b, c)' do
|
502
|
+
before do
|
503
|
+
subject.add_required(val :a)
|
504
|
+
subject.add_rest(val :b)
|
505
|
+
subject.add_required(val :c)
|
506
|
+
end
|
507
|
+
it { should be_valid([:a, :c]) }
|
508
|
+
it { should be_valid([:a, :b, :c]) }
|
509
|
+
it { should be_valid([:a, :b, :b, :c]) }
|
510
|
+
it { should_not be_valid([:a, :b, :c, :c]) }
|
511
|
+
it { should_not be_valid([:a]) }
|
512
|
+
end
|
513
|
+
describe 'with (?a, *b, c)' do
|
514
|
+
before do
|
515
|
+
subject.add_optional(val :a)
|
516
|
+
subject.add_rest(val :b)
|
517
|
+
subject.add_required(val :c)
|
518
|
+
end
|
519
|
+
it { should be_valid([:c]) }
|
520
|
+
it { should be_valid([:a, :c]) }
|
521
|
+
it { should be_valid([:a, :b, :c]) }
|
522
|
+
it { should be_valid([:a, :b, :b, :c]) }
|
523
|
+
it { should_not be_valid([]) }
|
524
|
+
it { should_not be_valid([:a, :b, :c, :c]) }
|
525
|
+
end
|
526
|
+
describe 'with (a, ?b, c)' # error
|
527
|
+
describe 'with (a, *b, *c)' # error
|
528
|
+
describe 'with (a, *b, ?c)' # error
|
529
|
+
end
|
530
|
+
|
531
|
+
describe 'tdoc :inherit' do
|
532
|
+
before do
|
533
|
+
@ns = Class.new
|
534
|
+
end
|
535
|
+
describe 'A < B' do
|
536
|
+
before do
|
537
|
+
class @ns::A
|
538
|
+
include Typedocs::DSL
|
539
|
+
tdoc 'Integer'
|
540
|
+
def foo; '1'; end
|
541
|
+
end
|
542
|
+
class @ns::B < @ns::A
|
543
|
+
include Typedocs::DSL
|
544
|
+
tdoc :inherit
|
545
|
+
def foo; '2'; end
|
546
|
+
end
|
547
|
+
end
|
548
|
+
it 'inherit super method spec' do
|
549
|
+
expect { @ns::A.new.foo }.to raise_error Typedocs::RetValError
|
550
|
+
expect { @ns::B.new.foo }.to raise_error Typedocs::RetValError
|
551
|
+
end
|
552
|
+
end
|
553
|
+
describe 'deep nested class' do
|
554
|
+
before do
|
555
|
+
module @ns::Module
|
556
|
+
def foo; end
|
557
|
+
end
|
558
|
+
class @ns::A
|
559
|
+
include Typedocs::DSL
|
560
|
+
tdoc 'Integer'
|
561
|
+
def foo; end
|
562
|
+
end
|
563
|
+
class @ns::X < @ns::A
|
564
|
+
end
|
565
|
+
class @ns::B < @ns::X
|
566
|
+
include Typedocs::DSL
|
567
|
+
end
|
568
|
+
ns = @ns
|
569
|
+
@ns::B.class_eval { include(ns::Module) }
|
570
|
+
class @ns::B < @ns::X
|
571
|
+
def self.foo; end
|
572
|
+
tdoc :inherit
|
573
|
+
def foo; end
|
574
|
+
end
|
575
|
+
end
|
576
|
+
it 'inherit super method spec' do
|
577
|
+
expect { @ns::B.new.foo }.to raise_error Typedocs::RetValError
|
578
|
+
end
|
579
|
+
end
|
580
|
+
describe 'class method' do
|
581
|
+
before do
|
582
|
+
class @ns::A
|
583
|
+
include Typedocs::DSL
|
584
|
+
tdoc 'Integer'
|
585
|
+
def self.foo; end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
it 'cant inherit' do
|
589
|
+
expect {
|
590
|
+
class @ns::B < @ns::A
|
591
|
+
include Typedocs::DSL
|
592
|
+
tdoc :inherit
|
593
|
+
def self.foo; end
|
594
|
+
end
|
595
|
+
}.to raise_error Typedocs::NoSuchMethod
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|