dry-types 1.2.1 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +405 -221
  3. data/LICENSE +1 -1
  4. data/README.md +14 -13
  5. data/dry-types.gemspec +26 -31
  6. data/lib/dry-types.rb +1 -1
  7. data/lib/dry/types.rb +55 -40
  8. data/lib/dry/types/any.rb +2 -2
  9. data/lib/dry/types/array.rb +2 -2
  10. data/lib/dry/types/array/constructor.rb +1 -1
  11. data/lib/dry/types/array/member.rb +1 -1
  12. data/lib/dry/types/builder.rb +70 -18
  13. data/lib/dry/types/builder_methods.rb +6 -3
  14. data/lib/dry/types/coercions.rb +6 -17
  15. data/lib/dry/types/coercions/json.rb +22 -5
  16. data/lib/dry/types/coercions/params.rb +21 -4
  17. data/lib/dry/types/compiler.rb +10 -10
  18. data/lib/dry/types/constrained.rb +6 -10
  19. data/lib/dry/types/constraints.rb +3 -3
  20. data/lib/dry/types/constructor.rb +40 -7
  21. data/lib/dry/types/constructor/function.rb +47 -32
  22. data/lib/dry/types/constructor/wrapper.rb +94 -0
  23. data/lib/dry/types/container.rb +1 -1
  24. data/lib/dry/types/core.rb +15 -13
  25. data/lib/dry/types/decorator.rb +2 -9
  26. data/lib/dry/types/default.rb +15 -3
  27. data/lib/dry/types/enum.rb +4 -4
  28. data/lib/dry/types/errors.rb +6 -6
  29. data/lib/dry/types/extensions.rb +2 -2
  30. data/lib/dry/types/extensions/maybe.rb +17 -17
  31. data/lib/dry/types/extensions/monads.rb +1 -1
  32. data/lib/dry/types/fn_container.rb +1 -1
  33. data/lib/dry/types/hash.rb +9 -15
  34. data/lib/dry/types/hash/constructor.rb +1 -1
  35. data/lib/dry/types/inflector.rb +1 -1
  36. data/lib/dry/types/json.rb +15 -15
  37. data/lib/dry/types/lax.rb +4 -7
  38. data/lib/dry/types/map.rb +2 -2
  39. data/lib/dry/types/meta.rb +3 -3
  40. data/lib/dry/types/module.rb +6 -6
  41. data/lib/dry/types/nominal.rb +11 -12
  42. data/lib/dry/types/params.rb +31 -28
  43. data/lib/dry/types/predicate_inferrer.rb +52 -11
  44. data/lib/dry/types/predicate_registry.rb +1 -1
  45. data/lib/dry/types/primitive_inferrer.rb +1 -1
  46. data/lib/dry/types/printer.rb +25 -25
  47. data/lib/dry/types/result.rb +3 -3
  48. data/lib/dry/types/schema.rb +26 -13
  49. data/lib/dry/types/schema/key.rb +20 -7
  50. data/lib/dry/types/spec/types.rb +65 -42
  51. data/lib/dry/types/sum.rb +6 -6
  52. data/lib/dry/types/type.rb +1 -1
  53. data/lib/dry/types/version.rb +1 -1
  54. metadata +27 -84
  55. data/.codeclimate.yml +0 -12
  56. data/.github/ISSUE_TEMPLATE/----please-don-t-ask-for-support-via-issues.md +0 -10
  57. data/.github/ISSUE_TEMPLATE/---bug-report.md +0 -34
  58. data/.github/ISSUE_TEMPLATE/---feature-request.md +0 -18
  59. data/.github/workflows/custom_ci.yml +0 -76
  60. data/.github/workflows/docsite.yml +0 -34
  61. data/.github/workflows/sync_configs.yml +0 -34
  62. data/.gitignore +0 -11
  63. data/.rspec +0 -4
  64. data/.rubocop.yml +0 -89
  65. data/.yardopts +0 -9
  66. data/CODE_OF_CONDUCT.md +0 -13
  67. data/CONTRIBUTING.md +0 -29
  68. data/Gemfile +0 -32
  69. data/Rakefile +0 -22
  70. data/benchmarks/hash_schemas.rb +0 -55
  71. data/benchmarks/lax_schema.rb +0 -15
  72. data/benchmarks/profile_invalid_input.rb +0 -15
  73. data/benchmarks/profile_lax_schema_valid.rb +0 -16
  74. data/benchmarks/profile_valid_input.rb +0 -15
  75. data/benchmarks/schema_valid_vs_invalid.rb +0 -21
  76. data/benchmarks/setup.rb +0 -17
  77. data/docsite/source/array-with-member.html.md +0 -13
  78. data/docsite/source/built-in-types.html.md +0 -116
  79. data/docsite/source/constraints.html.md +0 -31
  80. data/docsite/source/custom-types.html.md +0 -93
  81. data/docsite/source/default-values.html.md +0 -91
  82. data/docsite/source/enum.html.md +0 -69
  83. data/docsite/source/extensions.html.md +0 -15
  84. data/docsite/source/extensions/maybe.html.md +0 -57
  85. data/docsite/source/extensions/monads.html.md +0 -61
  86. data/docsite/source/getting-started.html.md +0 -57
  87. data/docsite/source/hash-schemas.html.md +0 -169
  88. data/docsite/source/index.html.md +0 -156
  89. data/docsite/source/map.html.md +0 -17
  90. data/docsite/source/optional-values.html.md +0 -35
  91. data/docsite/source/sum.html.md +0 -21
@@ -1,169 +0,0 @@
1
- ---
2
- title: Hash Schemas
3
- layout: gem-single
4
- name: dry-types
5
- ---
6
-
7
- It is possible to define a type for a hash with a known set of keys and corresponding value types. Let's say you want to describe a hash containing the name and the age of a user:
8
-
9
- ```ruby
10
- # using simple kernel coercions
11
- user_hash = Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer)
12
-
13
- user_hash[name: 'Jane', age: '21']
14
- # => { name: 'Jane', age: 21 }
15
- # :name left untouched and :age was coerced to Integer
16
- ```
17
-
18
- If a value doesn't conform to the type, an error is raised:
19
-
20
- ```ruby
21
- user_hash[name: :Jane, age: '21']
22
- # => Dry::Types::SchemaError: :Jane (Symbol) has invalid type
23
- # for :name violates constraints (type?(String, :Jane) failed)
24
- ```
25
-
26
- All keys are required by default:
27
-
28
- ```ruby
29
- user_hash[name: 'Jane']
30
- # => Dry::Types::MissingKeyError: :age is missing in Hash input
31
- ```
32
-
33
- Extra keys are omitted by default:
34
-
35
- ```ruby
36
- user_hash[name: 'Jane', age: '21', city: 'London']
37
- # => { name: 'Jane', age: 21 }
38
- ```
39
-
40
- ### Default values
41
-
42
- Default types are **only** evaluated if the corresponding key is missing in the input:
43
-
44
- ```ruby
45
- user_hash = Types::Hash.schema(
46
- name: Types::String,
47
- age: Types::Integer.default(18)
48
- )
49
- user_hash[name: 'Jane']
50
- # => { name: 'Jane', age: 18 }
51
-
52
- # nil violates the constraint
53
- user_hash[name: 'Jane', age: nil]
54
- # => Dry::Types::SchemaError: nil (NilClass) has invalid type
55
- # for :age violates constraints (type?(Integer, nil) failed)
56
- ```
57
-
58
- In order to evaluate default types on `nil`, wrap your type with a constructor and map `nil` to `Dry::Types::Undefined`:
59
-
60
- ```ruby
61
- user_hash = Types::Hash.schema(
62
- name: Types::String,
63
- age: Types::Integer.
64
- default(18).
65
- constructor { |value|
66
- value.nil? ? Dry::Types::Undefined : value
67
- }
68
- )
69
-
70
- user_hash[name: 'Jane', age: nil]
71
- # => { name: 'Jane', age: 18 }
72
- ```
73
-
74
- The process of converting types to constructors like that can be automated, see "Type transformations" below.
75
-
76
- ### Optional keys
77
-
78
- By default, all keys are required to present in the input. You can mark a key as optional by adding `?` to its name:
79
-
80
- ```ruby
81
- user_hash = Types::Hash.schema(name: Types::String, age?: Types::Integer)
82
-
83
- user_hash[name: 'Jane']
84
- # => { name: 'Jane' }
85
- ```
86
-
87
- ### Extra keys
88
-
89
- All keys not declared in the schema are silently ignored. This behavior can be changed by calling `.strict` on the schema:
90
-
91
- ```ruby
92
- user_hash = Types::Hash.schema(name: Types::String).strict
93
- user_hash[name: 'Jane', age: 21]
94
- # => Dry::Types::UnknownKeysError: unexpected keys [:age] in Hash input
95
- ```
96
-
97
- ### Transforming input keys
98
-
99
- Keys are supposed to be symbols but you can attach a key tranformation to a schema, e.g. for converting strings into symbols:
100
-
101
- ```ruby
102
- user_hash = Types::Hash.schema(name: Types::String).with_key_transform(&:to_sym)
103
- user_hash['name' => 'Jane']
104
-
105
- # => { name: 'Jane' }
106
- ```
107
-
108
- ### Inheritance
109
-
110
- Hash schemas can be inherited in a sense you can define a new schema based on an existing one. Declared keys will be merged, key and type transformations will be preserved. The `strict` option is also passed to the new schema if present.
111
-
112
- ```ruby
113
- # Building an empty base schema
114
- StrictSymbolizingHash = Types::Hash.schema({}).strict.with_key_transform(&:to_sym)
115
-
116
- user_hash = StrictSymbolizingHash.schema(
117
- name: Types::String
118
- )
119
-
120
- user_hash['name' => 'Jane']
121
- # => { name: 'Jane' }
122
-
123
- user_hash['name' => 'Jane', 'city' => 'London']
124
- # => Dry::Types::UnknownKeysError: unexpected keys [:city] in Hash input
125
- ```
126
-
127
- ### Transforming types
128
-
129
- A schema can transform types with a block. For example, the following code makes all keys optional:
130
-
131
- ```ruby
132
- user_hash = Types::Hash.with_type_transform { |type| type.required(false) }.schema(
133
- name: Types::String,
134
- age: Types::Integer
135
- )
136
-
137
- user_hash[name: 'Jane']
138
- # => { name: 'Jane' }
139
- user_hash[{}]
140
- # => {}
141
- ```
142
-
143
- Type transformations work perfectly with inheritance, you don't have to define same rules more than once:
144
-
145
- ```ruby
146
- SymbolizeAndOptionalSchema = Types::Hash.
147
- .schema({})
148
- .with_key_transform(&:to_sym)
149
- .with_type_transform { |type| type.required(false) }
150
-
151
- user_hash = SymbolizeAndOptionalSchema.schema(
152
- name: Types::String,
153
- age: Types::Integer
154
- )
155
-
156
- user_hash['name' => 'Jane']
157
- ```
158
-
159
- You can check key name by calling `.name` on the type argument:
160
-
161
- ```ruby
162
- Types::Hash.with_type_transform do |key|
163
- if key.name.to_s.end_with?('_at')
164
- key.constructor { |v| Time.iso8601(v) }
165
- else
166
- key
167
- end
168
- end
169
- ```
@@ -1,156 +0,0 @@
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)
@@ -1,17 +0,0 @@
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
- ```
@@ -1,35 +0,0 @@
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.
@@ -1,21 +0,0 @@
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
- ```