rtype-native 0.5.0 → 0.5.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/README.md CHANGED
@@ -1,568 +1,568 @@
1
- # Rtype: ruby with type
2
- [![Gem Version](https://badge.fury.io/rb/rtype.svg)](https://badge.fury.io/rb/rtype)
3
- [![Build Status](https://travis-ci.org/sputnikgugja/rtype.svg?branch=master)](https://travis-ci.org/sputnikgugja/rtype)
4
- [![Coverage Status](https://coveralls.io/repos/github/sputnikgugja/rtype/badge.svg?branch=master)](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
- class Test
12
- rtype [:to_i, Numeric] => Numeric
13
- def sum(a, b)
14
- a.to_i + b
15
- end
16
-
17
- rtype {state: Boolean} => Boolean
18
- def self.invert(state:)
19
- !state
20
- end
21
- end
22
-
23
- Test.new.sum(123, "asd")
24
- # (Rtype::ArgumentTypeError) for 2nd argument:
25
- # Expected "asd" to be a Numeric
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
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 arguments and return
41
- - Support type checking for [keyword argument](#keyword-argument)
42
- - [Type checking for array elements](#array)
43
- - [Type checking for hash elements](#hash)
44
- - [Duck typing](#duck-typing)
45
- - Custom type behavior
46
-
47
- ## Installation
48
- Run `gem install rtype` or add `gem 'rtype'` to your `Gemfile`
49
-
50
- And add to your `.rb` source file:
51
- ```ruby
52
- require 'rtype'
53
- ```
54
-
55
- ### Native extension
56
- Rtype itself is pure-ruby gem. but you can make it more faster by using native extension.
57
-
58
- #### Native extension for MRI
59
- Just run
60
- ```ruby
61
- gem install rtype-native
62
- ```
63
- or add to your `Gemfile`:
64
- ```ruby
65
- gem 'rtype-native'
66
- ```
67
- then, Rtype use it. (Do not `require 'rtype-native'`)
68
-
69
- #### Java extension for JRuby
70
- Just run
71
- ```ruby
72
- gem install rtype-java
73
- ```
74
- or add to your `Gemfile`:
75
- ```ruby
76
- gem 'rtype-java'
77
- ```
78
- then, Rtype use it. (Do not `require 'rtype-java'`)
79
-
80
- ## Usage
81
-
82
- ### Supported Type Behaviors
83
- - `Module`
84
- - Value must be an instance of this module/class or one of its superclasses
85
- - `Any` : An alias for `BasicObject` (means Any Object)
86
- - `Boolean` : `true` or `false`
87
- - `Symbol`
88
- - Value must have(respond to) a method with this name
89
- - `Regexp`
90
- - Value must match this regexp pattern
91
- - `Range`
92
- - Value must be included in this range
93
- - `Array` (tuple)
94
- - Value must be an array
95
- - Each of value's elements must be valid
96
- - Value's length must be equal to the array's length
97
- - Of course, nested array works
98
- - Example: [Array](#array)
99
- - This can be used as a tuple
100
- - `Hash`
101
- - Value must be an hash
102
- - Each of value’s elements must be valid
103
- - Value's key list must be equal to the hash's key list
104
- - **String** key is **different** from **symbol** key
105
- - vs Keyword arguments
106
- - `[{}]` is **not** hash type argument. it is keyword argument because its position is last
107
- - `[{}, {}]` is empty hash type argument (first) and one empty keyword argument (second)
108
- - `[{}, {}, {}]` is two empty hash type argument (first, second) and empty keyword argument (last)
109
- - `{}` is keyword argument. non-keyword arguments must be in array.
110
- - Of course, nested hash works
111
- - Example: [Hash](#hash)
112
- - `Proc`
113
- - Value must return a truthy value for this proc
114
- - `true`
115
- - Value must be **truthy**
116
- - `false`
117
- - Value must be **falsy**
118
- - `nil`
119
- - Only available for **return type**. void return type in other languages
120
- - Special Behaviors
121
- - `Rtype::and(*types)` : Ensure value is valid for all the types
122
- - `Rtype::and(*types)`
123
- - `Rtype::Behavior::And[*types]`
124
- - `include Rtype::Behavior; And[...]`
125
- - `obj.and(*others)` (core extension)
126
-
127
- - `Rtype::or(*types)` : Ensure value is valid for at least one of the types
128
- - `Rtype::or(*types)`
129
- - `Rtype::Behavior::Or[*types]`
130
- - `include Rtype::Behavior; Or[...]`
131
- - `obj.or(*others)` (core extension)
132
-
133
- - `Rtype::xor(*types)` : Ensure value is valid for only one of the types
134
- - `Rtype::xor(*types)`
135
- - `Rtype::Behavior::Xor[*types]`
136
- - `include Rtype::Behavior; Xor[...]`
137
- - `obj.xor(*others)` (core extension)
138
-
139
- - `Rtype::not(*types)` : Ensure value is not valid for all the types
140
- - `Rtype::not(*types)`
141
- - `Rtype::Behavior::Not[*types]`
142
- - `include Rtype::Behavior; Not[...]`
143
- - `obj.not` (core extension)
144
-
145
- - `Rtype::nilable(type)` : Ensure value can be nil
146
- - `Rtype::nilable(type)`
147
- - `Rtype::Behavior::Nilable[type]`
148
- - `include Rtype::Behavior; Nilable[...]`
149
- - `obj.nilable` (core extension)
150
- - `obj.or_nil` (core extension)
151
-
152
- - You can create custom behavior by extending `Rtype::Behavior::Base`
153
-
154
- ### Examples
155
-
156
- #### Basic
157
- ```ruby
158
- require 'rtype'
159
-
160
- class Example
161
- rtype [Integer] => nil
162
- def test(i)
163
- end
164
-
165
- rtype [Any] => nil
166
- def any_type_arg(arg)
167
- end
168
-
169
- rtype [] => Integer
170
- def return_type_test
171
- "not integer"
172
- end
173
- end
174
-
175
- e = Example.new
176
- e.test("not integer")
177
- # (Rtype::ArgumentTypeError) for 1st argument:
178
- # Expected "not integer" to be a Integer
179
-
180
- e.any_type_arg("Any argument!") # Works
181
-
182
- e.return_type_test
183
- # (Rtype::ReturnTypeError) for return:
184
- # Expected "not integer" to be a Integer
185
- ```
186
-
187
- #### Keyword argument
188
- ```ruby
189
- require 'rtype'
190
-
191
- class Example
192
- rtype {name: String} => Any
193
- def say_your_name(name:)
194
- puts "My name is #{name}"
195
- end
196
-
197
- # Mixing positional arguments and keyword arguments
198
- rtype [String, {age: Integer}] => Any
199
- def name_and_age(name, age:)
200
- puts "Name: #{name}, Age: #{age}"
201
- end
202
- end
203
-
204
- Example.new.say_your_name(name: "Babo") # My name is Babo
205
- Example.new.name_and_age("Bamboo", age: 100) # Name: Bamboo, Age: 100
206
-
207
- Example.new.say_your_name(name: 12345)
208
- # (Rtype::ArgumentTypeError) for 'name' argument:
209
- # Expected 12345 to be a String
210
- ```
211
-
212
- #### Duck typing
213
- ```ruby
214
- require 'rtype'
215
-
216
- class Duck
217
- rtype [:to_i] => Any
218
- def says(i)
219
- puts "duck:" + " quack"*i.to_i
220
- end
221
- end
222
-
223
- Duck.new.says("2") # duck: quack quack
224
- ```
225
-
226
- #### Array
227
- This can be used as a tuple.
228
-
229
- ```ruby
230
- rtype :func, [[Numeric, Numeric]] => Any
231
- def func(arr)
232
- puts "Your location is (#{arr[0]}, #{arr[1]}). I will look for you. I will find you"
233
- end
234
-
235
- func [1, "str"]
236
- # (Rtype::ArgumentTypeError) for 1st argument:
237
- # Expected [1, "str"] to be an array with 2 elements:
238
- # - [0] index : Expected 1 to be a Numeric
239
- # - [1] index : Expected "str" to be a Numeric
240
-
241
- func [1, 2, 3]
242
- # (Rtype::ArgumentTypeError) for 1st argument:
243
- # Expected [1, 2, 3] to be an array with 2 elements:
244
- # - [0] index : Expected 1 to be a Numeric
245
- # - [1] index : Expected 2 to be a Numeric
246
-
247
- func [1]
248
- # (Rtype::ArgumentTypeError) for 1st argument:
249
- # Expected [1] to be an array with 2 elements:
250
- # - [0] index : Expected 1 to be a Numeric
251
- # - [1] index : Expected nil to be a Numeric
252
-
253
- func [1, 2] # Your location is (1, 2). I will look for you. I will find you
254
- ```
255
-
256
- #### Hash
257
- ```ruby
258
- # last hash element is keyword arguments
259
- rtype :func, [{msg: String}, {}] => Any
260
- def func(hash)
261
- puts hash[:msg]
262
- end
263
-
264
- # last hash is keyword arguments
265
- func({}, {})
266
- # (Rtype::ArgumentTypeError) for 1st argument:
267
- # Expected {} to be an hash with 1 elements:
268
- # - msg : Expected nil to be a String
269
-
270
- func({msg: 123}, {})
271
- # (Rtype::ArgumentTypeError) for 1st argument:
272
- # Expected {:msg=>123} to be an hash with 1 elements:
273
- # - msg : Expected 123 to be a String
274
-
275
- func({msg: "hello", key: 'value'}, {})
276
- # (Rtype::ArgumentTypeError) for 1st argument:
277
- # Expected {:msg=>"hello", :key=>"value"} to be an hash with 1 elements:
278
- # - msg : Expected "hello" to be a String
279
-
280
- func({"msg" => "hello hash"}, {})
281
- # (Rtype::ArgumentTypeError) for 1st argument:
282
- # Expected {"msg"=>"hello hash"} to be an hash with 1 elements:
283
- # - msg : Expected nil to be a String
284
-
285
- func({msg: "hello hash"}, {}) # hello hash
286
- ```
287
-
288
- #### rtype with attr_accessor
289
- `rtype_accessor` : call attr_accessor and make it typed method
290
-
291
- You can use `rtype_accessor_self` for static accessor.
292
-
293
- ```ruby
294
- require 'rtype'
295
-
296
- class Example
297
- rtype_accessor :value, String
298
-
299
- def initialize
300
- @value = 456
301
- end
302
- end
303
-
304
- Example.new.value = 123
305
- # (Rtype::ArgumentTypeError) for 1st argument:
306
- # Expected 123 to be a String
307
-
308
- Example.new.value
309
- # (Rtype::ReturnTypeError) for return:
310
- # Expected 456 to be a String
311
- ```
312
-
313
- #### Combined type
314
- ```ruby
315
- ### TEST 1 ###
316
- require 'rtype'
317
-
318
- class Example
319
- rtype [String.and(:func)] => Any
320
- # also works:
321
- # rtype [Rtype::and(String, :func)] => Any
322
- def and_test(arg)
323
- end
324
- end
325
-
326
- Example.new.and_test("A string")
327
- # (Rtype::ArgumentTypeError) for 1st argument:
328
- # Expected "A string" to be a String
329
- # AND Expected "A string" to respond to :func
330
- ```
331
- ```ruby
332
- ### TEST 2 ###
333
- # ... require rtype and define Example the same as above ...
334
-
335
- class String
336
- def func; end
337
- end
338
-
339
- Example.new.and_test("A string") # Works!
340
- ```
341
-
342
- #### Combined duck type
343
- Application of duck typing and combined type
344
-
345
- ```ruby
346
- require 'rtype'
347
-
348
- module Game
349
- ENEMY = [
350
- :name,
351
- :level
352
- ]
353
-
354
- class Player < Entity
355
- include Rtype::Behavior
356
-
357
- rtype [And[*ENEMY]] => Any
358
- def attacks(enemy)
359
- "Player attacks '#{enemy.name}' (level #{enemy.level})!"
360
- end
361
- end
362
-
363
- class Slime < Entity
364
- def name
365
- "Powerful Slime"
366
- end
367
-
368
- def level
369
- 123
370
- end
371
- end
372
- end
373
-
374
- Game::Player.new.attacks Game::Slime.new
375
- # Player attacks 'Powerful Slime' (level 123)!
376
- ```
377
-
378
- #### Position of `rtype` && (Specify method name || annotation mode) && (Symbol || String)
379
- ```ruby
380
- require 'rtype'
381
-
382
- class Example
383
- # Recommended. Annotation mode (no method name required)
384
- rtype [Integer, String] => String
385
- def hello_world(i, str)
386
- puts "Hello? #{i} #{st
387
- end
388
-
389
- # Works (specifying method name)
390
- rtype :hello_world, [Integer, String] => String
391
- def hello_world(i, str)
392
- puts "Hello? #{i} #{st
393
- end
394
-
395
- # Works
396
- def hello_world_two(i, str)
397
- puts "Hello? #{i} #{str}"
398
- end
399
- rtype :hello_world_two, [Integer, String] => String
400
-
401
- # Also works (String will be converted to Symbol)
402
- rtype 'hello_world_three', [Integer, String] => String
403
- def hello_world_three(i, str)
404
- puts "Hello? #{i} #{str}"
405
- end
406
-
407
- # Don't works. `rtype` works for next method
408
- def hello_world_four(i, str)
409
- puts "Hello? #{i} #{str}"
410
- end
411
- rtype [Integer, String] => String
412
- end
413
- ```
414
-
415
- #### Outside of module (root)
416
- Outside of module, annotation mode don't works. You must specify method name.
417
-
418
- ```ruby
419
- rtype :say, [String] => Any
420
- def say(message)
421
- puts message
422
- end
423
-
424
- Test.new.say "Hello" # Hello
425
-
426
- rtype [String] => Any
427
- # (ArgumentError) Annotation mode not working out of module
428
- ```
429
-
430
- #### Static(singleton) method
431
- rtype annotation mode works both instance and class method
432
-
433
- ```ruby
434
- require 'rtype'
435
-
436
- class Example
437
- rtype [:to_i] => Any
438
- def self.say_ya(i)
439
- puts "say" + " ya"*i.to_i
440
- end
441
- end
442
-
443
- Example::say_ya(3) #say ya ya ya
444
- ```
445
-
446
- however, if you specify method name, you must use `rtype_self` instead of `rtype`
447
-
448
- ```ruby
449
- require 'rtype'
450
-
451
- class Example
452
- rtype_self :say_ya, [:to_i] => Any
453
- def self.say_ya(i)
454
- puts "say" + " ya"*i.to_i
455
- end
456
- end
457
-
458
- Example::say_ya(3) #say ya ya ya
459
- ```
460
-
461
- #### Check type information
462
- This is just the 'information'
463
-
464
- Any change of this doesn't affect type checking
465
-
466
- ```ruby
467
- require 'rtype'
468
-
469
- class Example
470
- rtype [:to_i] => Any
471
- def test(i)
472
- end
473
- end
474
-
475
- Example.new.method(:test).type_info
476
- # => [:to_i] => Any
477
- Example.new.method(:test).argument_type
478
- # => [:to_i]
479
- Example.new.method(:test).return_type
480
- # => Any
481
- ```
482
-
483
- ## Documentation
484
- [RubyDoc.info](http://www.rubydoc.info/gems/rtype)
485
-
486
- ## Benchmarks
487
- Result of `rake benchmark` ([source](https://github.com/sputnikgugja/rtype/tree/master/benchmark/benchmark.rb))
488
-
489
- ### MRI
490
- ```
491
- Rtype with C native extension
492
- Ruby version: 2.1.7
493
- Ruby engine: ruby
494
- Ruby description: ruby 2.1.7p400 (2015-08-18 revision 51632) [x64-mingw32]
495
- Rtype version: 0.3.0
496
- Rubype version: 0.3.1
497
- Sig version: 1.0.1
498
- Contracts version: 0.13.0
499
- Typecheck version: 0.1.2
500
- Warming up --------------------------------------
501
- pure 85.328k i/100ms
502
- rtype 25.665k i/100ms
503
- rubype 21.414k i/100ms
504
- sig 8.921k i/100ms
505
- contracts 4.638k i/100ms
506
- typecheck 1.110k i/100ms
507
- Calculating -------------------------------------
508
- pure 3.282M (± 2.7%) i/s - 16.468M
509
- rtype 339.065k (± 2.6%) i/s - 1.720M
510
- rubype 266.893k (± 5.9%) i/s - 1.349M
511
- sig 99.952k (± 2.1%) i/s - 499.576k
512
- contracts 49.693k (± 1.5%) i/s - 250.452k
513
- typecheck 11.356k (± 1.6%) i/s - 57.720k
514
-
515
- Comparison:
516
- pure: 3282431.9 i/s
517
- rtype: 339064.9 i/s - 9.68x slower
518
- rubype: 266892.9 i/s - 12.30x slower
519
- sig: 99952.2 i/s - 32.84x slower
520
- contracts: 49693.0 i/s - 66.05x slower
521
- typecheck: 11355.9 i/s - 289.05x slower
522
- ```
523
-
524
- ### JRuby
525
- Without Rubype that doesn't support JRuby
526
-
527
- ```
528
- Rtype with Java extension
529
- Ruby version: 2.2.3
530
- Ruby engine: jruby
531
- 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]
532
- Rtype version: 0.3.0
533
- Sig version: 1.0.1
534
- Contracts version: 0.13.0
535
- Typecheck version: 0.1.2
536
- Warming up --------------------------------------
537
- pure 9.994k i/100ms
538
- rtype 6.181k i/100ms
539
- sig 4.041k i/100ms
540
- contracts 951.000 i/100ms
541
- typecheck 970.000 i/100ms
542
- Calculating -------------------------------------
543
- pure 7.128M (?±35.6%) i/s - 30.831M
544
- rtype 121.556k (?± 6.2%) i/s - 605.738k
545
- sig 72.187k (?± 6.4%) i/s - 359.649k
546
- contracts 24.984k (?± 3.9%) i/s - 125.532k
547
- typecheck 12.041k (?± 9.5%) i/s - 60.140k
548
-
549
- Comparison:
550
- pure: 7128373.0 i/s
551
- rtype: 121555.8 i/s - 58.64x slower
552
- sig: 72186.8 i/s - 98.75x slower
553
- contracts: 24984.5 i/s - 285.31x slower
554
- typecheck: 12041.0 i/s - 592.01x slower
555
- ```
556
-
557
- ## Rubype, Sig
558
- Rtype is influenced by [Rubype](https://github.com/gogotanaka/Rubype) and [Sig](https://github.com/janlelis/sig).
559
-
560
- If you don't like Rtype, You can use other type checking gem such as Contracts, Rubype, Rtc, Typecheck, Sig.
561
-
562
- ## Author
563
- Sputnik Gugja (sputnikgugja@gmail.com)
564
-
565
- ## License
566
- MIT license (@ Sputnik Gugja)
567
-
1
+ # Rtype: ruby with type
2
+ [![Gem Version](https://badge.fury.io/rb/rtype.svg)](https://badge.fury.io/rb/rtype)
3
+ [![Build Status](https://travis-ci.org/sputnikgugja/rtype.svg?branch=master)](https://travis-ci.org/sputnikgugja/rtype)
4
+ [![Coverage Status](https://coveralls.io/repos/github/sputnikgugja/rtype/badge.svg?branch=master)](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
+ class Test
12
+ rtype [:to_i, Numeric] => Numeric
13
+ def sum(a, b)
14
+ a.to_i + b
15
+ end
16
+
17
+ rtype {state: Boolean} => Boolean
18
+ def self.invert(state:)
19
+ !state
20
+ end
21
+ end
22
+
23
+ Test.new.sum(123, "asd")
24
+ # (Rtype::ArgumentTypeError) for 2nd argument:
25
+ # Expected "asd" to be a Numeric
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
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 arguments and return
41
+ - Support type checking for [keyword argument](#keyword-argument)
42
+ - [Type checking for array elements](#array)
43
+ - [Type checking for hash elements](#hash)
44
+ - [Duck typing](#duck-typing)
45
+ - Custom type behavior
46
+
47
+ ## Installation
48
+ Run `gem install rtype` or add `gem 'rtype'` to your `Gemfile`
49
+
50
+ And add to your `.rb` source file:
51
+ ```ruby
52
+ require 'rtype'
53
+ ```
54
+
55
+ ### Native extension
56
+ Rtype itself is pure-ruby gem. but you can make it more faster by using native extension.
57
+
58
+ #### Native extension for MRI
59
+ Just run
60
+ ```ruby
61
+ gem install rtype-native
62
+ ```
63
+ or add to your `Gemfile`:
64
+ ```ruby
65
+ gem 'rtype-native'
66
+ ```
67
+ then, Rtype use it. (Do not `require 'rtype-native'`)
68
+
69
+ #### Java extension for JRuby
70
+ Just run
71
+ ```ruby
72
+ gem install rtype-java
73
+ ```
74
+ or add to your `Gemfile`:
75
+ ```ruby
76
+ gem 'rtype-java'
77
+ ```
78
+ then, Rtype use it. (Do not `require 'rtype-java'`)
79
+
80
+ ## Usage
81
+
82
+ ### Supported Type Behaviors
83
+ - `Module`
84
+ - Value must be an instance of this module/class or one of its superclasses
85
+ - `Any` : An alias for `BasicObject` (means Any Object)
86
+ - `Boolean` : `true` or `false`
87
+ - `Symbol`
88
+ - Value must have(respond to) a method with this name
89
+ - `Regexp`
90
+ - Value must match this regexp pattern
91
+ - `Range`
92
+ - Value must be included in this range
93
+ - `Array` (tuple)
94
+ - Value must be an array
95
+ - Each of value's elements must be valid
96
+ - Value's length must be equal to the array's length
97
+ - Of course, nested array works
98
+ - Example: [Array](#array)
99
+ - This can be used as a tuple
100
+ - `Hash`
101
+ - Value must be an hash
102
+ - Each of value’s elements must be valid
103
+ - Value's key list must be equal to the hash's key list
104
+ - **String** key is **different** from **symbol** key
105
+ - vs Keyword arguments
106
+ - `[{}]` is **not** hash type argument. it is keyword argument because its position is last
107
+ - `[{}, {}]` is empty hash type argument (first) and one empty keyword argument (second)
108
+ - `[{}, {}, {}]` is two empty hash type argument (first, second) and empty keyword argument (last)
109
+ - `{}` is keyword argument. non-keyword arguments must be in array.
110
+ - Of course, nested hash works
111
+ - Example: [Hash](#hash)
112
+ - `Proc`
113
+ - Value must return a truthy value for this proc
114
+ - `true`
115
+ - Value must be **truthy**
116
+ - `false`
117
+ - Value must be **falsy**
118
+ - `nil`
119
+ - Only available for **return type**. void return type in other languages
120
+ - Special Behaviors
121
+ - `Rtype::and(*types)` : Ensure value is valid for all the types
122
+ - `Rtype::and(*types)`
123
+ - `Rtype::Behavior::And[*types]`
124
+ - `include Rtype::Behavior; And[...]`
125
+ - `obj.and(*others)` (core extension)
126
+
127
+ - `Rtype::or(*types)` : Ensure value is valid for at least one of the types
128
+ - `Rtype::or(*types)`
129
+ - `Rtype::Behavior::Or[*types]`
130
+ - `include Rtype::Behavior; Or[...]`
131
+ - `obj.or(*others)` (core extension)
132
+
133
+ - `Rtype::xor(*types)` : Ensure value is valid for only one of the types
134
+ - `Rtype::xor(*types)`
135
+ - `Rtype::Behavior::Xor[*types]`
136
+ - `include Rtype::Behavior; Xor[...]`
137
+ - `obj.xor(*others)` (core extension)
138
+
139
+ - `Rtype::not(*types)` : Ensure value is not valid for all the types
140
+ - `Rtype::not(*types)`
141
+ - `Rtype::Behavior::Not[*types]`
142
+ - `include Rtype::Behavior; Not[...]`
143
+ - `obj.not` (core extension)
144
+
145
+ - `Rtype::nilable(type)` : Ensure value can be nil
146
+ - `Rtype::nilable(type)`
147
+ - `Rtype::Behavior::Nilable[type]`
148
+ - `include Rtype::Behavior; Nilable[...]`
149
+ - `obj.nilable` (core extension)
150
+ - `obj.or_nil` (core extension)
151
+
152
+ - You can create custom behavior by extending `Rtype::Behavior::Base`
153
+
154
+ ### Examples
155
+
156
+ #### Basic
157
+ ```ruby
158
+ require 'rtype'
159
+
160
+ class Example
161
+ rtype [Integer] => nil
162
+ def test(i)
163
+ end
164
+
165
+ rtype [Any] => nil
166
+ def any_type_arg(arg)
167
+ end
168
+
169
+ rtype [] => Integer
170
+ def return_type_test
171
+ "not integer"
172
+ end
173
+ end
174
+
175
+ e = Example.new
176
+ e.test("not integer")
177
+ # (Rtype::ArgumentTypeError) for 1st argument:
178
+ # Expected "not integer" to be a Integer
179
+
180
+ e.any_type_arg("Any argument!") # Works
181
+
182
+ e.return_type_test
183
+ # (Rtype::ReturnTypeError) for return:
184
+ # Expected "not integer" to be a Integer
185
+ ```
186
+
187
+ #### Keyword argument
188
+ ```ruby
189
+ require 'rtype'
190
+
191
+ class Example
192
+ rtype {name: String} => Any
193
+ def say_your_name(name:)
194
+ puts "My name is #{name}"
195
+ end
196
+
197
+ # Mixing positional arguments and keyword arguments
198
+ rtype [String, {age: Integer}] => Any
199
+ def name_and_age(name, age:)
200
+ puts "Name: #{name}, Age: #{age}"
201
+ end
202
+ end
203
+
204
+ Example.new.say_your_name(name: "Babo") # My name is Babo
205
+ Example.new.name_and_age("Bamboo", age: 100) # Name: Bamboo, Age: 100
206
+
207
+ Example.new.say_your_name(name: 12345)
208
+ # (Rtype::ArgumentTypeError) for 'name' argument:
209
+ # Expected 12345 to be a String
210
+ ```
211
+
212
+ #### Duck typing
213
+ ```ruby
214
+ require 'rtype'
215
+
216
+ class Duck
217
+ rtype [:to_i] => Any
218
+ def says(i)
219
+ puts "duck:" + " quack"*i.to_i
220
+ end
221
+ end
222
+
223
+ Duck.new.says("2") # duck: quack quack
224
+ ```
225
+
226
+ #### Array
227
+ This can be used as a tuple.
228
+
229
+ ```ruby
230
+ rtype :func, [[Numeric, Numeric]] => Any
231
+ def func(arr)
232
+ puts "Your location is (#{arr[0]}, #{arr[1]}). I will look for you. I will find you"
233
+ end
234
+
235
+ func [1, "str"]
236
+ # (Rtype::ArgumentTypeError) for 1st argument:
237
+ # Expected [1, "str"] to be an array with 2 elements:
238
+ # - [0] index : Expected 1 to be a Numeric
239
+ # - [1] index : Expected "str" to be a Numeric
240
+
241
+ func [1, 2, 3]
242
+ # (Rtype::ArgumentTypeError) for 1st argument:
243
+ # Expected [1, 2, 3] to be an array with 2 elements:
244
+ # - [0] index : Expected 1 to be a Numeric
245
+ # - [1] index : Expected 2 to be a Numeric
246
+
247
+ func [1]
248
+ # (Rtype::ArgumentTypeError) for 1st argument:
249
+ # Expected [1] to be an array with 2 elements:
250
+ # - [0] index : Expected 1 to be a Numeric
251
+ # - [1] index : Expected nil to be a Numeric
252
+
253
+ func [1, 2] # Your location is (1, 2). I will look for you. I will find you
254
+ ```
255
+
256
+ #### Hash
257
+ ```ruby
258
+ # last hash element is keyword arguments
259
+ rtype :func, [{msg: String}, {}] => Any
260
+ def func(hash)
261
+ puts hash[:msg]
262
+ end
263
+
264
+ # last hash is keyword arguments
265
+ func({}, {})
266
+ # (Rtype::ArgumentTypeError) for 1st argument:
267
+ # Expected {} to be an hash with 1 elements:
268
+ # - msg : Expected nil to be a String
269
+
270
+ func({msg: 123}, {})
271
+ # (Rtype::ArgumentTypeError) for 1st argument:
272
+ # Expected {:msg=>123} to be an hash with 1 elements:
273
+ # - msg : Expected 123 to be a String
274
+
275
+ func({msg: "hello", key: 'value'}, {})
276
+ # (Rtype::ArgumentTypeError) for 1st argument:
277
+ # Expected {:msg=>"hello", :key=>"value"} to be an hash with 1 elements:
278
+ # - msg : Expected "hello" to be a String
279
+
280
+ func({"msg" => "hello hash"}, {})
281
+ # (Rtype::ArgumentTypeError) for 1st argument:
282
+ # Expected {"msg"=>"hello hash"} to be an hash with 1 elements:
283
+ # - msg : Expected nil to be a String
284
+
285
+ func({msg: "hello hash"}, {}) # hello hash
286
+ ```
287
+
288
+ #### rtype with attr_accessor
289
+ `rtype_accessor` : calls `attr_accessor` if the accessor method(getter/setter) is not defined, and makes it typed method
290
+
291
+ You can use `rtype_accessor_self` for static accessor.
292
+
293
+ ```ruby
294
+ require 'rtype'
295
+
296
+ class Example
297
+ rtype_accessor :value, String
298
+
299
+ def initialize
300
+ @value = 456
301
+ end
302
+ end
303
+
304
+ Example.new.value = 123
305
+ # (Rtype::ArgumentTypeError) for 1st argument:
306
+ # Expected 123 to be a String
307
+
308
+ Example.new.value
309
+ # (Rtype::ReturnTypeError) for return:
310
+ # Expected 456 to be a String
311
+ ```
312
+
313
+ #### Combined type
314
+ ```ruby
315
+ ### TEST 1 ###
316
+ require 'rtype'
317
+
318
+ class Example
319
+ rtype [String.and(:func)] => Any
320
+ # also works:
321
+ # rtype [Rtype::and(String, :func)] => Any
322
+ def and_test(arg)
323
+ end
324
+ end
325
+
326
+ Example.new.and_test("A string")
327
+ # (Rtype::ArgumentTypeError) for 1st argument:
328
+ # Expected "A string" to be a String
329
+ # AND Expected "A string" to respond to :func
330
+ ```
331
+ ```ruby
332
+ ### TEST 2 ###
333
+ # ... require rtype and define Example the same as above ...
334
+
335
+ class String
336
+ def func; end
337
+ end
338
+
339
+ Example.new.and_test("A string") # Works!
340
+ ```
341
+
342
+ #### Combined duck type
343
+ Application of duck typing and combined type
344
+
345
+ ```ruby
346
+ require 'rtype'
347
+
348
+ module Game
349
+ ENEMY = [
350
+ :name,
351
+ :level
352
+ ]
353
+
354
+ class Player < Entity
355
+ include Rtype::Behavior
356
+
357
+ rtype [And[*ENEMY]] => Any
358
+ def attacks(enemy)
359
+ "Player attacks '#{enemy.name}' (level #{enemy.level})!"
360
+ end
361
+ end
362
+
363
+ class Slime < Entity
364
+ def name
365
+ "Powerful Slime"
366
+ end
367
+
368
+ def level
369
+ 123
370
+ end
371
+ end
372
+ end
373
+
374
+ Game::Player.new.attacks Game::Slime.new
375
+ # Player attacks 'Powerful Slime' (level 123)!
376
+ ```
377
+
378
+ #### Position of `rtype` && (Specify method name || annotation mode) && (Symbol || String)
379
+ ```ruby
380
+ require 'rtype'
381
+
382
+ class Example
383
+ # Recommended. Annotation mode (no method name required)
384
+ rtype [Integer, String] => String
385
+ def hello_world(i, str)
386
+ puts "Hello? #{i} #{st
387
+ end
388
+
389
+ # Works (specifying method name)
390
+ rtype :hello_world, [Integer, String] => String
391
+ def hello_world(i, str)
392
+ puts "Hello? #{i} #{st
393
+ end
394
+
395
+ # Works
396
+ def hello_world_two(i, str)
397
+ puts "Hello? #{i} #{str}"
398
+ end
399
+ rtype :hello_world_two, [Integer, String] => String
400
+
401
+ # Also works (String will be converted to Symbol)
402
+ rtype 'hello_world_three', [Integer, String] => String
403
+ def hello_world_three(i, str)
404
+ puts "Hello? #{i} #{str}"
405
+ end
406
+
407
+ # Don't works. `rtype` works for next method
408
+ def hello_world_four(i, str)
409
+ puts "Hello? #{i} #{str}"
410
+ end
411
+ rtype [Integer, String] => String
412
+ end
413
+ ```
414
+
415
+ #### Outside of module (root)
416
+ Outside of module, annotation mode don't works. You must specify method name.
417
+
418
+ ```ruby
419
+ rtype :say, [String] => Any
420
+ def say(message)
421
+ puts message
422
+ end
423
+
424
+ Test.new.say "Hello" # Hello
425
+
426
+ rtype [String] => Any
427
+ # (ArgumentError) Annotation mode not working out of module
428
+ ```
429
+
430
+ #### Static(singleton) method
431
+ rtype annotation mode works both instance and class method
432
+
433
+ ```ruby
434
+ require 'rtype'
435
+
436
+ class Example
437
+ rtype [:to_i] => Any
438
+ def self.say_ya(i)
439
+ puts "say" + " ya"*i.to_i
440
+ end
441
+ end
442
+
443
+ Example::say_ya(3) #say ya ya ya
444
+ ```
445
+
446
+ however, if you specify method name, you must use `rtype_self` instead of `rtype`
447
+
448
+ ```ruby
449
+ require 'rtype'
450
+
451
+ class Example
452
+ rtype_self :say_ya, [:to_i] => Any
453
+ def self.say_ya(i)
454
+ puts "say" + " ya"*i.to_i
455
+ end
456
+ end
457
+
458
+ Example::say_ya(3) #say ya ya ya
459
+ ```
460
+
461
+ #### Check type information
462
+ This is just the 'information'
463
+
464
+ Any change of this doesn't affect type checking
465
+
466
+ ```ruby
467
+ require 'rtype'
468
+
469
+ class Example
470
+ rtype [:to_i] => Any
471
+ def test(i)
472
+ end
473
+ end
474
+
475
+ Example.new.method(:test).type_info
476
+ # => [:to_i] => Any
477
+ Example.new.method(:test).argument_type
478
+ # => [:to_i]
479
+ Example.new.method(:test).return_type
480
+ # => Any
481
+ ```
482
+
483
+ ## Documentation
484
+ [RubyDoc.info](http://www.rubydoc.info/gems/rtype)
485
+
486
+ ## Benchmarks
487
+ Result of `rake benchmark` ([source](https://github.com/sputnikgugja/rtype/tree/master/benchmark/benchmark.rb))
488
+
489
+ ### MRI
490
+ ```
491
+ Rtype with C native extension
492
+ Ruby version: 2.1.7
493
+ Ruby engine: ruby
494
+ Ruby description: ruby 2.1.7p400 (2015-08-18 revision 51632) [x64-mingw32]
495
+ Rtype version: 0.3.0
496
+ Rubype version: 0.3.1
497
+ Sig version: 1.0.1
498
+ Contracts version: 0.13.0
499
+ Typecheck version: 0.1.2
500
+ Warming up --------------------------------------
501
+ pure 85.328k i/100ms
502
+ rtype 25.665k i/100ms
503
+ rubype 21.414k i/100ms
504
+ sig 8.921k i/100ms
505
+ contracts 4.638k i/100ms
506
+ typecheck 1.110k i/100ms
507
+ Calculating -------------------------------------
508
+ pure 3.282M (± 2.7%) i/s - 16.468M
509
+ rtype 339.065k (± 2.6%) i/s - 1.720M
510
+ rubype 266.893k (± 5.9%) i/s - 1.349M
511
+ sig 99.952k (± 2.1%) i/s - 499.576k
512
+ contracts 49.693k (± 1.5%) i/s - 250.452k
513
+ typecheck 11.356k (± 1.6%) i/s - 57.720k
514
+
515
+ Comparison:
516
+ pure: 3282431.9 i/s
517
+ rtype: 339064.9 i/s - 9.68x slower
518
+ rubype: 266892.9 i/s - 12.30x slower
519
+ sig: 99952.2 i/s - 32.84x slower
520
+ contracts: 49693.0 i/s - 66.05x slower
521
+ typecheck: 11355.9 i/s - 289.05x slower
522
+ ```
523
+
524
+ ### JRuby
525
+ Without Rubype that doesn't support JRuby
526
+
527
+ ```
528
+ Rtype with Java extension
529
+ Ruby version: 2.2.3
530
+ Ruby engine: jruby
531
+ 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]
532
+ Rtype version: 0.3.0
533
+ Sig version: 1.0.1
534
+ Contracts version: 0.13.0
535
+ Typecheck version: 0.1.2
536
+ Warming up --------------------------------------
537
+ pure 9.994k i/100ms
538
+ rtype 6.181k i/100ms
539
+ sig 4.041k i/100ms
540
+ contracts 951.000 i/100ms
541
+ typecheck 970.000 i/100ms
542
+ Calculating -------------------------------------
543
+ pure 7.128M (?±35.6%) i/s - 30.831M
544
+ rtype 121.556k (?± 6.2%) i/s - 605.738k
545
+ sig 72.187k (?± 6.4%) i/s - 359.649k
546
+ contracts 24.984k (?± 3.9%) i/s - 125.532k
547
+ typecheck 12.041k (?± 9.5%) i/s - 60.140k
548
+
549
+ Comparison:
550
+ pure: 7128373.0 i/s
551
+ rtype: 121555.8 i/s - 58.64x slower
552
+ sig: 72186.8 i/s - 98.75x slower
553
+ contracts: 24984.5 i/s - 285.31x slower
554
+ typecheck: 12041.0 i/s - 592.01x slower
555
+ ```
556
+
557
+ ## Rubype, Sig
558
+ Rtype is influenced by [Rubype](https://github.com/gogotanaka/Rubype) and [Sig](https://github.com/janlelis/sig).
559
+
560
+ If you don't like Rtype, You can use other type checking gem such as Contracts, Rubype, Rtc, Typecheck, Sig.
561
+
562
+ ## Author
563
+ Sputnik Gugja (sputnikgugja@gmail.com)
564
+
565
+ ## License
566
+ MIT license (@ Sputnik Gugja)
567
+
568
568
  See `LICENSE` file.