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,70 @@
|
|
1
|
+
require 'parslet'
|
2
|
+
|
3
|
+
class Typedocs::Parser::ASTBuilder < Parslet::Parser
|
4
|
+
root :method_spec
|
5
|
+
|
6
|
+
rule(:method_spec) { spaces >> rep1(method_spec1, s('||')).as(:method_spec) }
|
7
|
+
rule(:method_spec1) { (arg_spec >> s('->')).repeat.as(:arg_specs) >> (block_spec >> s('->')).maybe.as(:block_spec) >> return_spec.maybe.as(:return_spec) }
|
8
|
+
|
9
|
+
rule(:arg_spec) { arg_attr.maybe.as(:attr) >> named_type }
|
10
|
+
rule(:arg_name) { v(match['_a-z0-9?!'].repeat(1)) >> spaces }
|
11
|
+
rule(:arg_attr) { match['*?'] }
|
12
|
+
rule(:named_type) {type.as(:type) | arg_name.as(:name) >> (s(':') >> type).maybe.as(:type) }
|
13
|
+
|
14
|
+
rule(:block_spec) { s('?').maybe.as(:attr) >> s('&') >> arg_name.maybe.as(:name) }
|
15
|
+
|
16
|
+
rule(:return_spec) { named_type }
|
17
|
+
|
18
|
+
rule(:type) { rep1(type1, s('|') >> s('|').absent?) }
|
19
|
+
rule(:type1) {
|
20
|
+
t(:type_name) | t(:defined_type_name) | t(:any) | t(:void) | t(:array) | t(:tuple) | hashes | values
|
21
|
+
}
|
22
|
+
|
23
|
+
rule(:type_name) { str('::').maybe >> v(rep1(match['A-Z'] >> match['A-Za-z0-9_'].repeat, str('::'))) >> spaces }
|
24
|
+
rule(:defined_type_name) { str('@') >> type_name }
|
25
|
+
|
26
|
+
rule(:any) { s('_') }
|
27
|
+
rule(:void) { s('void') | s('--') }
|
28
|
+
|
29
|
+
rule(:array) { s('[') >> named_type >> s('...') >> s(']') }
|
30
|
+
rule(:tuple) { s('[') >> rep0(named_type, s(',')).as(:types) >> s(']') }
|
31
|
+
|
32
|
+
rule(:hashes) { t(:hash_v) | t(:hash_t) }
|
33
|
+
rule(:hash_t) { s('{') >> named_type.as(:key_t) >> s('=>') >> named_type.as(:val_t) >> s('}') }
|
34
|
+
rule(:hash_v) { s('{') >> rep1(hash_v_entry, s(',')).as(:entries) >> (s(',') >> s('...')).maybe.as(:anymore) >> s('}') }
|
35
|
+
rule(:hash_v_entry) { values.as(:key_v) >> s("=>") >> named_type.as(:val_t) }
|
36
|
+
|
37
|
+
rule(:values) { t(:nil_value) | t(:string_value) | t(:symbol_value) }
|
38
|
+
rule(:nil_value) { s('nil') }
|
39
|
+
rule(:string_value) { string_value_sq | string_value_dq }
|
40
|
+
rule(:string_value_sq) { s("'") >> v((match["^'"] | str("\\'")).repeat) >> s("'") }
|
41
|
+
rule(:string_value_dq) { s('"') >> v((match['^"'] | str('\\"')).repeat) >> s('"') }
|
42
|
+
rule(:symbol_value) { str(':') >> v(match['A-Za-z_'] >> match['A-Za-z0-9_'].repeat >> match['?!'].maybe) >> spaces }
|
43
|
+
|
44
|
+
rule(:spaces) { match('\\s').repeat }
|
45
|
+
|
46
|
+
private
|
47
|
+
def s(string)
|
48
|
+
str(string) >> spaces
|
49
|
+
end
|
50
|
+
|
51
|
+
def sv(string)
|
52
|
+
str(string).as(:value) >> spaces
|
53
|
+
end
|
54
|
+
|
55
|
+
def v(rule)
|
56
|
+
rule.as(:value)
|
57
|
+
end
|
58
|
+
|
59
|
+
def t(rule_name)
|
60
|
+
send(rule_name).as(rule_name)
|
61
|
+
end
|
62
|
+
|
63
|
+
def rep0(rule, separator)
|
64
|
+
rep1(rule, separator).repeat(0)
|
65
|
+
end
|
66
|
+
|
67
|
+
def rep1(rule, separator)
|
68
|
+
rule >> (separator >> rule).repeat
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
class Typedocs::Parser; end
|
2
|
+
|
3
|
+
require 'parslet'
|
4
|
+
require 'typedocs/type_spec'
|
5
|
+
|
6
|
+
class Typedocs::Parser::ObjectBuilder
|
7
|
+
module Helper
|
8
|
+
def self.array(a)
|
9
|
+
case a
|
10
|
+
when Array
|
11
|
+
a
|
12
|
+
else
|
13
|
+
[a]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
def self.create_builder_for(klass)
|
18
|
+
Parslet::Transform.new do
|
19
|
+
val = {value: simple(:v)}
|
20
|
+
ts = Typedocs::TypeSpec
|
21
|
+
h = Helper
|
22
|
+
dc = subtree(:_) # dont care
|
23
|
+
dc2 = subtree(:__)
|
24
|
+
mktype = ->(t, name) {
|
25
|
+
unnamed =
|
26
|
+
case t
|
27
|
+
when Array
|
28
|
+
ts::Or.new(t)
|
29
|
+
else
|
30
|
+
t || ts::Any.new
|
31
|
+
end
|
32
|
+
if name
|
33
|
+
ts::Named.new(name, unnamed)
|
34
|
+
else
|
35
|
+
unnamed
|
36
|
+
end
|
37
|
+
}
|
38
|
+
|
39
|
+
rule(method_spec: subtree(:ms)) {
|
40
|
+
specs = h.array(ms).map {|tree|
|
41
|
+
args_spec = Typedocs::ArgumentsSpec.new
|
42
|
+
tree[:arg_specs].each do|as|
|
43
|
+
type = as[:t] || Typedocs::TypeSpec::Any.new
|
44
|
+
case as[:a]
|
45
|
+
when '*'
|
46
|
+
args_spec.add_rest(type)
|
47
|
+
when '?'
|
48
|
+
args_spec.add_optional(type)
|
49
|
+
when nil
|
50
|
+
args_spec.add_required(type)
|
51
|
+
else
|
52
|
+
raise "Unknown attr: #{as[:a].inspect}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
return_spec = tree[:return_spec] || Typedocs::TypeSpec::Void.new
|
56
|
+
block_spec =
|
57
|
+
tree[:block_spec].tap do|bs|
|
58
|
+
break Typedocs::BlockSpec.new(:none, nil) if !bs
|
59
|
+
name = bs[:name] ? bs[:name][:value] : nil
|
60
|
+
if bs[:attr] == '?'
|
61
|
+
break Typedocs::BlockSpec.new(:opt, name)
|
62
|
+
else
|
63
|
+
break Typedocs::BlockSpec.new(:req, name)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
Typedocs::MethodSpec::Single.new(args_spec, block_spec, return_spec)
|
67
|
+
}
|
68
|
+
if specs.size > 1
|
69
|
+
Typedocs::MethodSpec::AnyOf.new(specs)
|
70
|
+
else
|
71
|
+
specs.first
|
72
|
+
end
|
73
|
+
}
|
74
|
+
|
75
|
+
# arg
|
76
|
+
rule(type: subtree(:t), attr: simple(:attr)) { {t: mktype[t, nil], a: attr} }
|
77
|
+
rule(type: subtree(:t), name: {value: simple(:name)}, attr: simple(:attr)) { {t: mktype[t, name], a: attr} }
|
78
|
+
# return
|
79
|
+
rule(type: subtree(:t)) { mktype[t, nil] }
|
80
|
+
rule(type: subtree(:t), name: {value: simple(:name)}) { mktype[t, name] }
|
81
|
+
|
82
|
+
rule(type_name: val) { ts::TypeIsA.new(klass, v.to_s) }
|
83
|
+
rule(defined_type_name: val) { ts::UserDefinedType.new(klass, "@#{v.to_s}") }
|
84
|
+
rule(any: dc) { ts::Any.new }
|
85
|
+
rule(void: dc) { ts::Void.new }
|
86
|
+
rule(array: simple(:v)) { ts::Array.new(v) }
|
87
|
+
rule(tuple: {types: subtree(:vs)}) { ts::ArrayAsStruct.new(vs) }
|
88
|
+
rule(hash_t: {key_t: simple(:k), val_t: simple(:v)}) { ts::HashType.new(k,v) }
|
89
|
+
rule(hash_v: {entries: subtree(:entries), anymore: simple(:anymore)}) {
|
90
|
+
kvs = h.array(entries).map{|e|
|
91
|
+
k = e[:key_v]
|
92
|
+
v = e[:val_t]
|
93
|
+
key_v = k.value
|
94
|
+
[key_v, v]
|
95
|
+
}
|
96
|
+
ts::HashValue.new(kvs, !!anymore)
|
97
|
+
}
|
98
|
+
|
99
|
+
rule(string_value: val) { ts::Value.new(v.to_s) }
|
100
|
+
rule(symbol_value: val) { ts::Value.new(v.to_sym) }
|
101
|
+
rule(nil_value: dc) { ts::Value.new(nil) }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
class Typedocs::TypeSpec
|
2
|
+
def error_message_for(obj)
|
3
|
+
"Expected #{self.to_source}, but #{obj.inspect}"
|
4
|
+
end
|
5
|
+
|
6
|
+
class Named < self
|
7
|
+
def initialize(name, spec)
|
8
|
+
Typedocs.ensure_klass(spec, Typedocs::TypeSpec)
|
9
|
+
@name = name
|
10
|
+
@spec = spec
|
11
|
+
end
|
12
|
+
attr_reader :name
|
13
|
+
attr_reader :spec
|
14
|
+
def valid?(arg); spec.valid?(arg); end
|
15
|
+
def to_source; "#{name}:#{spec.to_source}"; end
|
16
|
+
def error_message_for(arg)
|
17
|
+
spec.error_message_for(arg)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Any < self
|
22
|
+
def valid?(arg); true; end
|
23
|
+
def to_source; '_'; end
|
24
|
+
def error_message_for(arg)
|
25
|
+
raise "This spec accepts ANY value"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
class Void < Any
|
29
|
+
def to_source; 'void' end
|
30
|
+
end
|
31
|
+
class TypeIsA < self
|
32
|
+
def initialize(klass, name)
|
33
|
+
@klass = klass
|
34
|
+
@name = name
|
35
|
+
end
|
36
|
+
def target_klass
|
37
|
+
@target_klass ||= find_const @klass, @name
|
38
|
+
end
|
39
|
+
def valid?(arg);
|
40
|
+
arg.is_a? target_klass
|
41
|
+
end
|
42
|
+
def to_source
|
43
|
+
@name
|
44
|
+
end
|
45
|
+
private
|
46
|
+
def find_const(start, name)
|
47
|
+
raise ::ArgumentError, 'name' if name.empty?
|
48
|
+
raise ::ArgumentError, 'start' unless start
|
49
|
+
case name
|
50
|
+
when /^::/
|
51
|
+
const_get_from! ::Object, name
|
52
|
+
else
|
53
|
+
candidates = []
|
54
|
+
candidates << const_get_from(start, name)
|
55
|
+
candidates << const_get_with_nested(start, name)
|
56
|
+
candidates = candidates.reject(&:nil?).uniq
|
57
|
+
raise ::ArgumentError, "Type name #{name} is ambigious(search base: #{start}): #{candidates.map(&:name).join(' and ')}" if candidates.size > 1
|
58
|
+
raise ::ArgumentError, "Type not found: #{name}(search base: #{start})" unless candidates.size == 1
|
59
|
+
candidates.first
|
60
|
+
end
|
61
|
+
end
|
62
|
+
def const_get_with_nested(start, name)
|
63
|
+
top = name.split(/::/).first
|
64
|
+
root = start
|
65
|
+
until root.nil?
|
66
|
+
return const_get_from(root, name) if root.const_defined?(top, false)
|
67
|
+
root = parent_nest(root)
|
68
|
+
end
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
def parent_nest(klass)
|
72
|
+
return nil unless klass.name =~ /::/
|
73
|
+
name = klass.name.split(/::/)[0..-2].join('::')
|
74
|
+
const_get_from ::Object, name
|
75
|
+
end
|
76
|
+
def const_get_from(root, name)
|
77
|
+
begin
|
78
|
+
const_get_from! root, name
|
79
|
+
rescue NameError
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
def const_get_from!(root, name)
|
84
|
+
name.gsub(/^::/,'').split(/::/).inject(root) do|root, name|
|
85
|
+
root.const_get(name.to_sym)
|
86
|
+
end
|
87
|
+
rescue NameError => e
|
88
|
+
raise NameError, "NameError: #{name.inspect}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
class Nil < self
|
92
|
+
def initialize
|
93
|
+
@value = nil
|
94
|
+
end
|
95
|
+
def valid?(obj)
|
96
|
+
obj == @value
|
97
|
+
end
|
98
|
+
def to_source
|
99
|
+
@value.inspect
|
100
|
+
end
|
101
|
+
def error_message_for(obj)
|
102
|
+
"#{obj} should == #{@value.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
class ArrayAsStruct < self
|
106
|
+
def initialize(specs)
|
107
|
+
specs.each {|s| Typedocs.ensure_klass(s, Typedocs::TypeSpec) }
|
108
|
+
@specs = specs
|
109
|
+
end
|
110
|
+
def valid?(obj)
|
111
|
+
obj.is_a?(::Array) &&
|
112
|
+
@specs.size == obj.size &&
|
113
|
+
@specs.zip(obj).all?{|spec,elm| spec.valid?(elm)}
|
114
|
+
end
|
115
|
+
def to_source
|
116
|
+
"[#{@specs.map(&:to_source).join(', ')}]"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
class Array < self
|
120
|
+
def initialize(spec)
|
121
|
+
Typedocs.ensure_klass(spec, Typedocs::TypeSpec)
|
122
|
+
@spec = spec
|
123
|
+
end
|
124
|
+
def valid?(obj)
|
125
|
+
obj.is_a?(::Array) && obj.all?{|elm| @spec.valid?(elm)}
|
126
|
+
end
|
127
|
+
def to_source
|
128
|
+
"#{@spec.to_source}..."
|
129
|
+
end
|
130
|
+
end
|
131
|
+
class HashValue < self
|
132
|
+
# [key, spec]... ->
|
133
|
+
def initialize(entries, accept_others)
|
134
|
+
entries.each do|k, s|
|
135
|
+
Typedocs.ensure_klass(s, Typedocs::TypeSpec)
|
136
|
+
end
|
137
|
+
@entries = entries
|
138
|
+
@accept_others = accept_others
|
139
|
+
end
|
140
|
+
def valid?(obj)
|
141
|
+
obj.is_a?(::Hash) &&
|
142
|
+
(@accept_others || @entries.size == obj.size) &&
|
143
|
+
@entries.all? {|key, spec| obj.has_key?(key) && spec.valid?(obj[key]) }
|
144
|
+
end
|
145
|
+
def to_source
|
146
|
+
"{#{@entries.map{|key,value| "#{key.inspect} => #{value.to_source}"}.join(', ')}#{@accept_others ? ', ...' : ''}}"
|
147
|
+
end
|
148
|
+
end
|
149
|
+
class HashType < self
|
150
|
+
def initialize(key_spec, value_spec)
|
151
|
+
@key_spec = key_spec
|
152
|
+
@value_spec = value_spec
|
153
|
+
end
|
154
|
+
def valid?(obj)
|
155
|
+
obj.is_a?(::Hash) &&
|
156
|
+
obj.keys.all?{|k| @key_spec.valid? k} &&
|
157
|
+
obj.values.all?{|v| @value_spec.valid? v}
|
158
|
+
end
|
159
|
+
def to_source
|
160
|
+
"{#{@key_spec.to_source} => #{@value_spec.to_source}}"
|
161
|
+
end
|
162
|
+
end
|
163
|
+
class Or < self
|
164
|
+
def initialize(children)
|
165
|
+
raise ArgumentError, "Children is empty" if children.empty?
|
166
|
+
children.each do|c|
|
167
|
+
Typedocs.ensure_klass(c, Typedocs::TypeSpec)
|
168
|
+
end
|
169
|
+
@children = children
|
170
|
+
end
|
171
|
+
def valid?(obj)
|
172
|
+
@children.any?{|spec| spec.valid? obj}
|
173
|
+
end
|
174
|
+
def to_source
|
175
|
+
"#{@children.map(&:to_source).join('|')}"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
class UserDefinedType < self
|
179
|
+
def initialize(klass, name, spec = nil)
|
180
|
+
raise ArgumentError, "Invalid UDT name: #{name.inspect}" unless Typedocs::Context.valid_udt_name?(name)
|
181
|
+
@klass = klass
|
182
|
+
@name = name
|
183
|
+
@spec = spec
|
184
|
+
end
|
185
|
+
attr_reader :klass
|
186
|
+
attr_reader :name
|
187
|
+
def spec
|
188
|
+
@spec ||= Typedocs.context(klass).defined_type!(name)
|
189
|
+
end
|
190
|
+
def valid?(arg)
|
191
|
+
spec.valid?(arg)
|
192
|
+
end
|
193
|
+
def to_source
|
194
|
+
name
|
195
|
+
end
|
196
|
+
end
|
197
|
+
class Value < self
|
198
|
+
def initialize(val)
|
199
|
+
@value = val
|
200
|
+
end
|
201
|
+
attr_reader :value
|
202
|
+
def valid?(obj)
|
203
|
+
obj == value
|
204
|
+
end
|
205
|
+
def to_source
|
206
|
+
value.inspect
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
@@ -0,0 +1,130 @@
|
|
1
|
+
load File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe 'Usage examples' do
|
4
|
+
describe 'Simple example' do
|
5
|
+
class SimpleExample
|
6
|
+
include Typedocs::DSL
|
7
|
+
|
8
|
+
tdoc "Numeric -> Numeric"
|
9
|
+
def square x
|
10
|
+
x * x
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { SimpleExample.new }
|
15
|
+
it { subject.square(10).should == 100 }
|
16
|
+
it { expect { subject.square('10') }.to raise_error Typedocs::ArgumentError }
|
17
|
+
end
|
18
|
+
describe 'Basic usage' do
|
19
|
+
class BasicUsage
|
20
|
+
include Typedocs::DSL
|
21
|
+
|
22
|
+
tdoc "Numeric -> Numeric"
|
23
|
+
def square(x)
|
24
|
+
x * x
|
25
|
+
end
|
26
|
+
|
27
|
+
tdoc "String"
|
28
|
+
def expected_string_but_nil
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
tdoc "Numeric -> [Integer,String]"
|
33
|
+
def return_pair(num)
|
34
|
+
[num.to_i, num.to_s]
|
35
|
+
end
|
36
|
+
|
37
|
+
tdoc "[]"
|
38
|
+
def return_empty_array
|
39
|
+
[]
|
40
|
+
end
|
41
|
+
|
42
|
+
tdoc "_ -> String | Integer"
|
43
|
+
def return_int_or_string(is_str)
|
44
|
+
is_str ? 'string' : 100
|
45
|
+
end
|
46
|
+
|
47
|
+
tdoc "nil"
|
48
|
+
def return_nil
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe BasicUsage do
|
54
|
+
subject { BasicUsage.new }
|
55
|
+
it do
|
56
|
+
subject.square(10).should == 100
|
57
|
+
end
|
58
|
+
it do
|
59
|
+
expect { subject.square('10') }.to raise_error Typedocs::ArgumentError
|
60
|
+
end
|
61
|
+
it do
|
62
|
+
expect { subject.expected_string_but_nil }.to raise_error Typedocs::RetValError
|
63
|
+
end
|
64
|
+
it do
|
65
|
+
subject.return_pair(1).should == [1, '1']
|
66
|
+
end
|
67
|
+
|
68
|
+
it do
|
69
|
+
subject.return_empty_array.should == []
|
70
|
+
end
|
71
|
+
|
72
|
+
it do
|
73
|
+
subject.return_int_or_string(true).should == 'string'
|
74
|
+
subject.return_int_or_string(false).should == 100
|
75
|
+
end
|
76
|
+
it do
|
77
|
+
subject.return_nil.should == nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
class Initialize
|
82
|
+
include Typedocs::DSL
|
83
|
+
|
84
|
+
tdoc "Integer ->"
|
85
|
+
def initialize(n)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
describe Initialize do
|
89
|
+
it do
|
90
|
+
Initialize.new(1)
|
91
|
+
end
|
92
|
+
it do
|
93
|
+
expect { Initialize.new(nil) }.to raise_error Typedocs::ArgumentError
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class KlassMethods
|
98
|
+
include Typedocs::DSL
|
99
|
+
|
100
|
+
tdoc "String->Integer"
|
101
|
+
def self.class_method(s); s.to_i; end
|
102
|
+
|
103
|
+
def untyped_instance_method(arg); arg; end
|
104
|
+
end
|
105
|
+
describe KlassMethods do
|
106
|
+
it do
|
107
|
+
KlassMethods.new.untyped_instance_method(1).should == 1
|
108
|
+
end
|
109
|
+
it do
|
110
|
+
expect { KlassMethods.class_method(1) }.to raise_error Typedocs::ArgumentError
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class RelativeTypeNames
|
115
|
+
class Outer
|
116
|
+
include Typedocs::DSL
|
117
|
+
|
118
|
+
tdoc "A"
|
119
|
+
def self.a; A.new; end
|
120
|
+
end
|
121
|
+
class A; end
|
122
|
+
end
|
123
|
+
describe RelativeTypeNames do
|
124
|
+
it do
|
125
|
+
RelativeTypeNames::Outer.a.should be_is_a(RelativeTypeNames::A)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|