rtype 0.2.0 → 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 +4 -4
- data/README.md +62 -40
- data/lib/rtype/version.rb +1 -1
- data/lib/rtype.rb +136 -125
- data/spec/rtype_spec.rb +8 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 580a93433f2e77fc81b6a9abd7b14d390952f05d
|
4
|
+
data.tar.gz: 78f18ddfed8634652bc9c0da161dec708db99293
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e6df637fa65774fcf9addcf06dc4b8fc526587e1cfe448229644b8756f17129f603086a90a68d16422900f5b20020927bd67e403f8550ffb1823b4484d365d39
|
7
|
+
data.tar.gz: 653c366459022762cf1e5ded44eac4452365b6549f23efad8c1f875eb785ed14d56788d72efe222d6a76906367f908b7594f22c6372707c931079d2af104a5bb
|
data/README.md
CHANGED
@@ -31,7 +31,10 @@ Test::invert(state: 0)
|
|
31
31
|
|
32
32
|
## Requirements
|
33
33
|
- Ruby >= 2.1
|
34
|
-
- MRI or RBX
|
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
|
35
38
|
|
36
39
|
## Features
|
37
40
|
- Provide type checking for argument and return
|
@@ -49,12 +52,29 @@ require 'rtype'
|
|
49
52
|
```
|
50
53
|
|
51
54
|
### Native extension
|
52
|
-
Rtype itself is pure-ruby gem. but you can make it more faster by native extension.
|
55
|
+
Rtype itself is pure-ruby gem. but you can make it more faster by using native extension.
|
53
56
|
|
54
|
-
|
55
|
-
|
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'`)
|
56
67
|
|
57
|
-
|
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'`)
|
58
78
|
|
59
79
|
## Usage
|
60
80
|
|
@@ -370,68 +390,70 @@ Result of `rake benchmark` ([source](https://github.com/sputnikgugja/rtype/tree/
|
|
370
390
|
|
371
391
|
### MRI
|
372
392
|
```
|
393
|
+
Rtype with C native extension
|
373
394
|
Ruby version: 2.1.7
|
374
395
|
Ruby engine: ruby
|
375
396
|
Ruby description: ruby 2.1.7p400 (2015-08-18 revision 51632) [x64-mingw32]
|
376
|
-
Rtype version: 0.
|
397
|
+
Rtype version: 0.3.0
|
377
398
|
Rubype version: 0.3.1
|
378
399
|
Sig version: 1.0.1
|
379
400
|
Contracts version: 0.13.0
|
380
401
|
Typecheck version: 0.1.2
|
381
402
|
Warming up --------------------------------------
|
382
|
-
pure 85.
|
383
|
-
rtype 25.
|
384
|
-
rubype
|
385
|
-
sig 8.
|
386
|
-
contracts 4.
|
387
|
-
typecheck 1.
|
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
|
388
409
|
Calculating -------------------------------------
|
389
|
-
pure 3.
|
390
|
-
rtype
|
391
|
-
rubype
|
392
|
-
sig
|
393
|
-
contracts
|
394
|
-
typecheck 11.
|
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
|
395
416
|
|
396
417
|
Comparison:
|
397
|
-
pure:
|
398
|
-
rtype:
|
399
|
-
rubype:
|
400
|
-
sig:
|
401
|
-
contracts:
|
402
|
-
typecheck:
|
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
|
403
424
|
```
|
404
425
|
|
405
426
|
### JRuby
|
406
427
|
Without Rubype that doesn't support JRuby
|
407
428
|
|
408
429
|
```
|
430
|
+
Rtype with Java extension
|
409
431
|
Ruby version: 2.2.3
|
410
432
|
Ruby engine: jruby
|
411
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]
|
412
|
-
Rtype version: 0.
|
434
|
+
Rtype version: 0.3.0
|
413
435
|
Sig version: 1.0.1
|
414
436
|
Contracts version: 0.13.0
|
415
437
|
Typecheck version: 0.1.2
|
416
438
|
Warming up --------------------------------------
|
417
|
-
pure
|
418
|
-
rtype
|
419
|
-
sig 4.
|
420
|
-
contracts
|
421
|
-
typecheck
|
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
|
422
444
|
Calculating -------------------------------------
|
423
|
-
pure
|
424
|
-
rtype
|
425
|
-
sig
|
426
|
-
contracts
|
427
|
-
typecheck 12.
|
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
|
428
450
|
|
429
451
|
Comparison:
|
430
|
-
pure:
|
431
|
-
rtype:
|
432
|
-
sig:
|
433
|
-
contracts:
|
434
|
-
typecheck:
|
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
|
435
457
|
```
|
436
458
|
|
437
459
|
## Rubype, Sig
|
data/lib/rtype/version.rb
CHANGED
data/lib/rtype.rb
CHANGED
@@ -1,8 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
2
|
+
begin
|
3
|
+
require 'java'
|
4
|
+
require 'rtype/rtype_java'
|
5
|
+
puts "Rtype with Java extension"
|
6
|
+
rescue LoadError
|
7
|
+
puts "Rtype without native extension"
|
8
|
+
end
|
9
|
+
else
|
10
|
+
begin
|
11
|
+
require "rtype/rtype_native"
|
12
|
+
puts "Rtype with C native extension"
|
13
|
+
rescue LoadError
|
14
|
+
puts "Rtype without native extension"
|
15
|
+
end
|
6
16
|
end
|
7
17
|
|
8
18
|
require_relative 'rtype/core_ext'
|
@@ -14,152 +24,153 @@ require_relative 'rtype/type_signature'
|
|
14
24
|
require_relative 'rtype/behavior'
|
15
25
|
|
16
26
|
module Rtype
|
27
|
+
extend self
|
28
|
+
|
17
29
|
# This is just the 'information'
|
18
30
|
# Any change of this doesn't affect type checking
|
19
31
|
@@type_signatures = Hash.new({})
|
20
32
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
expected_kwargs = {}
|
37
|
-
end
|
38
|
-
elsif arg_sig.is_a?(Hash)
|
39
|
-
expected_args = []
|
40
|
-
expected_kwargs = arg_sig
|
33
|
+
def define_typed_method(owner, method_name, type_sig_info)
|
34
|
+
raise TypeSignatureError, "Invalid type signature" unless valid_type_sig_info_form?(type_sig_info)
|
35
|
+
method_name = method_name.to_sym
|
36
|
+
raise ArgumentError, "method_name is nil" if method_name.nil?
|
37
|
+
|
38
|
+
el = type_sig_info.first
|
39
|
+
arg_sig = el[0]
|
40
|
+
return_sig = el[1]
|
41
|
+
|
42
|
+
if arg_sig.is_a?(Array)
|
43
|
+
expected_args = arg_sig
|
44
|
+
if expected_args.last.is_a?(Hash)
|
45
|
+
expected_kwargs = expected_args.pop
|
46
|
+
else
|
47
|
+
expected_kwargs = {}
|
41
48
|
end
|
49
|
+
elsif arg_sig.is_a?(Hash)
|
50
|
+
expected_args = []
|
51
|
+
expected_kwargs = arg_sig
|
52
|
+
end
|
42
53
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
54
|
+
expected_args.each { |e| valid?(e, nil) }
|
55
|
+
if expected_kwargs.keys.any? { |e| !e.is_a?(Symbol) }
|
56
|
+
raise TypeSignatureError, "Invalid type signature: keyword arguments contain non-symbol key"
|
57
|
+
end
|
58
|
+
expected_kwargs.each_value { |e| valid?(e, nil) }
|
59
|
+
valid?(return_sig, nil) unless return_sig.nil?
|
49
60
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
61
|
+
sig = TypeSignature.new
|
62
|
+
sig.argument_type = arg_sig
|
63
|
+
sig.return_type = return_sig
|
64
|
+
@@type_signatures[owner][method_name] = sig
|
54
65
|
|
55
|
-
|
56
|
-
|
66
|
+
define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
67
|
+
end
|
57
68
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
69
|
+
def define_typed_accessor(owner, accessor_name, type_behavior)
|
70
|
+
getter = accessor_name.to_sym
|
71
|
+
setter = :"#{accessor_name}="
|
72
|
+
valid?(type_behavior, nil)
|
73
|
+
define_typed_method owner, getter, [] => type_behavior
|
74
|
+
define_typed_method owner, setter, [type_behavior] => Any
|
75
|
+
end
|
65
76
|
|
66
|
-
|
67
|
-
|
68
|
-
|
77
|
+
def type_signatures
|
78
|
+
@@type_signatures
|
79
|
+
end
|
69
80
|
|
70
81
|
=begin
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
end
|
82
|
+
def assert_keyword_arguments_type(expected_kwargs, kwargs)
|
83
|
+
kwargs.each do |key, value|
|
84
|
+
expected = expected_kwargs[key]
|
85
|
+
unless expected.nil?
|
86
|
+
unless valid?(expected, value)
|
87
|
+
raise ArgumentTypeError, "for '#{key}' argument:\n" + type_error_message(expected, value)
|
78
88
|
end
|
79
89
|
end
|
80
90
|
end
|
91
|
+
end
|
81
92
|
=end
|
82
93
|
|
83
|
-
|
84
|
-
|
85
|
-
|
94
|
+
def arg_type_error_message(idx, expected, value)
|
95
|
+
"#{arg_message(idx)}\n" + type_error_message(expected, value)
|
96
|
+
end
|
86
97
|
|
87
|
-
|
88
|
-
|
89
|
-
|
98
|
+
def kwarg_type_error_message(key, expected, value)
|
99
|
+
"#{kwarg_message(key)}\n" + type_error_message(expected, value)
|
100
|
+
end
|
90
101
|
|
91
|
-
|
92
|
-
|
93
|
-
|
102
|
+
def arg_message(idx)
|
103
|
+
"for #{(idx+1).ordinalize} argument:"
|
104
|
+
end
|
94
105
|
|
95
|
-
|
96
|
-
|
97
|
-
|
106
|
+
def kwarg_message(key)
|
107
|
+
"for '#{key}' argument:"
|
108
|
+
end
|
98
109
|
|
99
110
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
111
|
+
def type_error_message(expected, value)
|
112
|
+
case expected
|
113
|
+
when Rtype::Behavior::Base
|
114
|
+
expected.error_message(value)
|
115
|
+
when Module
|
116
|
+
"Expected #{value.inspect} to be a #{expected}"
|
117
|
+
when Symbol
|
118
|
+
"Expected #{value.inspect} to respond to :#{expected}"
|
119
|
+
when Regexp
|
120
|
+
"Expected stringified #{value.inspect} to match regexp #{expected.inspect}"
|
121
|
+
when Range
|
122
|
+
"Expected #{value.inspect} to be included in range #{expected.inspect}"
|
123
|
+
when Array
|
124
|
+
if value.is_a?(Array)
|
125
|
+
arr = expected.map.with_index do |e, idx|
|
126
|
+
if e.is_a?(Array)
|
127
|
+
"- [#{idx}] index : {\n" + type_error_message(e, value[idx]) + "\n}"
|
128
|
+
else
|
129
|
+
"- [#{idx}] index : " + type_error_message(e, value[idx])
|
120
130
|
end
|
121
|
-
"Expected #{value.inspect} to be an array with #{expected.length} elements:\n" + arr.join("\n")
|
122
|
-
else
|
123
|
-
"Expected #{value.inspect} to be an array"
|
124
131
|
end
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
"Expected #{value.inspect} to be a truthy value"
|
129
|
-
when false
|
130
|
-
"Expected #{value.inspect} to be a falsy value"
|
131
|
-
when nil # for return
|
132
|
-
"Expected #{value.inspect} to be nil"
|
132
|
+
"Expected #{value.inspect} to be an array with #{expected.length} elements:\n" + arr.join("\n")
|
133
|
+
else
|
134
|
+
"Expected #{value.inspect} to be an array"
|
133
135
|
end
|
136
|
+
when Proc
|
137
|
+
"Expected #{value.inspect} to return a truthy value for proc #{expected}"
|
138
|
+
when true
|
139
|
+
"Expected #{value.inspect} to be a truthy value"
|
140
|
+
when false
|
141
|
+
"Expected #{value.inspect} to be a falsy value"
|
142
|
+
when nil # for return
|
143
|
+
"Expected #{value.inspect} to be nil"
|
134
144
|
end
|
145
|
+
end
|
135
146
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
147
|
+
private
|
148
|
+
def valid_type_sig_info_form?(hash)
|
149
|
+
return false unless hash.is_a?(Hash)
|
150
|
+
arg_sig = hash.first[0]
|
151
|
+
arg_sig.is_a?(Array) || arg_sig.is_a?(Hash)
|
152
|
+
end
|
142
153
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
end
|
153
|
-
::Rtype.assert_return_type(return_sig, result)
|
154
|
-
result
|
154
|
+
def define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
155
|
+
# `send` is faster than `method(...).call`
|
156
|
+
owner.send(:_rtype_proxy).send :define_method, method_name do |*args, **kwargs, &block|
|
157
|
+
if kwargs.empty?
|
158
|
+
::Rtype.assert_arguments_type(expected_args, args)
|
159
|
+
result = super(*args, &block)
|
160
|
+
else
|
161
|
+
::Rtype.assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
162
|
+
result = super(*args, **kwargs, &block)
|
155
163
|
end
|
156
|
-
|
164
|
+
::Rtype.assert_return_type(return_sig, result)
|
165
|
+
result
|
157
166
|
end
|
167
|
+
nil
|
158
168
|
end
|
159
|
-
|
160
|
-
|
169
|
+
|
170
|
+
public
|
171
|
+
unless respond_to?(:valid?)
|
161
172
|
# validate argument type
|
162
|
-
def
|
173
|
+
def valid?(expected, value)
|
163
174
|
case expected
|
164
175
|
when Module
|
165
176
|
value.is_a? expected
|
@@ -188,8 +199,8 @@ module Rtype
|
|
188
199
|
end
|
189
200
|
end
|
190
201
|
|
191
|
-
unless
|
192
|
-
def
|
202
|
+
unless respond_to?(:assert_arguments_type)
|
203
|
+
def assert_arguments_type(expected_args, args)
|
193
204
|
# `length.times` is faster than `each_with_index`
|
194
205
|
args.length.times do |i|
|
195
206
|
expected = expected_args[i]
|
@@ -203,8 +214,8 @@ module Rtype
|
|
203
214
|
end
|
204
215
|
end
|
205
216
|
|
206
|
-
unless
|
207
|
-
def
|
217
|
+
unless respond_to?(:assert_arguments_type_with_keywords)
|
218
|
+
def assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
208
219
|
# `length.times` is faster than `each_with_index`
|
209
220
|
args.length.times do |i|
|
210
221
|
expected = expected_args[i]
|
@@ -226,8 +237,8 @@ module Rtype
|
|
226
237
|
end
|
227
238
|
end
|
228
239
|
|
229
|
-
unless
|
230
|
-
def
|
240
|
+
unless respond_to?(:assert_return_type)
|
241
|
+
def assert_return_type(expected, result)
|
231
242
|
if expected.nil?
|
232
243
|
unless result.nil?
|
233
244
|
raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
|
data/spec/rtype_spec.rb
CHANGED
@@ -236,29 +236,35 @@ describe Rtype do
|
|
236
236
|
describe 'true' do
|
237
237
|
it "is right" do
|
238
238
|
klass.send :rtype, :return_arg, [true] => Any
|
239
|
+
instance.return_arg(true)
|
239
240
|
instance.return_arg(123)
|
240
241
|
end
|
241
242
|
it "is wrong args" do
|
242
243
|
klass.send :rtype, :return_arg, [true] => Any
|
244
|
+
expect {instance.return_arg(false)}.to raise_error Rtype::ArgumentTypeError
|
243
245
|
expect {instance.return_arg(nil)}.to raise_error Rtype::ArgumentTypeError
|
244
246
|
end
|
245
247
|
it "is wrong result" do
|
246
|
-
klass.send :rtype, :
|
247
|
-
expect {instance.
|
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
|
248
251
|
end
|
249
252
|
end
|
250
253
|
|
251
254
|
describe 'false' do
|
252
255
|
it "is right" do
|
253
256
|
klass.send :rtype, :return_arg, [false] => Any
|
257
|
+
instance.return_arg(false)
|
254
258
|
instance.return_arg(nil)
|
255
259
|
end
|
256
260
|
it "is wrong args" do
|
257
261
|
klass.send :rtype, :return_arg, [false] => Any
|
262
|
+
expect {instance.return_arg(true)}.to raise_error Rtype::ArgumentTypeError
|
258
263
|
expect {instance.return_arg(123)}.to raise_error Rtype::ArgumentTypeError
|
259
264
|
end
|
260
265
|
it "is wrong result" do
|
261
266
|
klass.send :rtype, :return_arg, [Any] => false
|
267
|
+
expect {instance.return_arg(true)}.to raise_error Rtype::ReturnTypeError
|
262
268
|
expect {instance.return_arg(123)}.to raise_error Rtype::ReturnTypeError
|
263
269
|
end
|
264
270
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rtype
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sputnik Gugja
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|