dry-types 1.1.1 → 1.2.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.
- checksums.yaml +4 -4
- data/.codeclimate.yml +2 -5
- data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +10 -0
- data/.github/ISSUE_TEMPLATE/---bug-report.md +34 -0
- data/.github/ISSUE_TEMPLATE/---feature-request.md +18 -0
- data/.github/workflows/custom_ci.yml +76 -0
- data/.github/workflows/docsite.yml +34 -0
- data/.github/workflows/sync_configs.yml +34 -0
- data/.gitignore +1 -1
- data/.rspec +3 -1
- data/.rubocop.yml +34 -4
- data/CHANGELOG.md +69 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/CONTRIBUTING.md +2 -2
- data/Gemfile +11 -6
- data/LICENSE +17 -17
- data/docsite/source/array-with-member.html.md +13 -0
- data/docsite/source/built-in-types.html.md +116 -0
- data/docsite/source/constraints.html.md +31 -0
- data/docsite/source/custom-types.html.md +93 -0
- data/docsite/source/default-values.html.md +91 -0
- data/docsite/source/enum.html.md +69 -0
- data/docsite/source/extensions/maybe.html.md +57 -0
- data/docsite/source/extensions/monads.html.md +61 -0
- data/docsite/source/extensions.html.md +15 -0
- data/docsite/source/getting-started.html.md +57 -0
- data/docsite/source/hash-schemas.html.md +169 -0
- data/docsite/source/index.html.md +156 -0
- data/docsite/source/map.html.md +17 -0
- data/docsite/source/optional-values.html.md +35 -0
- data/docsite/source/sum.html.md +21 -0
- data/lib/dry/types/any.rb +2 -2
- data/lib/dry/types/array/member.rb +3 -3
- data/lib/dry/types/builder.rb +1 -1
- data/lib/dry/types/builder_methods.rb +18 -8
- data/lib/dry/types/compiler.rb +2 -2
- data/lib/dry/types/constrained.rb +1 -1
- data/lib/dry/types/constructor.rb +6 -33
- data/lib/dry/types/decorator.rb +3 -2
- data/lib/dry/types/enum.rb +1 -1
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/extensions.rb +4 -0
- data/lib/dry/types/hash.rb +3 -2
- data/lib/dry/types/lax.rb +1 -1
- data/lib/dry/types/meta.rb +1 -1
- data/lib/dry/types/module.rb +3 -3
- data/lib/dry/types/params.rb +5 -0
- data/lib/dry/types/predicate_inferrer.rb +197 -0
- data/lib/dry/types/predicate_registry.rb +34 -0
- data/lib/dry/types/primitive_inferrer.rb +97 -0
- data/lib/dry/types/spec/types.rb +3 -1
- data/lib/dry/types/sum.rb +2 -2
- data/lib/dry/types/version.rb +1 -1
- data/lib/dry/types.rb +7 -2
- metadata +29 -4
- data/.travis.yml +0 -27
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Introduction
|
|
3
|
+
layout: gem-single
|
|
4
|
+
type: gem
|
|
5
|
+
name: dry-types
|
|
6
|
+
sections:
|
|
7
|
+
- getting-started
|
|
8
|
+
- built-in-types
|
|
9
|
+
- optional-values
|
|
10
|
+
- default-values
|
|
11
|
+
- sum
|
|
12
|
+
- constraints
|
|
13
|
+
- hash-schemas
|
|
14
|
+
- array-with-member
|
|
15
|
+
- enum
|
|
16
|
+
- map
|
|
17
|
+
- custom-types
|
|
18
|
+
- extensions
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
`dry-types` is a simple and extendable type system for Ruby; useful for value coercions, applying constraints, defining complex structs or value objects and more. It was created as a successor to [Virtus](https://github.com/solnic/virtus).
|
|
22
|
+
|
|
23
|
+
### Example usage
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
require 'dry-types'
|
|
27
|
+
require 'dry-struct'
|
|
28
|
+
|
|
29
|
+
module Types
|
|
30
|
+
include Dry.Types()
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
User = Dry.Struct(name: Types::String, age: Types::Integer)
|
|
34
|
+
|
|
35
|
+
User.new(name: 'Bob', age: 35)
|
|
36
|
+
# => #<User name="Bob" age=35>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
See [Built-in Types](docs::built-in-types/) for a full list of available types.
|
|
40
|
+
|
|
41
|
+
By themselves, the basic type definitions like `Types::String` and `Types::Integer` don't do anything except provide documentation about which type an attribute is expected to have. However, there are many more advanced possibilities:
|
|
42
|
+
|
|
43
|
+
- `Strict` types will raise an error if passed an attribute of the wrong type:
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
class User < Dry::Struct
|
|
47
|
+
attribute :name, Types::Strict::String
|
|
48
|
+
attribute :age, Types::Strict::Integer
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
User.new(name: 'Bob', age: '18')
|
|
52
|
+
# => Dry::Struct::Error: [User.new] "18" (String) has invalid type for :age
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
- `Coercible` types will attempt to convert an attribute to the correct class
|
|
56
|
+
using Ruby's built-in coercion methods:
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
class User < Dry::Struct
|
|
60
|
+
attribute :name, Types::Coercible::String
|
|
61
|
+
attribute :age, Types::Coercible::Integer
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
User.new(name: 'Bob', age: '18')
|
|
65
|
+
# => #<User name="Bob" age=18>
|
|
66
|
+
User.new(name: 'Bob', age: 'not coercible')
|
|
67
|
+
# => ArgumentError: invalid value for Integer(): "not coercible"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- Use `.optional` to denote that an attribute can be `nil` (see [Optional Values](docs::optional-values)):
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
class User < Dry::Struct
|
|
74
|
+
attribute :name, Types::String
|
|
75
|
+
attribute :age, Types::Integer.optional
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
User.new(name: 'Bob', age: nil)
|
|
79
|
+
# => #<User name="Bob" age=nil>
|
|
80
|
+
# name is not optional:
|
|
81
|
+
User.new(name: nil, age: 18)
|
|
82
|
+
# => Dry::Struct::Error: [User.new] nil (NilClass) has invalid type for :name
|
|
83
|
+
# keys must still be present:
|
|
84
|
+
User.new(name: 'Bob')
|
|
85
|
+
# => Dry::Struct::Error: [User.new] :age is missing in Hash input
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
- Add custom constraints (see [Constraints](docs::constraints.html)):
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
class User < Dry::Struct
|
|
92
|
+
attribute :name, Types::Strict::String
|
|
93
|
+
attribute :age, Types::Strict::Integer.constrained(gteq: 18)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
User.new(name: 'Bob', age: 17)
|
|
97
|
+
# => Dry::Struct::Error: [User.new] 17 (Fixnum) has invalid type for :age
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- Add custom metadata to a type:
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
class User < Dry::Struct
|
|
104
|
+
attribute :name, Types::String
|
|
105
|
+
attribute :age, Types::Integer.meta(info: 'extra info about age')
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- Pass values directly to `Dry::Types` without creating an object using `[]`:
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
Types::Strict::String["foo"]
|
|
113
|
+
# => "foo"
|
|
114
|
+
Types::Strict::String["10000"]
|
|
115
|
+
# => "10000"
|
|
116
|
+
Types::Coercible::String[10000]
|
|
117
|
+
# => "10000"
|
|
118
|
+
Types::Strict::String[10000]
|
|
119
|
+
# Dry::Types::ConstraintError: 1000 violates constraints
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Features
|
|
123
|
+
|
|
124
|
+
* Support for [constrained types](docs::constraints)
|
|
125
|
+
* Support for [optional values](docs::optional-values)
|
|
126
|
+
* Support for [default values](docs::default-values)
|
|
127
|
+
* Support for [sum types](docs::sum)
|
|
128
|
+
* Support for [enums](docs::enum)
|
|
129
|
+
* Support for [hash type with type schemas](docs::hash-schemas)
|
|
130
|
+
* Support for [array type with members](docs::array-with-member)
|
|
131
|
+
* Support for arbitrary meta information
|
|
132
|
+
* Support for typed struct objects via [dry-struct](/gems/dry-struct)
|
|
133
|
+
* Types are [categorized](docs::built-in-types), which is especially important for optimized and dedicated coercion logic
|
|
134
|
+
* Types are composable and reusable objects
|
|
135
|
+
* No const-missing magic and complicated const lookups
|
|
136
|
+
* Roughly 6-10 x faster than Virtus
|
|
137
|
+
|
|
138
|
+
### Use cases
|
|
139
|
+
|
|
140
|
+
`dry-types` is suitable for many use-cases, for example:
|
|
141
|
+
|
|
142
|
+
* Value coercions
|
|
143
|
+
* Processing arrays
|
|
144
|
+
* Processing hashes with explicit schemas
|
|
145
|
+
* Defining various domain-specific information shared between multiple parts of your application
|
|
146
|
+
* Annotating objects
|
|
147
|
+
|
|
148
|
+
### Other gems using dry-types
|
|
149
|
+
|
|
150
|
+
`dry-types` is often used as a low-level abstraction. The following gems use it already:
|
|
151
|
+
|
|
152
|
+
* [dry-struct](/gems/dry-struct)
|
|
153
|
+
* [dry-initializer](/gems/dry-initializer)
|
|
154
|
+
* [Hanami](http://hanamirb.org)
|
|
155
|
+
* [rom-rb](http://rom-rb.org)
|
|
156
|
+
* [Trailblazer](http://trailblazer.to)
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Map
|
|
3
|
+
layout: gem-single
|
|
4
|
+
name: dry-types
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
`Map` describes a homogeneous hashmap. This means only types of keys and values are known. You can simply imagine a map input as a list of key-value pairs.
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
int_float_hash = Types::Hash.map(Types::Integer, Types::Float)
|
|
11
|
+
int_float_hash[100 => 300.0, 42 => 70.0]
|
|
12
|
+
# => {100=>300.0, 42=>70.0}
|
|
13
|
+
|
|
14
|
+
# Only accepts mappings of integers to floats
|
|
15
|
+
int_float_hash[name: 'Jane']
|
|
16
|
+
# => Dry::Types::MapError: input key :name is invalid: type?(Integer, :name)
|
|
17
|
+
```
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Type Attributes
|
|
3
|
+
layout: gem-single
|
|
4
|
+
name: dry-types
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Types themselves have optional attributes you can apply to get further functionality.
|
|
8
|
+
|
|
9
|
+
### Append `.optional` to a _Type_ to allow `nil`
|
|
10
|
+
|
|
11
|
+
By default, nil values raise an error:
|
|
12
|
+
|
|
13
|
+
``` ruby
|
|
14
|
+
Types::Strict::String[nil]
|
|
15
|
+
# => raises Dry::Types::ConstraintError
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Add `.optional` and `nil` values become valid:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
optional_string = Types::Strict::String.optional
|
|
22
|
+
|
|
23
|
+
optional_string[nil]
|
|
24
|
+
# => nil
|
|
25
|
+
optional_string['something']
|
|
26
|
+
# => "something"
|
|
27
|
+
optional_string[123]
|
|
28
|
+
# raises Dry::Types::ConstraintError
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`Types::String.optional` is just syntactic sugar for `Types::Strict::Nil | Types::Strict::String`.
|
|
32
|
+
|
|
33
|
+
### Handle optional values using Monads
|
|
34
|
+
|
|
35
|
+
See [Maybe](docs::extensions/maybe) extension for another approach to handling optional values by returning a [_Monad_](/gems/dry-monads/) object.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Sum
|
|
3
|
+
layout: gem-single
|
|
4
|
+
name: dry-types
|
|
5
|
+
order: 7
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
You can specify sum types using `|` operator, it is an explicit way of defining what the valid types of a value are.
|
|
9
|
+
|
|
10
|
+
For example `dry-types` defines the `Bool` type which is a sum consisting of the `True` and `False` types, expressed as `Types::True | Types::False`.
|
|
11
|
+
|
|
12
|
+
Another common case is defining that something can be either `nil` or something else:
|
|
13
|
+
|
|
14
|
+
``` ruby
|
|
15
|
+
nil_or_string = Types::Nil | Types::String
|
|
16
|
+
|
|
17
|
+
nil_or_string[nil] # => nil
|
|
18
|
+
nil_or_string["hello"] # => "hello"
|
|
19
|
+
|
|
20
|
+
nil_or_string[123] # raises Dry::Types::ConstraintError
|
|
21
|
+
```
|
data/lib/dry/types/any.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Dry
|
|
|
15
15
|
|
|
16
16
|
# @api private
|
|
17
17
|
def initialize(**options)
|
|
18
|
-
super(::Object, options)
|
|
18
|
+
super(::Object, **options)
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# @return [String]
|
|
@@ -30,7 +30,7 @@ module Dry
|
|
|
30
30
|
# @return [Type]
|
|
31
31
|
#
|
|
32
32
|
# @api public
|
|
33
|
-
def with(new_options)
|
|
33
|
+
def with(**new_options)
|
|
34
34
|
self.class.new(**options, meta: @meta, **new_options)
|
|
35
35
|
end
|
|
36
36
|
|
|
@@ -18,7 +18,7 @@ module Dry
|
|
|
18
18
|
# @option options [Type] :member
|
|
19
19
|
#
|
|
20
20
|
# @api private
|
|
21
|
-
def initialize(primitive, options
|
|
21
|
+
def initialize(primitive, **options)
|
|
22
22
|
@member = options.fetch(:member)
|
|
23
23
|
super
|
|
24
24
|
end
|
|
@@ -89,7 +89,7 @@ module Dry
|
|
|
89
89
|
block ? yield(failure) : failure
|
|
90
90
|
end
|
|
91
91
|
else
|
|
92
|
-
failure = failure(input, "#{input} is not an array")
|
|
92
|
+
failure = failure(input, CoercionError.new("#{input} is not an array"))
|
|
93
93
|
block ? yield(failure) : failure
|
|
94
94
|
end
|
|
95
95
|
end
|
|
@@ -100,7 +100,7 @@ module Dry
|
|
|
100
100
|
#
|
|
101
101
|
# @api public
|
|
102
102
|
def lax
|
|
103
|
-
Lax.new(Member.new(primitive,
|
|
103
|
+
Lax.new(Member.new(primitive, **options, member: member.lax, meta: meta))
|
|
104
104
|
end
|
|
105
105
|
|
|
106
106
|
# @see Nominal#to_ast
|
data/lib/dry/types/builder.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Dry
|
|
|
25
25
|
#
|
|
26
26
|
# @return [Dry::Types::Array]
|
|
27
27
|
def Array(type)
|
|
28
|
-
|
|
28
|
+
Strict(::Array).of(type)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
# Build a hash schema
|
|
@@ -34,7 +34,7 @@ module Dry
|
|
|
34
34
|
#
|
|
35
35
|
# @return [Dry::Types::Array]
|
|
36
36
|
def Hash(type_map)
|
|
37
|
-
|
|
37
|
+
Strict(::Hash).schema(type_map)
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Build a type which values are instances of a given class
|
|
@@ -49,7 +49,7 @@ module Dry
|
|
|
49
49
|
#
|
|
50
50
|
# @return [Dry::Types::Type]
|
|
51
51
|
def Instance(klass)
|
|
52
|
-
Nominal
|
|
52
|
+
Nominal(klass).constrained(type: klass)
|
|
53
53
|
end
|
|
54
54
|
alias_method :Strict, :Instance
|
|
55
55
|
|
|
@@ -60,7 +60,7 @@ module Dry
|
|
|
60
60
|
#
|
|
61
61
|
# @return [Dry::Types::Type]
|
|
62
62
|
def Value(value)
|
|
63
|
-
Nominal
|
|
63
|
+
Nominal(value.class).constrained(eql: value)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# Build a type with a single value
|
|
@@ -70,7 +70,7 @@ module Dry
|
|
|
70
70
|
#
|
|
71
71
|
# @return [Dry::Types::Type]
|
|
72
72
|
def Constant(object)
|
|
73
|
-
Nominal
|
|
73
|
+
Nominal(object.class).constrained(is: object)
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
# Build a constructor type
|
|
@@ -82,7 +82,11 @@ module Dry
|
|
|
82
82
|
#
|
|
83
83
|
# @return [Dry::Types::Type]
|
|
84
84
|
def Constructor(klass, cons = nil, &block)
|
|
85
|
-
|
|
85
|
+
if klass.is_a?(Type)
|
|
86
|
+
klass.constructor(cons || block || klass.method(:new))
|
|
87
|
+
else
|
|
88
|
+
Nominal(klass).constructor(cons || block || klass.method(:new))
|
|
89
|
+
end
|
|
86
90
|
end
|
|
87
91
|
|
|
88
92
|
# Build a nominal type
|
|
@@ -91,7 +95,13 @@ module Dry
|
|
|
91
95
|
#
|
|
92
96
|
# @return [Dry::Types::Type]
|
|
93
97
|
def Nominal(klass)
|
|
94
|
-
|
|
98
|
+
if klass <= ::Array
|
|
99
|
+
Array.new(klass)
|
|
100
|
+
elsif klass <= ::Hash
|
|
101
|
+
Hash.new(klass)
|
|
102
|
+
else
|
|
103
|
+
Nominal.new(klass)
|
|
104
|
+
end
|
|
95
105
|
end
|
|
96
106
|
|
|
97
107
|
# Build a map type
|
|
@@ -105,7 +115,7 @@ module Dry
|
|
|
105
115
|
#
|
|
106
116
|
# @return [Dry::Types::Map]
|
|
107
117
|
def Map(key_type, value_type)
|
|
108
|
-
|
|
118
|
+
Nominal(::Hash).map(key_type, value_type)
|
|
109
119
|
end
|
|
110
120
|
|
|
111
121
|
# Builds a constrained nominal type accepting any value that
|
data/lib/dry/types/compiler.rb
CHANGED
|
@@ -68,12 +68,12 @@ module Dry
|
|
|
68
68
|
|
|
69
69
|
def visit_hash(node)
|
|
70
70
|
opts, meta = node
|
|
71
|
-
registry['nominal.hash'].with(opts
|
|
71
|
+
registry['nominal.hash'].with(**opts, meta: meta)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def visit_schema(node)
|
|
75
75
|
keys, options, meta = node
|
|
76
|
-
registry['nominal.hash'].schema(keys.map { |key| visit(key) }).with(options
|
|
76
|
+
registry['nominal.hash'].schema(keys.map { |key| visit(key) }).with(**options, meta: meta)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
def visit_json_hash(node)
|
|
@@ -12,15 +12,13 @@ module Dry
|
|
|
12
12
|
class Constructor < Nominal
|
|
13
13
|
include Dry::Equalizer(:type, :options, inspect: false)
|
|
14
14
|
|
|
15
|
-
private :meta
|
|
16
|
-
|
|
17
15
|
# @return [#call]
|
|
18
16
|
attr_reader :fn
|
|
19
17
|
|
|
20
18
|
# @return [Type]
|
|
21
19
|
attr_reader :type
|
|
22
20
|
|
|
23
|
-
undef :constrained
|
|
21
|
+
undef :constrained?, :meta, :optional?, :primitive, :default?, :name
|
|
24
22
|
|
|
25
23
|
# @param [Builder, Object] input
|
|
26
24
|
# @param [Hash] options
|
|
@@ -46,31 +44,6 @@ module Dry
|
|
|
46
44
|
super(type, **options, fn: fn)
|
|
47
45
|
end
|
|
48
46
|
|
|
49
|
-
# Return the inner type's primitive
|
|
50
|
-
#
|
|
51
|
-
# @return [Class]
|
|
52
|
-
#
|
|
53
|
-
# @api public
|
|
54
|
-
def primitive
|
|
55
|
-
type.primitive
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
# Return the inner type's name
|
|
59
|
-
#
|
|
60
|
-
# @return [String]
|
|
61
|
-
#
|
|
62
|
-
# @api public
|
|
63
|
-
def name
|
|
64
|
-
type.name
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# @return [Boolean]
|
|
68
|
-
#
|
|
69
|
-
# @api public
|
|
70
|
-
def default?
|
|
71
|
-
type.default?
|
|
72
|
-
end
|
|
73
|
-
|
|
74
47
|
# @return [Object]
|
|
75
48
|
#
|
|
76
49
|
# @api private
|
|
@@ -112,7 +85,7 @@ module Dry
|
|
|
112
85
|
#
|
|
113
86
|
# @api public
|
|
114
87
|
def constructor(new_fn = nil, **options, &block)
|
|
115
|
-
with(
|
|
88
|
+
with(**options, fn: fn >> (new_fn || block))
|
|
116
89
|
end
|
|
117
90
|
alias_method :append, :constructor
|
|
118
91
|
alias_method :>>, :constructor
|
|
@@ -141,7 +114,7 @@ module Dry
|
|
|
141
114
|
#
|
|
142
115
|
# @api public
|
|
143
116
|
def prepend(new_fn = nil, **options, &block)
|
|
144
|
-
with(
|
|
117
|
+
with(**options, fn: fn << (new_fn || block))
|
|
145
118
|
end
|
|
146
119
|
alias_method :<<, :prepend
|
|
147
120
|
|
|
@@ -150,7 +123,7 @@ module Dry
|
|
|
150
123
|
# @return [Lax]
|
|
151
124
|
# @api public
|
|
152
125
|
def lax
|
|
153
|
-
Lax.new(Constructor.new(type.lax, options))
|
|
126
|
+
Lax.new(Constructor.new(type.lax, **options))
|
|
154
127
|
end
|
|
155
128
|
|
|
156
129
|
# Wrap the type with a proc
|
|
@@ -182,10 +155,10 @@ module Dry
|
|
|
182
155
|
# @api private
|
|
183
156
|
def method_missing(method, *args, &block)
|
|
184
157
|
if type.respond_to?(method)
|
|
185
|
-
response = type.
|
|
158
|
+
response = type.public_send(method, *args, &block)
|
|
186
159
|
|
|
187
160
|
if response.is_a?(Type) && type.class == response.class
|
|
188
|
-
response.constructor_type.new(response, options)
|
|
161
|
+
response.constructor_type.new(response, **options)
|
|
189
162
|
else
|
|
190
163
|
response
|
|
191
164
|
end
|
data/lib/dry/types/decorator.rb
CHANGED
|
@@ -14,7 +14,7 @@ module Dry
|
|
|
14
14
|
attr_reader :type
|
|
15
15
|
|
|
16
16
|
# @param [Type] type
|
|
17
|
-
def initialize(type,
|
|
17
|
+
def initialize(type, *, **)
|
|
18
18
|
super
|
|
19
19
|
@type = type
|
|
20
20
|
end
|
|
@@ -90,7 +90,7 @@ module Dry
|
|
|
90
90
|
# @api private
|
|
91
91
|
def method_missing(meth, *args, &block)
|
|
92
92
|
if type.respond_to?(meth)
|
|
93
|
-
response = type.
|
|
93
|
+
response = type.public_send(meth, *args, &block)
|
|
94
94
|
|
|
95
95
|
if decorate?(response)
|
|
96
96
|
__new__(response)
|
|
@@ -101,6 +101,7 @@ module Dry
|
|
|
101
101
|
super
|
|
102
102
|
end
|
|
103
103
|
end
|
|
104
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
|
104
105
|
|
|
105
106
|
# Replace underlying type
|
|
106
107
|
#
|
data/lib/dry/types/enum.rb
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'dry/monads/result'
|
|
4
|
+
|
|
5
|
+
module Dry
|
|
6
|
+
module Types
|
|
7
|
+
# Monad extension for Result
|
|
8
|
+
#
|
|
9
|
+
# @api public
|
|
10
|
+
class Result
|
|
11
|
+
include Dry::Monads::Result::Mixin
|
|
12
|
+
|
|
13
|
+
# Turn result into a monad
|
|
14
|
+
#
|
|
15
|
+
# This makes result objects work with dry-monads (or anything with a compatible interface)
|
|
16
|
+
#
|
|
17
|
+
# @return [Dry::Monads::Success,Dry::Monads::Failure]
|
|
18
|
+
#
|
|
19
|
+
# @api public
|
|
20
|
+
def to_monad
|
|
21
|
+
if success?
|
|
22
|
+
Success(input)
|
|
23
|
+
else
|
|
24
|
+
Failure([error, input])
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/dry/types/extensions.rb
CHANGED
data/lib/dry/types/hash.rb
CHANGED
|
@@ -113,7 +113,7 @@ module Dry
|
|
|
113
113
|
|
|
114
114
|
type_map.map do |map_key, type|
|
|
115
115
|
name, options = key_name(map_key)
|
|
116
|
-
key = Schema::Key.new(resolve_type(type), name, options)
|
|
116
|
+
key = Schema::Key.new(resolve_type(type), name, **options)
|
|
117
117
|
type_transform.(key)
|
|
118
118
|
end
|
|
119
119
|
end
|
|
@@ -121,7 +121,8 @@ module Dry
|
|
|
121
121
|
# @api private
|
|
122
122
|
def resolve_type(type)
|
|
123
123
|
case type
|
|
124
|
-
when
|
|
124
|
+
when Type then type
|
|
125
|
+
when ::Class, ::String then Types[type]
|
|
125
126
|
else type
|
|
126
127
|
end
|
|
127
128
|
end
|
data/lib/dry/types/lax.rb
CHANGED
data/lib/dry/types/meta.rb
CHANGED
data/lib/dry/types/module.rb
CHANGED
|
@@ -18,10 +18,10 @@ module Dry
|
|
|
18
18
|
#
|
|
19
19
|
# @api public
|
|
20
20
|
class Module < ::Module
|
|
21
|
-
def initialize(registry, *args)
|
|
21
|
+
def initialize(registry, *args, **kwargs)
|
|
22
22
|
@registry = registry
|
|
23
|
-
check_parameters(*args)
|
|
24
|
-
constants = type_constants(*args)
|
|
23
|
+
check_parameters(*args, **kwargs)
|
|
24
|
+
constants = type_constants(*args, **kwargs)
|
|
25
25
|
define_constants(constants)
|
|
26
26
|
extend(BuilderMethods)
|
|
27
27
|
|
data/lib/dry/types/params.rb
CHANGED
|
@@ -55,5 +55,10 @@ module Dry
|
|
|
55
55
|
register('params.symbol') do
|
|
56
56
|
self['nominal.symbol'].constructor(Coercions::Params.method(:to_symbol))
|
|
57
57
|
end
|
|
58
|
+
|
|
59
|
+
COERCIBLE.each_key do |name|
|
|
60
|
+
next if name.equal?(:string)
|
|
61
|
+
register("optional.params.#{name}", self['params.nil'] | self["params.#{name}"])
|
|
62
|
+
end
|
|
58
63
|
end
|
|
59
64
|
end
|