rtype-java 0.3.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 +470 -0
- data/Rakefile +12 -0
- data/benchmark/benchmark.rb +192 -0
- data/ext/rtype/rtype_java.jar +0 -0
- data/spec/rtype_spec.rb +547 -0
- data/spec/spec_helper.rb +5 -0
- metadata +110 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 507e4b1a4282df7820b412b246c6686a9b934fea
|
4
|
+
data.tar.gz: 1cc94f2f7401f544cc10c81b7cd9ef27fe8911e1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e8aa68a2002d14d410d161616a32dd8ced5c6bd4e8069b3053dbdde838fd0130a9e39d20f81d0e473cf7e269e5433bbfb8266abcd81c5ddee3d60ae2cf717020
|
7
|
+
data.tar.gz: fbf54ee612438f1eaff516deb3439732c3cd8ab88810184f0abb9dc1bd71c82d0f0e359192d706360b7ee98ffdf46938c669c1b1b6bc80723a37013314ef8c1c
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Sputnik Gugja
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
6
|
+
|
7
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
8
|
+
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,470 @@
|
|
1
|
+
# Rtype: ruby with type
|
2
|
+
[](https://badge.fury.io/rb/rtype)
|
3
|
+
[](https://travis-ci.org/sputnikgugja/rtype)
|
4
|
+
[](https://coveralls.io/github/sputnikgugja/rtype?branch=master)
|
5
|
+
|
6
|
+
You can do the type checking in Ruby with this gem!
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
require 'rtype'
|
10
|
+
|
11
|
+
rtype :sum, [:to_i, Numeric] => Numeric
|
12
|
+
def sum(a, b)
|
13
|
+
a.to_i + b
|
14
|
+
end
|
15
|
+
|
16
|
+
sum(123, "asd")
|
17
|
+
# (Rtype::ArgumentTypeError) for 2nd argument:
|
18
|
+
# Expected "asd" to be a Numeric
|
19
|
+
|
20
|
+
class Test
|
21
|
+
rtype_self :invert, {state: Boolean} => Boolean
|
22
|
+
def self.invert(state:)
|
23
|
+
!state
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
Test::invert(state: 0)
|
28
|
+
# (Rtype::ArgumentTypeError) for 'state' argument:
|
29
|
+
# Expected 0 to be a Boolean
|
30
|
+
```
|
31
|
+
|
32
|
+
## Requirements
|
33
|
+
- Ruby >= 2.1
|
34
|
+
- MRI or RBX
|
35
|
+
- If C native extension is used, otherwise it is not required
|
36
|
+
- JRuby
|
37
|
+
- If Java extension is used, otherwise it is not required
|
38
|
+
|
39
|
+
## Features
|
40
|
+
- Provide type checking for argument and return
|
41
|
+
- Support type checking for [keyword argument](#keyword-argument)
|
42
|
+
- [Type checking for array elements](#array)
|
43
|
+
- [Duck typing](#duck-typing)
|
44
|
+
- Custom type behavior
|
45
|
+
|
46
|
+
## Installation
|
47
|
+
Run `gem install rtype` or add `gem 'rtype'` to your `Gemfile`
|
48
|
+
|
49
|
+
And add to your `.rb` source file:
|
50
|
+
```ruby
|
51
|
+
require 'rtype'
|
52
|
+
```
|
53
|
+
|
54
|
+
### Native extension
|
55
|
+
Rtype itself is pure-ruby gem. but you can make it more faster by using native extension.
|
56
|
+
|
57
|
+
#### Native extension for MRI, RBX
|
58
|
+
Just run
|
59
|
+
```ruby
|
60
|
+
gem install rtype-native
|
61
|
+
```
|
62
|
+
or add to your `Gemfile`:
|
63
|
+
```ruby
|
64
|
+
gem 'rtype-native'
|
65
|
+
```
|
66
|
+
then, Rtype use it. (Do not `require 'rtype-native'`)
|
67
|
+
|
68
|
+
#### Java extension for JRuby
|
69
|
+
Just run
|
70
|
+
```ruby
|
71
|
+
gem install rtype-java
|
72
|
+
```
|
73
|
+
or add to your `Gemfile`:
|
74
|
+
```ruby
|
75
|
+
gem 'rtype-java'
|
76
|
+
```
|
77
|
+
then, Rtype use it. (Do not `require 'rtype-java'`)
|
78
|
+
|
79
|
+
## Usage
|
80
|
+
|
81
|
+
### Supported Type Behaviors
|
82
|
+
- `Module`
|
83
|
+
- Value must be an instance of this module/class or one of its superclasses
|
84
|
+
- `Any` : An alias for `BasicObject` (means Any Object)
|
85
|
+
- `Boolean` : `true` or `false`
|
86
|
+
- `Symbol`
|
87
|
+
- Value must have(respond to) a method with this name
|
88
|
+
- `Regexp`
|
89
|
+
- Value must match this regexp pattern
|
90
|
+
- `Range`
|
91
|
+
- Value must be included in this range
|
92
|
+
- `Array` (tuple)
|
93
|
+
- Value must be an array
|
94
|
+
- Each of value's elements must be valid
|
95
|
+
- Value's length must be equal to the array's length
|
96
|
+
- Of course, nested array works
|
97
|
+
- Example: [Array](#array)
|
98
|
+
- This can be used as a tuple
|
99
|
+
- `Proc`
|
100
|
+
- Value must return a truthy value for this proc
|
101
|
+
- `true`
|
102
|
+
- Value must be **truthy**
|
103
|
+
- `false`
|
104
|
+
- Value must be **falsy**
|
105
|
+
- `nil`
|
106
|
+
- Only available for **return type**. void return type in other languages
|
107
|
+
- Special Behaviors
|
108
|
+
- `Rtype::and(*types)` : Ensure value is valid for all the types
|
109
|
+
- It also can be used as `Rtype::Behavior::And[*types]` or `include Rtype::Behavior; And[...]`
|
110
|
+
- `Rtype::or(*types)` : Ensure value is valid for at least one of the types
|
111
|
+
- It also can be used as `Rtype::Behavior::Or[*types]` or `include Rtype::Behavior; Or[...]`
|
112
|
+
- `Rtype::xor(*types)` : Ensure value is valid for only one of the types
|
113
|
+
- It also can be used as `Rtype::Behavior::Xor[*types]` or `include Rtype::Behavior; Xor[...]`
|
114
|
+
- `Rtype::not(*types)` : Ensure value is not valid for all the types
|
115
|
+
- It also can be used as `Rtype::Behavior::Not[*types]` or `include Rtype::Behavior; Not[...]`
|
116
|
+
- `Rtype::nilable(type)` : Ensure value can be nil
|
117
|
+
- It also can be used as `Rtype::Behavior::Nilable[type]` or `include Rtype::Behavior; Nilable[...]`
|
118
|
+
- You can create custom behavior by extending `Rtype::Behavior::Base`
|
119
|
+
|
120
|
+
### Examples
|
121
|
+
|
122
|
+
#### Basic
|
123
|
+
```ruby
|
124
|
+
require 'rtype'
|
125
|
+
|
126
|
+
class Example
|
127
|
+
rtype :test, [Integer] => nil
|
128
|
+
def test(i)
|
129
|
+
end
|
130
|
+
|
131
|
+
rtype :any_type_arg, [Any] => nil
|
132
|
+
def any_type_arg(arg)
|
133
|
+
end
|
134
|
+
|
135
|
+
rtype :return_type_test, [] => Integer
|
136
|
+
def return_type_test
|
137
|
+
"not integer"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
e = Example.new
|
142
|
+
e.test("not integer")
|
143
|
+
# (Rtype::ArgumentTypeError) for 1st argument:
|
144
|
+
# Expected "not integer" to be a Integer
|
145
|
+
|
146
|
+
e.any_type_arg("Any argument!") # Works
|
147
|
+
|
148
|
+
e.return_type_test
|
149
|
+
# (Rtype::ReturnTypeError) for return:
|
150
|
+
# Expected "not integer" to be a Integer
|
151
|
+
```
|
152
|
+
|
153
|
+
#### Keyword argument
|
154
|
+
```ruby
|
155
|
+
require 'rtype'
|
156
|
+
|
157
|
+
class Example
|
158
|
+
rtype :say_your_name, {name: String} => Any
|
159
|
+
def say_your_name(name:)
|
160
|
+
puts "My name is #{name}"
|
161
|
+
end
|
162
|
+
|
163
|
+
# Mixing positional arguments and keyword arguments
|
164
|
+
rtype :name_and_age, [String, {age: Integer}] => Any
|
165
|
+
def name_and_age(name, age:)
|
166
|
+
puts "Name: #{name}, Age: #{age}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
Example.new.say_your_name(name: "Babo") # My name is Babo
|
171
|
+
Example.new.name_and_age("Bamboo", age: 100) # Name: Bamboo, Age: 100
|
172
|
+
|
173
|
+
Example.new.say_your_name(name: 12345)
|
174
|
+
# (Rtype::ArgumentTypeError) for 'name' argument:
|
175
|
+
# Expected 12345 to be a String
|
176
|
+
```
|
177
|
+
|
178
|
+
#### Duck typing
|
179
|
+
```ruby
|
180
|
+
require 'rtype'
|
181
|
+
|
182
|
+
class Duck
|
183
|
+
rtype :says, [:to_i] => Any
|
184
|
+
def says(i)
|
185
|
+
puts "duck:" + " quack"*i.to_i
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
Duck.new.says("2") # duck: quack quack
|
190
|
+
```
|
191
|
+
|
192
|
+
#### Array
|
193
|
+
This can be used as a tuple.
|
194
|
+
|
195
|
+
```ruby
|
196
|
+
rtype :func, [[Numeric, Numeric]] => Any
|
197
|
+
def func(arr)
|
198
|
+
puts "Your location is (#{arr[0]}, #{arr[1]}). I will look for you. I will find you"
|
199
|
+
end
|
200
|
+
|
201
|
+
func [1, "str"]
|
202
|
+
# (Rtype::ArgumentTypeError) for 1st argument:
|
203
|
+
# Expected [1, "str"] to be an array with 2 elements:
|
204
|
+
# - [0] index : Expected 1 to be a Numeric
|
205
|
+
# - [1] index : Expected "str" to be a Numeric
|
206
|
+
|
207
|
+
func [1, 2, 3]
|
208
|
+
# (Rtype::ArgumentTypeError) for 1st argument:
|
209
|
+
# Expected [1, 2, 3] to be an array with 2 elements:
|
210
|
+
# - [0] index : Expected 1 to be a Numeric
|
211
|
+
# - [1] index : Expected 2 to be a Numeric
|
212
|
+
|
213
|
+
func [1]
|
214
|
+
# (Rtype::ArgumentTypeError) for 1st argument:
|
215
|
+
# Expected [1] to be an array with 2 elements:
|
216
|
+
# - [0] index : Expected 1 to be a Numeric
|
217
|
+
# - [1] index : Expected nil to be a Numeric
|
218
|
+
|
219
|
+
func [1, 2] # Your location is (1, 2). I will look for you. I will find you
|
220
|
+
```
|
221
|
+
|
222
|
+
#### rtype with attr_accessor
|
223
|
+
`rtype_accessor`
|
224
|
+
|
225
|
+
You can use `rtype_accessor_self` for static accessor.
|
226
|
+
|
227
|
+
```ruby
|
228
|
+
require 'rtype'
|
229
|
+
|
230
|
+
class Example
|
231
|
+
rtype_accessor :value, String
|
232
|
+
attr_accessor :value
|
233
|
+
def initialize
|
234
|
+
@value = 456
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
Example.new.value = 123
|
239
|
+
# (Rtype::ArgumentTypeError) for 1st argument:
|
240
|
+
# Expected 123 to be a String
|
241
|
+
|
242
|
+
Example.new.value
|
243
|
+
# (Rtype::ReturnTypeError) for return:
|
244
|
+
# Expected 456 to be a String
|
245
|
+
```
|
246
|
+
|
247
|
+
#### Combined type
|
248
|
+
```ruby
|
249
|
+
### TEST 1 ###
|
250
|
+
require 'rtype'
|
251
|
+
|
252
|
+
class Example
|
253
|
+
rtype :and_test, [Rtype::and(String, :func)] => Any
|
254
|
+
def and_test(arg)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
Example.new.and_test("A string")
|
259
|
+
# (Rtype::ArgumentTypeError) for 1st argument:
|
260
|
+
# Expected "A string" to be a String
|
261
|
+
# AND Expected "A string" to respond to :func
|
262
|
+
```
|
263
|
+
```ruby
|
264
|
+
### TEST 2 ###
|
265
|
+
# ... require rtype and define Example the same as above ...
|
266
|
+
|
267
|
+
class String
|
268
|
+
def func; end
|
269
|
+
end
|
270
|
+
|
271
|
+
Example.new.and_test("A string") # Works!
|
272
|
+
```
|
273
|
+
|
274
|
+
#### Combined duck type
|
275
|
+
Application of duck typing and combined type
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
require 'rtype'
|
279
|
+
|
280
|
+
module Game
|
281
|
+
ENEMY = [
|
282
|
+
:name,
|
283
|
+
:level
|
284
|
+
]
|
285
|
+
|
286
|
+
class Player < Entity
|
287
|
+
include Rtype::Behavior
|
288
|
+
|
289
|
+
rtype :attack, [And[*ENEMY]] => Any
|
290
|
+
def attacks(enemy)
|
291
|
+
"Player attacks '#{enemy.name}' (level #{enemy.level})!"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
class Slime < Entity
|
296
|
+
def name
|
297
|
+
"Powerful Slime"
|
298
|
+
end
|
299
|
+
|
300
|
+
def level
|
301
|
+
123
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
Game::Player.new.attacks Game::Slime.new
|
307
|
+
# Player attacks 'Powerful Slime' (level 123)!
|
308
|
+
```
|
309
|
+
|
310
|
+
#### Position of `rtype` && (symbol || string)
|
311
|
+
```ruby
|
312
|
+
require 'rtype'
|
313
|
+
|
314
|
+
class Example
|
315
|
+
# Works. Recommended
|
316
|
+
rtype :hello_world, [Integer, String] => String
|
317
|
+
def hello_world(i, str)
|
318
|
+
puts "Hello? #{i} #{st
|
319
|
+
end
|
320
|
+
|
321
|
+
# Works
|
322
|
+
def hello_world_two(i, str)
|
323
|
+
puts "Hello? #{i} #{str}"
|
324
|
+
end
|
325
|
+
rtype :hello_world_two, [Integer, String] => String
|
326
|
+
|
327
|
+
# Also works (String will be converted to Symbol)
|
328
|
+
rtype 'hello_world_three', [Integer, String] => String
|
329
|
+
def hello_world_three(i, str)
|
330
|
+
puts "Hello? #{i} #{str}"
|
331
|
+
end
|
332
|
+
end
|
333
|
+
```
|
334
|
+
|
335
|
+
#### Outside of module (root)
|
336
|
+
Yes, it works
|
337
|
+
|
338
|
+
```ruby
|
339
|
+
rtype :say, [String] => Any
|
340
|
+
def say(message)
|
341
|
+
puts message
|
342
|
+
end
|
343
|
+
|
344
|
+
say "Hello" # Hello
|
345
|
+
```
|
346
|
+
|
347
|
+
#### Static method
|
348
|
+
Use `rtype_self`
|
349
|
+
|
350
|
+
```ruby
|
351
|
+
require 'rtype'
|
352
|
+
|
353
|
+
class Example
|
354
|
+
rtype_self :say_ya, [:to_i] => Any
|
355
|
+
def self.say_ya(i)
|
356
|
+
puts "say" + " ya"*i.to_i
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
Example::say_ya(3) #say ya ya ya
|
361
|
+
```
|
362
|
+
|
363
|
+
#### Check type information
|
364
|
+
This is just the 'information'
|
365
|
+
|
366
|
+
Any change of this doesn't affect type checking
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
require 'rtype'
|
370
|
+
|
371
|
+
class Example
|
372
|
+
rtype :test, [:to_i] => Any
|
373
|
+
def test(i)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
Example.new.method(:test).type_info
|
378
|
+
# => [:to_i] => Any
|
379
|
+
Example.new.method(:test).argument_type
|
380
|
+
# => [:to_i]
|
381
|
+
Example.new.method(:test).return_type
|
382
|
+
# => Any
|
383
|
+
```
|
384
|
+
|
385
|
+
## Documentation
|
386
|
+
[RubyDoc.info](http://www.rubydoc.info/gems/rtype)
|
387
|
+
|
388
|
+
## Benchmarks
|
389
|
+
Result of `rake benchmark` ([source](https://github.com/sputnikgugja/rtype/tree/master/benchmark/benchmark.rb))
|
390
|
+
|
391
|
+
### MRI
|
392
|
+
```
|
393
|
+
Rtype with C native extension
|
394
|
+
Ruby version: 2.1.7
|
395
|
+
Ruby engine: ruby
|
396
|
+
Ruby description: ruby 2.1.7p400 (2015-08-18 revision 51632) [x64-mingw32]
|
397
|
+
Rtype version: 0.3.0
|
398
|
+
Rubype version: 0.3.1
|
399
|
+
Sig version: 1.0.1
|
400
|
+
Contracts version: 0.13.0
|
401
|
+
Typecheck version: 0.1.2
|
402
|
+
Warming up --------------------------------------
|
403
|
+
pure 85.328k i/100ms
|
404
|
+
rtype 25.665k i/100ms
|
405
|
+
rubype 21.414k i/100ms
|
406
|
+
sig 8.921k i/100ms
|
407
|
+
contracts 4.638k i/100ms
|
408
|
+
typecheck 1.110k i/100ms
|
409
|
+
Calculating -------------------------------------
|
410
|
+
pure 3.282M (± 2.7%) i/s - 16.468M
|
411
|
+
rtype 339.065k (± 2.6%) i/s - 1.720M
|
412
|
+
rubype 266.893k (± 5.9%) i/s - 1.349M
|
413
|
+
sig 99.952k (± 2.1%) i/s - 499.576k
|
414
|
+
contracts 49.693k (± 1.5%) i/s - 250.452k
|
415
|
+
typecheck 11.356k (± 1.6%) i/s - 57.720k
|
416
|
+
|
417
|
+
Comparison:
|
418
|
+
pure: 3282431.9 i/s
|
419
|
+
rtype: 339064.9 i/s - 9.68x slower
|
420
|
+
rubype: 266892.9 i/s - 12.30x slower
|
421
|
+
sig: 99952.2 i/s - 32.84x slower
|
422
|
+
contracts: 49693.0 i/s - 66.05x slower
|
423
|
+
typecheck: 11355.9 i/s - 289.05x slower
|
424
|
+
```
|
425
|
+
|
426
|
+
### JRuby
|
427
|
+
Without Rubype that doesn't support JRuby
|
428
|
+
|
429
|
+
```
|
430
|
+
Rtype with Java extension
|
431
|
+
Ruby version: 2.2.3
|
432
|
+
Ruby engine: jruby
|
433
|
+
Ruby description: jruby 9.0.5.0 (2.2.3) 2016-01-26 7bee00d Java HotSpot(TM) 64-Bit Server VM 25.60-b23 on 1.8.0_60-b27 +jit [Windows 10-amd64]
|
434
|
+
Rtype version: 0.3.0
|
435
|
+
Sig version: 1.0.1
|
436
|
+
Contracts version: 0.13.0
|
437
|
+
Typecheck version: 0.1.2
|
438
|
+
Warming up --------------------------------------
|
439
|
+
pure 9.994k i/100ms
|
440
|
+
rtype 6.181k i/100ms
|
441
|
+
sig 4.041k i/100ms
|
442
|
+
contracts 951.000 i/100ms
|
443
|
+
typecheck 970.000 i/100ms
|
444
|
+
Calculating -------------------------------------
|
445
|
+
pure 7.128M (?±35.6%) i/s - 30.831M
|
446
|
+
rtype 121.556k (?± 6.2%) i/s - 605.738k
|
447
|
+
sig 72.187k (?± 6.4%) i/s - 359.649k
|
448
|
+
contracts 24.984k (?± 3.9%) i/s - 125.532k
|
449
|
+
typecheck 12.041k (?± 9.5%) i/s - 60.140k
|
450
|
+
|
451
|
+
Comparison:
|
452
|
+
pure: 7128373.0 i/s
|
453
|
+
rtype: 121555.8 i/s - 58.64x slower
|
454
|
+
sig: 72186.8 i/s - 98.75x slower
|
455
|
+
contracts: 24984.5 i/s - 285.31x slower
|
456
|
+
typecheck: 12041.0 i/s - 592.01x slower
|
457
|
+
```
|
458
|
+
|
459
|
+
## Rubype, Sig
|
460
|
+
Rtype is influenced by [Rubype](https://github.com/gogotanaka/Rubype) and [Sig](https://github.com/janlelis/sig).
|
461
|
+
|
462
|
+
If you don't like Rtype, You can use other type checking gem such as Contracts, Rubype, Rtc, Typecheck, Sig.
|
463
|
+
|
464
|
+
## Author
|
465
|
+
Sputnik Gugja (sputnikgugja@gmail.com)
|
466
|
+
|
467
|
+
## License
|
468
|
+
MIT license (@ Sputnik Gugja)
|
469
|
+
|
470
|
+
See `LICENSE` file.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require "rspec/core/rake_task"
|
2
|
+
|
3
|
+
# Default pattern is 'spec/**{,/*/**}/*_spec.rb'
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
# Benchmark
|
9
|
+
desc "Compare with pure ruby and other gems"
|
10
|
+
task :benchmark do
|
11
|
+
ruby "benchmark/benchmark.rb"
|
12
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
require 'benchmark/ips'
|
2
|
+
|
3
|
+
is_mri = RUBY_ENGINE == 'ruby'
|
4
|
+
|
5
|
+
require "rtype"
|
6
|
+
require "rubype" if is_mri
|
7
|
+
require "sig"
|
8
|
+
require "contracts"
|
9
|
+
require "contracts/version"
|
10
|
+
require "typecheck"
|
11
|
+
|
12
|
+
puts "Ruby version: #{RUBY_VERSION}"
|
13
|
+
puts "Ruby engine: #{RUBY_ENGINE}"
|
14
|
+
puts "Ruby description: #{RUBY_DESCRIPTION}"
|
15
|
+
|
16
|
+
puts "Rtype version: #{Rtype::VERSION}"
|
17
|
+
puts "Rubype version: #{Rubype::VERSION}" if is_mri
|
18
|
+
puts "Sig version: #{Sig::VERSION}"
|
19
|
+
puts "Contracts version: #{Contracts::VERSION}"
|
20
|
+
puts "Typecheck version: #{Typecheck::VERSION}"
|
21
|
+
|
22
|
+
class PureTest
|
23
|
+
def sum(x, y)
|
24
|
+
x + y
|
25
|
+
end
|
26
|
+
|
27
|
+
def mul(x, y)
|
28
|
+
x * y
|
29
|
+
end
|
30
|
+
|
31
|
+
def args(a, b, c, d)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
pure_obj = PureTest.new
|
35
|
+
|
36
|
+
class RtypeTest
|
37
|
+
rtype :sum, [Numeric, Numeric] => Numeric
|
38
|
+
def sum(x, y)
|
39
|
+
x + y
|
40
|
+
end
|
41
|
+
|
42
|
+
rtype :mul, [:to_i, :to_i] => Numeric
|
43
|
+
def mul(x, y)
|
44
|
+
x * y
|
45
|
+
end
|
46
|
+
|
47
|
+
rtype :args, [Integer, Numeric, String, :to_i] => Any
|
48
|
+
def args(a, b, c, d)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
rtype_obj = RtypeTest.new
|
52
|
+
|
53
|
+
if is_mri
|
54
|
+
class RubypeTest
|
55
|
+
def sum(x, y)
|
56
|
+
x + y
|
57
|
+
end
|
58
|
+
typesig :sum, [Numeric, Numeric] => Numeric
|
59
|
+
|
60
|
+
def mul(x, y)
|
61
|
+
x * y
|
62
|
+
end
|
63
|
+
typesig :mul, [:to_i, :to_i] => Numeric
|
64
|
+
|
65
|
+
def args(a, b, c, d)
|
66
|
+
end
|
67
|
+
typesig :args, [Integer, Numeric, String, :to_i] => Any
|
68
|
+
end
|
69
|
+
rubype_obj = RubypeTest.new
|
70
|
+
end
|
71
|
+
|
72
|
+
class SigTest
|
73
|
+
sig [Numeric, Numeric], Numeric,
|
74
|
+
def sum(x, y)
|
75
|
+
x + y
|
76
|
+
end
|
77
|
+
|
78
|
+
sig [:to_i, :to_i], Numeric,
|
79
|
+
def mul(x, y)
|
80
|
+
x * y
|
81
|
+
end
|
82
|
+
|
83
|
+
# nil means wildcard
|
84
|
+
sig [Integer, Numeric, String, :to_i], nil,
|
85
|
+
def args(a, b, c, d)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
sig_obj = SigTest.new
|
89
|
+
|
90
|
+
class ContractsTest
|
91
|
+
include Contracts
|
92
|
+
|
93
|
+
Contract Num, Num => Num
|
94
|
+
def sum(x, y)
|
95
|
+
x + y
|
96
|
+
end
|
97
|
+
|
98
|
+
Contract RespondTo[:to_i], RespondTo[:to_i] => Num
|
99
|
+
def mul(x, y)
|
100
|
+
x * y
|
101
|
+
end
|
102
|
+
|
103
|
+
Contract Int, Num, String, RespondTo[:to_i] => Any
|
104
|
+
def args(a, b, c, d)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
contracts_obj = ContractsTest.new
|
108
|
+
|
109
|
+
class TypecheckTest
|
110
|
+
extend Typecheck
|
111
|
+
|
112
|
+
typecheck 'Numeric, Numeric -> Numeric',
|
113
|
+
def sum(x, y)
|
114
|
+
x + y
|
115
|
+
end
|
116
|
+
|
117
|
+
typecheck '#to_i, #to_i -> Numeric',
|
118
|
+
def mul(x, y)
|
119
|
+
x * y
|
120
|
+
end
|
121
|
+
|
122
|
+
typecheck 'Integer, Numeric, String, #to_i -> BasicObject',
|
123
|
+
def args(a, b, c, d)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
typecheck_obj = TypecheckTest.new
|
127
|
+
|
128
|
+
Benchmark.ips do |x|
|
129
|
+
x.report("pure") do |times|
|
130
|
+
i = 0
|
131
|
+
while i < times
|
132
|
+
pure_obj.sum(1, 2)
|
133
|
+
pure_obj.mul(1, 2)
|
134
|
+
pure_obj.args(1, 2, "c", 4)
|
135
|
+
i += 1
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
x.report("rtype") do |times|
|
140
|
+
i = 0
|
141
|
+
while i < times
|
142
|
+
rtype_obj.sum(1, 2)
|
143
|
+
rtype_obj.mul(1, 2)
|
144
|
+
rtype_obj.args(1, 2, "c", 4)
|
145
|
+
i += 1
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
if is_mri
|
150
|
+
x.report("rubype") do |times|
|
151
|
+
i = 0
|
152
|
+
while i < times
|
153
|
+
rubype_obj.sum(1, 2)
|
154
|
+
rubype_obj.mul(1, 2)
|
155
|
+
rubype_obj.args(1, 2, "c", 4)
|
156
|
+
i += 1
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
x.report("sig") do |times|
|
162
|
+
i = 0
|
163
|
+
while i < times
|
164
|
+
sig_obj.sum(1, 2)
|
165
|
+
sig_obj.mul(1, 2)
|
166
|
+
sig_obj.args(1, 2, "c", 4)
|
167
|
+
i += 1
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
x.report("contracts") do |times|
|
172
|
+
i = 0
|
173
|
+
while i < times
|
174
|
+
contracts_obj.sum(1, 2)
|
175
|
+
contracts_obj.mul(1, 2)
|
176
|
+
contracts_obj.args(1, 2, "c", 4)
|
177
|
+
i += 1
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
x.report("typecheck") do |times|
|
182
|
+
i = 0
|
183
|
+
while i < times
|
184
|
+
typecheck_obj.sum(1, 2)
|
185
|
+
typecheck_obj.mul(1, 2)
|
186
|
+
typecheck_obj.args(1, 2, "c", 4)
|
187
|
+
i += 1
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
x.compare!
|
192
|
+
end
|
Binary file
|
data/spec/rtype_spec.rb
ADDED
@@ -0,0 +1,547 @@
|
|
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(true)
|
240
|
+
instance.return_arg(123)
|
241
|
+
end
|
242
|
+
it "is wrong args" do
|
243
|
+
klass.send :rtype, :return_arg, [true] => Any
|
244
|
+
expect {instance.return_arg(false)}.to raise_error Rtype::ArgumentTypeError
|
245
|
+
expect {instance.return_arg(nil)}.to raise_error Rtype::ArgumentTypeError
|
246
|
+
end
|
247
|
+
it "is wrong result" do
|
248
|
+
klass.send :rtype, :return_arg, [Any] => true
|
249
|
+
expect {instance.return_arg(false)}.to raise_error Rtype::ReturnTypeError
|
250
|
+
expect {instance.return_arg(nil)}.to raise_error Rtype::ReturnTypeError
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
describe 'false' do
|
255
|
+
it "is right" do
|
256
|
+
klass.send :rtype, :return_arg, [false] => Any
|
257
|
+
instance.return_arg(false)
|
258
|
+
instance.return_arg(nil)
|
259
|
+
end
|
260
|
+
it "is wrong args" do
|
261
|
+
klass.send :rtype, :return_arg, [false] => Any
|
262
|
+
expect {instance.return_arg(true)}.to raise_error Rtype::ArgumentTypeError
|
263
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ArgumentTypeError
|
264
|
+
end
|
265
|
+
it "is wrong result" do
|
266
|
+
klass.send :rtype, :return_arg, [Any] => false
|
267
|
+
expect {instance.return_arg(true)}.to raise_error Rtype::ReturnTypeError
|
268
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ReturnTypeError
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
describe 'nil' do
|
273
|
+
it "is only for return" do
|
274
|
+
klass.send :rtype, :return_nil, [] => nil
|
275
|
+
instance.return_nil(123)
|
276
|
+
|
277
|
+
klass.send :rtype, :return_arg, [] => nil
|
278
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ReturnTypeError
|
279
|
+
end
|
280
|
+
it "could not be used for args" do
|
281
|
+
expect {
|
282
|
+
klass.send :rtype, :return_arg, [nil] => Any
|
283
|
+
}.to raise_error Rtype::TypeSignatureError
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
describe 'Special type behaviors' do
|
288
|
+
it 'Rtype::Behavior::And' do
|
289
|
+
klass.send :rtype, :return_nil, [Rtype.and(:to_i, :chars)] => nil
|
290
|
+
instance.return_nil("Hello")
|
291
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ArgumentTypeError
|
292
|
+
end
|
293
|
+
|
294
|
+
it 'Rtype::Behavior::Or' do
|
295
|
+
klass.send :rtype, :return_nil, [Rtype.or(Integer, String)] => nil
|
296
|
+
instance.return_nil(123)
|
297
|
+
instance.return_nil("abc")
|
298
|
+
expect {instance.return_nil(nil)}.to raise_error Rtype::ArgumentTypeError
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'Rtype::Behavior::Nilable' do
|
302
|
+
klass.send :rtype, :return_nil, [Rtype.nilable(Integer)] => nil
|
303
|
+
instance.return_nil(nil)
|
304
|
+
instance.return_nil(123)
|
305
|
+
expect {instance.return_nil("abc")}.to raise_error Rtype::ArgumentTypeError
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'Rtype::Behavior::Not' do
|
309
|
+
klass.send :rtype, :return_nil, [Rtype.not(String)] => nil
|
310
|
+
instance.return_nil(123)
|
311
|
+
expect {instance.return_nil("abc")}.to raise_error Rtype::ArgumentTypeError
|
312
|
+
end
|
313
|
+
|
314
|
+
it 'Rtype::Behavior::Xor' do
|
315
|
+
klass.send :rtype, :return_nil, [Rtype.xor(:to_i, String)] => nil
|
316
|
+
instance.return_nil(123)
|
317
|
+
expect {instance.return_nil("abc")}.to raise_error Rtype::ArgumentTypeError
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe 'Signature' do
|
323
|
+
describe 'check arguments' do
|
324
|
+
it 'nothing' do
|
325
|
+
klass.send :rtype, :sum, [] => Any
|
326
|
+
instance.sum(1, 2)
|
327
|
+
instance.sum(1, 2.0)
|
328
|
+
instance.sum(1.0, 2.0)
|
329
|
+
instance.sum("a", "b")
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'two' do
|
333
|
+
klass.send :rtype, :sum, [Integer, Integer] => Any
|
334
|
+
expect {instance.sum(1, 2.0)}.to raise_error Rtype::ArgumentTypeError
|
335
|
+
end
|
336
|
+
|
337
|
+
it 'one keyword argument' do
|
338
|
+
klass.send :rtype, :kwarg, {a: Float} => Any
|
339
|
+
expect {instance.kwarg(a: 1)}.to raise_error Rtype::ArgumentTypeError
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'two keyword argument' do
|
343
|
+
klass.send :rtype, :sum_kwargs, {a: Integer, b: Float} => Any
|
344
|
+
expect {instance.sum_kwargs(a: 1, b: 2)}.to raise_error Rtype::ArgumentTypeError
|
345
|
+
end
|
346
|
+
|
347
|
+
it 'one with one keyword argument' do
|
348
|
+
klass.send :rtype, :arg_and_kwarg, [Integer, {b: Float}] => Any
|
349
|
+
expect {instance.arg_and_kwarg(1, b: 2)}.to raise_error Rtype::ArgumentTypeError
|
350
|
+
end
|
351
|
+
|
352
|
+
it 'one with two keyword argument' do
|
353
|
+
klass.send :rtype, :arg_and_kwargs, [Integer, {c: String, d: String}] => Any
|
354
|
+
expect {instance.arg_and_kwargs(1, b: 2, c: 3)}.to raise_error Rtype::ArgumentTypeError
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'two with two keyword argument' do
|
358
|
+
klass.send :rtype, :args_and_kwargs, [Integer, Integer, {c: String, d: String}] => Any
|
359
|
+
expect {instance.args_and_kwargs(1, 2, c: 3, d: 4)}.to raise_error Rtype::ArgumentTypeError
|
360
|
+
end
|
361
|
+
|
362
|
+
it 'string key could not be used for keyword argument' do
|
363
|
+
expect {
|
364
|
+
klass.send :rtype, :kwarg, {'a' => Float} => Any
|
365
|
+
}.to raise_error Rtype::TypeSignatureError
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'only symbol key is used for keyword argument' do
|
369
|
+
klass.send :rtype, :kwarg, {:a => Float} => Any
|
370
|
+
expect {instance.kwarg(a: 1)}.to raise_error Rtype::ArgumentTypeError
|
371
|
+
expect {instance.kwarg(:a => 1)}.to raise_error Rtype::ArgumentTypeError
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
describe 'check return' do
|
376
|
+
it 'Any' do
|
377
|
+
klass.send :rtype, :return_arg, [] => Any
|
378
|
+
instance.return_arg("str")
|
379
|
+
end
|
380
|
+
|
381
|
+
it 'Array (tuple)' do
|
382
|
+
klass.send :rtype, :return_arg, [] => [Integer, Float]
|
383
|
+
expect {instance.return_arg([1, 2])}.to raise_error Rtype::ReturnTypeError
|
384
|
+
end
|
385
|
+
end
|
386
|
+
|
387
|
+
it 'check arguments and return value' do
|
388
|
+
klass.send :rtype, :return_nil, [Float] => nil
|
389
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ArgumentTypeError
|
390
|
+
klass.send :rtype, :return_nil, [Integer] => Integer
|
391
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ReturnTypeError
|
392
|
+
end
|
393
|
+
|
394
|
+
describe 'wrong case' do
|
395
|
+
describe 'invalid signature form' do
|
396
|
+
it 'invalid argument signature' do
|
397
|
+
expect {
|
398
|
+
klass.send :rtype, :return_arg, Any => nil
|
399
|
+
}.to raise_error Rtype::TypeSignatureError
|
400
|
+
end
|
401
|
+
it 'invalid return signature' do
|
402
|
+
expect {
|
403
|
+
klass.send :rtype, :return_arg, [] => {}
|
404
|
+
}.to raise_error Rtype::TypeSignatureError
|
405
|
+
end
|
406
|
+
|
407
|
+
it 'invalid type behavior in arguments' do
|
408
|
+
expect {
|
409
|
+
klass.send :rtype, :sum_kwargs, [{a: Integer}, {b: Integer}] => Any
|
410
|
+
}.to raise_error Rtype::TypeSignatureError
|
411
|
+
expect {
|
412
|
+
klass.send :rtype, :return_arg, [123] => Any
|
413
|
+
}.to raise_error Rtype::TypeSignatureError
|
414
|
+
expect {
|
415
|
+
klass.send :rtype, :return_arg, ["abc"] => Any
|
416
|
+
}.to raise_error Rtype::TypeSignatureError
|
417
|
+
expect {
|
418
|
+
klass.send :rtype, :kwarg, {a: 123} => Any
|
419
|
+
}.to raise_error Rtype::TypeSignatureError
|
420
|
+
expect {
|
421
|
+
klass.send :rtype, :kwarg, {a: {b: Integer}} => Any
|
422
|
+
}.to raise_error Rtype::TypeSignatureError
|
423
|
+
expect {
|
424
|
+
klass.send :rtype, :kwarg, {Object.new => Integer} => Any
|
425
|
+
}.to raise_error Rtype::TypeSignatureError
|
426
|
+
end
|
427
|
+
|
428
|
+
it 'invalid type behavior in return' do
|
429
|
+
expect {
|
430
|
+
klass.send :rtype, :return_arg, [] => 123
|
431
|
+
}.to raise_error Rtype::TypeSignatureError
|
432
|
+
expect {
|
433
|
+
klass.send :rtype, :return_arg, [] => "abc"
|
434
|
+
}.to raise_error Rtype::TypeSignatureError
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
describe "Implementation" do
|
441
|
+
it 'can be called before method definition' do
|
442
|
+
class TestClass
|
443
|
+
rtype :method_def, [Integer] => Any
|
444
|
+
def method_def(i)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
expect {
|
448
|
+
TestClass.new.method_def("abc")
|
449
|
+
}.to raise_error Rtype::ArgumentTypeError
|
450
|
+
end
|
451
|
+
|
452
|
+
it 'can be called after method definition' do
|
453
|
+
class TestClass
|
454
|
+
def method_def_2(i)
|
455
|
+
end
|
456
|
+
rtype :method_def_2, [Integer] => Any
|
457
|
+
end
|
458
|
+
expect {
|
459
|
+
TestClass.new.method_def_2("abc")
|
460
|
+
}.to raise_error Rtype::ArgumentTypeError
|
461
|
+
end
|
462
|
+
|
463
|
+
it 'method name can be both symbol and string' do
|
464
|
+
class TestClass
|
465
|
+
rtype 'method_def_3', [Integer] => Any
|
466
|
+
def method_def_3(i)
|
467
|
+
end
|
468
|
+
rtype :method_def_4, [Integer] => Any
|
469
|
+
def method_def_4(i)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
expect {
|
473
|
+
TestClass.new.method_def_3("abc")
|
474
|
+
}.to raise_error Rtype::ArgumentTypeError
|
475
|
+
expect {
|
476
|
+
TestClass.new.method_def_4("abc")
|
477
|
+
}.to raise_error Rtype::ArgumentTypeError
|
478
|
+
end
|
479
|
+
|
480
|
+
describe 'method visibility works' do
|
481
|
+
it 'protected' do
|
482
|
+
expect {instance.protected_func}.to raise_error NoMethodError
|
483
|
+
end
|
484
|
+
it 'private' do
|
485
|
+
expect {instance.private_func}.to raise_error NoMethodError
|
486
|
+
end
|
487
|
+
end
|
488
|
+
|
489
|
+
context 'with empty argument signature' do
|
490
|
+
it 'accept any arguments' do
|
491
|
+
klass.send :rtype, :three_args, [] => Any
|
492
|
+
instance.three_args("abc", 123, 456)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
context 'when args length is more than arg signature length' do
|
497
|
+
it 'type checking ignore rest args' do
|
498
|
+
klass.send :rtype, :three_args, [String] => Any
|
499
|
+
instance.three_args("abc", 123, 456)
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
context 'when keyword args contain a key not configured to rtype' do
|
504
|
+
it 'type checking ignore the key' do
|
505
|
+
klass.send :rtype, :three_kwargs, {a: String} => Any
|
506
|
+
instance.three_kwargs(a: "abc", b: 123, c: 456)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
end
|
510
|
+
|
511
|
+
describe "Call Rtype`s static method directly" do
|
512
|
+
it 'Rtype::define_typed_method' do
|
513
|
+
Rtype::define_typed_method klass, :return_arg, [String] => Any
|
514
|
+
expect {instance.return_arg(123)}.to raise_error Rtype::ArgumentTypeError
|
515
|
+
end
|
516
|
+
|
517
|
+
it 'Rtype::define_typed_accessor' do
|
518
|
+
Rtype::define_typed_accessor klass, :value, String
|
519
|
+
expect { instance.value = 123 }.to raise_error Rtype::ArgumentTypeError
|
520
|
+
expect { instance.value }.to raise_error Rtype::ReturnTypeError
|
521
|
+
end
|
522
|
+
|
523
|
+
it 'Rtype::valid?' do
|
524
|
+
expect {
|
525
|
+
Rtype::valid?("Invalid type behavior", "Test Value")
|
526
|
+
}.to raise_error Rtype::TypeSignatureError
|
527
|
+
end
|
528
|
+
|
529
|
+
it 'Rtype::assert_arguments_type' do
|
530
|
+
expect {
|
531
|
+
Rtype::assert_arguments_type([Integer, String], [123, 123])
|
532
|
+
}.to raise_error Rtype::ArgumentTypeError
|
533
|
+
end
|
534
|
+
|
535
|
+
it 'Rtype::assert_arguments_type_with_keywords' do
|
536
|
+
expect {
|
537
|
+
Rtype::assert_arguments_type_with_keywords([Integer, String], [123, "abc"], {arg: String}, {arg: 123})
|
538
|
+
}.to raise_error Rtype::ArgumentTypeError
|
539
|
+
end
|
540
|
+
|
541
|
+
it 'Rtype::assert_return_type' do
|
542
|
+
expect {
|
543
|
+
Rtype::assert_return_type nil, "No nil"
|
544
|
+
}.to raise_error Rtype::ReturnTypeError
|
545
|
+
end
|
546
|
+
end
|
547
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rtype-java
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sputnik Gugja
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-03-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rtype
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.3.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.3.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: coveralls
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Java extension for Rtype
|
70
|
+
email:
|
71
|
+
- sputnikgugja@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- Gemfile
|
77
|
+
- LICENSE
|
78
|
+
- README.md
|
79
|
+
- Rakefile
|
80
|
+
- benchmark/benchmark.rb
|
81
|
+
- ext/rtype/rtype_java.jar
|
82
|
+
- spec/rtype_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
homepage: https://github.com/sputnikgugja/rtype
|
85
|
+
licenses:
|
86
|
+
- MIT
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- ext
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.1'
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.4.8
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Java extension for Rtype
|
108
|
+
test_files:
|
109
|
+
- spec/rtype_spec.rb
|
110
|
+
- spec/spec_helper.rb
|