dry-types 1.2.2 → 1.5.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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +405 -225
  3. data/LICENSE +1 -1
  4. data/README.md +14 -12
  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 +1 -2
  14. data/lib/dry/types/coercions.rb +0 -17
  15. data/lib/dry/types/coercions/json.rb +22 -5
  16. data/lib/dry/types/coercions/params.rb +20 -3
  17. data/lib/dry/types/compiler.rb +10 -10
  18. data/lib/dry/types/constrained.rb +6 -9
  19. data/lib/dry/types/constraints.rb +3 -3
  20. data/lib/dry/types/constructor.rb +40 -6
  21. data/lib/dry/types/constructor/function.rb +48 -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 +14 -1
  27. data/lib/dry/types/enum.rb +4 -3
  28. data/lib/dry/types/errors.rb +1 -1
  29. data/lib/dry/types/extensions.rb +2 -2
  30. data/lib/dry/types/extensions/maybe.rb +18 -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 +3 -6
  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 -11
  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 +15 -6
  50. data/lib/dry/types/spec/types.rb +65 -42
  51. data/lib/dry/types/sum.rb +6 -5
  52. data/lib/dry/types/type.rb +1 -1
  53. data/lib/dry/types/version.rb +1 -1
  54. metadata +27 -78
  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 -30
  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 -92
  65. data/.yardopts +0 -9
  66. data/CODE_OF_CONDUCT.md +0 -13
  67. data/CONTRIBUTING.md +0 -29
  68. data/Gemfile +0 -34
  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,69 +0,0 @@
1
- ---
2
- title: Enum
3
- layout: gem-single
4
- name: dry-types
5
- ---
6
-
7
- In many cases you may want to define an enum. For example, in a blog application a post may have a finite list of statuses. Apart from accessing the current status value, it is useful to have all possible values accessible too. Furthermore, an enum can be a map from, e.g., strings to integers. This is useful for mapping externally-provided integer values to human-readable strings without explicit conversions, see examples.
8
-
9
- ``` ruby
10
- require 'dry-types'
11
- require 'dry-struct'
12
-
13
- module Types
14
- include Dry.Types()
15
- end
16
-
17
- class Post < Dry::Struct
18
- Statuses = Types::String.enum('draft', 'published', 'archived')
19
-
20
- attribute :title, Types::String
21
- attribute :body, Types::String
22
- attribute :status, Statuses
23
- end
24
-
25
- # enum values are frozen, let's be paranoid, doesn't hurt and have potential to
26
- # eliminate silly bugs
27
- Post::Statuses.values.frozen? # => true
28
- Post::Statuses.values.all?(&:frozen?) # => true
29
-
30
- Post::Statuses['draft'] # => "draft"
31
-
32
- # it'll raise if something silly was passed in
33
- Post::Statuses['something silly']
34
- # => Dry::Types::ConstraintError: "something silly" violates constraints
35
-
36
- # nil is considered as something silly too
37
- Post::Statuses[nil]
38
- # => Dry::Types::ConstraintError: nil violates constraints
39
- ```
40
-
41
- Note that if you want to define an enum type with a default, you must call `.default` *before* calling `.enum`, not the other way around:
42
-
43
- ```ruby
44
- # this is the correct usage:
45
- Dry::Types::String.default('red').enum('blue', 'green', 'red')
46
-
47
- # this will raise an error:
48
- Dry::Types::String.enum('blue', 'green', 'red').default('red')
49
- ```
50
-
51
- ### Mappings
52
-
53
- A classic example is mapping integers coming from somewhere (API/database/etc) to something more understandable:
54
-
55
- ```ruby
56
- class Cell < Dry::Struct
57
- attribute :state, Types::String.enum('locked' => 0, 'open' => 1)
58
- end
59
-
60
-
61
- Cell.new(state: 'locked')
62
- # => #<Cell state="locked">
63
-
64
- # Integers are accepted too
65
- Cell.new(state: 0)
66
- # => #<Cell state="locked">
67
- Cell.new(state: 1)
68
- # => #<Cell state="open">
69
- ```
@@ -1,15 +0,0 @@
1
- ---
2
- title: Extensions
3
- layout: gem-single
4
- name: dry-types
5
- sections:
6
- - maybe
7
- - monads
8
- ---
9
-
10
- `dry-types` can be extended with extension. Those extensions are loaded with `Dry::Types.load_extensions`.
11
-
12
- Available extensions:
13
-
14
- - [Maybe](docs::extensions/maybe)
15
- - [Monads](docs::extensions/monads)
@@ -1,57 +0,0 @@
1
- ---
2
- title: Maybe
3
- layout: gem-single
4
- name: dry-types
5
- ---
6
-
7
- The [dry-monads gem](/gems/dry-monads/) provides approach to handling optional values by returning a [_Monad_](/gems/dry-monads/) object. This allows you to pass your type to a `Maybe(x)` block that only executes if `x` returns `Some` or `None`.
8
-
9
- > NOTE: Requires the [dry-monads gem](/gems/dry-monads/) to be loaded.
10
- 1. Load the `:maybe` extension in your application.
11
-
12
- ```ruby
13
- require 'dry-types'
14
-
15
- Dry::Types.load_extensions(:maybe)
16
- module Types
17
- include Dry.Types()
18
- end
19
- ```
20
-
21
- 2. Append `.maybe` to a _Type_ to return a _Monad_ object
22
-
23
- ```ruby
24
- x = Types::Maybe::Strict::Integer[nil]
25
- Maybe(x) { puts(x) }
26
- x = Types::Maybe::Coercible::String[nil]
27
- Maybe(x) { puts(x) }
28
- x = Types::Maybe::Strict::Integer[123]
29
- Maybe(x) { puts(x) }
30
- x = Types::Maybe::Strict::String[123]
31
- Maybe(x) { puts(x) }
32
- ```
33
-
34
- ```ruby
35
- Types::Maybe::Strict::Integer[nil] # None
36
- Types::Maybe::Strict::Integer[123] # Some(123)
37
- Types::Maybe::Coercible::Float[nil] # None
38
- Types::Maybe::Coercible::Float['12.3'] # Some(12.3)
39
- # 'Maybe' types can also accessed by calling '.maybe' on a regular type:
40
- Types::Strict::Integer.maybe # equivalent to Types::Maybe::Strict::Integer
41
- ```
42
-
43
- You can define your own optional types:
44
-
45
- ``` ruby
46
- maybe_string = Types::Strict::String.maybe
47
- maybe_string[nil]
48
- # => None
49
- maybe_string[nil].fmap(&:upcase)
50
- # => None
51
- maybe_string['something']
52
- # => Some('something')
53
- maybe_string['something'].fmap(&:upcase)
54
- # => Some('SOMETHING')
55
- maybe_string['something'].fmap(&:upcase).value_or('NOTHING')
56
- # => "SOMETHING"
57
- ```
@@ -1,61 +0,0 @@
1
- ---
2
- title: Monads
3
- layout: gem-single
4
- name: dry-types
5
- ---
6
-
7
- The monads extension makes `Dry::Types::Result` objects compatible with `dry-monads`.
8
-
9
- To enable the extension:
10
-
11
- ```ruby
12
- require 'dry/types'
13
- Dry::Types.load_extensions(:monads)
14
- ```
15
-
16
- After loading the extension, you can leverage monad API:
17
-
18
- ```ruby
19
- Types = Dry.Types()
20
-
21
- result = Types::String.try('Jane')
22
- result.class #=> Dry::Types::Result::Success
23
- monad = result.to_monad
24
- monad.class #=> Dry::Monads::Result::Success
25
- monad.value! # => 'Jane'
26
- result = Types::String.try(nil)
27
- result.class #=> Dry::Types::Result::Failure
28
- monad = result.to_monad
29
- monad.class #=> Dry::Monads::Result::Failure
30
- monad.failure # => [#<Dry::Types::ConstraintError>, nil]
31
- Types::String.try(nil)
32
- .to_monad
33
- .fmap { |result| puts "passed: #{result.inspect}" }
34
- .or { |error, input| puts "input '#{input.inspect}' failed with error: #{error.to_s}" }
35
- ```
36
-
37
- This can be useful when used with `dry-monads` and the [`do` notation](/gems/dry-monads/1.0/do-notation/):
38
-
39
- ```ruby
40
- require 'dry/types'
41
- Types = Dry.Types()
42
- Dry::Types.load_extensions(:monads)
43
-
44
- class AddTen
45
- include Dry::Monads[:result, :do]
46
-
47
- def call(input)
48
- integer = yield Types::Coercible::Integer.try(input)
49
-
50
- Success(integer + 10)
51
- end
52
- end
53
-
54
- add_ten = AddTen.new
55
-
56
- add_ten.call(10)
57
- # => Success(20)
58
-
59
- add_ten.call('integer')
60
- # => Failure([#<Dry::Types::CoercionError: invalid value for Integer(): "integer">, "integer"])
61
- ```
@@ -1,57 +0,0 @@
1
- ---
2
- title: Getting Started
3
- layout: gem-single
4
- name: dry-types
5
- ---
6
-
7
- ### Using `Dry::Types` in Your Application
8
-
9
- 1. Make `Dry::Types` available to the application by creating a namespace that includes `Dry::Types`:
10
-
11
- ```ruby
12
- module Types
13
- include Dry.Types()
14
- end
15
- ```
16
-
17
- 2. Reload the environment, & type `Types::Coercible::String` in the ruby console to confirm it worked:
18
-
19
- ``` ruby
20
- Types::Coercible::String
21
- # => #<Dry::Types::Constructor type=#<Dry::Types::Definition primitive=String options={}>>
22
- ```
23
-
24
- ### Creating Your First Type
25
-
26
- 1. Define a struct's types by passing the name & type to the `attribute` method:
27
-
28
- ```ruby
29
- class User < Dry::Struct
30
- attribute :name, Types::String
31
- end
32
- ```
33
-
34
- 2. Define [Custom Types](docs::custom-types) in the `Types` module, then pass the name & type to `attribute`:
35
-
36
- ```ruby
37
- module Types
38
- include Dry.Types()
39
-
40
- Email = String.constrained(format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i)
41
- Age = Integer.constrained(gt: 18)
42
- end
43
- class User < Dry::Struct
44
- attribute :name, Types::String
45
- attribute :email, Types::Email
46
- attribute :age, Types::Age
47
- end
48
- ```
49
-
50
- 3. Use a `Dry::Struct` as a type:
51
-
52
- ```ruby
53
- class Message < Dry::Struct
54
- attribute :body, Types::String
55
- attribute :to, User
56
- end
57
- ```
@@ -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)