rtype-native 0.2.0
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.
- checksums.yaml +7 -0
- data/Gemfile +3 -0
- data/LICENSE +9 -0
- data/README.md +448 -0
- data/Rakefile +12 -0
- data/benchmark/benchmark.rb +192 -0
- data/ext/rtype/extconf.rb +3 -0
- data/ext/rtype/rtype.c +122 -0
- data/ext/rtype/rtype.h +5 -0
- data/spec/rtype_spec.rb +541 -0
- data/spec/spec_helper.rb +5 -0
- metadata +113 -0
data/ext/rtype/rtype.c
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
#include "rtype.h"
|
2
|
+
|
3
|
+
VALUE rb_mRtype, rb_mRtypeBehavior, rb_cRtypeBehaviorBase, rb_eRtypeArgumentTypeError, rb_eRtypeTypeSignatureError, rb_eRtypeReturnTypeError;
|
4
|
+
|
5
|
+
VALUE
|
6
|
+
rb_rtype_valid(VALUE self, VALUE expected, VALUE value) {
|
7
|
+
switch(TYPE(expected)) {
|
8
|
+
case T_MODULE:
|
9
|
+
case T_CLASS:
|
10
|
+
return rb_obj_is_kind_of(value, expected) ? Qtrue : Qfalse;
|
11
|
+
case T_SYMBOL:
|
12
|
+
return rb_respond_to(value, rb_to_id(expected)) ? Qtrue : Qfalse;
|
13
|
+
case T_REGEXP:
|
14
|
+
return rb_reg_match( expected, rb_funcall(value, rb_intern("to_s"), 0) ) != Qnil ? Qtrue : Qfalse;
|
15
|
+
case T_ARRAY:
|
16
|
+
if( !RB_TYPE_P(value, T_ARRAY) ) {
|
17
|
+
return Qfalse;
|
18
|
+
}
|
19
|
+
else if( RARRAY_LEN(expected) != RARRAY_LEN(value) ) {
|
20
|
+
return Qfalse;
|
21
|
+
}
|
22
|
+
else {
|
23
|
+
// 'for' loop initial declarations are only allowed in c99 mode
|
24
|
+
long i;
|
25
|
+
for(i = 0; i < RARRAY_LEN(expected); i++) {
|
26
|
+
VALUE e = rb_ary_entry(expected, i);
|
27
|
+
VALUE v = rb_ary_entry(value, i);
|
28
|
+
VALUE valid = rb_rtype_valid(self, e, v);
|
29
|
+
if(valid == Qfalse) {
|
30
|
+
return Qfalse;
|
31
|
+
}
|
32
|
+
}
|
33
|
+
return Qtrue;
|
34
|
+
}
|
35
|
+
case T_TRUE:
|
36
|
+
return RTEST(value) ? Qtrue : Qfalse;
|
37
|
+
case T_FALSE:
|
38
|
+
return !RTEST(value) ? Qtrue : Qfalse;
|
39
|
+
default:
|
40
|
+
if(CLASS_OF(expected) == rb_cRange) {
|
41
|
+
return rb_funcall(expected, rb_intern("include?"), 1, value);
|
42
|
+
}
|
43
|
+
else if(CLASS_OF(expected) == rb_cProc) {
|
44
|
+
return RTEST(rb_funcall(expected, rb_intern("call"), 1, value)) ? Qtrue : Qfalse;
|
45
|
+
}
|
46
|
+
else if( RTEST(rb_obj_is_kind_of(expected, rb_cRtypeBehaviorBase)) ) {
|
47
|
+
return rb_funcall(expected, rb_intern("valid?"), 1, value);
|
48
|
+
}
|
49
|
+
else {
|
50
|
+
VALUE str = rb_any_to_s(expected);
|
51
|
+
rb_raise(rb_eRtypeTypeSignatureError, "Invalid type signature: Unknown type behavior %s", StringValueCStr(str));
|
52
|
+
return Qfalse;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
}
|
56
|
+
|
57
|
+
VALUE
|
58
|
+
rb_rtype_assert_arguments_type(VALUE self, VALUE expected_args, VALUE args) {
|
59
|
+
// 'for' loop initial declarations are only allowed in c99 mode
|
60
|
+
long i;
|
61
|
+
for(i = 0; i < RARRAY_LEN(args); i++) {
|
62
|
+
VALUE e = rb_ary_entry(expected_args, i);
|
63
|
+
VALUE v = rb_ary_entry(args, i);
|
64
|
+
if(e != Qnil) {
|
65
|
+
if( !RTEST(rb_rtype_valid(self, e, v)) ) {
|
66
|
+
VALUE msg = rb_funcall(rb_mRtype, rb_intern("arg_type_error_message"), 3, LONG2FIX(i), e, v);
|
67
|
+
rb_raise(rb_eRtypeArgumentTypeError, "%s", StringValueCStr(msg));
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|
71
|
+
return Qnil;
|
72
|
+
}
|
73
|
+
|
74
|
+
static int
|
75
|
+
kwargs_do_each(VALUE key, VALUE val, VALUE in) {
|
76
|
+
VALUE expected = rb_hash_aref(in, key);
|
77
|
+
if(expected != Qnil) {
|
78
|
+
if( !RTEST(rb_rtype_valid((VALUE) NULL, expected, val)) ) {
|
79
|
+
VALUE msg = rb_funcall(rb_mRtype, rb_intern("kwarg_type_error_message"), 3, key, expected, val);
|
80
|
+
rb_raise(rb_eRtypeArgumentTypeError, "%s", StringValueCStr(msg));
|
81
|
+
}
|
82
|
+
}
|
83
|
+
return ST_CONTINUE;
|
84
|
+
}
|
85
|
+
|
86
|
+
VALUE
|
87
|
+
rb_rtype_assert_arguments_type_with_keywords(VALUE self, VALUE expected_args, VALUE args, VALUE expected_kwargs, VALUE kwargs) {
|
88
|
+
rb_rtype_assert_arguments_type(self, expected_args, args);
|
89
|
+
rb_hash_foreach(kwargs, kwargs_do_each, expected_kwargs);
|
90
|
+
return Qnil;
|
91
|
+
}
|
92
|
+
|
93
|
+
VALUE
|
94
|
+
rb_rtype_assert_return_type(VALUE self, VALUE expected, VALUE result) {
|
95
|
+
if(expected == Qnil) {
|
96
|
+
if(result != Qnil) {
|
97
|
+
VALUE msg = rb_funcall(rb_mRtype, rb_intern("type_error_message"), 2, expected, result);
|
98
|
+
rb_raise(rb_eRtypeReturnTypeError, "for return:\n %s", StringValueCStr(msg));
|
99
|
+
}
|
100
|
+
}
|
101
|
+
else {
|
102
|
+
if( !RTEST(rb_rtype_valid(self, expected, result)) ) {
|
103
|
+
VALUE msg = rb_funcall(rb_mRtype, rb_intern("type_error_message"), 2, expected, result);
|
104
|
+
rb_raise(rb_eRtypeReturnTypeError, "for return:\n %s", StringValueCStr(msg));
|
105
|
+
}
|
106
|
+
}
|
107
|
+
return Qnil;
|
108
|
+
}
|
109
|
+
|
110
|
+
void Init_rtype_native(void) {
|
111
|
+
rb_mRtype = rb_define_module("Rtype");
|
112
|
+
rb_mRtypeBehavior = rb_define_module_under(rb_mRtype, "Behavior");
|
113
|
+
rb_cRtypeBehaviorBase = rb_define_class_under(rb_mRtypeBehavior, "Base", rb_cObject);
|
114
|
+
rb_eRtypeArgumentTypeError = rb_define_class_under(rb_mRtype, "ArgumentTypeError", rb_eArgError);
|
115
|
+
rb_eRtypeTypeSignatureError = rb_define_class_under(rb_mRtype, "TypeSignatureError", rb_eArgError);
|
116
|
+
rb_eRtypeReturnTypeError = rb_define_class_under(rb_mRtype, "ReturnTypeError", rb_eStandardError);
|
117
|
+
|
118
|
+
rb_define_singleton_method(rb_mRtype, "valid?", rb_rtype_valid, 2);
|
119
|
+
rb_define_singleton_method(rb_mRtype, "assert_arguments_type", rb_rtype_assert_arguments_type, 2);
|
120
|
+
rb_define_singleton_method(rb_mRtype, "assert_arguments_type_with_keywords", rb_rtype_assert_arguments_type_with_keywords, 4);
|
121
|
+
rb_define_singleton_method(rb_mRtype, "assert_return_type", rb_rtype_assert_return_type, 2);
|
122
|
+
}
|
data/ext/rtype/rtype.h
ADDED
data/spec/rtype_spec.rb
ADDED
@@ -0,0 +1,541 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe Rtype do
|
4
|
+
let(:klass) do
|
5
|
+
Class.new do
|
6
|
+
attr_accessor :value
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@value = 123
|
10
|
+
end
|
11
|
+
|
12
|
+
def return_arg(obj)
|
13
|
+
obj
|
14
|
+
end
|
15
|
+
|
16
|
+
def three_args(a, b, c)
|
17
|
+
end
|
18
|
+
|
19
|
+
def three_kwargs(a:, b:, c:)
|
20
|
+
end
|
21
|
+
|
22
|
+
def return_nil(obj)
|
23
|
+
nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def sum(a, b)
|
27
|
+
a + b
|
28
|
+
end
|
29
|
+
|
30
|
+
def kwarg(a:)
|
31
|
+
a
|
32
|
+
end
|
33
|
+
|
34
|
+
def sum_kwargs(a:, b:)
|
35
|
+
a + b
|
36
|
+
end
|
37
|
+
|
38
|
+
def arg_and_kwarg(a, b:)
|
39
|
+
end
|
40
|
+
|
41
|
+
def arg_and_kwargs(a, b:, c:)
|
42
|
+
end
|
43
|
+
|
44
|
+
def args_and_kwargs(a, b, c:, d:)
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
def protected_func
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
def private_func
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
let(:instance) do
|
58
|
+
klass.new
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'Kernel#rtype' do
|
62
|
+
it "outside of module" do
|
63
|
+
rtype :test_args, [String] => Any
|
64
|
+
def test_args(str)
|
65
|
+
end
|
66
|
+
|
67
|
+
expect {test_args 123}.to raise_error Rtype::ArgumentTypeError
|
68
|
+
|
69
|
+
rtype :test_return, [] => String
|
70
|
+
def test_return
|
71
|
+
369
|
72
|
+
end
|
73
|
+
|
74
|
+
expect {test_return}.to raise_error Rtype::ReturnTypeError
|
75
|
+
end
|
76
|
+
|
77
|
+
it "in module" do
|
78
|
+
class TestClass
|
79
|
+
rtype :test_args, [String] => Any
|
80
|
+
def test_args(str)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
expect {TestClass.new.test_args 123}.to raise_error Rtype::ArgumentTypeError
|
85
|
+
|
86
|
+
class TestClass
|
87
|
+
rtype :test_return, [] => String
|
88
|
+
def test_return
|
89
|
+
369
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
expect {TestClass.new.test_return}.to raise_error Rtype::ReturnTypeError
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "Kernel#rtype_self" do
|
98
|
+
class TestClass
|
99
|
+
rtype_self :static_test_args, [String] => Any
|
100
|
+
def self.static_test_args(str)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
expect {TestClass::static_test_args 123}.to raise_error Rtype::ArgumentTypeError
|
105
|
+
|
106
|
+
class TestClass
|
107
|
+
rtype_self :static_test_return, [] => String
|
108
|
+
def self.static_test_return
|
109
|
+
369
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
expect {TestClass::static_test_return}.to raise_error Rtype::ReturnTypeError
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'Kernel#rtype_accessor' do
|
117
|
+
class TestClass
|
118
|
+
rtype_accessor :value, String
|
119
|
+
attr_accessor :value
|
120
|
+
|
121
|
+
def initialize
|
122
|
+
@value = 123
|
123
|
+
end
|
124
|
+
end
|
125
|
+
expect {TestClass.new.value = 123}.to raise_error Rtype::ArgumentTypeError
|
126
|
+
expect {TestClass.new.value}.to raise_error Rtype::ReturnTypeError
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'Kernel#rtype_accessor_self' do
|
130
|
+
class TestClass
|
131
|
+
@@val = 123
|
132
|
+
|
133
|
+
rtype_accessor_self :value, String
|
134
|
+
def self.value=(val)
|
135
|
+
@@val = val
|
136
|
+
end
|
137
|
+
def self.value
|
138
|
+
@@val
|
139
|
+
end
|
140
|
+
end
|
141
|
+
expect {TestClass::value = 123}.to raise_error Rtype::ArgumentTypeError
|
142
|
+
expect {TestClass::value}.to raise_error Rtype::ReturnTypeError
|
143
|
+
end
|
144
|
+
|
145
|
+
describe 'Test type behaviors' do
|
146
|
+
describe 'Module' do
|
147
|
+
it "is right" do
|
148
|
+
klass.send :rtype, :return_arg, [String] => Any
|
149
|
+
instance.return_arg("This is a string!")
|
150
|
+
end
|
151
|
+
it "is wrong" do
|
152
|
+
klass.send :rtype, :return_arg, [String] => Any
|
153
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ArgumentTypeError
|
154
|
+
end
|
155
|
+
it "is wrong result" do
|
156
|
+
klass.send :rtype, :return_nil, [Any] => String
|
157
|
+
expect {instance.return_nil("This is a string!")}.to raise_error Rtype::ReturnTypeError
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe 'Symbol' do
|
162
|
+
it "is right" do
|
163
|
+
klass.send :rtype, :return_arg, [:to_i] => Any
|
164
|
+
instance.return_arg(123)
|
165
|
+
end
|
166
|
+
it "is wrong args" do
|
167
|
+
klass.send :rtype, :return_arg, [:to_i] => Any
|
168
|
+
expect {instance.return_arg(true)}.to raise_error Rtype::ArgumentTypeError
|
169
|
+
end
|
170
|
+
it "is wrong result" do
|
171
|
+
klass.send :rtype, :return_nil, [Any] => :odd?
|
172
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ReturnTypeError
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
describe 'Regexp' do
|
177
|
+
it "is right" do
|
178
|
+
klass.send :rtype, :return_arg, [/cuba/] => Any
|
179
|
+
instance.return_arg("cuba")
|
180
|
+
end
|
181
|
+
it "is wrong args" do
|
182
|
+
klass.send :rtype, :return_arg, [/cuba/] => Any
|
183
|
+
expect {instance.return_arg("brazil")}.to raise_error Rtype::ArgumentTypeError
|
184
|
+
end
|
185
|
+
it "is wrong result" do
|
186
|
+
klass.send :rtype, :return_nil, [Any] => /cuba/
|
187
|
+
expect {instance.return_nil("cuba")}.to raise_error Rtype::ReturnTypeError
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe 'Range' do
|
192
|
+
it "is right" do
|
193
|
+
klass.send :rtype, :return_arg, [1..10] => Any
|
194
|
+
instance.return_arg(5)
|
195
|
+
end
|
196
|
+
it "is wrong args" do
|
197
|
+
klass.send :rtype, :return_arg, [1..10] => Any
|
198
|
+
expect {instance.return_arg(1001)}.to raise_error Rtype::ArgumentTypeError
|
199
|
+
end
|
200
|
+
it "is wrong result" do
|
201
|
+
klass.send :rtype, :return_nil, [Any] => 1..10
|
202
|
+
expect {instance.return_nil(5)}.to raise_error Rtype::ReturnTypeError
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
describe 'Array' do
|
207
|
+
it "is right" do
|
208
|
+
klass.send :rtype, :return_arg, [[:to_i, :to_i]] => Any
|
209
|
+
instance.return_arg([123, 456])
|
210
|
+
end
|
211
|
+
it "is wrong args" do
|
212
|
+
klass.send :rtype, :return_arg, [[:to_i, :to_i]] => Any
|
213
|
+
expect {instance.return_arg([123, true])}.to raise_error Rtype::ArgumentTypeError
|
214
|
+
end
|
215
|
+
it "is wrong result" do
|
216
|
+
klass.send :rtype, :return_arg, [Any] => [:to_i, :to_i]
|
217
|
+
expect {instance.return_arg(true)}.to raise_error Rtype::ReturnTypeError
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe 'Proc' do
|
222
|
+
it "is right" do
|
223
|
+
klass.send :rtype, :return_arg, [->(arg){!arg.nil?}] => Any
|
224
|
+
instance.return_arg(123)
|
225
|
+
end
|
226
|
+
it "is wrong args" do
|
227
|
+
klass.send :rtype, :return_arg, [->(arg){!arg.nil?}] => Any
|
228
|
+
expect {instance.return_arg(nil)}.to raise_error Rtype::ArgumentTypeError
|
229
|
+
end
|
230
|
+
it "is wrong result" do
|
231
|
+
klass.send :rtype, :return_nil, [Any] => ->(arg){!arg.nil?}
|
232
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ReturnTypeError
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
describe 'true' do
|
237
|
+
it "is right" do
|
238
|
+
klass.send :rtype, :return_arg, [true] => Any
|
239
|
+
instance.return_arg(123)
|
240
|
+
end
|
241
|
+
it "is wrong args" do
|
242
|
+
klass.send :rtype, :return_arg, [true] => Any
|
243
|
+
expect {instance.return_arg(nil)}.to raise_error Rtype::ArgumentTypeError
|
244
|
+
end
|
245
|
+
it "is wrong result" do
|
246
|
+
klass.send :rtype, :return_nil, [Any] => true
|
247
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ReturnTypeError
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe 'false' do
|
252
|
+
it "is right" do
|
253
|
+
klass.send :rtype, :return_arg, [false] => Any
|
254
|
+
instance.return_arg(nil)
|
255
|
+
end
|
256
|
+
it "is wrong args" do
|
257
|
+
klass.send :rtype, :return_arg, [false] => Any
|
258
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ArgumentTypeError
|
259
|
+
end
|
260
|
+
it "is wrong result" do
|
261
|
+
klass.send :rtype, :return_arg, [Any] => false
|
262
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ReturnTypeError
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe 'nil' do
|
267
|
+
it "is only for return" do
|
268
|
+
klass.send :rtype, :return_nil, [] => nil
|
269
|
+
instance.return_nil(123)
|
270
|
+
|
271
|
+
klass.send :rtype, :return_arg, [] => nil
|
272
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ReturnTypeError
|
273
|
+
end
|
274
|
+
it "could not be used for args" do
|
275
|
+
expect {
|
276
|
+
klass.send :rtype, :return_arg, [nil] => Any
|
277
|
+
}.to raise_error Rtype::TypeSignatureError
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
describe 'Special type behaviors' do
|
282
|
+
it 'Rtype::Behavior::And' do
|
283
|
+
klass.send :rtype, :return_nil, [Rtype.and(:to_i, :chars)] => nil
|
284
|
+
instance.return_nil("Hello")
|
285
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ArgumentTypeError
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'Rtype::Behavior::Or' do
|
289
|
+
klass.send :rtype, :return_nil, [Rtype.or(Integer, String)] => nil
|
290
|
+
instance.return_nil(123)
|
291
|
+
instance.return_nil("abc")
|
292
|
+
expect {instance.return_nil(nil)}.to raise_error Rtype::ArgumentTypeError
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'Rtype::Behavior::Nilable' do
|
296
|
+
klass.send :rtype, :return_nil, [Rtype.nilable(Integer)] => nil
|
297
|
+
instance.return_nil(nil)
|
298
|
+
instance.return_nil(123)
|
299
|
+
expect {instance.return_nil("abc")}.to raise_error Rtype::ArgumentTypeError
|
300
|
+
end
|
301
|
+
|
302
|
+
it 'Rtype::Behavior::Not' do
|
303
|
+
klass.send :rtype, :return_nil, [Rtype.not(String)] => nil
|
304
|
+
instance.return_nil(123)
|
305
|
+
expect {instance.return_nil("abc")}.to raise_error Rtype::ArgumentTypeError
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'Rtype::Behavior::Xor' do
|
309
|
+
klass.send :rtype, :return_nil, [Rtype.xor(:to_i, String)] => nil
|
310
|
+
instance.return_nil(123)
|
311
|
+
expect {instance.return_nil("abc")}.to raise_error Rtype::ArgumentTypeError
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
describe 'Signature' do
|
317
|
+
describe 'check arguments' do
|
318
|
+
it 'nothing' do
|
319
|
+
klass.send :rtype, :sum, [] => Any
|
320
|
+
instance.sum(1, 2)
|
321
|
+
instance.sum(1, 2.0)
|
322
|
+
instance.sum(1.0, 2.0)
|
323
|
+
instance.sum("a", "b")
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'two' do
|
327
|
+
klass.send :rtype, :sum, [Integer, Integer] => Any
|
328
|
+
expect {instance.sum(1, 2.0)}.to raise_error Rtype::ArgumentTypeError
|
329
|
+
end
|
330
|
+
|
331
|
+
it 'one keyword argument' do
|
332
|
+
klass.send :rtype, :kwarg, {a: Float} => Any
|
333
|
+
expect {instance.kwarg(a: 1)}.to raise_error Rtype::ArgumentTypeError
|
334
|
+
end
|
335
|
+
|
336
|
+
it 'two keyword argument' do
|
337
|
+
klass.send :rtype, :sum_kwargs, {a: Integer, b: Float} => Any
|
338
|
+
expect {instance.sum_kwargs(a: 1, b: 2)}.to raise_error Rtype::ArgumentTypeError
|
339
|
+
end
|
340
|
+
|
341
|
+
it 'one with one keyword argument' do
|
342
|
+
klass.send :rtype, :arg_and_kwarg, [Integer, {b: Float}] => Any
|
343
|
+
expect {instance.arg_and_kwarg(1, b: 2)}.to raise_error Rtype::ArgumentTypeError
|
344
|
+
end
|
345
|
+
|
346
|
+
it 'one with two keyword argument' do
|
347
|
+
klass.send :rtype, :arg_and_kwargs, [Integer, {c: String, d: String}] => Any
|
348
|
+
expect {instance.arg_and_kwargs(1, b: 2, c: 3)}.to raise_error Rtype::ArgumentTypeError
|
349
|
+
end
|
350
|
+
|
351
|
+
it 'two with two keyword argument' do
|
352
|
+
klass.send :rtype, :args_and_kwargs, [Integer, Integer, {c: String, d: String}] => Any
|
353
|
+
expect {instance.args_and_kwargs(1, 2, c: 3, d: 4)}.to raise_error Rtype::ArgumentTypeError
|
354
|
+
end
|
355
|
+
|
356
|
+
it 'string key could not be used for keyword argument' do
|
357
|
+
expect {
|
358
|
+
klass.send :rtype, :kwarg, {'a' => Float} => Any
|
359
|
+
}.to raise_error Rtype::TypeSignatureError
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'only symbol key is used for keyword argument' do
|
363
|
+
klass.send :rtype, :kwarg, {:a => Float} => Any
|
364
|
+
expect {instance.kwarg(a: 1)}.to raise_error Rtype::ArgumentTypeError
|
365
|
+
expect {instance.kwarg(:a => 1)}.to raise_error Rtype::ArgumentTypeError
|
366
|
+
end
|
367
|
+
end
|
368
|
+
|
369
|
+
describe 'check return' do
|
370
|
+
it 'Any' do
|
371
|
+
klass.send :rtype, :return_arg, [] => Any
|
372
|
+
instance.return_arg("str")
|
373
|
+
end
|
374
|
+
|
375
|
+
it 'Array (tuple)' do
|
376
|
+
klass.send :rtype, :return_arg, [] => [Integer, Float]
|
377
|
+
expect {instance.return_arg([1, 2])}.to raise_error Rtype::ReturnTypeError
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'check arguments and return value' do
|
382
|
+
klass.send :rtype, :return_nil, [Float] => nil
|
383
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ArgumentTypeError
|
384
|
+
klass.send :rtype, :return_nil, [Integer] => Integer
|
385
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ReturnTypeError
|
386
|
+
end
|
387
|
+
|
388
|
+
describe 'wrong case' do
|
389
|
+
describe 'invalid signature form' do
|
390
|
+
it 'invalid argument signature' do
|
391
|
+
expect {
|
392
|
+
klass.send :rtype, :return_arg, Any => nil
|
393
|
+
}.to raise_error Rtype::TypeSignatureError
|
394
|
+
end
|
395
|
+
it 'invalid return signature' do
|
396
|
+
expect {
|
397
|
+
klass.send :rtype, :return_arg, [] => {}
|
398
|
+
}.to raise_error Rtype::TypeSignatureError
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'invalid type behavior in arguments' do
|
402
|
+
expect {
|
403
|
+
klass.send :rtype, :sum_kwargs, [{a: Integer}, {b: Integer}] => Any
|
404
|
+
}.to raise_error Rtype::TypeSignatureError
|
405
|
+
expect {
|
406
|
+
klass.send :rtype, :return_arg, [123] => Any
|
407
|
+
}.to raise_error Rtype::TypeSignatureError
|
408
|
+
expect {
|
409
|
+
klass.send :rtype, :return_arg, ["abc"] => Any
|
410
|
+
}.to raise_error Rtype::TypeSignatureError
|
411
|
+
expect {
|
412
|
+
klass.send :rtype, :kwarg, {a: 123} => Any
|
413
|
+
}.to raise_error Rtype::TypeSignatureError
|
414
|
+
expect {
|
415
|
+
klass.send :rtype, :kwarg, {a: {b: Integer}} => Any
|
416
|
+
}.to raise_error Rtype::TypeSignatureError
|
417
|
+
expect {
|
418
|
+
klass.send :rtype, :kwarg, {Object.new => Integer} => Any
|
419
|
+
}.to raise_error Rtype::TypeSignatureError
|
420
|
+
end
|
421
|
+
|
422
|
+
it 'invalid type behavior in return' do
|
423
|
+
expect {
|
424
|
+
klass.send :rtype, :return_arg, [] => 123
|
425
|
+
}.to raise_error Rtype::TypeSignatureError
|
426
|
+
expect {
|
427
|
+
klass.send :rtype, :return_arg, [] => "abc"
|
428
|
+
}.to raise_error Rtype::TypeSignatureError
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
describe "Implementation" do
|
435
|
+
it 'can be called before method definition' do
|
436
|
+
class TestClass
|
437
|
+
rtype :method_def, [Integer] => Any
|
438
|
+
def method_def(i)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
expect {
|
442
|
+
TestClass.new.method_def("abc")
|
443
|
+
}.to raise_error Rtype::ArgumentTypeError
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'can be called after method definition' do
|
447
|
+
class TestClass
|
448
|
+
def method_def_2(i)
|
449
|
+
end
|
450
|
+
rtype :method_def_2, [Integer] => Any
|
451
|
+
end
|
452
|
+
expect {
|
453
|
+
TestClass.new.method_def_2("abc")
|
454
|
+
}.to raise_error Rtype::ArgumentTypeError
|
455
|
+
end
|
456
|
+
|
457
|
+
it 'method name can be both symbol and string' do
|
458
|
+
class TestClass
|
459
|
+
rtype 'method_def_3', [Integer] => Any
|
460
|
+
def method_def_3(i)
|
461
|
+
end
|
462
|
+
rtype :method_def_4, [Integer] => Any
|
463
|
+
def method_def_4(i)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
expect {
|
467
|
+
TestClass.new.method_def_3("abc")
|
468
|
+
}.to raise_error Rtype::ArgumentTypeError
|
469
|
+
expect {
|
470
|
+
TestClass.new.method_def_4("abc")
|
471
|
+
}.to raise_error Rtype::ArgumentTypeError
|
472
|
+
end
|
473
|
+
|
474
|
+
describe 'method visibility works' do
|
475
|
+
it 'protected' do
|
476
|
+
expect {instance.protected_func}.to raise_error NoMethodError
|
477
|
+
end
|
478
|
+
it 'private' do
|
479
|
+
expect {instance.private_func}.to raise_error NoMethodError
|
480
|
+
end
|
481
|
+
end
|
482
|
+
|
483
|
+
context 'with empty argument signature' do
|
484
|
+
it 'accept any arguments' do
|
485
|
+
klass.send :rtype, :three_args, [] => Any
|
486
|
+
instance.three_args("abc", 123, 456)
|
487
|
+
end
|
488
|
+
end
|
489
|
+
|
490
|
+
context 'when args length is more than arg signature length' do
|
491
|
+
it 'type checking ignore rest args' do
|
492
|
+
klass.send :rtype, :three_args, [String] => Any
|
493
|
+
instance.three_args("abc", 123, 456)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
497
|
+
context 'when keyword args contain a key not configured to rtype' do
|
498
|
+
it 'type checking ignore the key' do
|
499
|
+
klass.send :rtype, :three_kwargs, {a: String} => Any
|
500
|
+
instance.three_kwargs(a: "abc", b: 123, c: 456)
|
501
|
+
end
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
describe "Call Rtype`s static method directly" do
|
506
|
+
it 'Rtype::define_typed_method' do
|
507
|
+
Rtype::define_typed_method klass, :return_arg, [String] => Any
|
508
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ArgumentTypeError
|
509
|
+
end
|
510
|
+
|
511
|
+
it 'Rtype::define_typed_accessor' do
|
512
|
+
Rtype::define_typed_accessor klass, :value, String
|
513
|
+
expect { instance.value = 123 }.to raise_error Rtype::ArgumentTypeError
|
514
|
+
expect { instance.value }.to raise_error Rtype::ReturnTypeError
|
515
|
+
end
|
516
|
+
|
517
|
+
it 'Rtype::valid?' do
|
518
|
+
expect {
|
519
|
+
Rtype::valid?("Invalid type behavior", "Test Value")
|
520
|
+
}.to raise_error Rtype::TypeSignatureError
|
521
|
+
end
|
522
|
+
|
523
|
+
it 'Rtype::assert_arguments_type' do
|
524
|
+
expect {
|
525
|
+
Rtype::assert_arguments_type([Integer, String], [123, 123])
|
526
|
+
}.to raise_error Rtype::ArgumentTypeError
|
527
|
+
end
|
528
|
+
|
529
|
+
it 'Rtype::assert_arguments_type_with_keywords' do
|
530
|
+
expect {
|
531
|
+
Rtype::assert_arguments_type_with_keywords([Integer, String], [123, "abc"], {arg: String}, {arg: 123})
|
532
|
+
}.to raise_error Rtype::ArgumentTypeError
|
533
|
+
end
|
534
|
+
|
535
|
+
it 'Rtype::assert_return_type' do
|
536
|
+
expect {
|
537
|
+
Rtype::assert_return_type nil, "No nil"
|
538
|
+
}.to raise_error Rtype::ReturnTypeError
|
539
|
+
end
|
540
|
+
end
|
541
|
+
end
|