dry-validation 0.6.0 → 0.7.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.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +1 -0
  3. data/.travis.yml +3 -2
  4. data/CHANGELOG.md +42 -0
  5. data/Gemfile +8 -1
  6. data/README.md +13 -89
  7. data/config/errors.yml +35 -29
  8. data/dry-validation.gemspec +2 -2
  9. data/examples/basic.rb +3 -7
  10. data/examples/each.rb +3 -8
  11. data/examples/form.rb +3 -6
  12. data/examples/nested.rb +7 -15
  13. data/lib/dry/validation.rb +33 -5
  14. data/lib/dry/validation/error.rb +10 -26
  15. data/lib/dry/validation/error_compiler.rb +69 -99
  16. data/lib/dry/validation/error_compiler/input.rb +148 -0
  17. data/lib/dry/validation/hint_compiler.rb +83 -33
  18. data/lib/dry/validation/input_processor_compiler.rb +98 -0
  19. data/lib/dry/validation/input_processor_compiler/form.rb +46 -0
  20. data/lib/dry/validation/input_processor_compiler/sanitizer.rb +46 -0
  21. data/lib/dry/validation/messages/abstract.rb +30 -10
  22. data/lib/dry/validation/messages/i18n.rb +2 -1
  23. data/lib/dry/validation/messages/namespaced.rb +1 -0
  24. data/lib/dry/validation/messages/yaml.rb +8 -5
  25. data/lib/dry/validation/result.rb +33 -25
  26. data/lib/dry/validation/schema.rb +168 -61
  27. data/lib/dry/validation/schema/attr.rb +5 -27
  28. data/lib/dry/validation/schema/check.rb +24 -0
  29. data/lib/dry/validation/schema/dsl.rb +97 -0
  30. data/lib/dry/validation/schema/form.rb +2 -26
  31. data/lib/dry/validation/schema/key.rb +32 -28
  32. data/lib/dry/validation/schema/rule.rb +88 -32
  33. data/lib/dry/validation/schema/value.rb +77 -27
  34. data/lib/dry/validation/schema_compiler.rb +38 -0
  35. data/lib/dry/validation/version.rb +1 -1
  36. data/spec/fixtures/locales/pl.yml +1 -1
  37. data/spec/integration/attr_spec.rb +122 -0
  38. data/spec/integration/custom_error_messages_spec.rb +9 -11
  39. data/spec/integration/custom_predicates_spec.rb +68 -18
  40. data/spec/integration/error_compiler_spec.rb +259 -65
  41. data/spec/integration/hints_spec.rb +28 -9
  42. data/spec/integration/injecting_rules_spec.rb +11 -12
  43. data/spec/integration/localized_error_messages_spec.rb +16 -16
  44. data/spec/integration/messages/i18n_spec.rb +9 -5
  45. data/spec/integration/optional_keys_spec.rb +9 -11
  46. data/spec/integration/schema/array_schema_spec.rb +23 -0
  47. data/spec/integration/schema/check_rules_spec.rb +39 -31
  48. data/spec/integration/schema/check_with_nth_el_spec.rb +25 -0
  49. data/spec/integration/schema/each_with_set_spec.rb +23 -24
  50. data/spec/integration/schema/form_spec.rb +122 -0
  51. data/spec/integration/schema/inheriting_schema_spec.rb +31 -0
  52. data/spec/integration/schema/input_processor_spec.rb +46 -0
  53. data/spec/integration/schema/macros/confirmation_spec.rb +33 -0
  54. data/spec/integration/schema/macros/maybe_spec.rb +32 -0
  55. data/spec/integration/schema/macros/required_spec.rb +59 -0
  56. data/spec/integration/schema/macros/when_spec.rb +65 -0
  57. data/spec/integration/schema/nested_values_spec.rb +41 -0
  58. data/spec/integration/schema/not_spec.rb +14 -14
  59. data/spec/integration/schema/option_with_default_spec.rb +30 -0
  60. data/spec/integration/schema/reusing_schema_spec.rb +33 -0
  61. data/spec/integration/schema/using_types_spec.rb +29 -0
  62. data/spec/integration/schema/xor_spec.rb +17 -14
  63. data/spec/integration/schema_spec.rb +75 -245
  64. data/spec/shared/rule_compiler.rb +8 -0
  65. data/spec/spec_helper.rb +13 -0
  66. data/spec/unit/hint_compiler_spec.rb +10 -10
  67. data/spec/unit/{input_type_compiler_spec.rb → input_processor_compiler/form_spec.rb} +88 -73
  68. data/spec/unit/schema/key_spec.rb +33 -0
  69. data/spec/unit/schema/rule_spec.rb +7 -6
  70. data/spec/unit/schema/value_spec.rb +187 -54
  71. metadata +53 -31
  72. data/.rubocop.yml +0 -16
  73. data/.rubocop_todo.yml +0 -7
  74. data/lib/dry/validation/input_type_compiler.rb +0 -83
  75. data/lib/dry/validation/schema/definition.rb +0 -74
  76. data/lib/dry/validation/schema/result.rb +0 -68
  77. data/rakelib/rubocop.rake +0 -18
  78. data/spec/integration/rule_groups_spec.rb +0 -94
  79. data/spec/integration/schema/attrs_spec.rb +0 -38
  80. data/spec/integration/schema/default_key_behavior_spec.rb +0 -23
  81. data/spec/integration/schema/grouped_rules_spec.rb +0 -57
  82. data/spec/integration/schema/nested_spec.rb +0 -31
  83. data/spec/integration/schema_form_spec.rb +0 -97
@@ -0,0 +1,8 @@
1
+ require 'dry/logic/rule_compiler'
2
+ require 'dry/logic/predicates'
3
+
4
+ RSpec.shared_context 'rule compiler' do
5
+ let(:rule_compiler) do
6
+ Dry::Logic::RuleCompiler.new(Dry::Logic::Predicates)
7
+ end
8
+ end
@@ -10,6 +10,7 @@ if RUBY_ENGINE == "rbx"
10
10
  end
11
11
 
12
12
  require 'dry-validation'
13
+ require 'ostruct'
13
14
 
14
15
  SPEC_ROOT = Pathname(__dir__)
15
16
 
@@ -18,6 +19,18 @@ Dir[SPEC_ROOT.join('support/**/*.rb')].each(&method(:require))
18
19
 
19
20
  include Dry::Validation
20
21
 
22
+ class Schema::DSL < BasicObject
23
+ def inspect
24
+ to_ast.inspect
25
+ end
26
+ end
27
+
28
+ class Schema::Rule < BasicObject
29
+ def inspect
30
+ to_ast.inspect
31
+ end
32
+ end
33
+
21
34
  RSpec.configure do |config|
22
35
  config.disable_monkey_patching!
23
36
 
@@ -7,14 +7,14 @@ RSpec.describe HintCompiler, '#call' do
7
7
  [
8
8
  [
9
9
  :and, [
10
- [:key, [:age, [:predicate, [:key?, []]]]],
10
+ [:val, [:predicate, [:key?, [:age]]]],
11
11
  [
12
12
  :or, [
13
- [:val, [:age, [:predicate, [:none?, []]]]],
13
+ [:key, [:age, [:predicate, [:none?, []]]]],
14
14
  [
15
15
  :and, [
16
- [:val, [:age, [:predicate, [:int?, []]]]],
17
- [:val, [:age, [:predicate, [:gt?, [18]]]]]
16
+ [:key, [:age, [:predicate, [:int?, []]]]],
17
+ [:key, [:age, [:predicate, [:gt?, [18]]]]]
18
18
  ]
19
19
  ]
20
20
  ]
@@ -23,14 +23,14 @@ RSpec.describe HintCompiler, '#call' do
23
23
  ],
24
24
  [
25
25
  :and, [
26
- [:attr, [:height, [:predicate, [:attr?, []]]]],
26
+ [:val, [:predicate, [:attr?, [:height]]]],
27
27
  [
28
28
  :or, [
29
- [:val, [:height, [:predicate, [:none?, []]]]],
29
+ [:attr, [:height, [:predicate, [:none?, []]]]],
30
30
  [
31
31
  :and, [
32
- [:val, [:height, [:predicate, [:int?, []]]]],
33
- [:val, [:height, [:predicate, [:gt?, [180]]]]]
32
+ [:attr, [:height, [:predicate, [:int?, []]]]],
33
+ [:attr, [:height, [:predicate, [:gt?, [180]]]]]
34
34
  ]
35
35
  ]
36
36
  ]
@@ -42,8 +42,8 @@ RSpec.describe HintCompiler, '#call' do
42
42
 
43
43
  it 'returns hint messages for given rules' do
44
44
  expect(compiler.call).to eql(
45
- age: ['age must be an integer', 'age must be greater than 18'],
46
- height: ['height must be an integer', 'height must be greater than 180'],
45
+ age: ['must be greater than 18'],
46
+ height: ['must be greater than 180'],
47
47
  )
48
48
  end
49
49
  end
@@ -1,31 +1,29 @@
1
- require 'dry/validation/input_type_compiler'
2
-
3
- RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
4
- subject(:compiler) { Dry::Validation::InputTypeCompiler.new }
1
+ RSpec.describe Dry::Validation::InputProcessorCompiler::Form, '#call' do
2
+ subject(:compiler) { Dry::Validation::InputProcessorCompiler::Form.new }
5
3
 
6
4
  let(:rule_ast) do
7
5
  [
8
6
  [
9
7
  :and, [
10
- [:key, [:email, [:predicate, [:key?, [:email]]]]],
8
+ [:val, [:predicate, [:key?, [:email]]]],
11
9
  [
12
10
  :and, [
13
- [:val, [:email, [:predicate, [:str?, []]]]],
14
- [:val, [:email, [:predicate, [:filled?, []]]]]
11
+ [:key, [:email, [:predicate, [:str?, []]]]],
12
+ [:key, [:email, [:predicate, [:filled?, []]]]]
15
13
  ]
16
14
  ]
17
15
  ]
18
16
  ],
19
17
  [
20
18
  :and, [
21
- [:key, [:age, [:predicate, [:key?, [:age]]]]],
19
+ [:val, [:predicate, [:key?, [:age]]]],
22
20
  [
23
21
  :or, [
24
- [:val, [:age, [:predicate, [:none?, []]]]],
22
+ [:key, [:age, [:predicate, [:none?, []]]]],
25
23
  [
26
24
  :and, [
27
- [:val, [:age, [:predicate, [:int?, []]]]],
28
- [:val, [:age, [:predicate, [:filled?, []]]]]
25
+ [:key, [:age, [:predicate, [:int?, []]]]],
26
+ [:key, [:age, [:predicate, [:filled?, []]]]]
29
27
  ]
30
28
  ]
31
29
  ]
@@ -34,32 +32,64 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
34
32
  ],
35
33
  [
36
34
  :and, [
37
- [:key, [:address, [:predicate, [:key?, [:address]]]]],
38
- [:val, [:address, [:predicate, [:str?, []]]]]
35
+ [:val, [:predicate, [:key?, [:address]]]],
36
+ [:key, [:address, [:predicate, [:str?, []]]]]
39
37
  ]
40
38
  ]
41
- ].map(&:to_ary)
39
+ ]
42
40
  end
43
41
 
44
- let(:params) do
42
+ let(:output) do
45
43
  { 'email' => 'jane@doe.org', 'age' => '20', 'address' => 'City, Street 1/2' }
46
44
  end
47
45
 
48
46
  it 'builds an input dry-data type' do
49
47
  input_type = compiler.(rule_ast)
50
48
 
51
- result = input_type[params]
49
+ result = input_type[output]
52
50
 
53
51
  expect(result).to eql(email: 'jane@doe.org', age: 20, address: 'City, Street 1/2')
54
52
  end
55
53
 
54
+ it 'supports arbitrary types via type?(const) => "form.const"' do
55
+ rule_ast = [
56
+ [
57
+ :and,
58
+ [
59
+ [:val, [:predicate, [:key?, [:age]]]],
60
+ [:key, [:age, [:predicate, [:type?, [Fixnum]]]]]
61
+ ]
62
+ ]
63
+ ]
64
+
65
+ input_type = compiler.(rule_ast)
66
+
67
+ expect(input_type['age' => '21']).to eql(age: 21)
68
+ end
69
+
70
+ it 'supports arbitrary types via type?(conts)' do
71
+ rule_ast = [
72
+ [
73
+ :and,
74
+ [
75
+ [:val, [:predicate, [:key?, [:admin]]]],
76
+ [:key, [:admin, [:predicate, [:type?, ['Form::Bool']]]]]
77
+ ]
78
+ ]
79
+ ]
80
+
81
+ input_type = compiler.(rule_ast)
82
+
83
+ expect(input_type['admin' => '0']).to eql(admin: false)
84
+ end
85
+
56
86
  it 'supports int? => "form.int"' do
57
87
  rule_ast = [
58
88
  [
59
89
  :and,
60
90
  [
61
- [:key, [:age, [:predicate, [:key?, [:age]]]]],
62
- [:val, [:age, [:predicate, [:int?, []]]]],
91
+ [:val, [:predicate, [:key?, [:age]]]],
92
+ [:key, [:age, [:predicate, [:int?, []]]]],
63
93
  ]
64
94
  ]
65
95
  ]
@@ -74,11 +104,11 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
74
104
  [
75
105
  :and,
76
106
  [
77
- [:key, [:age, [:predicate, [:key?, [:age]]]]],
107
+ [:val, [:predicate, [:key?, [:age]]]],
78
108
  [
79
109
  :or, [
80
- [:val, [:age, [:predicate, [:none?, []]]]],
81
- [:val, [:age, [:predicate, [:int?, []]]]],
110
+ [:key, [:age, [:predicate, [:none?, []]]]],
111
+ [:key, [:age, [:predicate, [:int?, []]]]],
82
112
  ]
83
113
  ]
84
114
  ]
@@ -96,8 +126,8 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
96
126
  [
97
127
  :and,
98
128
  [
99
- [:key, [:lat, [:predicate, [:key?, [:lat]]]]],
100
- [:val, [:lat, [:predicate, [:float?, []]]]],
129
+ [:val, [:predicate, [:key?, [:lat]]]],
130
+ [:key, [:lat, [:predicate, [:float?, []]]]],
101
131
  ]
102
132
  ]
103
133
  ]
@@ -112,8 +142,8 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
112
142
  [
113
143
  :and,
114
144
  [
115
- [:key, [:lat, [:predicate, [:key?, [:lat]]]]],
116
- [:val, [:lat, [:predicate, [:decimal?, []]]]],
145
+ [:val, [:predicate, [:key?, [:lat]]]],
146
+ [:key, [:lat, [:predicate, [:decimal?, []]]]],
117
147
  ]
118
148
  ]
119
149
  ]
@@ -128,8 +158,8 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
128
158
  [
129
159
  :and,
130
160
  [
131
- [:key, [:bday, [:predicate, [:key?, [:bday]]]]],
132
- [:val, [:bday, [:predicate, [:date?, []]]]],
161
+ [:val, [:predicate, [:key?, [:bday]]]],
162
+ [:key, [:bday, [:predicate, [:date?, []]]]],
133
163
  ]
134
164
  ]
135
165
  ]
@@ -144,8 +174,8 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
144
174
  [
145
175
  :and,
146
176
  [
147
- [:key, [:bday, [:predicate, [:key?, [:bday]]]]],
148
- [:val, [:bday, [:predicate, [:date_time?, []]]]],
177
+ [:val, [:predicate, [:key?, [:bday]]]],
178
+ [:key, [:bday, [:predicate, [:date_time?, []]]]],
149
179
  ]
150
180
  ]
151
181
  ]
@@ -160,8 +190,8 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
160
190
  [
161
191
  :and,
162
192
  [
163
- [:key, [:bday, [:predicate, [:key?, [:bday]]]]],
164
- [:val, [:bday, [:predicate, [:time?, []]]]],
193
+ [:val, [:predicate, [:key?, [:bday]]]],
194
+ [:key, [:bday, [:predicate, [:time?, []]]]],
165
195
  ]
166
196
  ]
167
197
  ]
@@ -176,8 +206,8 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
176
206
  [
177
207
  :and,
178
208
  [
179
- [:key, [:bday, [:predicate, [:key?, [:bday]]]]],
180
- [:val, [:bday, [:predicate, [:time?, []]]]],
209
+ [:val, [:predicate, [:key?, [:bday]]]],
210
+ [:key, [:bday, [:predicate, [:time?, []]]]],
181
211
  ]
182
212
  ]
183
213
  ]
@@ -192,8 +222,8 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
192
222
  [
193
223
  :and,
194
224
  [
195
- [:key, [:admin, [:predicate, [:key?, [:admin]]]]],
196
- [:val, [:admin, [:predicate, [:bool?, []]]]],
225
+ [:val, [:predicate, [:key?, [:admin]]]],
226
+ [:key, [:admin, [:predicate, [:bool?, []]]]],
197
227
  ]
198
228
  ]
199
229
  ]
@@ -208,35 +238,24 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
208
238
  rule_ast = [
209
239
  [
210
240
  :and, [
211
- [:key, [:author, [:predicate, [:key?, []]]]],
212
- [
213
- :set, [
214
- :author, [
215
- [
216
- :and, [
217
- [:key, [:books, [:predicate, [:key?, []]]]],
241
+ [:val, [:predicate, [:key?, [:author]]]],
242
+ [:set, [
243
+ [:and, [
244
+ [:val, [:predicate, [:key?, [:books]]]],
245
+ [
246
+ :each, [
247
+ :set, [
218
248
  [
219
- :each, [
220
- :books,
221
- [
222
- :set, [
223
- :books, [
224
- [
225
- :and, [
226
- [:key, [:published, [:predicate, [:key?, []]]]],
227
- [:val, [:published, [:predicate, [:bool?, []]]]]
228
- ]
229
- ]
230
- ]
231
- ]
232
- ]
249
+ :and, [
250
+ [:val, [:predicate, [:key?, [:published]]]],
251
+ [:key, [:published, [:predicate, [:bool?, []]]]]
233
252
  ]
234
253
  ]
235
254
  ]
236
255
  ]
237
256
  ]
238
- ]
239
- ]
257
+ ]]
258
+ ]]
240
259
  ]
241
260
  ]
242
261
  ]
@@ -256,13 +275,11 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
256
275
  rule_ast = [
257
276
  [
258
277
  :and, [
259
- [:key, [:ids, [:predicate, [:key?, []]]]],
260
- [
261
- :and, [
262
- [:val, [:ids, [:predicate, [:array?, []]]]],
263
- [:each, [:ids, [:val, [:ids, [:predicate, [:int?, []]]]]]]
264
- ]
265
- ]
278
+ [:val, [:predicate, [:key?, [:ids]]]],
279
+ [:and, [
280
+ [:key, [:ids, [:predicate, [:array?, []]]]],
281
+ [:each, [:val, [:predicate, [:int?, []]]]]
282
+ ]]
266
283
  ]
267
284
  ]
268
285
  ]
@@ -278,18 +295,16 @@ RSpec.describe Dry::Validation::InputTypeCompiler, '#call' do
278
295
  rule_ast = [
279
296
  [
280
297
  :and, [
281
- [:key, [:address, [:predicate, [:key?, []]]]],
298
+ [:val, [:predicate, [:key?, [:address]]]],
282
299
  [
283
300
  :and, [
284
- [:val, [:address, [:predicate, [:hash?, []]]]],
301
+ [:key, [:address, [:predicate, [:hash?, []]]]],
285
302
  [
286
303
  :set, [
287
- :address, [
288
- [
289
- :and, [
290
- [:key, [:street, [:predicate, [:key?, []]]]],
291
- [:val, [:street, [:predicate, [:filled?, []]]]]
292
- ]
304
+ [
305
+ :and, [
306
+ [:val, [:predicate, [:key?, [:street]]]],
307
+ [:key, [:street, [:predicate, [:filled?, []]]]]
293
308
  ]
294
309
  ]
295
310
  ]
@@ -0,0 +1,33 @@
1
+ RSpec.describe Schema::Key do
2
+ describe '#key?' do
3
+ subject(:user) { Schema::Key[:user] }
4
+
5
+ it 'returns a key rule' do
6
+ rule = user.key?(:address)
7
+
8
+ expect(rule.to_ast).to eql([:key, [:user, [:predicate, [:key?, [:address]]]]])
9
+ end
10
+
11
+ it 'returns a key rule & disjunction rule created within the block' do
12
+ user.hash? do
13
+ key(:email) { none? | filled? }
14
+ end
15
+
16
+ expect(user.to_ast).to eql([
17
+ :key, [:user, [
18
+ :and, [
19
+ [:val, [:predicate, [:hash?, []]]],
20
+ [:key, [:user, [:set, [
21
+ [:and, [
22
+ [:val, [:predicate, [:key?, [:email]]]],
23
+ [:or, [
24
+ [:key, [:email, [:predicate, [:none?, []]]]],
25
+ [:key, [:email, [:predicate, [:filled?, []]]]]]
26
+ ]]
27
+ ]]]]]
28
+ ]
29
+ ]]
30
+ ])
31
+ end
32
+ end
33
+ end
@@ -2,30 +2,31 @@ RSpec.describe Schema::Rule do
2
2
  let(:filled) { [:val, [:email, [:predicate, [:filled?, []]]]] }
3
3
  let(:format) { [:val, [:email, [:predicate, [:format?, [/regex/]]]]] }
4
4
 
5
- let(:left) { Schema::Rule.new(:email, filled) }
6
- let(:right) { Schema::Rule.new(:email, format) }
5
+ let(:left) { Schema::Rule.new(filled, name: :email, target: target) }
6
+ let(:right) { Schema::Rule.new(format, name: :email, target: target) }
7
+ let(:target) { double(:target, id: :user) }
7
8
 
8
9
  describe '#and' do
9
10
  it 'returns a conjunction' do
10
- expect(left.and(right).to_ary).to match_array([:and, [filled, format]])
11
+ expect(left.and(right).to_ast).to match_array([:and, [filled, format]])
11
12
  end
12
13
  end
13
14
 
14
15
  describe '#or' do
15
16
  it 'returns a disjunction' do
16
- expect(left.or(right).to_ary).to match_array([:or, [filled, format]])
17
+ expect(left.or(right).to_ast).to match_array([:or, [filled, format]])
17
18
  end
18
19
  end
19
20
 
20
21
  describe '#xor' do
21
22
  it 'returns an exclusive disjunction' do
22
- expect(left.xor(right).to_ary).to match_array([:xor, [filled, format]])
23
+ expect(left.xor(right).to_ast).to match_array([:xor, [filled, format]])
23
24
  end
24
25
  end
25
26
 
26
27
  describe '#then' do
27
28
  it 'returns an implication' do
28
- expect(left.then(right).to_ary).to match_array([:implication, [filled, format]])
29
+ expect(left.then(right).to_ast).to match_array([:implication, [filled, format]])
29
30
  end
30
31
  end
31
32
  end
@@ -1,82 +1,215 @@
1
1
  RSpec.describe Schema::Value do
2
+ include_context 'rule compiler'
3
+
4
+ describe '#key' do
5
+ subject(:value) { Schema::Value.new }
6
+
7
+ let(:expected_ast) do
8
+ [:and, [
9
+ [:val, [:predicate, [:key?, [:address]]]],
10
+ [:key, [:address, [:predicate, [:filled?, []]]]]
11
+ ]]
12
+ end
13
+
14
+ it 'creates a rule for a specified key using a block' do
15
+ rule = value.key(:address, &:filled?)
16
+
17
+ expect(rule.to_ast).to eql(expected_ast)
18
+ end
19
+
20
+ it 'creates a rule for a specified key using a macro' do
21
+ rule = value.key(:address).required
22
+
23
+ expect(rule.to_ast).to eql(expected_ast)
24
+ end
25
+ end
26
+
27
+ describe '#key deeply nested' do
28
+ subject(:value) { Schema::Value.new }
29
+
30
+ let(:expected_ast) do
31
+ [:and, [
32
+ [:val, [:predicate, [:key?, [:address]]]],
33
+ [:key, [:address,
34
+ [:and, [
35
+ [:val, [:predicate, [:key?, [:location]]]],
36
+ [:key, [:location,
37
+ [:set, [
38
+ [:and, [
39
+ [:val, [:predicate, [:key?, [:lat]]]],
40
+ [:key, [:lat, [:predicate, [:filled?, []]]]]]
41
+ ],
42
+ [:and, [
43
+ [:val, [:predicate, [:key?, [:lng]]]],
44
+ [:key, [:lng, [:predicate, [:filled?, []]]]]
45
+ ]]
46
+ ]]
47
+ ]]]
48
+ ]]]
49
+ ]]
50
+ end
51
+
52
+ it 'creates a rule for specified keys using blocks' do
53
+ rule = value.key(:address) do
54
+ key(:location) do
55
+ key(:lat) { filled? }
56
+ key(:lng) { filled? }
57
+ end
58
+ end
59
+
60
+ expect(rule.to_ast).to eql(expected_ast)
61
+ end
62
+
63
+ it 'creates a rule for specified keys using macros' do
64
+ rule = value.key(:address) do
65
+ key(:location) do
66
+ key(:lat).required
67
+ key(:lng).required
68
+ end
69
+ end
70
+
71
+ expect(rule.to_ast).to eql(expected_ast)
72
+ end
73
+ end
74
+
75
+ describe '#key with multiple inner-keys' do
76
+ subject(:value) { Schema::Value.new }
77
+
78
+ let(:expected_ast) do
79
+ [:and, [
80
+ [:val, [:predicate, [:key?, [:address]]]],
81
+ [:key, [:address, [:set, [
82
+ [:and, [
83
+ [:val, [:predicate, [:key?, [:city]]]],
84
+ [:key, [:city, [:predicate, [:filled?, []]]]]]
85
+ ],
86
+ [:and, [
87
+ [:val, [:predicate, [:key?, [:zipcode]]]],
88
+ [:key, [:zipcode, [:predicate, [:filled?, []]]]]
89
+ ]]
90
+ ]]]]
91
+ ]]
92
+ end
93
+
94
+ it 'creates a rule for specified keys using blocks' do
95
+ rule = value.key(:address) do
96
+ key(:city) { filled? }
97
+ key(:zipcode) { filled? }
98
+ end
99
+
100
+ expect(rule.to_ast).to eql(expected_ast)
101
+ end
102
+
103
+ it 'creates a rule for specified keys using macros' do
104
+ rule = value.key(:address) do
105
+ key(:city).required
106
+ key(:zipcode).required
107
+ end
108
+
109
+ expect(rule.to_ast).to eql(expected_ast)
110
+ end
111
+ end
112
+
2
113
  describe '#each' do
3
- subject(:value) { Schema::Value.new(:payments) }
114
+ subject(:value) { Schema::Value.new }
4
115
 
5
116
  it 'creates an each rule with another rule returned from the block' do
6
- rule = value.each do
7
- value.key?(:method)
8
- end
117
+ rule = value.each { key?(:method) }
9
118
 
10
- expect(rule.to_ary).to match_array(
11
- [:each, [
12
- :payments, [:val, [:payments, [:predicate, [:key?, [:method]]]]]]
13
- ]
119
+ expect(rule.to_ast).to eql(
120
+ [:and, [
121
+ [:val, [:predicate, [:array?, []]]],
122
+ [:each, [:val, [:predicate, [:key?, [:method]]]]]
123
+ ]]
14
124
  )
15
125
  end
16
126
 
17
127
  it 'creates an each rule with other rules returned from the block' do
18
128
  rule = value.each do
19
- value.key(:method) { |method| method.str? }
20
- value.key(:amount) { |amount| amount.float? }
129
+ key(:method) { str? }
130
+ key(:amount) { float? }
21
131
  end
22
132
 
23
- expect(rule.to_ary).to match_array(
24
- [:each, [
25
- :payments, [
26
- :set, [
27
- :payments, [
28
- [:and, [
29
- [:key, [:method, [:predicate, [:key?, []]]]],
30
- [:val, [:method, [:predicate, [:str?, []]]]]
31
- ]],
32
- [:and, [
33
- [:key, [:amount, [:predicate, [:key?, []]]]],
34
- [:val, [:amount, [:predicate, [:float?, []]]]]
35
- ]],
36
- ]
37
- ]
38
- ]
133
+ expect(rule.to_ast).to eql(
134
+ [:and, [
135
+ [:val, [:predicate, [:array?, []]]],
136
+ [:each,
137
+ [:set, [
138
+ [:and, [
139
+ [:val, [:predicate, [:key?, [:method]]]],
140
+ [:key, [:method, [:predicate, [:str?, []]]]]
141
+ ]],
142
+ [:and, [
143
+ [:val, [:predicate, [:key?, [:amount]]]],
144
+ [:key, [:amount, [:predicate, [:float?, []]]]]
145
+ ]]
146
+ ]]]
39
147
  ]]
40
148
  )
41
149
  end
42
150
  end
43
151
 
44
- describe '#rule' do
45
- subject(:pills) { Schema::Value.new(:pills) }
46
-
47
- it 'appends new check rule' do
48
- pills.key(:red, &:filled?)
49
- pills.key(:blue, &:filled?)
50
-
51
- pills.rule(:destiny) { pills.rule(:red) | pills.rule(:blue) }
52
-
53
- expect(pills.checks.map(&:to_ary)).to match_array([
54
- [
55
- :check, [
56
- :destiny, [
57
- :or, [
58
- [:check, [:red, [:predicate, [:red, []]]]],
59
- [:check, [:blue, [:predicate, [:blue, []]]]]
60
- ]
61
- ]
62
- ]
152
+ describe '#hash? with block' do
153
+ subject(:user) { Schema::Value.new }
154
+
155
+ it 'builds hash? & rule created within the block' do
156
+ rule = user.hash? { key(:email).required }
157
+
158
+ expect(rule.to_ast).to eql([
159
+ :and, [
160
+ [:val, [:predicate, [:hash?, []]]],
161
+ [:and, [
162
+ [:val, [:predicate, [:key?, [:email]]]],
163
+ [:key, [:email, [:predicate, [:filled?, []]]]]
164
+ ]]
63
165
  ]
64
166
  ])
65
167
  end
168
+
169
+ it 'builds hash? & rule created within the block with deep nesting' do
170
+ rule = user.hash? do
171
+ key(:address) do
172
+ hash? do
173
+ key(:city).required
174
+ key(:zipcode).required
175
+ end
176
+ end
177
+ end
178
+
179
+ expect(rule.to_ast).to eql(
180
+ [:and, [
181
+ [:val, [:predicate, [:hash?, []]]],
182
+ [:and, [
183
+ [:val, [:predicate, [:key?, [:address]]]],
184
+ [:and, [
185
+ [:val, [:predicate, [:hash?, []]]],
186
+ [:key, [
187
+ :address, [:set, [
188
+ [:and, [
189
+ [:val, [:predicate, [:key?, [:city]]]],
190
+ [:key, [:city, [:predicate, [:filled?, []]]]]]],
191
+ [:and, [
192
+ [:val, [:predicate, [:key?, [:zipcode]]]],
193
+ [:key, [:zipcode, [:predicate, [:filled?, []]]]]]]
194
+ ]]
195
+ ]]
196
+ ]]
197
+ ]]
198
+ ]]
199
+ )
200
+ end
66
201
  end
67
202
 
68
203
  describe '#not' do
69
- subject(:user) { Schema::Value.new(:user) }
204
+ subject(:user) { Schema::Value.new }
70
205
 
71
206
  it 'builds a negated rule' do
72
- not_email = user.key(:email, &:str?).first.not
73
-
74
- expect(not_email.to_ary).to eql([
75
- :not, [
76
- :and, [
77
- [:key, [:email, [:predicate, [:key?, []]]]],
78
- [:val, [:email, [:predicate, [:str?, []]]]]
79
- ]
207
+ not_email = user.key(:email) { str?.not }
208
+
209
+ expect(not_email.to_ast).to eql([
210
+ :and, [
211
+ [:val, [:predicate, [:key?, [:email]]]],
212
+ [:not, [:key, [:email, [:predicate, [:str?, []]]]]]
80
213
  ]
81
214
  ])
82
215
  end