dry-validation 0.7.4 → 0.8.0
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/.gitignore +1 -0
- data/.travis.yml +9 -6
- data/CHANGELOG.md +58 -0
- data/Gemfile +3 -3
- data/benchmarks/benchmark_form_invalid.rb +64 -0
- data/benchmarks/benchmark_form_valid.rb +64 -0
- data/benchmarks/profile_schema_call_invalid.rb +20 -0
- data/benchmarks/profile_schema_call_valid.rb +20 -0
- data/benchmarks/profile_schema_definition.rb +14 -0
- data/benchmarks/profile_schema_messages_invalid.rb +20 -0
- data/benchmarks/suite.rb +5 -0
- data/config/errors.yml +12 -5
- data/dry-validation.gemspec +2 -2
- data/examples/basic.rb +2 -2
- data/examples/form.rb +2 -2
- data/examples/json.rb +2 -2
- data/examples/nested.rb +6 -6
- data/lib/dry/validation.rb +22 -3
- data/lib/dry/validation/deprecations.rb +23 -0
- data/lib/dry/validation/error_compiler.rb +31 -11
- data/lib/dry/validation/error_compiler/input.rb +44 -57
- data/lib/dry/validation/hint_compiler.rb +15 -7
- data/lib/dry/validation/input_processor_compiler.rb +13 -6
- data/lib/dry/validation/input_processor_compiler/form.rb +2 -0
- data/lib/dry/validation/input_processor_compiler/sanitizer.rb +1 -1
- data/lib/dry/validation/messages/abstract.rb +9 -1
- data/lib/dry/validation/predicate_registry.rb +101 -0
- data/lib/dry/validation/result.rb +8 -1
- data/lib/dry/validation/schema.rb +110 -44
- data/lib/dry/validation/schema/check.rb +4 -2
- data/lib/dry/validation/schema/deprecated.rb +31 -0
- data/lib/dry/validation/schema/dsl.rb +18 -11
- data/lib/dry/validation/schema/form.rb +1 -0
- data/lib/dry/validation/schema/json.rb +1 -0
- data/lib/dry/validation/schema/key.rb +23 -10
- data/lib/dry/validation/schema/rule.rb +81 -20
- data/lib/dry/validation/schema/value.rb +110 -25
- data/lib/dry/validation/version.rb +1 -1
- data/spec/fixtures/locales/en.yml +1 -0
- data/spec/fixtures/locales/pl.yml +1 -1
- data/spec/integration/custom_error_messages_spec.rb +2 -2
- data/spec/integration/custom_predicates_spec.rb +98 -15
- data/spec/integration/error_compiler_spec.rb +111 -96
- data/spec/integration/form/predicates/array_spec.rb +243 -0
- data/spec/integration/form/predicates/empty_spec.rb +263 -0
- data/spec/integration/form/predicates/eql_spec.rb +327 -0
- data/spec/integration/form/predicates/even_spec.rb +455 -0
- data/spec/integration/form/predicates/excluded_from_spec.rb +455 -0
- data/spec/integration/form/predicates/excludes_spec.rb +391 -0
- data/spec/integration/form/predicates/false_spec.rb +455 -0
- data/spec/integration/form/predicates/filled_spec.rb +467 -0
- data/spec/integration/form/predicates/format_spec.rb +454 -0
- data/spec/integration/form/predicates/gt_spec.rb +519 -0
- data/spec/integration/form/predicates/gteq_spec.rb +519 -0
- data/spec/integration/form/predicates/included_in_spec.rb +455 -0
- data/spec/integration/form/predicates/includes_spec.rb +391 -0
- data/spec/integration/form/predicates/key_spec.rb +75 -0
- data/spec/integration/form/predicates/lt_spec.rb +519 -0
- data/spec/integration/form/predicates/lteq_spec.rb +519 -0
- data/spec/integration/form/predicates/max_size_spec.rb +391 -0
- data/spec/integration/form/predicates/min_size_spec.rb +391 -0
- data/spec/integration/form/predicates/none_spec.rb +265 -0
- data/spec/integration/form/predicates/not_eql_spec.rb +327 -0
- data/spec/integration/form/predicates/odd_spec.rb +455 -0
- data/spec/integration/form/predicates/size/fixed_spec.rb +399 -0
- data/spec/integration/form/predicates/size/range_spec.rb +398 -0
- data/spec/integration/form/predicates/true_spec.rb +455 -0
- data/spec/integration/form/predicates/type_spec.rb +391 -0
- data/spec/integration/hints_spec.rb +90 -4
- data/spec/integration/injecting_rules_spec.rb +2 -2
- data/spec/integration/localized_error_messages_spec.rb +2 -2
- data/spec/integration/messages/i18n_spec.rb +3 -3
- data/spec/integration/optional_keys_spec.rb +3 -3
- data/spec/integration/schema/array_schema_spec.rb +49 -13
- data/spec/integration/schema/check_rules_spec.rb +6 -6
- data/spec/integration/schema/check_with_nested_el_spec.rb +3 -3
- data/spec/integration/schema/check_with_nth_el_spec.rb +1 -1
- data/spec/integration/schema/each_with_set_spec.rb +3 -3
- data/spec/integration/schema/extending_dsl_spec.rb +27 -0
- data/spec/integration/schema/form/explicit_types_spec.rb +182 -0
- data/spec/integration/schema/form_spec.rb +90 -18
- data/spec/integration/schema/hash_schema_spec.rb +47 -0
- data/spec/integration/schema/inheriting_schema_spec.rb +4 -4
- data/spec/integration/schema/input_processor_spec.rb +8 -8
- data/spec/integration/schema/json/explicit_types_spec.rb +157 -0
- data/spec/integration/schema/json_spec.rb +18 -18
- data/spec/integration/schema/macros/confirmation_spec.rb +1 -1
- data/spec/integration/schema/macros/each_spec.rb +177 -43
- data/spec/integration/schema/macros/{required_spec.rb → filled_spec.rb} +34 -6
- data/spec/integration/schema/macros/input_spec.rb +21 -0
- data/spec/integration/schema/macros/maybe_spec.rb +39 -2
- data/spec/integration/schema/macros/value_spec.rb +105 -0
- data/spec/integration/schema/macros/when_spec.rb +28 -8
- data/spec/integration/schema/nested_values_spec.rb +11 -8
- data/spec/integration/schema/not_spec.rb +2 -2
- data/spec/integration/schema/numbers_spec.rb +1 -1
- data/spec/integration/schema/option_with_default_spec.rb +1 -1
- data/spec/integration/schema/predicate_verification_spec.rb +9 -0
- data/spec/integration/schema/predicates/array_spec.rb +295 -0
- data/spec/integration/schema/predicates/custom_spec.rb +103 -0
- data/spec/integration/schema/predicates/empty_spec.rb +263 -0
- data/spec/integration/schema/predicates/eql_spec.rb +327 -0
- data/spec/integration/schema/predicates/even_spec.rb +455 -0
- data/spec/integration/schema/predicates/excluded_from_spec.rb +455 -0
- data/spec/integration/schema/predicates/excludes_spec.rb +391 -0
- data/spec/integration/schema/predicates/filled_spec.rb +467 -0
- data/spec/integration/schema/predicates/format_spec.rb +455 -0
- data/spec/integration/schema/predicates/gt_spec.rb +519 -0
- data/spec/integration/schema/predicates/gteq_spec.rb +519 -0
- data/spec/integration/schema/predicates/hash_spec.rb +69 -0
- data/spec/integration/schema/predicates/included_in_spec.rb +455 -0
- data/spec/integration/schema/predicates/includes_spec.rb +391 -0
- data/spec/integration/schema/predicates/key_spec.rb +88 -0
- data/spec/integration/schema/predicates/lt_spec.rb +520 -0
- data/spec/integration/schema/predicates/lteq_spec.rb +519 -0
- data/spec/integration/schema/predicates/max_size_spec.rb +391 -0
- data/spec/integration/schema/predicates/min_size_spec.rb +391 -0
- data/spec/integration/schema/predicates/none_spec.rb +265 -0
- data/spec/integration/schema/predicates/not_eql_spec.rb +391 -0
- data/spec/integration/schema/predicates/odd_spec.rb +455 -0
- data/spec/integration/schema/predicates/size/fixed_spec.rb +401 -0
- data/spec/integration/schema/predicates/size/range_spec.rb +399 -0
- data/spec/integration/schema/predicates/type_spec.rb +391 -0
- data/spec/integration/schema/reusing_schema_spec.rb +4 -4
- data/spec/integration/schema/using_types_spec.rb +24 -6
- data/spec/integration/schema/xor_spec.rb +2 -2
- data/spec/integration/schema_builders_spec.rb +15 -0
- data/spec/integration/schema_spec.rb +37 -12
- data/spec/shared/predicate_helper.rb +13 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/matchers.rb +38 -0
- data/spec/support/predicates_integration.rb +7 -0
- data/spec/unit/hint_compiler_spec.rb +10 -8
- data/spec/unit/input_processor_compiler/form_spec.rb +45 -43
- data/spec/unit/input_processor_compiler/json_spec.rb +37 -35
- data/spec/unit/predicate_registry_spec.rb +34 -0
- data/spec/unit/schema/key_spec.rb +12 -14
- data/spec/unit/schema/rule_spec.rb +4 -2
- data/spec/unit/schema/value_spec.rb +38 -121
- metadata +150 -16
- data/lib/dry/validation/schema/attr.rb +0 -15
- data/spec/integration/attr_spec.rb +0 -122
@@ -1,7 +1,7 @@
|
|
1
1
|
RSpec.describe 'Schema / Injecting Rules' do
|
2
2
|
subject(:schema) do
|
3
3
|
Dry::Validation.Schema(rules: other.class.rules) do
|
4
|
-
|
4
|
+
required(:email).maybe
|
5
5
|
|
6
6
|
rule(:email) { value(:login).true? > value(:email).filled? }
|
7
7
|
end
|
@@ -9,7 +9,7 @@ RSpec.describe 'Schema / Injecting Rules' do
|
|
9
9
|
|
10
10
|
let(:other) do
|
11
11
|
Dry::Validation.Schema do
|
12
|
-
|
12
|
+
required(:login) { |value| value.bool? }
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -15,7 +15,7 @@ RSpec.describe Dry::Validation, 'with localized messages' do
|
|
15
15
|
config.messages = :i18n
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
required(:email) { |email| email.filled? }
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
@@ -38,7 +38,7 @@ RSpec.describe Dry::Validation, 'with localized messages' do
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
41
|
+
required(:email) { |email| email.filled? }
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
@@ -30,13 +30,13 @@ RSpec.describe Messages::I18n do
|
|
30
30
|
it 'returns a message for a specific val type' do
|
31
31
|
message = messages[:size?, rule: :pages, val_type: String]
|
32
32
|
|
33
|
-
expect(message).to eql("length must be %{
|
33
|
+
expect(message).to eql("length must be %{size}")
|
34
34
|
end
|
35
35
|
|
36
36
|
it 'returns a message for a specific rule and its default arg type' do
|
37
37
|
message = messages[:size?, rule: :pages]
|
38
38
|
|
39
|
-
expect(message).to eql("size must be %{
|
39
|
+
expect(message).to eql("size must be %{size}")
|
40
40
|
end
|
41
41
|
|
42
42
|
it 'returns a message for a specific rule and its arg type' do
|
@@ -62,7 +62,7 @@ RSpec.describe Messages::I18n do
|
|
62
62
|
it 'returns a message for a specific rule and its default arg type' do
|
63
63
|
message = messages[:size?, rule: :pages, locale: :pl]
|
64
64
|
|
65
|
-
expect(message).to eql("wielkość musi być równa %{
|
65
|
+
expect(message).to eql("wielkość musi być równa %{size}")
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'returns a message for a specific rule and its arg type' do
|
@@ -4,9 +4,9 @@ RSpec.describe Dry::Validation::Schema do
|
|
4
4
|
Dry::Validation.Schema do
|
5
5
|
optional(:email) { |email| email.filled? }
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
required(:address).schema do
|
8
|
+
required(:city, &:filled?)
|
9
|
+
required(:street, &:filled?)
|
10
10
|
|
11
11
|
optional(:phone_number) do
|
12
12
|
none? | str?
|
@@ -1,23 +1,59 @@
|
|
1
1
|
RSpec.describe Dry::Validation::Schema, 'for an array' do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
context 'without type specs' do
|
3
|
+
subject(:schema) do
|
4
|
+
Dry::Validation.Schema do
|
5
|
+
each do
|
6
|
+
schema do
|
7
|
+
required(:prefix).filled
|
8
|
+
required(:value).filled
|
9
|
+
end
|
10
|
+
end
|
7
11
|
end
|
8
12
|
end
|
13
|
+
|
14
|
+
it 'applies its rules to array input' do
|
15
|
+
result = schema.([{ prefix: 1, value: 123 }, { prefix: 2, value: 456 }])
|
16
|
+
|
17
|
+
expect(result).to be_success
|
18
|
+
|
19
|
+
result = schema.([{ prefix: 1, value: nil }, { prefix: nil, value: 456 }])
|
20
|
+
|
21
|
+
expect(result.messages).to eql(
|
22
|
+
0 => { value: ["must be filled"] },
|
23
|
+
1 => { prefix: ["must be filled"] }
|
24
|
+
)
|
25
|
+
end
|
9
26
|
end
|
10
27
|
|
11
|
-
|
12
|
-
|
28
|
+
context 'with type specs' do
|
29
|
+
subject(:schema) do
|
30
|
+
Dry::Validation.Form do
|
31
|
+
configure { config.type_specs = true }
|
13
32
|
|
14
|
-
|
33
|
+
each do
|
34
|
+
schema do
|
35
|
+
required(:prefix, :int).filled
|
36
|
+
required(:value, :int).filled
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'applies its rules to coerced array input' do
|
43
|
+
result = schema.([{ prefix: 1, value: '123' }, { prefix: 2, value: 456 }])
|
44
|
+
|
45
|
+
expect(result).to be_success
|
15
46
|
|
16
|
-
|
47
|
+
expect(result.output).to eql(
|
48
|
+
[{ prefix: 1, value: 123 }, { prefix: 2, value: 456 }]
|
49
|
+
)
|
17
50
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
51
|
+
result = schema.([{ prefix: 1, value: nil }, { prefix: nil, value: 456 }])
|
52
|
+
|
53
|
+
expect(result.messages).to eql(
|
54
|
+
0 => { value: ["must be filled"] },
|
55
|
+
1 => { prefix: ["must be filled"] }
|
56
|
+
)
|
57
|
+
end
|
22
58
|
end
|
23
59
|
end
|
@@ -50,8 +50,8 @@ RSpec.describe Schema, 'using high-level rules' do
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
|
-
|
53
|
+
required(:login).filled(:bool?)
|
54
|
+
required(:email).maybe
|
55
55
|
|
56
56
|
rule(:email_presence) { value(:login).true?.then(value(:email).filled?) }
|
57
57
|
|
@@ -83,18 +83,18 @@ RSpec.describe Schema, 'using high-level rules' do
|
|
83
83
|
describe 'with nested schemas' do
|
84
84
|
subject(:schema) do
|
85
85
|
Dry::Validation.Schema do
|
86
|
-
|
86
|
+
required(:command).filled(:str?, included_in?: %w(First Second))
|
87
87
|
|
88
|
-
|
88
|
+
required(:args).filled(:hash?)
|
89
89
|
|
90
90
|
rule(first_args: [:command, :args]) do |command, args|
|
91
91
|
command.eql?('First')
|
92
|
-
.then(args.schema {
|
92
|
+
.then(args.schema { required(:first).filled(:bool?) })
|
93
93
|
end
|
94
94
|
|
95
95
|
rule(second_args: [:command, :args]) do |command, args|
|
96
96
|
command.eql?('Second')
|
97
|
-
.then(args.schema {
|
97
|
+
.then(args.schema { required(:second).filled(:bool?) })
|
98
98
|
end
|
99
99
|
end
|
100
100
|
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
RSpec.describe 'Check depending on a nested value from a hash' do
|
2
2
|
subject(:schema) do
|
3
3
|
Dry::Validation.Schema do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
required(:tag).schema do
|
5
|
+
required(:color).schema do
|
6
|
+
required(:value).filled(:str?)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
@@ -1,9 +1,9 @@
|
|
1
1
|
RSpec.describe 'Schema with each and set rules' do
|
2
2
|
subject(:schema) do
|
3
3
|
Dry::Validation.Schema do
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
required(:payments).each do
|
5
|
+
required(:method).filled(:str?)
|
6
|
+
required(:amount).filled(:float?)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
RSpec.describe 'Extending DSL' do
|
2
|
+
it 'allows configuring custom DSL methods' do
|
3
|
+
dsl_ext = Module.new do
|
4
|
+
def maybe_int(name, *predicates, &block)
|
5
|
+
required(name, [:nil, :int]).maybe(:int?, *predicates, &block)
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Dry::Validation::Schema.configure do |config|
|
10
|
+
config.dsl_extensions = dsl_ext
|
11
|
+
end
|
12
|
+
|
13
|
+
schema = Dry::Validation.Schema do
|
14
|
+
configure do
|
15
|
+
config.input_processor = :form
|
16
|
+
config.type_specs = true
|
17
|
+
end
|
18
|
+
|
19
|
+
maybe_int(:age)
|
20
|
+
end
|
21
|
+
|
22
|
+
expect(schema.(age: nil)).to be_success
|
23
|
+
expect(schema.(age: 1)).to be_success
|
24
|
+
expect(schema.(age: '1')).to be_success
|
25
|
+
expect(schema.(age: 'foo').messages).to eql(age: ['must be an integer'])
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
RSpec.describe Dry::Validation::Schema::Form, 'explicit types' do
|
2
|
+
context 'single type spec without rules' do
|
3
|
+
subject(:schema) do
|
4
|
+
Dry::Validation.Form do
|
5
|
+
configure { config.type_specs = true }
|
6
|
+
required(:age, :int)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'uses form coercion' do
|
11
|
+
expect(schema.('age' => '19').to_h).to eql(age: 19)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'single type spec with rules' do
|
16
|
+
subject(:schema) do
|
17
|
+
Dry::Validation.Form do
|
18
|
+
configure { config.type_specs = true }
|
19
|
+
required(:age, :int).value(:int?, gt?: 18)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'applies rules to coerced value' do
|
24
|
+
expect(schema.(age: 19).messages).to be_empty
|
25
|
+
expect(schema.(age: 18).messages).to eql(age: ['must be greater than 18'])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'single type spec with an array' do
|
30
|
+
subject(:schema) do
|
31
|
+
Dry::Validation.Form do
|
32
|
+
configure { config.type_specs = true }
|
33
|
+
required(:nums, [:int])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'uses form coercion' do
|
38
|
+
expect(schema.(nums: %w(1 2 3)).to_h).to eql(nums: [1, 2, 3])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'sum type spec without rules' do
|
43
|
+
subject(:schema) do
|
44
|
+
Dry::Validation.Form do
|
45
|
+
configure { config.type_specs = true }
|
46
|
+
required(:age, [:nil, :int])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'uses form coercion' do
|
51
|
+
expect(schema.('age' => '19').to_h).to eql(age: 19)
|
52
|
+
expect(schema.('age' => '').to_h).to eql(age: nil)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'sum type spec with rules' do
|
57
|
+
subject(:schema) do
|
58
|
+
Dry::Validation.Form do
|
59
|
+
configure { config.type_specs = true }
|
60
|
+
required(:age, [:nil, :int]).maybe(:int?, gt?: 18)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'applies rules to coerced value' do
|
65
|
+
expect(schema.(age: nil).messages).to be_empty
|
66
|
+
expect(schema.(age: 19).messages).to be_empty
|
67
|
+
expect(schema.(age: 18).messages).to eql(age: ['must be greater than 18'])
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'using a type object' do
|
72
|
+
subject(:schema) do
|
73
|
+
Dry::Validation.Form do
|
74
|
+
configure { config.type_specs = true }
|
75
|
+
required(:age, Types::Form::Nil | Types::Form::Int)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'uses form coercion' do
|
80
|
+
expect(schema.('age' => '').to_h).to eql(age: nil)
|
81
|
+
expect(schema.('age' => '19').to_h).to eql(age: 19)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'nested schema' do
|
86
|
+
subject(:schema) do
|
87
|
+
Dry::Validation.Form do
|
88
|
+
configure { config.type_specs = true }
|
89
|
+
|
90
|
+
required(:user).schema do
|
91
|
+
required(:email, :string)
|
92
|
+
required(:age, :int)
|
93
|
+
|
94
|
+
required(:address).schema do
|
95
|
+
required(:street, :string)
|
96
|
+
required(:city, :string)
|
97
|
+
required(:zipcode, :string)
|
98
|
+
|
99
|
+
required(:location).schema do
|
100
|
+
required(:lat, :float)
|
101
|
+
required(:lng, :float)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'uses form coercion for nested input' do
|
109
|
+
input = {
|
110
|
+
'user' => {
|
111
|
+
'email' => 'jane@doe.org',
|
112
|
+
'age' => '21',
|
113
|
+
'address' => {
|
114
|
+
'street' => 'Street 1',
|
115
|
+
'city' => 'NYC',
|
116
|
+
'zipcode' => '1234',
|
117
|
+
'location' => { 'lat' => '1.23', 'lng' => '4.56' }
|
118
|
+
}
|
119
|
+
}
|
120
|
+
}
|
121
|
+
|
122
|
+
expect(schema.(input).to_h).to eql(
|
123
|
+
user: {
|
124
|
+
email: 'jane@doe.org',
|
125
|
+
age: 21,
|
126
|
+
address: {
|
127
|
+
street: 'Street 1',
|
128
|
+
city: 'NYC',
|
129
|
+
zipcode: '1234',
|
130
|
+
location: { lat: 1.23, lng: 4.56 }
|
131
|
+
}
|
132
|
+
}
|
133
|
+
)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'nested schema with arrays' do
|
138
|
+
subject(:schema) do
|
139
|
+
Dry::Validation.Form do
|
140
|
+
configure { config.type_specs = true }
|
141
|
+
|
142
|
+
required(:song).schema do
|
143
|
+
required(:title, :string)
|
144
|
+
|
145
|
+
required(:tags).each do
|
146
|
+
schema do
|
147
|
+
required(:name, :string)
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'fails to coerce gracefuly' do
|
155
|
+
result = schema.(song: nil)
|
156
|
+
|
157
|
+
expect(result.messages).to eql(song: ['must be a hash'])
|
158
|
+
expect(result.to_h).to eql(song: nil)
|
159
|
+
|
160
|
+
result = schema.(song: { tags: nil })
|
161
|
+
|
162
|
+
expect(result.messages).to eql(song: { tags: ['must be an array'] })
|
163
|
+
expect(result.to_h).to eql(song: { tags: nil })
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'uses form coercion for nested input' do
|
167
|
+
input = {
|
168
|
+
'song' => {
|
169
|
+
'title' => 'dry-rb is awesome lala',
|
170
|
+
'tags' => [{ 'name' => 'red' }, { 'name' => 'blue' }]
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
expect(schema.(input).to_h).to eql(
|
175
|
+
song: {
|
176
|
+
title: 'dry-rb is awesome lala',
|
177
|
+
tags: [{ name: 'red' }, { name: 'blue' }]
|
178
|
+
}
|
179
|
+
)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -1,17 +1,23 @@
|
|
1
1
|
RSpec.describe Dry::Validation::Schema::Form, 'defining a schema' do
|
2
2
|
subject(:schema) do
|
3
3
|
Dry::Validation.Form do
|
4
|
-
|
4
|
+
configure do
|
5
|
+
def email?(value)
|
6
|
+
true
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
required(:email).filled
|
5
11
|
|
6
|
-
|
12
|
+
required(:age).maybe(:int?, gt?: 18)
|
7
13
|
|
8
|
-
|
9
|
-
|
10
|
-
|
14
|
+
required(:address).schema do
|
15
|
+
required(:city).filled
|
16
|
+
required(:street).filled
|
11
17
|
|
12
|
-
|
13
|
-
|
14
|
-
|
18
|
+
required(:loc).schema do
|
19
|
+
required(:lat).filled(:float?)
|
20
|
+
required(:lng).filled(:float?)
|
15
21
|
end
|
16
22
|
end
|
17
23
|
|
@@ -20,12 +26,6 @@ RSpec.describe Dry::Validation::Schema::Form, 'defining a schema' do
|
|
20
26
|
optional(:phone_number).maybe(:int?, gt?: 0)
|
21
27
|
|
22
28
|
rule(:email_valid) { value(:email).email? }
|
23
|
-
|
24
|
-
configure do
|
25
|
-
def email?(value)
|
26
|
-
true
|
27
|
-
end
|
28
|
-
end
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
@@ -118,12 +118,84 @@ RSpec.describe Dry::Validation::Schema::Form, 'defining a schema' do
|
|
118
118
|
end
|
119
119
|
end
|
120
120
|
|
121
|
+
describe 'with an each and nested schema' do
|
122
|
+
subject(:schema) do
|
123
|
+
Dry::Validation.Form do
|
124
|
+
required(:items).each do
|
125
|
+
schema do
|
126
|
+
required(:title).filled(:str?)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'passes when each element passes schema check' do
|
133
|
+
expect(schema.(items: [{ title: 'Foo' }])).to be_success
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'fails when one or more elements did not pass schema check' do
|
137
|
+
expect(schema.(items: [{ title: 'Foo' }, { title: :Foo }]).messages).to eql(
|
138
|
+
items: { 1 => { title: ['must be a string'] } }
|
139
|
+
)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe 'symbolizing keys when coercion fails' do
|
144
|
+
subject(:schema) do
|
145
|
+
Dry::Validation.Form do
|
146
|
+
required(:email).value(size?: 8..60)
|
147
|
+
required(:birthdate).value(:date?)
|
148
|
+
required(:age).value(:int?, gt?: 23)
|
149
|
+
required(:tags).maybe(max_size?: 3)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
let(:tags) { %(al b c) }
|
154
|
+
|
155
|
+
it 'symbolizes keys and coerces values' do
|
156
|
+
result = schema.(
|
157
|
+
'email' => 'jane@doe.org',
|
158
|
+
'birthdate' => '2001-02-03',
|
159
|
+
'age' => '24',
|
160
|
+
'tags' => tags
|
161
|
+
).to_h
|
162
|
+
|
163
|
+
expect(result.to_h).to eql(
|
164
|
+
email: 'jane@doe.org',
|
165
|
+
birthdate: Date.new(2001, 2, 3),
|
166
|
+
age: 24,
|
167
|
+
tags: tags
|
168
|
+
)
|
169
|
+
end
|
170
|
+
|
171
|
+
it 'symbolizes keys even when coercion fails' do
|
172
|
+
result = schema.(
|
173
|
+
'email' => 'jane@doe.org',
|
174
|
+
'birthdate' => '2001-sutin-03',
|
175
|
+
'age' => ['oops'],
|
176
|
+
'tags' => nil
|
177
|
+
)
|
178
|
+
|
179
|
+
expect(result.to_h).to eql(
|
180
|
+
email: 'jane@doe.org',
|
181
|
+
birthdate: '2001-sutin-03',
|
182
|
+
age: ['oops'],
|
183
|
+
tags: nil
|
184
|
+
)
|
185
|
+
|
186
|
+
expect(result.messages).to eql(
|
187
|
+
birthdate: ['must be a date'],
|
188
|
+
age: ['must be an integer', 'must be greater than 23']
|
189
|
+
)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
121
193
|
describe 'with nested schema in a high-level rule' do
|
122
194
|
subject(:schema) do
|
123
195
|
Dry::Validation.Form do
|
124
|
-
|
196
|
+
required(:address).maybe(:hash?)
|
125
197
|
|
126
|
-
|
198
|
+
required(:delivery).filled(:bool?)
|
127
199
|
|
128
200
|
rule(address: [:delivery, :address]) do |delivery, address|
|
129
201
|
delivery.true?.then(address.schema(AddressSchema))
|
@@ -133,8 +205,8 @@ RSpec.describe Dry::Validation::Schema::Form, 'defining a schema' do
|
|
133
205
|
|
134
206
|
before do
|
135
207
|
AddressSchema = Dry::Validation.Form do
|
136
|
-
|
137
|
-
|
208
|
+
required(:city).filled
|
209
|
+
required(:zipcode).filled(:int?)
|
138
210
|
end
|
139
211
|
end
|
140
212
|
|