dry-types 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f4827f984803481533626b7ebe6e51fe6b5cf5da3691a7e0f9681c4d321bd1a
4
- data.tar.gz: 755af2027b59115fd97e1051bdea5eb400ca1a76cf39b19953538e4fb5bb28dc
3
+ metadata.gz: 87e78ecf331ef9c4d95759bf28c2694e413df18fd0397d3c3c51380320328417
4
+ data.tar.gz: ca84bf0a23d37d7f0beda024d573852d2a0701a76f54dee2255166981bd716c5
5
5
  SHA512:
6
- metadata.gz: 18aa4a59b9fbe19721d65c57ca91f29b4f3935298e5947182953b9ffe92c7213fe4f209e55d3472a86d3d4645e8e0ccd200e5456af492026570b0f28f9255973
7
- data.tar.gz: e9b2886e43868b544ec1e4cb9c30949bbd3bec9dba1089dcd08c7ba36e456458a8746ea2537d5241c63322b38505ddc86fecda4035ab0f545e675cbc4188ba15
6
+ metadata.gz: 8ae0d71644bfc391ada28229a68010a378b07e252c6ea5fdc27327065cdfc980b27a25bd9d63068966803a800a262958ef4ea0fc0296d4e865534e3fb3458dc8
7
+ data.tar.gz: a1e899fb498a414f3920f9a19bc8ca22fc4db2bf7e6063a7baca389c528c0fb4d9114522d57606d7143c872b6847b9f1134f04326d1dc5ba694853a0c3977390
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: "⚠️ Please don't ask for support via issues"
3
+ about: See CONTRIBUTING.md for more information
4
+ title: ''
5
+ labels: ''
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: "\U0001F41B Bug report"
3
+ about: See CONTRIBUTING.md for more information
4
+ title: ''
5
+ labels: bug
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ **Before you submit this: WE ONLY ACCEPT BUG REPORTS AND FEATURE REQUESTS**
11
+
12
+ For more information see [our contribution guidelines](https://github.com/rom-rb/rom/blob/master/CONTRIBUTING.md)
13
+
14
+ **Before you report**
15
+
16
+ :warning: If you have a problem related to a schema, please **report it under [dry-schema issues](https://github.com/dry-rb/dry-schema/issues/new?assignees=&labels=bug&template=---bug-report.md&title=)** instead.
17
+
18
+ **Describe the bug**
19
+
20
+ A clear and concise description of what the bug is.
21
+
22
+ **To Reproduce**
23
+
24
+ Provide detailed steps to reproduce, an executable script would be best.
25
+
26
+ **Expected behavior**
27
+
28
+ A clear and concise description of what you expected to happen.
29
+
30
+ **Your environment**
31
+
32
+ - Affects my production application: **YES/NO**
33
+ - Ruby version: ...
34
+ - OS: ...
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: "\U0001F6E0 Feature request"
3
+ about: See CONTRIBUTING.md for more information
4
+ title: ''
5
+ labels: feature
6
+ assignees: ''
7
+
8
+ ---
9
+
10
+ Summary of what the feature is supposed to do.
11
+
12
+ ## Examples
13
+
14
+ Code examples showing how the feature could be used.
15
+
16
+ ## Resources
17
+
18
+ Additional information, like a link to the discussion forum thread where the feature was discussed etc.
@@ -10,13 +10,19 @@ after_script:
10
10
  script:
11
11
  - bundle exec rake
12
12
  rvm:
13
- - 2.6.3
14
- - 2.5.5
15
- - 2.4.6
16
- - jruby-9.2.7.0
13
+ - 2.6.5
14
+ - 2.5.7
15
+ - 2.4.9
16
+ - ruby-head
17
17
  env:
18
18
  global:
19
19
  - COVERAGE=true
20
+ matrix:
21
+ allow_failures:
22
+ - rvm: ruby-head
23
+ include:
24
+ - rvm: jruby-9.2.8.0
25
+ jdk: openjdk8
20
26
  notifications:
21
27
  email: false
22
28
  webhooks:
@@ -1,3 +1,63 @@
1
+ # 1.2.0 2019-10-06
2
+
3
+ ## Changed
4
+
5
+ - `Dry::Types.[]` used to work with classes, now it's deprecated (flash-gordon)
6
+
7
+ ## Fixed
8
+
9
+ - Bug with using a `Bool`-named struct as a schema key (flash-gordon)
10
+ - A bunch of issues related to using `meta` on complex types (flash-gordon)
11
+ - `Types.Constructor(...)` returns a `Types::Array` as it should (flash-gordon)
12
+
13
+ ## Added
14
+
15
+ - `Optional::Params` types that coerce empty strings to `nil` (flash-gordon)
16
+ ```ruby
17
+ Dry::Types['optional.params.integer'].('') # => nil
18
+ Dry::Types['optional.params.integer'].('140') # => 140
19
+ Dry::Types['optional.params.integer'].('asd') # => exception!
20
+ ```
21
+ Keep in mind, Dry::Types['optional.params.integer'] and Dry::Types['params.integer'].optional are not the same, the latter doesn't handle empty strings.
22
+ - Predicate inferrer was ported from dry-schema (authored by solnic)
23
+ ```ruby
24
+ require 'dry/types/predicate_inferrer'
25
+ Dry::Types::PredicateInferrer.new[Types::String]
26
+ # => [:str?]
27
+ Dry::Types::PredicateInferrer.new[Types::String | Types::Integer]
28
+ # => [[[:str?], [:int?]]]
29
+ ```
30
+ Note that the API of the predicate inferrer can change in the stable version, it's dictated by the needs of dry-schema so it should be considered as semi-stable. If you depend on it, write specs covering the desired behavior. Another option is copy-and-paste the whole thing to your project.
31
+ - Primitive inferrer was ported from dry-schema (authored by solnic)
32
+ ```ruby
33
+ require 'dry/types/primitive_inferrer'
34
+ Dry::Types::PrimitiveInferrer.new[Types::String]
35
+ # => [String]
36
+ Dry::Types::PrimitiveInferrer.new[Types::String | Types::Integer]
37
+ # => [String, Integer]
38
+ Dry::Types::PrimitiveInferrer.new[Types::String.optional]
39
+ # => [NilClass, String]
40
+ ```
41
+ The primitive inferrer should be stable by now, you can rely on it.
42
+ - The `monads` extension adds `Dry::Types::Result#to_monad`. This makes it compatible with do notation from dry-monads. Load it with `Dry::Types.load_extensions(:monads)` (skryukov)
43
+
44
+ ```ruby
45
+ Types = Dry.Types
46
+ Dry::Types.load_extensions(:monads)
47
+
48
+ class AddTen
49
+ include Dry::Monads[:result, :do]
50
+
51
+ def call(input)
52
+ integer = yield Types::Coercible::Integer.try(input)
53
+
54
+ Success(integer + 10)
55
+ end
56
+ end
57
+ ```
58
+
59
+ [Compare v1.1.1...v1.2.0](https://github.com/dry-rb/dry-types/compare/v1.1.1...v1.2.0)
60
+
1
61
  # 1.1.1 2019-07-26
2
62
 
3
63
  ## Fixed
data/Gemfile CHANGED
@@ -18,10 +18,12 @@ group :tools do
18
18
  end
19
19
 
20
20
  group :benchmarks do
21
- gem 'attrio'
22
- gem 'benchmark-ips'
23
- gem 'dry-struct'
24
- gem 'fast_attributes'
25
- gem 'hotch'
26
- gem 'virtus'
21
+ platform :mri do
22
+ gem 'attrio'
23
+ gem 'benchmark-ips'
24
+ gem 'dry-struct'
25
+ gem 'fast_attributes'
26
+ gem 'hotch'
27
+ gem 'virtus'
28
+ end
27
29
  end
@@ -0,0 +1,13 @@
1
+ ---
2
+ title: Array With Member
3
+ layout: gem-single
4
+ name: dry-types
5
+ ---
6
+
7
+ The built-in array type supports defining the member's type:
8
+
9
+ ``` ruby
10
+ PostStatuses = Types::Array.of(Types::Coercible::String)
11
+
12
+ PostStatuses[[:foo, :bar]] # ["foo", "bar"]
13
+ ```
@@ -0,0 +1,116 @@
1
+ ---
2
+ title: Built-in Types
3
+ layout: gem-single
4
+ name: dry-types
5
+ ---
6
+
7
+ Built-in types are grouped under 6 categories:
8
+
9
+ - `nominal` - base type definitions with a primitive class and options
10
+ - `strict` - constrained types with a primitive type check applied to input
11
+ - `coercible` - types with constructors using kernel coercions
12
+ - `params` - types with constructors performing non-strict coercions specific to HTTP parameters
13
+ - `json` - types with constructors performing non-strict coercions specific to JSON
14
+ - `maybe` - types accepting either nil or a specific primitive type
15
+
16
+ ### Categories
17
+
18
+ Assuming you included `Dry::Types` ([see instructions](/gems/dry-types/1.0/getting-started)) in a module called `Types`:
19
+
20
+ * Nominal types:
21
+ - `Types::Nominal::Any`
22
+ - `Types::Nominal::Nil`
23
+ - `Types::Nominal::Symbol`
24
+ - `Types::Nominal::Class`
25
+ - `Types::Nominal::True`
26
+ - `Types::Nominal::False`
27
+ - `Types::Nominal::Bool`
28
+ - `Types::Nominal::Integer`
29
+ - `Types::Nominal::Float`
30
+ - `Types::Nominal::Decimal`
31
+ - `Types::Nominal::String`
32
+ - `Types::Nominal::Date`
33
+ - `Types::Nominal::DateTime`
34
+ - `Types::Nominal::Time`
35
+ - `Types::Nominal::Array`
36
+ - `Types::Nominal::Hash`
37
+
38
+ * `Strict` types will raise an error if passed a value of the wrong type:
39
+ - `Types::Strict::Nil`
40
+ - `Types::Strict::Symbol`
41
+ - `Types::Strict::Class`
42
+ - `Types::Strict::True`
43
+ - `Types::Strict::False`
44
+ - `Types::Strict::Bool`
45
+ - `Types::Strict::Integer`
46
+ - `Types::Strict::Float`
47
+ - `Types::Strict::Decimal`
48
+ - `Types::Strict::String`
49
+ - `Types::Strict::Date`
50
+ - `Types::Strict::DateTime`
51
+ - `Types::Strict::Time`
52
+ - `Types::Strict::Array`
53
+ - `Types::Strict::Hash`
54
+
55
+ > All types in the `strict` category are [constrained](/gems/dry-types/1.0/constraints) by a type-check that is applied to make sure that the input is an instance of the primitive:
56
+
57
+ ``` ruby
58
+ Types::Strict::Integer[1] # => 1
59
+ Types::Strict::Integer['1'] # => raises Dry::Types::ConstraintError
60
+ ```
61
+
62
+ * `Coercible` types will attempt to cast values to the correct class using kernel coercion methods:
63
+ - `Types::Coercible::String`
64
+ - `Types::Coercible::Integer`
65
+ - `Types::Coercible::Float`
66
+ - `Types::Coercible::Decimal`
67
+ - `Types::Coercible::Array`
68
+ - `Types::Coercible::Hash`
69
+
70
+ * Types suitable for `Params` param processing with coercions:
71
+ - `Types::Params::Nil`
72
+ - `Types::Params::Date`
73
+ - `Types::Params::DateTime`
74
+ - `Types::Params::Time`
75
+ - `Types::Params::True`
76
+ - `Types::Params::False`
77
+ - `Types::Params::Bool`
78
+ - `Types::Params::Integer`
79
+ - `Types::Params::Float`
80
+ - `Types::Params::Decimal`
81
+ - `Types::Params::Array`
82
+ - `Types::Params::Hash`
83
+
84
+ * Types suitable for `JSON` processing with coercions:
85
+ - `Types::JSON::Nil`
86
+ - `Types::JSON::Date`
87
+ - `Types::JSON::DateTime`
88
+ - `Types::JSON::Time`
89
+ - `Types::JSON::Decimal`
90
+ - `Types::JSON::Array`
91
+ - `Types::JSON::Hash`
92
+
93
+ * `Maybe` strict types:
94
+ - `Types::Maybe::Strict::Class`
95
+ - `Types::Maybe::Strict::String`
96
+ - `Types::Maybe::Strict::Symbol`
97
+ - `Types::Maybe::Strict::True`
98
+ - `Types::Maybe::Strict::False`
99
+ - `Types::Maybe::Strict::Integer`
100
+ - `Types::Maybe::Strict::Float`
101
+ - `Types::Maybe::Strict::Decimal`
102
+ - `Types::Maybe::Strict::Date`
103
+ - `Types::Maybe::Strict::DateTime`
104
+ - `Types::Maybe::Strict::Time`
105
+ - `Types::Maybe::Strict::Array`
106
+ - `Types::Maybe::Strict::Hash`
107
+
108
+ * `Maybe` coercible types:
109
+ - `Types::Maybe::Coercible::String`
110
+ - `Types::Maybe::Coercible::Integer`
111
+ - `Types::Maybe::Coercible::Float`
112
+ - `Types::Maybe::Coercible::Decimal`
113
+ - `Types::Maybe::Coercible::Array`
114
+ - `Types::Maybe::Coercible::Hash`
115
+
116
+ > `Maybe` types are not available by default - they must be loaded using `Dry::Types.load_extensions(:maybe)`. See [Optional Values](/gems/dry-types/1.0/optional-values) for more information.
@@ -0,0 +1,31 @@
1
+ ---
2
+ title: Constraints
3
+ layout: gem-single
4
+ name: dry-types
5
+ ---
6
+
7
+ You can create constrained types that will use validation rules to check that the input is not violating any of the configured constraints. You can treat it as a lower level guarantee that you're not instantiating objects that are broken.
8
+
9
+ All types support the constraints API, but not all constraints are suitable for a particular primitive, it's up to you to set up constraints that make sense.
10
+
11
+ Under the hood it uses [`dry-logic`](/gems/dry-logic) and all of its predicates are supported.
12
+
13
+ ``` ruby
14
+ string = Types::String.constrained(min_size: 3)
15
+
16
+ string['foo']
17
+ # => "foo"
18
+
19
+ string['fo']
20
+ # => Dry::Types::ConstraintError: "fo" violates constraints
21
+
22
+ email = Types::String.constrained(
23
+ format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
24
+ )
25
+
26
+ email["jane@doe.org"]
27
+ # => "jane@doe.org"
28
+
29
+ email["jane"]
30
+ # => Dry::Types::ConstraintError: "jane" violates constraints
31
+ ```
@@ -0,0 +1,93 @@
1
+ ---
2
+ title: Custom Types
3
+ layout: gem-single
4
+ name: dry-types
5
+ ---
6
+
7
+ There are a bunch of helpers for building your own types based on existing classes and values. These helpers are automatically defined if you're imported types in a module.
8
+
9
+ ### `Types.Instance`
10
+
11
+ `Types.Instance` builds a type that checks if a value has the given class.
12
+
13
+ ```ruby
14
+ range_type = Types.Instance(Range)
15
+ range_type[1..2] # => 1..2
16
+ ```
17
+
18
+ ### `Types.Value`
19
+
20
+ `Types.Value` builds a type that checks a value for equality (using `==`).
21
+
22
+ ```ruby
23
+ valid = Types.Value('valid')
24
+ valid['valid'] # => 'valid'
25
+ valid['invalid']
26
+ # => Dry::Types::ConstraintError: "invalid" violates constraints (eql?("valid", "invalid") failed)
27
+ ```
28
+
29
+ ### `Types.Constant`
30
+
31
+ `Types.Constant` builds a type that checks a value for identity (using `equal?`).
32
+
33
+ ```ruby
34
+ valid = Types.Constant(:valid)
35
+ valid[:valid] # => :valid
36
+ valid[:invalid]
37
+ # => Dry::Types::ConstraintError: :invalid violates constraints (is?(:valid, :invalid) failed)
38
+ ```
39
+
40
+ ### `Types.Constructor`
41
+
42
+ `Types.Constructor` builds a new constructor type for the given class. By default uses the `new` method as a constructor.
43
+
44
+ ```ruby
45
+ user_type = Types.Constructor(User)
46
+
47
+ # It is equivalent to User.new(name: 'John')
48
+ user_type[name: 'John']
49
+
50
+ # Using a block
51
+ user_type = Types.Constructor(User) { |values| User.new(values) }
52
+ ```
53
+
54
+ ### `Types.Nominal`
55
+
56
+ `Types.Nominal` wraps the given class with a simple definition without any behavior attached.
57
+
58
+ ```ruby
59
+ int = Types.Nominal(Integer)
60
+ int[1] # => 1
61
+
62
+ # The type doesn't have any checks
63
+ int['one'] # => 'one'
64
+ ```
65
+
66
+ ### `Types.Hash`
67
+
68
+ `Types.Hash` builds a new hash schema.
69
+
70
+ ```ruby
71
+ # In the full form
72
+ Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer)
73
+
74
+ # Using Types.Hash()
75
+ Types.Hash(:permissive, name: Types::String, age: Types::Coercible::Integer)
76
+ ```
77
+
78
+ ### `Types.Array`
79
+
80
+ `Types.Array` is a shortcut for `Types::Array.of`
81
+
82
+ ```ruby
83
+ ListOfStrings = Types.Array(Types::String)
84
+ ```
85
+
86
+ ### `Types.Interface`
87
+
88
+ `Types.Interface` builds a type that checks a value responds to given methods.
89
+
90
+ ```ruby
91
+ Callable = Types.Interface(:call)
92
+ Contact = Types.Interface(:name, :phone)
93
+ ```
@@ -0,0 +1,91 @@
1
+ ---
2
+ title: Default Values
3
+ layout: gem-single
4
+ name: dry-types
5
+ ---
6
+
7
+ A type with a default value will return the configured value when the input is not defined:
8
+
9
+ ``` ruby
10
+ PostStatus = Types::String.default('draft')
11
+
12
+ PostStatus[] # "draft"
13
+ PostStatus["published"] # "published"
14
+ PostStatus[true] # raises ConstraintError
15
+ ```
16
+
17
+ It works with a callable value:
18
+
19
+ ``` ruby
20
+ CallableDateTime = Types::DateTime.default { DateTime.now }
21
+
22
+ CallableDateTime[]
23
+ # => #<DateTime: 2017-05-06T00:43:06+03:00 ((2457879j,78186s,649279000n),+10800s,2299161j)>
24
+ CallableDateTime[]
25
+ # => #<DateTime: 2017-05-06T00:43:07+03:00 ((2457879j,78187s,635494000n),+10800s,2299161j)>
26
+ ```
27
+
28
+ `Dry::Types::Undefined` can be passed explicitly as a missing value:
29
+
30
+ ```ruby
31
+ PostStatus = Types::String.default('draft')
32
+
33
+ PostStatus[Dry::Types::Undefined] # "draft"
34
+ ```
35
+
36
+ It also receives the type constructor as an argument:
37
+
38
+ ```ruby
39
+ CallableDateTime = Types::DateTime.constructor(&:to_datetime).default { |type| type[Time.now] }
40
+
41
+ CallableDateTime[Time.now]
42
+ # => #<DateTime: 2017-05-06T01:13:06+03:00 ((2457879j,79986s,63464000n),+10800s,2299161j)>
43
+ CallableDateTime[Date.today]
44
+ # => #<DateTime: 2017-05-06T00:00:00+00:00 ((2457880j,0s,0n),+0s,2299161j)>
45
+ CallableDateTime[]
46
+ # => #<DateTime: 2017-05-06T01:13:06+03:00 ((2457879j,79986s,63503000n),+10800s,2299161j)>
47
+ ```
48
+
49
+ **Be careful:** types will return the **same instance** of the default value every time. This may cause problems if you mutate the returned value after receiving it:
50
+
51
+ ```ruby
52
+ default_0 = PostStatus.()
53
+ # => "draft"
54
+ default_1 = PostStatus.()
55
+ # => "draft"
56
+
57
+ # Both variables point to the same string:
58
+ default_0.object_id == default_1.object_id
59
+ # => true
60
+
61
+ # Mutating the string will change the default value of type:
62
+ default_0 << '_mutated'
63
+ PostStatus.(nil)
64
+ # => "draft_mutated" # not "draft"
65
+ ```
66
+
67
+ You can guard against these kind of errors by calling `freeze` when setting the default:
68
+
69
+ ```ruby
70
+ PostStatus = Types::Params::String.default('draft'.freeze)
71
+ default = PostStatus.()
72
+ default << 'attempt to mutate default'
73
+ # => RuntimeError: can't modify frozen string
74
+
75
+ # If you really want to mutate it, call `dup` on it first:
76
+ default = default.dup
77
+ default << "this time it'll work"
78
+ ```
79
+
80
+ **Warning on using with constrained types**: If the value passed to the `.default` block does not match the type constraints, this will not throw an exception, because it is not passed to the constructor and will be used as is.
81
+
82
+ ```ruby
83
+ CallableDateTime = Types::DateTime.constructor(&:to_datetime).default { Time.now }
84
+
85
+ CallableDateTime[Time.now]
86
+ # => #<DateTime: 2017-05-06T00:50:09+03:00 ((2457879j,78609s,839588000n),+10800s,2299161j)>
87
+ CallableDateTime[Date.today]
88
+ # => #<DateTime: 2017-05-06T00:00:00+00:00 ((2457880j,0s,0n),+0s,2299161j)>
89
+ CallableDateTime[]
90
+ # => 2017-05-06 00:50:15 +0300
91
+ ```