dry-types 0.10.3 → 0.11.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/.travis.yml +2 -6
- data/CHANGELOG.md +10 -0
- data/CONTRIBUTING.md +29 -0
- data/README.md +1 -1
- data/dry-types.gemspec +1 -1
- data/lib/dry/types.rb +6 -0
- data/lib/dry/types/array.rb +2 -0
- data/lib/dry/types/array/member.rb +7 -0
- data/lib/dry/types/compiler.rb +38 -35
- data/lib/dry/types/constrained.rb +9 -0
- data/lib/dry/types/constructor.rb +13 -0
- data/lib/dry/types/definition.rb +9 -0
- data/lib/dry/types/enum.rb +7 -0
- data/lib/dry/types/errors.rb +3 -2
- data/lib/dry/types/fn_container.rb +33 -0
- data/lib/dry/types/hash/schema.rb +39 -2
- data/lib/dry/types/options.rb +7 -1
- data/lib/dry/types/safe.rb +13 -0
- data/lib/dry/types/sum.rb +7 -0
- data/lib/dry/types/version.rb +1 -1
- metadata +6 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12cffdbb9c18b6f1ab0bb726c5a13fbd13c1178e
|
4
|
+
data.tar.gz: 150931c98379624c229416370bd63cd8830bbab5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b6bd371faf9ae94c6559c01309216205c3df88614c86cb2862929b2836d8cc4c32b76d916f6deeb6e4390bb296c8e1733e85e8997b3f00092584960961e02ee
|
7
|
+
data.tar.gz: 584c9552a9185595008a63c2ca6f3a8cc88ec56b7c063f720d36ff10d9c1b26b403443456416a484429cae4b7e10bd6a06995acd3adf99eb89b1026825a9fd83
|
data/.travis.yml
CHANGED
@@ -9,17 +9,13 @@ script:
|
|
9
9
|
- bundle exec rake
|
10
10
|
rvm:
|
11
11
|
- 2.2.7
|
12
|
-
- 2.3.
|
12
|
+
- 2.3.4
|
13
13
|
- 2.4.1
|
14
|
-
- jruby-9.1.
|
15
|
-
- rbx-3
|
14
|
+
- jruby-9.1.10.0
|
16
15
|
env:
|
17
16
|
global:
|
18
17
|
- COVERAGE=true
|
19
18
|
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
20
|
-
matrix:
|
21
|
-
allow_failures:
|
22
|
-
- rvm: rbx-3
|
23
19
|
notifications:
|
24
20
|
email: false
|
25
21
|
webhooks:
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
# v0.11.0 2017-06-30
|
2
|
+
|
3
|
+
## Added
|
4
|
+
|
5
|
+
* `#to_ast` available for all type objects (GustavoCaso)
|
6
|
+
* `Types::Array#of` as an alias for `#member` (maliqq)
|
7
|
+
* Detailed failure objects are passed to results which improves constraint violation messages (GustavoCaso)
|
8
|
+
|
9
|
+
[Compare v0.10.3...v0.11.0](https://github.com/dry-rb/dry-types/compare/v0.10.3...v0.11.0)
|
10
|
+
|
1
11
|
# v0.10.3 2017-05-06
|
2
12
|
|
3
13
|
## Added
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Issue Guidelines
|
2
|
+
|
3
|
+
## Reporting bugs
|
4
|
+
|
5
|
+
If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated.
|
6
|
+
|
7
|
+
## Reporting feature requests
|
8
|
+
|
9
|
+
Report a feature request **only after discussing it first on [discuss.dry-rb.org](https://discuss.dry-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discussion thread, and instead summarize what was discussed.
|
10
|
+
|
11
|
+
## Reporting questions, support requests, ideas, concerns etc.
|
12
|
+
|
13
|
+
**PLEASE DON'T** - use [discuss.dry-rb.org](http://discuss.dry-rb.org) instead.
|
14
|
+
|
15
|
+
# Pull Request Guidelines
|
16
|
+
|
17
|
+
A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc.
|
18
|
+
|
19
|
+
Other requirements:
|
20
|
+
|
21
|
+
1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue.
|
22
|
+
2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style.
|
23
|
+
3) Add API documentation if it's a new feature
|
24
|
+
4) Update API documentation if it changes an existing feature
|
25
|
+
5) Bonus points for sending a PR to [github.com/dry-rb/dry-rb.org](github.com/dry-rb/dry-rb.org) which updates user documentation and guides
|
26
|
+
|
27
|
+
# Asking for help
|
28
|
+
|
29
|
+
If these guidelines aren't helpful, and you're stuck, please post a message on [discuss.dry-rb.org](https://discuss.dry-rb.org).
|
data/README.md
CHANGED
@@ -20,7 +20,7 @@
|
|
20
20
|
|
21
21
|
## Development
|
22
22
|
|
23
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake
|
23
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake run_specs` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
24
24
|
|
25
25
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
26
26
|
|
data/dry-types.gemspec
CHANGED
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.bindir = "exe"
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
28
28
|
spec.require_paths = ["lib"]
|
29
|
-
spec.required_ruby_version = ">= 2.
|
29
|
+
spec.required_ruby_version = ">= 2.2.0"
|
30
30
|
|
31
31
|
spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
|
32
32
|
spec.add_runtime_dependency 'dry-core', '~> 0.2', '>= 0.2.1'
|
data/lib/dry/types.rb
CHANGED
@@ -15,6 +15,7 @@ require 'dry/types/container'
|
|
15
15
|
require 'dry/types/type'
|
16
16
|
require 'dry/types/definition'
|
17
17
|
require 'dry/types/constructor'
|
18
|
+
require 'dry/types/fn_container'
|
18
19
|
|
19
20
|
require 'dry/types/errors'
|
20
21
|
|
@@ -51,6 +52,11 @@ module Dry
|
|
51
52
|
@container ||= Container.new
|
52
53
|
end
|
53
54
|
|
55
|
+
# @api private
|
56
|
+
def self.registered?(class_or_identifier)
|
57
|
+
container.key?(identifier(class_or_identifier))
|
58
|
+
end
|
59
|
+
|
54
60
|
# @param [String] name
|
55
61
|
# @param [Type] type
|
56
62
|
# @param [#call,nil] block
|
data/lib/dry/types/array.rb
CHANGED
data/lib/dry/types/compiler.rb
CHANGED
@@ -11,73 +11,76 @@ module Dry
|
|
11
11
|
visit(ast)
|
12
12
|
end
|
13
13
|
|
14
|
-
def visit(node
|
15
|
-
|
14
|
+
def visit(node)
|
15
|
+
type, body = node
|
16
|
+
send(:"visit_#{ type }", body)
|
16
17
|
end
|
17
18
|
|
18
19
|
def visit_constructor(node)
|
19
|
-
|
20
|
+
definition, fn_register_name, meta = node
|
21
|
+
fn = Dry::Types::FnContainer[fn_register_name]
|
22
|
+
primitive = visit(definition)
|
20
23
|
Types::Constructor.new(primitive, &fn)
|
21
24
|
end
|
22
25
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
+
def visit_safe(node)
|
27
|
+
ast, meta = node
|
28
|
+
Types::Safe.new(visit(ast), meta: meta)
|
29
|
+
end
|
30
|
+
|
31
|
+
def visit_definition(node)
|
32
|
+
type, meta = node
|
26
33
|
|
27
|
-
if
|
28
|
-
|
34
|
+
if registry.registered?(type)
|
35
|
+
registry[type].meta(meta)
|
29
36
|
else
|
30
|
-
|
37
|
+
Definition.new(type, meta: meta)
|
31
38
|
end
|
32
39
|
end
|
33
40
|
|
34
41
|
def visit_sum(node)
|
35
|
-
|
42
|
+
*types, meta = node
|
43
|
+
types.map { |type| visit(type) }.reduce(:|).meta(meta)
|
36
44
|
end
|
37
45
|
|
38
46
|
def visit_array(node)
|
39
|
-
|
47
|
+
member, meta = node
|
48
|
+
registry['array'].member(visit(member)).meta(meta)
|
40
49
|
end
|
41
50
|
|
42
|
-
def
|
43
|
-
|
51
|
+
def visit_hash(node)
|
52
|
+
constructor, schema, meta = node
|
53
|
+
merge_with('hash', constructor, schema).meta(meta)
|
44
54
|
end
|
45
55
|
|
46
|
-
def
|
47
|
-
|
56
|
+
def visit_json_hash(node)
|
57
|
+
schema, meta = node
|
58
|
+
merge_with('json.hash', :symbolized, schema).meta(meta)
|
48
59
|
end
|
49
60
|
|
50
|
-
def
|
51
|
-
|
52
|
-
|
61
|
+
def visit_json_array(node)
|
62
|
+
member, meta = node
|
63
|
+
registry['json.array'].member(visit(member)).meta(meta)
|
53
64
|
end
|
54
65
|
|
55
66
|
def visit_form_hash(node)
|
56
|
-
|
57
|
-
|
58
|
-
merge_with('form.hash', constructor, schema)
|
59
|
-
else
|
60
|
-
registry['form.hash']
|
61
|
-
end
|
67
|
+
schema, meta = node
|
68
|
+
merge_with('form.hash', :symbolized, schema).meta(meta)
|
62
69
|
end
|
63
70
|
|
64
|
-
def
|
65
|
-
|
66
|
-
|
67
|
-
merge_with('json.hash', constructor, schema)
|
68
|
-
else
|
69
|
-
registry['json.hash']
|
70
|
-
end
|
71
|
+
def visit_form_array(node)
|
72
|
+
member, meta = node
|
73
|
+
registry['form.array'].member(visit(member)).meta(meta)
|
71
74
|
end
|
72
75
|
|
73
|
-
def
|
74
|
-
name,
|
75
|
-
{ name => visit(
|
76
|
+
def visit_member(node)
|
77
|
+
name, type = node
|
78
|
+
{ name => visit(type) }
|
76
79
|
end
|
77
80
|
|
78
81
|
def merge_with(hash_id, constructor, schema)
|
79
82
|
registry[hash_id].__send__(
|
80
|
-
constructor, schema.map { |key| visit(key) }.reduce({}, :
|
83
|
+
constructor, schema.map { |key| visit(key) }.reduce({}, :update)
|
81
84
|
)
|
82
85
|
end
|
83
86
|
end
|
@@ -73,6 +73,15 @@ module Dry
|
|
73
73
|
valid?(value)
|
74
74
|
end
|
75
75
|
|
76
|
+
# @api public
|
77
|
+
#
|
78
|
+
# @see Definition#to_ast
|
79
|
+
def to_ast(meta: true)
|
80
|
+
[:constrained, [type.to_ast(meta: meta),
|
81
|
+
rule.to_ast,
|
82
|
+
meta ? self.meta : EMPTY_HASH]]
|
83
|
+
end
|
84
|
+
|
76
85
|
private
|
77
86
|
|
78
87
|
# @param [Object] response
|
@@ -72,8 +72,21 @@ module Dry
|
|
72
72
|
Constrained::Coercible
|
73
73
|
end
|
74
74
|
|
75
|
+
# @api public
|
76
|
+
#
|
77
|
+
# @see Definition#to_ast
|
78
|
+
def to_ast(meta: true)
|
79
|
+
[:constructor, [type.to_ast(meta: meta),
|
80
|
+
register_fn(fn),
|
81
|
+
meta ? self.meta : EMPTY_HASH]]
|
82
|
+
end
|
83
|
+
|
75
84
|
private
|
76
85
|
|
86
|
+
def register_fn(fn)
|
87
|
+
Dry::Types::FnContainer.register(fn)
|
88
|
+
end
|
89
|
+
|
77
90
|
# @param [Symbol] meth
|
78
91
|
# @param [Boolean] include_private
|
79
92
|
# @return [Boolean]
|
data/lib/dry/types/definition.rb
CHANGED
@@ -106,6 +106,15 @@ module Dry
|
|
106
106
|
end
|
107
107
|
alias_method :valid?, :primitive?
|
108
108
|
alias_method :===, :primitive?
|
109
|
+
|
110
|
+
# Return AST representation of a type definition
|
111
|
+
#
|
112
|
+
# @api public
|
113
|
+
#
|
114
|
+
# @return [Array]
|
115
|
+
def to_ast(meta: true)
|
116
|
+
[:definition, [primitive, meta ? self.meta : EMPTY_HASH]]
|
117
|
+
end
|
109
118
|
end
|
110
119
|
end
|
111
120
|
end
|
data/lib/dry/types/enum.rb
CHANGED
@@ -41,6 +41,13 @@ module Dry
|
|
41
41
|
raise '.enum(*values).default(value) is not supported. Call '\
|
42
42
|
'.default(value).enum(*values) instead'
|
43
43
|
end
|
44
|
+
|
45
|
+
# @api public
|
46
|
+
#
|
47
|
+
# @see Definition#to_ast
|
48
|
+
def to_ast(meta: true)
|
49
|
+
[:enum, [type.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
|
50
|
+
end
|
44
51
|
end
|
45
52
|
end
|
46
53
|
end
|
data/lib/dry/types/errors.rb
CHANGED
@@ -9,8 +9,9 @@ module Dry
|
|
9
9
|
class SchemaError < TypeError
|
10
10
|
# @param [String,Symbol] key
|
11
11
|
# @param [Object] value
|
12
|
-
|
13
|
-
|
12
|
+
# @param [String, #to_s] result
|
13
|
+
def initialize(key, value, result)
|
14
|
+
super("#{value.inspect} (#{value.class}) has invalid type for :#{key} violates constraints (#{result} failed)")
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'dry/types/container'
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Types
|
5
|
+
class FnContainer
|
6
|
+
# @api private
|
7
|
+
def self.container
|
8
|
+
@container ||= Container.new
|
9
|
+
end
|
10
|
+
|
11
|
+
# @api private
|
12
|
+
def self.register(function)
|
13
|
+
register_function_name = register_name(function)
|
14
|
+
container.register(register_function_name, function) unless container.key?(register_function_name)
|
15
|
+
register_function_name
|
16
|
+
end
|
17
|
+
|
18
|
+
# @api private
|
19
|
+
def self.[](function_name)
|
20
|
+
if container.key?(function_name)
|
21
|
+
container[function_name]
|
22
|
+
else
|
23
|
+
function_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @api private
|
28
|
+
def self.register_name(function)
|
29
|
+
"fn_#{function.object_id}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -58,8 +58,23 @@ module Dry
|
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
+
def to_ast(meta: true)
|
62
|
+
[
|
63
|
+
:hash,
|
64
|
+
[
|
65
|
+
hash_type,
|
66
|
+
member_types.map { |name, member| [:member, [name, member.to_ast(meta: meta)]] },
|
67
|
+
meta ? self.meta : EMPTY_HASH
|
68
|
+
]
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
61
72
|
private
|
62
73
|
|
74
|
+
def hash_type
|
75
|
+
:schema
|
76
|
+
end
|
77
|
+
|
63
78
|
# @param [Hash] hash
|
64
79
|
# @return [Hash{Symbol => Object}]
|
65
80
|
def try_coerce(hash)
|
@@ -74,8 +89,8 @@ module Dry
|
|
74
89
|
resolve(hash) do |type, key, value|
|
75
90
|
begin
|
76
91
|
type.call(value)
|
77
|
-
rescue ConstraintError
|
78
|
-
raise SchemaError.new(key, value)
|
92
|
+
rescue ConstraintError => e
|
93
|
+
raise SchemaError.new(key, value, e.result)
|
79
94
|
end
|
80
95
|
end
|
81
96
|
end
|
@@ -114,6 +129,10 @@ module Dry
|
|
114
129
|
class Permissive < Schema
|
115
130
|
private
|
116
131
|
|
132
|
+
def hash_type
|
133
|
+
:permissive
|
134
|
+
end
|
135
|
+
|
117
136
|
# @param [Symbol] key
|
118
137
|
# @raise [MissingKeyError] when key is missing in given input
|
119
138
|
def resolve_missing_value(_, key, _)
|
@@ -132,6 +151,10 @@ module Dry
|
|
132
151
|
class Strict < Permissive
|
133
152
|
private
|
134
153
|
|
154
|
+
def hash_type
|
155
|
+
:strict
|
156
|
+
end
|
157
|
+
|
135
158
|
# @param [Hash] hash
|
136
159
|
# @return [Hash{Symbol => Object}]
|
137
160
|
# @raise [UnknownKeysError]
|
@@ -160,6 +183,10 @@ module Dry
|
|
160
183
|
class StrictWithDefaults < Strict
|
161
184
|
private
|
162
185
|
|
186
|
+
def hash_type
|
187
|
+
:strict_with_defaults
|
188
|
+
end
|
189
|
+
|
163
190
|
# @param [Hash] result
|
164
191
|
# @param [Symbol] key
|
165
192
|
# @param [Type] type
|
@@ -203,12 +230,22 @@ module Dry
|
|
203
230
|
block ? yield(result) : result
|
204
231
|
end
|
205
232
|
end
|
233
|
+
|
234
|
+
private
|
235
|
+
|
236
|
+
def hash_type
|
237
|
+
:weak
|
238
|
+
end
|
206
239
|
end
|
207
240
|
|
208
241
|
# {Symbolized} hash will turn string key names into symbols.
|
209
242
|
class Symbolized < Weak
|
210
243
|
private
|
211
244
|
|
245
|
+
def hash_type
|
246
|
+
:symbolized
|
247
|
+
end
|
248
|
+
|
212
249
|
def resolve(hash)
|
213
250
|
result = {}
|
214
251
|
member_types.each do |key, type|
|
data/lib/dry/types/options.rb
CHANGED
@@ -24,7 +24,13 @@ module Dry
|
|
24
24
|
# @param [Hash] new metadata to merge into existing metadata
|
25
25
|
# @return [Type] new type with added metadata
|
26
26
|
def meta(data = nil)
|
27
|
-
|
27
|
+
if !data
|
28
|
+
@meta
|
29
|
+
elsif data.empty?
|
30
|
+
self
|
31
|
+
else
|
32
|
+
with(meta: @meta.merge(data))
|
33
|
+
end
|
28
34
|
end
|
29
35
|
|
30
36
|
# Resets meta
|
data/lib/dry/types/safe.rb
CHANGED
@@ -33,6 +33,19 @@ module Dry
|
|
33
33
|
block ? yield(result) : result
|
34
34
|
end
|
35
35
|
|
36
|
+
# @api public
|
37
|
+
#
|
38
|
+
# @see Definition#to_ast
|
39
|
+
def to_ast(meta: true)
|
40
|
+
[:safe, [type.to_ast, meta ? self.meta : EMPTY_HASH]]
|
41
|
+
end
|
42
|
+
|
43
|
+
# @api public
|
44
|
+
# @return [Safe]
|
45
|
+
def safe
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
36
49
|
private
|
37
50
|
|
38
51
|
# @param [Object, Dry::Types::Constructor] response
|
data/lib/dry/types/sum.rb
CHANGED
@@ -102,6 +102,13 @@ module Dry
|
|
102
102
|
def valid?(value)
|
103
103
|
left.valid?(value) || right.valid?(value)
|
104
104
|
end
|
105
|
+
|
106
|
+
# @api public
|
107
|
+
#
|
108
|
+
# @see Definition#to_ast
|
109
|
+
def to_ast(meta: true)
|
110
|
+
[:sum, [left.to_ast(meta: meta), right.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
|
111
|
+
end
|
105
112
|
end
|
106
113
|
end
|
107
114
|
end
|
data/lib/dry/types/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dry-types
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Solnica
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -209,6 +209,7 @@ files:
|
|
209
209
|
- ".travis.yml"
|
210
210
|
- ".yardopts"
|
211
211
|
- CHANGELOG.md
|
212
|
+
- CONTRIBUTING.md
|
212
213
|
- Gemfile
|
213
214
|
- LICENSE
|
214
215
|
- README.md
|
@@ -238,6 +239,7 @@ files:
|
|
238
239
|
- lib/dry/types/errors.rb
|
239
240
|
- lib/dry/types/extensions.rb
|
240
241
|
- lib/dry/types/extensions/maybe.rb
|
242
|
+
- lib/dry/types/fn_container.rb
|
241
243
|
- lib/dry/types/form.rb
|
242
244
|
- lib/dry/types/hash.rb
|
243
245
|
- lib/dry/types/hash/schema.rb
|
@@ -262,7 +264,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
262
264
|
requirements:
|
263
265
|
- - ">="
|
264
266
|
- !ruby/object:Gem::Version
|
265
|
-
version: 2.
|
267
|
+
version: 2.2.0
|
266
268
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
267
269
|
requirements:
|
268
270
|
- - ">="
|
@@ -270,7 +272,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
272
|
version: '0'
|
271
273
|
requirements: []
|
272
274
|
rubyforge_project:
|
273
|
-
rubygems_version: 2.6.
|
275
|
+
rubygems_version: 2.6.9
|
274
276
|
signing_key:
|
275
277
|
specification_version: 4
|
276
278
|
summary: Type system for Ruby supporting coercions, constraints and complex types
|