dry-types 1.2.2 → 1.5.1

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 -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,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- $LOAD_PATH.unshift('lib')
4
-
5
- require 'bundler/setup'
6
- require 'dry-types'
7
-
8
- module SchemaBench
9
- def self.hash_schema(type)
10
- Dry::Types['nominal.hash'].public_send(
11
- type,
12
- email: Dry::Types['nominal.string'],
13
- age: Dry::Types['params.integer'],
14
- admin: Dry::Types['params.bool'],
15
- address: Dry::Types['nominal.hash'].public_send(
16
- type,
17
- city: Dry::Types['nominal.string'],
18
- street: Dry::Types['nominal.string']
19
- )
20
- )
21
- end
22
-
23
- private_class_method(:hash_schema)
24
-
25
- SCHEMAS =
26
- Dry::Types::Hash
27
- .public_instance_methods(false)
28
- .map { |schema_type| [schema_type, hash_schema(schema_type)] }
29
- .to_h
30
-
31
- INPUT = {
32
- email: 'jane@doe.org',
33
- age: '20',
34
- admin: '1',
35
- address: { city: 'NYC', street: 'Street 1/2' }
36
- }.freeze
37
- end
38
-
39
- require 'benchmark/ips'
40
-
41
- Benchmark.ips do |x|
42
- SchemaBench::SCHEMAS.each do |schema_type, schema|
43
- x.report("#{schema_type}#call") do
44
- schema.call(SchemaBench::INPUT)
45
- end
46
- end
47
-
48
- SchemaBench::SCHEMAS.each do |schema_type, schema|
49
- x.report("#{schema_type}#try") do
50
- schema.try(SchemaBench::INPUT)
51
- end
52
- end
53
-
54
- x.compare!
55
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'setup'
4
-
5
- schema = Dry::Types['params.hash'].schema(
6
- email?: 'string',
7
- age?: 'params.integer'
8
- ).lax
9
-
10
- params = { email: 'jane@doe.org', age: '19' }
11
-
12
- Benchmark.ips do |x|
13
- x.report("valid input") { schema.(params) }
14
- x.compare!
15
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'setup'
4
-
5
- INVALID_INPUT = {
6
- name: :John,
7
- age: '20',
8
- email: nil
9
- }.freeze
10
-
11
- profile do
12
- 10_000.times do
13
- PersonSchema.(INVALID_INPUT)
14
- end
15
- end
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'setup'
4
-
5
- Schema = Dry::Types['params.hash'].schema(
6
- email?: 'string',
7
- age?: 'coercible.integer'
8
- ).lax
9
-
10
- ValidInput = { email: 'jane@doe.org', age: '19' }.freeze
11
-
12
- profile do
13
- 10_000.times do
14
- Schema.(ValidInput)
15
- end
16
- end
@@ -1,15 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'setup'
4
-
5
- VALID_INPUT = {
6
- name: 'John',
7
- age: 20,
8
- email: 'john@doe.com'
9
- }.freeze
10
-
11
- profile do
12
- 10_000.times do
13
- PersonSchema.(VALID_INPUT)
14
- end
15
- end
@@ -1,21 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'setup'
4
-
5
- VALID_INPUT = {
6
- name: 'John',
7
- age: 20,
8
- email: 'john@doe.com'
9
- }
10
-
11
- INVALID_INPUT = {
12
- name: :John,
13
- age: '20',
14
- email: nil
15
- }
16
-
17
- Benchmark.ips do |x|
18
- x.report("valid input") { PersonSchema.(VALID_INPUT) }
19
- x.report("invalid input") { PersonSchema.(INVALID_INPUT) }
20
- x.compare!
21
- end
data/benchmarks/setup.rb DELETED
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'benchmark/ips'
4
- require 'hotch'
5
- ENV['HOTCH_VIEWER'] ||= 'open'
6
-
7
- require 'dry/types'
8
-
9
- PersonSchema = Dry::Types['hash'].schema(
10
- name: 'string',
11
- age: 'integer',
12
- email: 'string'
13
- ).lax
14
-
15
- def profile(&block)
16
- Hotch(filter: 'Dry', &block)
17
- end
@@ -1,13 +0,0 @@
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
- ```
@@ -1,116 +0,0 @@
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](docs::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](docs::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 [Maybe extension](docs::extensions/maybe) for more information.
@@ -1,31 +0,0 @@
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
- ```
@@ -1,93 +0,0 @@
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
- ```
@@ -1,91 +0,0 @@
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
- ```