rtype 0.6.0 → 0.6.3
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 +73 -35
- data/lib/rtype.rb +153 -39
- data/lib/rtype/behavior.rb +1 -0
- data/lib/rtype/behavior/core_ext.rb +10 -0
- data/lib/rtype/behavior/typed_array.rb +25 -0
- data/lib/rtype/core_ext.rb +71 -24
- data/lib/rtype/type_signature.rb +3 -2
- data/lib/rtype/version.rb +1 -1
- data/spec/rtype_spec.rb +37 -3
- data/spec/spec_helper.rb +9 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4edbfe36753db1a9d6a413bf31ccbed37c499df5
|
4
|
+
data.tar.gz: bfa734ba5689e7fca457b247e0f03dbcde507f8e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 60031209128806548c02edc8944e18a103c3f34a2ddca144dd53716050e939d9f7e4baf9ec7d33a3cf70da28ef156b5fcafd8177cddba6f11be6029cf0a4357a
|
7
|
+
data.tar.gz: 5f40a360733043804e02478347ec2a8a3c187b3d7ea943e31d470a7b4727fa4d89770ad93d1e08bb349550ea1a733fc4f2908d0776db4df777567dcc6083b8e1
|
data/README.md
CHANGED
@@ -32,9 +32,9 @@ Test::invert(state: 0)
|
|
32
32
|
## Requirements
|
33
33
|
- Ruby >= 2.1
|
34
34
|
- MRI
|
35
|
-
- If C native extension is used
|
36
|
-
- JRuby
|
37
|
-
- If Java extension is used
|
35
|
+
- If C native extension is used. otherwise it is not required
|
36
|
+
- JRuby (JRuby 9000+)
|
37
|
+
- If Java extension is used. otherwise it is not required
|
38
38
|
|
39
39
|
## Features
|
40
40
|
- Provides type checking for arguments and return
|
@@ -80,52 +80,58 @@ then, Rtype use it. (Do not `require 'rtype-java'`)
|
|
80
80
|
|
81
81
|
### Supported Type Behaviors
|
82
82
|
- `Module`
|
83
|
-
-
|
83
|
+
- A value must be an instance of the module/class or one of its superclasses (`is_a?`)
|
84
84
|
- `Any` : An alias for `BasicObject` (means Any Object)
|
85
85
|
- `Boolean` : `true` or `false`
|
86
86
|
- `Symbol`
|
87
|
-
-
|
87
|
+
- A value must have(respond to) a method with the name
|
88
88
|
- `Regexp`
|
89
|
-
-
|
89
|
+
- A value must match the regexp pattern
|
90
90
|
- `Range`
|
91
|
-
-
|
91
|
+
- A value must be included in the range
|
92
92
|
- `Array`
|
93
|
-
-
|
93
|
+
- A value can be any type in the array
|
94
94
|
- `Hash`
|
95
|
-
-
|
96
|
-
- Each of value’s elements must be valid
|
97
|
-
-
|
95
|
+
- A value must be a hash
|
96
|
+
- Each of the value’s elements must be valid
|
97
|
+
- The value's key list must be equal to the hash's key list
|
98
98
|
- **String** key is **different** from **symbol** key
|
99
|
-
- vs Keyword arguments (e.g.)
|
100
|
-
- `[{}]` is **not** hash type argument. it is keyword argument because its position is last
|
101
|
-
- `[{}, {}]` is empty hash type argument (first) and one empty keyword argument (second)
|
102
|
-
- `[{}, {}, {}]` is two empty hash type argument (first, second) and empty keyword argument (last)
|
99
|
+
- vs. Keyword arguments (e.g.)
|
100
|
+
- `[{}]` is **not** hash type argument. it is keyword argument, because its position is last
|
101
|
+
- `[{}, {}]` is empty hash type argument (first), and one empty keyword argument (second)
|
102
|
+
- `[{}, {}, {}]` is two empty hash type argument (first, second), and empty keyword argument (last)
|
103
103
|
- `{}` is keyword argument. non-keyword arguments must be in array.
|
104
104
|
- Of course, nested hash works
|
105
105
|
- Example: [Hash](#hash)
|
106
106
|
- `Proc`
|
107
|
-
-
|
107
|
+
- A value must return a truthy value for the proc
|
108
108
|
- `true`
|
109
|
-
-
|
109
|
+
- A value must be **truthy**
|
110
110
|
- `false`
|
111
|
-
-
|
111
|
+
- A value must be **falsy**
|
112
112
|
- `nil`
|
113
|
-
-
|
113
|
+
- A value must be nil
|
114
|
+
|
114
115
|
- Special Behaviors
|
115
|
-
- `Rtype::
|
116
|
+
- `Rtype::TypedArray` : Ensures a value is an array with the type (type signature)
|
117
|
+
- `Array::of(type)` (recommended)
|
118
|
+
- `Rtype::Behavior::TypedArray[type]`
|
119
|
+
- Example: [TypedArray](#typed-array)
|
120
|
+
|
121
|
+
- `Rtype::and(*types)` : Ensures a value is valid for all the types
|
116
122
|
- `Rtype::and(*types)`, `Rtype::Behavior::And[*types]`, `include Rtype::Behavior; And[...]`
|
117
123
|
- `Array#comb`
|
118
124
|
- `Object#and(*others)`
|
119
125
|
|
120
|
-
- `Rtype::xor(*types)` :
|
126
|
+
- `Rtype::xor(*types)` : Ensures a value is valid for only one of the types
|
121
127
|
- `Rtype::xor(*types)`, `Rtype::Behavior::Xor[*types]`, `include Rtype::Behavior; Xor[...]`
|
122
128
|
- `Object#xor(*others)`
|
123
129
|
|
124
|
-
- `Rtype::not(*types)` :
|
130
|
+
- `Rtype::not(*types)` : Ensures a value is not valid for all the types
|
125
131
|
- `Rtype::not(*types)`, `Rtype::Behavior::Not[*types]`, `include Rtype::Behavior; Not[...]`
|
126
132
|
- `Object#not`
|
127
133
|
|
128
|
-
- `Rtype::nilable(type)` :
|
134
|
+
- `Rtype::nilable(type)` : Ensures a value can be nil
|
129
135
|
- `Rtype::nilable(type)`, `Rtype::Behavior::Nilable[type]`, `include Rtype::Behavior; Nilable[...]`
|
130
136
|
- `Object#nilable`
|
131
137
|
- `Object#or_nil`
|
@@ -231,29 +237,29 @@ end
|
|
231
237
|
# last hash is keyword arguments
|
232
238
|
func({}, {})
|
233
239
|
# (Rtype::ArgumentTypeError) for 1st argument:
|
234
|
-
# Expected {} to be
|
240
|
+
# Expected {} to be a hash with 1 elements:
|
235
241
|
# - msg : Expected nil to be a String
|
236
242
|
|
237
243
|
func({msg: 123}, {})
|
238
244
|
# (Rtype::ArgumentTypeError) for 1st argument:
|
239
|
-
# Expected {:msg=>123} to be
|
245
|
+
# Expected {:msg=>123} to be a hash with 1 elements:
|
240
246
|
# - msg : Expected 123 to be a String
|
241
247
|
|
242
248
|
func({msg: "hello", key: 'value'}, {})
|
243
249
|
# (Rtype::ArgumentTypeError) for 1st argument:
|
244
|
-
# Expected {:msg=>"hello", :key=>"value"} to be
|
250
|
+
# Expected {:msg=>"hello", :key=>"value"} to be a hash with 1 elements:
|
245
251
|
# - msg : Expected "hello" to be a String
|
246
252
|
|
247
253
|
func({"msg" => "hello hash"}, {})
|
248
254
|
# (Rtype::ArgumentTypeError) for 1st argument:
|
249
|
-
# Expected {"msg"=>"hello hash"} to be
|
255
|
+
# Expected {"msg"=>"hello hash"} to be a hash with 1 elements:
|
250
256
|
# - msg : Expected nil to be a String
|
251
257
|
|
252
258
|
func({msg: "hello hash"}, {}) # hello hash
|
253
259
|
```
|
254
260
|
|
255
261
|
#### rtype with attr_accessor
|
256
|
-
`rtype_accessor` : calls `attr_accessor` if the accessor method(getter/setter) is not defined
|
262
|
+
`rtype_accessor` : calls `attr_accessor` if the accessor method(getter/setter) is not defined. and makes it typed
|
257
263
|
|
258
264
|
You can use `rtype_accessor_self` for static accessor.
|
259
265
|
|
@@ -277,6 +283,38 @@ Example.new.value
|
|
277
283
|
# Expected 456 to be a String
|
278
284
|
```
|
279
285
|
|
286
|
+
#### Typed Array
|
287
|
+
```ruby
|
288
|
+
### TEST 1 ###
|
289
|
+
class Test
|
290
|
+
rtype [Array.of(Integer)] => Any
|
291
|
+
def sum(args)
|
292
|
+
num = 0
|
293
|
+
args.each { |e| num += e }
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
sum([1, 2, 3]) # => 6
|
298
|
+
|
299
|
+
sum([1.0, 2, 3])
|
300
|
+
# (Rtype::ArgumentTypeError) for 1st argument:
|
301
|
+
# Expected [1.0, 2, 3] to be an array with type Integer"
|
302
|
+
```
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
### TEST 2 ###
|
306
|
+
class Test
|
307
|
+
rtype [ Array.of([Integer, Float]) ] => Any
|
308
|
+
def sum(args)
|
309
|
+
num = 0
|
310
|
+
args.each { |e| num += e }
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
sum([1, 2, 3]) # => 6
|
315
|
+
sum([1.0, 2, 3]) # => 6.0
|
316
|
+
```
|
317
|
+
|
280
318
|
#### Combined type
|
281
319
|
```ruby
|
282
320
|
### TEST 1 ###
|
@@ -342,7 +380,7 @@ Game::Player.new.attacks Game::Slime.new
|
|
342
380
|
# Player attacks 'Powerful Slime' (level 123)!
|
343
381
|
```
|
344
382
|
|
345
|
-
#### Position of `rtype` && (
|
383
|
+
#### Position of `rtype` && (specifying method name || annotation mode) && (symbol || string)
|
346
384
|
```ruby
|
347
385
|
require 'rtype'
|
348
386
|
|
@@ -371,7 +409,7 @@ class Example
|
|
371
409
|
puts "Hello? #{i} #{str}"
|
372
410
|
end
|
373
411
|
|
374
|
-
#
|
412
|
+
# Doesn't work. `rtype` works for following (next) method
|
375
413
|
def hello_world_four(i, str)
|
376
414
|
puts "Hello? #{i} #{str}"
|
377
415
|
end
|
@@ -380,7 +418,7 @@ end
|
|
380
418
|
```
|
381
419
|
|
382
420
|
#### Outside of module (root)
|
383
|
-
|
421
|
+
In the outside of module, annotation mode don't works. You must specify method name.
|
384
422
|
|
385
423
|
```ruby
|
386
424
|
rtype :say, [String] => Any
|
@@ -394,7 +432,7 @@ rtype [String] => Any
|
|
394
432
|
# (ArgumentError) Annotation mode not working out of module
|
395
433
|
```
|
396
434
|
|
397
|
-
####
|
435
|
+
#### Class method
|
398
436
|
rtype annotation mode works both instance and class method
|
399
437
|
|
400
438
|
```ruby
|
@@ -410,7 +448,7 @@ end
|
|
410
448
|
Example::say_ya(3) #say ya ya ya
|
411
449
|
```
|
412
450
|
|
413
|
-
|
451
|
+
if you specify method name, however, you must use `rtype_self` instead of `rtype`
|
414
452
|
|
415
453
|
```ruby
|
416
454
|
require 'rtype'
|
@@ -425,7 +463,7 @@ end
|
|
425
463
|
Example::say_ya(3) #say ya ya ya
|
426
464
|
```
|
427
465
|
|
428
|
-
####
|
466
|
+
#### Checking type information
|
429
467
|
This is just the 'information'
|
430
468
|
|
431
469
|
Any change of this doesn't affect type checking
|
@@ -524,7 +562,7 @@ Comparison:
|
|
524
562
|
## Rubype, Sig
|
525
563
|
Rtype is influenced by [Rubype](https://github.com/gogotanaka/Rubype) and [Sig](https://github.com/janlelis/sig).
|
526
564
|
|
527
|
-
If you don't like Rtype, You can use other
|
565
|
+
If you don't like Rtype, You can use other library such as Contracts, Rubype, Rtc, Typecheck, Sig.
|
528
566
|
|
529
567
|
## Author
|
530
568
|
Sputnik Gugja (sputnikgugja@gmail.com)
|
data/lib/rtype.rb
CHANGED
@@ -2,16 +2,16 @@ if defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby"
|
|
2
2
|
begin
|
3
3
|
require 'java'
|
4
4
|
require 'rtype/rtype_java'
|
5
|
-
puts "Rtype with Java extension"
|
5
|
+
# puts "Rtype with Java extension"
|
6
6
|
rescue LoadError
|
7
|
-
puts "Rtype without native extension"
|
7
|
+
# puts "Rtype without native extension"
|
8
8
|
end
|
9
9
|
else
|
10
10
|
begin
|
11
11
|
require "rtype/rtype_native"
|
12
|
-
puts "Rtype with C native extension"
|
12
|
+
# puts "Rtype with C native extension"
|
13
13
|
rescue LoadError
|
14
|
-
puts "Rtype without native extension"
|
14
|
+
# puts "Rtype without native extension"
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
@@ -28,10 +28,18 @@ require_relative 'rtype/behavior'
|
|
28
28
|
module Rtype
|
29
29
|
extend self
|
30
30
|
|
31
|
-
# This is just
|
31
|
+
# This is just 'information'
|
32
32
|
# Any change of this doesn't affect type checking
|
33
33
|
@@type_signatures = Hash.new
|
34
34
|
|
35
|
+
# Makes the method typed
|
36
|
+
# @param owner Owner of the method
|
37
|
+
# @param [#to_sym] method_name
|
38
|
+
# @param [Hash] type_sig_info A type signature. e.g. `[Integer, Float] => Float`
|
39
|
+
# @return [void]
|
40
|
+
#
|
41
|
+
# @raise [ArgumentError] If method_name is nil
|
42
|
+
# @raise [TypeSignatureError] If type_sig_info is invalid
|
35
43
|
def define_typed_method(owner, method_name, type_sig_info)
|
36
44
|
method_name = method_name.to_sym
|
37
45
|
raise ArgumentError, "method_name is nil" if method_name.nil?
|
@@ -64,7 +72,20 @@ module Rtype
|
|
64
72
|
define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
65
73
|
end
|
66
74
|
|
75
|
+
# Calls `attr_accessor` if the accessor method(getter/setter) is not defined.
|
76
|
+
# and makes it typed.
|
77
|
+
#
|
78
|
+
# this method uses `define_typed_method` for getter and setter.
|
79
|
+
#
|
80
|
+
# @param owner Owner of the accessor
|
81
|
+
# @param [#to_sym] accessor_name
|
82
|
+
# @param type_behavior A type behavior. e.g. Integer
|
83
|
+
# @return [void]
|
84
|
+
#
|
85
|
+
# @raise [ArgumentError] If accessor_name is nil
|
86
|
+
# @raise [TypeSignatureError]
|
67
87
|
def define_typed_accessor(owner, accessor_name, type_behavior)
|
88
|
+
raise ArgumentError, "accessor_name is nil" if accessor_name.nil?
|
68
89
|
getter = accessor_name.to_sym
|
69
90
|
setter = :"#{accessor_name}="
|
70
91
|
valid?(type_behavior, nil)
|
@@ -72,6 +93,11 @@ module Rtype
|
|
72
93
|
define_typed_method owner, setter, [type_behavior] => Any
|
73
94
|
end
|
74
95
|
|
96
|
+
# This is just 'information'
|
97
|
+
# Any change of this doesn't affect type checking
|
98
|
+
#
|
99
|
+
# @return [Hash]
|
100
|
+
# @note type_signatures[owner][method_name]
|
75
101
|
def type_signatures
|
76
102
|
@@type_signatures
|
77
103
|
end
|
@@ -89,22 +115,44 @@ module Rtype
|
|
89
115
|
end
|
90
116
|
=end
|
91
117
|
|
118
|
+
# @param [Integer] idx
|
119
|
+
# @param expected A type behavior
|
120
|
+
# @param value
|
121
|
+
# @return [String] A error message
|
122
|
+
#
|
123
|
+
# @raise [ArgumentError] If expected is invalid
|
92
124
|
def arg_type_error_message(idx, expected, value)
|
93
125
|
"#{arg_message(idx)}\n" + type_error_message(expected, value)
|
94
126
|
end
|
95
127
|
|
128
|
+
# @param [String, Symbol] key
|
129
|
+
# @param expected A type behavior
|
130
|
+
# @param value
|
131
|
+
# @return [String] A error message
|
132
|
+
#
|
133
|
+
# @raise [ArgumentError] If expected is invalid
|
96
134
|
def kwarg_type_error_message(key, expected, value)
|
97
135
|
"#{kwarg_message(key)}\n" + type_error_message(expected, value)
|
98
136
|
end
|
99
|
-
|
137
|
+
|
138
|
+
# @return [String]
|
100
139
|
def arg_message(idx)
|
101
140
|
"for #{ordinalize_number(idx+1)} argument:"
|
102
141
|
end
|
103
142
|
|
143
|
+
# @return [String]
|
104
144
|
def kwarg_message(key)
|
105
145
|
"for '#{key}' argument:"
|
106
146
|
end
|
107
147
|
|
148
|
+
# Returns a error message for the pair of type behavior and value
|
149
|
+
#
|
150
|
+
# @param expected A type behavior
|
151
|
+
# @param value
|
152
|
+
# @return [String] error message
|
153
|
+
#
|
154
|
+
# @note This method doesn't check the value is valid
|
155
|
+
# @raise [TypeSignatureError] If expected is invalid
|
108
156
|
def type_error_message(expected, value)
|
109
157
|
case expected
|
110
158
|
when Rtype::Behavior::Base
|
@@ -130,9 +178,9 @@ module Rtype
|
|
130
178
|
arr << "- #{k} : " + type_error_message(v, value[k])
|
131
179
|
end
|
132
180
|
end
|
133
|
-
"Expected #{value.inspect} to be
|
181
|
+
"Expected #{value.inspect} to be a hash with #{expected.length} elements:\n" + arr.join("\n")
|
134
182
|
else
|
135
|
-
"Expected #{value.inspect} to be
|
183
|
+
"Expected #{value.inspect} to be a hash"
|
136
184
|
end
|
137
185
|
when Proc
|
138
186
|
"Expected #{value.inspect} to return a truthy value for proc #{expected}"
|
@@ -142,9 +190,19 @@ module Rtype
|
|
142
190
|
"Expected #{value.inspect} to be a falsy value"
|
143
191
|
when nil # for return
|
144
192
|
"Expected #{value.inspect} to be nil"
|
193
|
+
else
|
194
|
+
raise TypeSignatureError, "Invalid type behavior #{expected}"
|
145
195
|
end
|
146
196
|
end
|
147
197
|
|
198
|
+
# Checks the type signature is valid
|
199
|
+
#
|
200
|
+
# e.g.
|
201
|
+
# `[Integer] => Any` is valid.
|
202
|
+
# `[Integer]` or `Any` are invalid
|
203
|
+
#
|
204
|
+
# @param sig A type signature
|
205
|
+
# @raise [TypeSignatureError] If sig is invalid
|
148
206
|
def assert_valid_type_sig(sig)
|
149
207
|
unless sig.is_a?(Hash)
|
150
208
|
raise TypeSignatureError, "Invalid type signature: type signature is not hash"
|
@@ -156,6 +214,14 @@ module Rtype
|
|
156
214
|
assert_valid_return_type_sig(sig.first[1])
|
157
215
|
end
|
158
216
|
|
217
|
+
# Checks the arguments type signature is valid
|
218
|
+
#
|
219
|
+
# e.g.
|
220
|
+
# `[Integer]`, `{key: "value"} are valid.
|
221
|
+
# `Integer` is invalid
|
222
|
+
#
|
223
|
+
# @param sig A arguments type signature
|
224
|
+
# @raise [TypeSignatureError] If sig is invalid
|
159
225
|
def assert_valid_arguments_type_sig(sig)
|
160
226
|
if sig.is_a?(Array)
|
161
227
|
sig = sig.dup
|
@@ -179,6 +245,10 @@ module Rtype
|
|
179
245
|
end
|
180
246
|
end
|
181
247
|
|
248
|
+
# Checks the type behavior is valid
|
249
|
+
#
|
250
|
+
# @param sig A type behavior
|
251
|
+
# @raise [TypeSignatureError] If sig is invalid
|
182
252
|
def assert_valid_argument_type_sig_element(sig)
|
183
253
|
case sig
|
184
254
|
when Rtype::Behavior::Base
|
@@ -203,42 +273,19 @@ module Rtype
|
|
203
273
|
end
|
204
274
|
end
|
205
275
|
|
276
|
+
# @see #assert_valid_argument_type_sig_element
|
206
277
|
def assert_valid_return_type_sig(sig)
|
207
278
|
assert_valid_argument_type_sig_element(sig)
|
208
279
|
end
|
209
|
-
|
210
|
-
private
|
211
|
-
def define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
212
|
-
# `send` is faster than `method(...).call`
|
213
|
-
owner.send(:_rtype_proxy).send :define_method, method_name do |*args, **kwargs, &block|
|
214
|
-
if kwargs.empty?
|
215
|
-
::Rtype::assert_arguments_type(expected_args, args)
|
216
|
-
result = super(*args, &block)
|
217
|
-
else
|
218
|
-
::Rtype::assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
219
|
-
result = super(*args, **kwargs, &block)
|
220
|
-
end
|
221
|
-
::Rtype::assert_return_type(return_sig, result)
|
222
|
-
result
|
223
|
-
end
|
224
|
-
nil
|
225
|
-
end
|
226
280
|
|
227
|
-
def ordinalize_number(num)
|
228
|
-
if (11..13).include?(num % 100)
|
229
|
-
"#{num}th"
|
230
|
-
else
|
231
|
-
case num % 10
|
232
|
-
when 1; "#{num}st"
|
233
|
-
when 2; "#{num}nd"
|
234
|
-
when 3; "#{num}rd"
|
235
|
-
else "#{num}th"
|
236
|
-
end
|
237
|
-
end
|
238
|
-
end
|
239
|
-
public
|
240
281
|
unless respond_to?(:valid?)
|
241
|
-
#
|
282
|
+
# Checks the value is valid for the type behavior
|
283
|
+
#
|
284
|
+
# @param expected A type behavior
|
285
|
+
# @param value
|
286
|
+
# @return [Boolean]
|
287
|
+
#
|
288
|
+
# @raise [TypeSignatureError] If expected is invalid
|
242
289
|
def valid?(expected, value)
|
243
290
|
case expected
|
244
291
|
when Module
|
@@ -272,6 +319,14 @@ public
|
|
272
319
|
end
|
273
320
|
|
274
321
|
unless respond_to?(:assert_arguments_type)
|
322
|
+
# Validates arguments
|
323
|
+
#
|
324
|
+
# @param [Array] expected_args A type signature for non-keyword arguments
|
325
|
+
# @param args
|
326
|
+
# @return [void]
|
327
|
+
#
|
328
|
+
# @raise [TypeSignatureError] If expected_args is invalid
|
329
|
+
# @raise [ArgumentTypeError] If args is invalid
|
275
330
|
def assert_arguments_type(expected_args, args)
|
276
331
|
e_len = expected_args.length
|
277
332
|
# `length.times` is faster than `each_with_index`
|
@@ -283,10 +338,21 @@ public
|
|
283
338
|
raise ArgumentTypeError, "#{arg_message(i)}\n" + type_error_message(expected, value)
|
284
339
|
end
|
285
340
|
end
|
341
|
+
nil
|
286
342
|
end
|
287
343
|
end
|
288
344
|
|
289
345
|
unless respond_to?(:assert_arguments_type_with_keywords)
|
346
|
+
# Validates arguments and keyword arguments
|
347
|
+
#
|
348
|
+
# @param [Array] expected_args A type signature for non-keyword arguments
|
349
|
+
# @param args Arguments
|
350
|
+
# @param [Hash] expected_kwargs A type signature for keyword arguments
|
351
|
+
# @param kwargs Keword arguments
|
352
|
+
# @return [void]
|
353
|
+
#
|
354
|
+
# @raise [TypeSignatureError] If expected_args or expected_kwargs are invalid
|
355
|
+
# @raise [ArgumentTypeError] If args or kwargs are invalid
|
290
356
|
def assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
291
357
|
e_len = expected_args.length
|
292
358
|
# `length.times` is faster than `each_with_index`
|
@@ -307,14 +373,62 @@ public
|
|
307
373
|
end
|
308
374
|
end
|
309
375
|
end
|
376
|
+
nil
|
310
377
|
end
|
311
378
|
end
|
312
379
|
|
380
|
+
# Validates result
|
381
|
+
#
|
382
|
+
# @param expected A type behavior
|
383
|
+
# @param result
|
384
|
+
# @return [void]
|
385
|
+
#
|
386
|
+
# @raise [TypeSignatureError] If expected is invalid
|
387
|
+
# @raise [ReturnTypeError] If result is invalid
|
313
388
|
unless respond_to?(:assert_return_type)
|
314
389
|
def assert_return_type(expected, result)
|
315
390
|
unless valid?(expected, result)
|
316
391
|
raise ReturnTypeError, "for return:\n" + type_error_message(expected, result)
|
317
392
|
end
|
393
|
+
nil
|
318
394
|
end
|
319
395
|
end
|
396
|
+
|
397
|
+
private
|
398
|
+
# @param owner
|
399
|
+
# @param [Symbol] method_name
|
400
|
+
# @param expected_args
|
401
|
+
# @param expected_kwargs
|
402
|
+
# @param return_sig
|
403
|
+
# @return [void]
|
404
|
+
def define_typed_method_to_proxy(owner, method_name, expected_args, expected_kwargs, return_sig)
|
405
|
+
# `send` is faster than `method(...).call`
|
406
|
+
owner.send(:_rtype_proxy).send :define_method, method_name do |*args, **kwargs, &block|
|
407
|
+
if kwargs.empty?
|
408
|
+
::Rtype::assert_arguments_type(expected_args, args)
|
409
|
+
result = super(*args, &block)
|
410
|
+
else
|
411
|
+
::Rtype::assert_arguments_type_with_keywords(expected_args, args, expected_kwargs, kwargs)
|
412
|
+
result = super(*args, **kwargs, &block)
|
413
|
+
end
|
414
|
+
::Rtype::assert_return_type(return_sig, result)
|
415
|
+
result
|
416
|
+
end
|
417
|
+
nil
|
418
|
+
end
|
419
|
+
|
420
|
+
# @param [Integer] num
|
421
|
+
# @return [String]
|
422
|
+
def ordinalize_number(num)
|
423
|
+
if (11..13).include?(num % 100)
|
424
|
+
"#{num}th"
|
425
|
+
else
|
426
|
+
case num % 10
|
427
|
+
when 1; "#{num}st"
|
428
|
+
when 2; "#{num}nd"
|
429
|
+
when 3; "#{num}rd"
|
430
|
+
else "#{num}th"
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
320
434
|
end
|
data/lib/rtype/behavior.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rtype
|
2
|
+
module Behavior
|
3
|
+
class TypedArray < Base
|
4
|
+
def initialize(type)
|
5
|
+
@type = type
|
6
|
+
Rtype.assert_valid_argument_type_sig_element(@type)
|
7
|
+
end
|
8
|
+
|
9
|
+
def valid?(value)
|
10
|
+
if value.is_a?(Array)
|
11
|
+
any = value.any? do |e|
|
12
|
+
!Rtype::valid?(@type, e)
|
13
|
+
end
|
14
|
+
!any
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def error_message(value)
|
21
|
+
"Expected #{value.inspect} to be an array with type #{@type.inspect}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/rtype/core_ext.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# true or false
|
1
2
|
module Boolean; end
|
2
3
|
class TrueClass; include Boolean; end
|
3
4
|
class FalseClass; include Boolean; end
|
5
|
+
|
4
6
|
Any = BasicObject
|
5
7
|
|
6
8
|
class Object
|
@@ -17,74 +19,119 @@ private
|
|
17
19
|
@_rtype_proxy
|
18
20
|
end
|
19
21
|
|
22
|
+
# Makes the method typed
|
23
|
+
#
|
24
|
+
# With 'annotation mode', this method works for both instance method and singleton method (class method).
|
25
|
+
# Without it (specifying method name), this method only works for instance method.
|
26
|
+
#
|
27
|
+
# @param [#to_sym, nil] method_name The name of method. If nil, annotation mode works
|
28
|
+
# @param [Hash] type_sig_info A type signature. e.g. [Integer] => Any
|
29
|
+
# @return [void]
|
30
|
+
#
|
31
|
+
# @note Annotation mode doesn't work in the outside of module
|
32
|
+
# @raise [ArgumentError] If method_name is nil in the outside of module
|
33
|
+
# @raise [TypeSignatureError] If type_sig_info is invalid
|
20
34
|
def rtype(method_name=nil, type_sig_info)
|
21
35
|
if is_a?(Module)
|
22
36
|
if method_name.nil?
|
23
37
|
::Rtype::assert_valid_type_sig(type_sig_info)
|
24
38
|
_rtype_proxy.annotation_mode = true
|
25
39
|
_rtype_proxy.annotation_type_sig = type_sig_info
|
40
|
+
nil
|
26
41
|
else
|
27
42
|
::Rtype::define_typed_method(self, method_name, type_sig_info)
|
28
43
|
end
|
29
44
|
else
|
30
45
|
if method_name.nil?
|
31
|
-
raise ArgumentError, "Annotation mode
|
46
|
+
raise ArgumentError, "Annotation mode doesn't work in the outside of module"
|
32
47
|
else
|
33
48
|
rtype_self(method_name, type_sig_info)
|
34
49
|
end
|
35
50
|
end
|
36
51
|
end
|
37
52
|
|
53
|
+
# Makes the singleton method (class method) typed
|
54
|
+
#
|
55
|
+
# @param [#to_sym] method_name
|
56
|
+
# @param [Hash] type_sig_info A type signature. e.g. [Integer] => Any
|
57
|
+
# @return [void]
|
58
|
+
#
|
59
|
+
# @raise [ArgumentError] If method_name is nil
|
60
|
+
# @raise [TypeSignatureError] If type_sig_info is invalid
|
38
61
|
def rtype_self(method_name, type_sig_info)
|
39
62
|
::Rtype.define_typed_method(singleton_class, method_name, type_sig_info)
|
40
63
|
end
|
64
|
+
|
65
|
+
# Makes the accessor methods (getter and setter) typed
|
66
|
+
#
|
67
|
+
# @param [Array<#to_sym>] accessor_names
|
68
|
+
# @param type_behavior A type behavior
|
69
|
+
# @return [void]
|
70
|
+
#
|
71
|
+
# @raise [ArgumentError] If accessor_names contains nil
|
72
|
+
# @raise [TypeSignatureError] If type_behavior is invalid
|
73
|
+
# @see #rtype
|
74
|
+
def rtype_accessor(*accessor_names, type_behavior)
|
75
|
+
accessor_names.each do |accessor_name|
|
76
|
+
accessor_name = accessor_name.to_sym
|
77
|
+
if !respond_to?(accessor_name) || !respond_to?(:"#{accessor_name}=")
|
78
|
+
attr_accessor accessor_name
|
79
|
+
end
|
41
80
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
if is_a?(Module)
|
49
|
-
::Rtype::define_typed_accessor(self, accessor_name, type_behavior)
|
50
|
-
else
|
51
|
-
rtype_accessor_self(accessor_name, type_behavior)
|
81
|
+
if is_a?(Module)
|
82
|
+
::Rtype::define_typed_accessor(self, accessor_name, type_behavior)
|
83
|
+
else
|
84
|
+
rtype_accessor_self(accessor_name, type_behavior)
|
85
|
+
end
|
52
86
|
end
|
87
|
+
nil
|
53
88
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
89
|
+
|
90
|
+
# Makes the accessor methods (getter and setter) typed
|
91
|
+
#
|
92
|
+
# @param [Array<#to_sym>] accessor_names
|
93
|
+
# @param type_behavior A type behavior
|
94
|
+
# @return [void]
|
95
|
+
#
|
96
|
+
# @raise [ArgumentError] If accessor_names contains nil
|
97
|
+
# @raise [TypeSignatureError] If type_behavior is invalid
|
98
|
+
# @see #rtype_self
|
99
|
+
def rtype_accessor_self(*accessor_names, type_behavior)
|
100
|
+
accessor_names.each do |accessor_name|
|
101
|
+
accessor_name = accessor_name.to_sym
|
102
|
+
if !respond_to?(accessor_name) || !respond_to?(:"#{accessor_name}=")
|
103
|
+
singleton_class.send(:attr_accessor, accessor_name)
|
104
|
+
end
|
105
|
+
::Rtype::define_typed_accessor(singleton_class, accessor_name, type_behavior)
|
59
106
|
end
|
60
|
-
|
107
|
+
nil
|
61
108
|
end
|
62
109
|
end
|
63
110
|
|
64
111
|
class Method
|
112
|
+
# @return [Boolean] Whether the method is typed with rtype
|
65
113
|
def typed?
|
66
114
|
!!::Rtype.type_signatures[owner][name]
|
67
115
|
end
|
68
116
|
|
117
|
+
# @return [TypeSignature]
|
69
118
|
def type_signature
|
70
119
|
::Rtype.type_signatures[owner][name]
|
71
120
|
end
|
72
121
|
|
122
|
+
# @return [Hash]
|
123
|
+
# @see TypeSignature#info
|
73
124
|
def type_info
|
74
125
|
::Rtype.type_signatures[owner][name].info
|
75
126
|
end
|
76
|
-
|
127
|
+
|
128
|
+
# @return [Array, Hash]
|
77
129
|
def argument_type
|
78
130
|
::Rtype.type_signatures[owner][name].argument_type
|
79
131
|
end
|
80
132
|
|
133
|
+
# @return A type behavior
|
81
134
|
def return_type
|
82
135
|
::Rtype.type_signatures[owner][name].return_type
|
83
136
|
end
|
84
137
|
end
|
85
|
-
|
86
|
-
class Array
|
87
|
-
def comb
|
88
|
-
::Rtype::Behavior::And[*self]
|
89
|
-
end
|
90
|
-
end
|
data/lib/rtype/type_signature.rb
CHANGED
data/lib/rtype/version.rb
CHANGED
data/spec/rtype_spec.rb
CHANGED
@@ -150,24 +150,30 @@ describe Rtype do
|
|
150
150
|
|
151
151
|
it 'Kernel#rtype_accessor' do
|
152
152
|
class TestClass
|
153
|
-
rtype_accessor :value, String
|
153
|
+
rtype_accessor :value, :value2, String
|
154
154
|
|
155
155
|
def initialize
|
156
156
|
@value = 123
|
157
|
+
@value2 = 123
|
157
158
|
end
|
158
159
|
end
|
159
160
|
expect {TestClass.new.value = 123}.to raise_error Rtype::ArgumentTypeError
|
160
161
|
expect {TestClass.new.value}.to raise_error Rtype::ReturnTypeError
|
162
|
+
expect {TestClass.new.value2 = 123}.to raise_error Rtype::ArgumentTypeError
|
163
|
+
expect {TestClass.new.value2}.to raise_error Rtype::ReturnTypeError
|
161
164
|
end
|
162
165
|
|
163
166
|
it 'Kernel#rtype_accessor_self' do
|
164
167
|
class TestClass
|
165
|
-
@@
|
168
|
+
@@value = 123
|
169
|
+
@@value2 = 123
|
166
170
|
|
167
|
-
rtype_accessor_self :value, String
|
171
|
+
rtype_accessor_self :value, :value2, String
|
168
172
|
end
|
169
173
|
expect {TestClass::value = 123}.to raise_error Rtype::ArgumentTypeError
|
170
174
|
expect {TestClass::value}.to raise_error Rtype::ReturnTypeError
|
175
|
+
expect {TestClass::value2 = 123}.to raise_error Rtype::ArgumentTypeError
|
176
|
+
expect {TestClass::value2}.to raise_error Rtype::ReturnTypeError
|
171
177
|
end
|
172
178
|
|
173
179
|
describe 'Test type behaviors' do
|
@@ -440,6 +446,34 @@ describe Rtype do
|
|
440
446
|
expect {instance.return_nil("abc")}.to raise_error Rtype::ArgumentTypeError
|
441
447
|
end
|
442
448
|
end
|
449
|
+
|
450
|
+
describe 'Rtype::Behavior::TypedArray' do
|
451
|
+
it 'class singleton [] method' do
|
452
|
+
klass.send :rtype, :return_nil, [ Rtype::Behavior::TypedArray[Integer] ] => nil
|
453
|
+
instance.return_nil([123])
|
454
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ArgumentTypeError
|
455
|
+
expect {instance.return_nil([1.0])}.to raise_error Rtype::ArgumentTypeError
|
456
|
+
end
|
457
|
+
|
458
|
+
it 'core extension method (Array::of)' do
|
459
|
+
klass.send :rtype, :return_nil, [ Array.of(Integer) ] => nil
|
460
|
+
instance.return_nil([123])
|
461
|
+
expect {instance.return_nil(123)}.to raise_error Rtype::ArgumentTypeError
|
462
|
+
expect {instance.return_nil([1.0])}.to raise_error Rtype::ArgumentTypeError
|
463
|
+
end
|
464
|
+
|
465
|
+
it 'complicated type sig' do
|
466
|
+
klass.send :rtype, :return_nil, [ Array.of(:to_i.and(:chars)) ] => nil
|
467
|
+
instance.return_nil(["hello"])
|
468
|
+
expect {instance.return_nil("hello")}.to raise_error Rtype::ArgumentTypeError
|
469
|
+
expect {instance.return_nil([123])}.to raise_error Rtype::ArgumentTypeError
|
470
|
+
end
|
471
|
+
|
472
|
+
it 'allows empty array' do
|
473
|
+
klass.send :rtype, :return_nil, [ Array.of(Integer) ] => nil
|
474
|
+
instance.return_nil([])
|
475
|
+
end
|
476
|
+
end
|
443
477
|
end
|
444
478
|
end
|
445
479
|
|
data/spec/spec_helper.rb
CHANGED
@@ -2,4 +2,12 @@ require 'coveralls'
|
|
2
2
|
Coveralls.wear!
|
3
3
|
|
4
4
|
require 'rtype'
|
5
|
-
require 'rspec'
|
5
|
+
require 'rspec'
|
6
|
+
|
7
|
+
if !Rtype::NATIVE_EXT_VERSION.nil?
|
8
|
+
puts "Rtype with native extension"
|
9
|
+
elsif !Rtype::JAVA_EXT_VERSION.nil?
|
10
|
+
puts "Rtype with java extension"
|
11
|
+
else
|
12
|
+
puts "Rtype without native extension"
|
13
|
+
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.6.
|
4
|
+
version: 0.6.3
|
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-
|
11
|
+
date: 2016-07-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- lib/rtype/behavior/core_ext.rb
|
73
73
|
- lib/rtype/behavior/nilable.rb
|
74
74
|
- lib/rtype/behavior/not.rb
|
75
|
+
- lib/rtype/behavior/typed_array.rb
|
75
76
|
- lib/rtype/behavior/xor.rb
|
76
77
|
- lib/rtype/core_ext.rb
|
77
78
|
- lib/rtype/method_annotator.rb
|