rasti-form 3.1.2 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +36 -0
  3. data/README.md +6 -61
  4. data/lib/rasti/form/errors.rb +6 -52
  5. data/lib/rasti/form/validable.rb +10 -7
  6. data/lib/rasti/form.rb +40 -131
  7. data/rasti-form.gemspec +2 -12
  8. data/spec/coverage_helper.rb +1 -3
  9. data/spec/minitest_helper.rb +1 -7
  10. data/spec/validations_spec.rb +333 -0
  11. metadata +19 -48
  12. data/.travis.yml +0 -20
  13. data/lib/rasti/form/castable.rb +0 -25
  14. data/lib/rasti/form/types/array.rb +0 -54
  15. data/lib/rasti/form/types/boolean.rb +0 -42
  16. data/lib/rasti/form/types/enum.rb +0 -48
  17. data/lib/rasti/form/types/float.rb +0 -33
  18. data/lib/rasti/form/types/form.rb +0 -40
  19. data/lib/rasti/form/types/hash.rb +0 -39
  20. data/lib/rasti/form/types/integer.rb +0 -21
  21. data/lib/rasti/form/types/io.rb +0 -23
  22. data/lib/rasti/form/types/regexp.rb +0 -23
  23. data/lib/rasti/form/types/string.rb +0 -40
  24. data/lib/rasti/form/types/symbol.rb +0 -23
  25. data/lib/rasti/form/types/time.rb +0 -40
  26. data/lib/rasti/form/types/uuid.rb +0 -7
  27. data/lib/rasti/form/version.rb +0 -5
  28. data/spec/form_spec.rb +0 -411
  29. data/spec/types/array_spec.rb +0 -54
  30. data/spec/types/boolean_spec.rb +0 -24
  31. data/spec/types/enum_spec.rb +0 -28
  32. data/spec/types/float_spec.rb +0 -18
  33. data/spec/types/form_spec.rb +0 -41
  34. data/spec/types/hash_spec.rb +0 -20
  35. data/spec/types/integer_spec.rb +0 -18
  36. data/spec/types/io_spec.rb +0 -18
  37. data/spec/types/regexp_spec.rb +0 -18
  38. data/spec/types/string_formatted_spec.rb +0 -20
  39. data/spec/types/string_spec.rb +0 -16
  40. data/spec/types/symbol_spec.rb +0 -16
  41. data/spec/types/time_spec.rb +0 -25
  42. data/spec/types/uuid_spec.rb +0 -18
@@ -1,39 +0,0 @@
1
- module Rasti
2
- class Form
3
- module Types
4
- class Hash
5
-
6
- include Castable
7
-
8
- attr_reader :key_type, :value_type
9
-
10
- def self.[](key_type, value_type)
11
- new key_type, value_type
12
- end
13
-
14
- def to_s
15
- "#{self.class}[#{key_type}, #{value_type}]"
16
- end
17
- alias_method :inspect, :to_s
18
-
19
- private
20
-
21
- def initialize(key_type, value_type)
22
- @key_type = key_type
23
- @value_type = value_type
24
- end
25
-
26
- def valid?(value)
27
- value.is_a? ::Hash
28
- end
29
-
30
- def transform(value)
31
- value.each_with_object({}) do |(k,v),h|
32
- h[key_type.cast k] = value_type.cast v
33
- end
34
- end
35
-
36
- end
37
- end
38
- end
39
- end
@@ -1,21 +0,0 @@
1
- module Rasti
2
- class Form
3
- module Types
4
- class Integer < Float
5
- class << self
6
-
7
- private
8
-
9
- def transform(value)
10
- value.to_i
11
- end
12
-
13
- def transformable?(value)
14
- !value.is_a?(::String) && value.respond_to?(:to_i)
15
- end
16
-
17
- end
18
- end
19
- end
20
- end
21
- end
@@ -1,23 +0,0 @@
1
- module Rasti
2
- class Form
3
- module Types
4
- class IO
5
- class << self
6
-
7
- include Castable
8
-
9
- private
10
-
11
- def valid?(value)
12
- value.respond_to?(:read) && value.respond_to?(:write)
13
- end
14
-
15
- def transform(value)
16
- value
17
- end
18
-
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,23 +0,0 @@
1
- module Rasti
2
- class Form
3
- module Types
4
- class Regexp
5
- class << self
6
-
7
- include Castable
8
-
9
- private
10
-
11
- def valid?(value)
12
- value.is_a?(::Regexp) || value.is_a?(::String)
13
- end
14
-
15
- def transform(value)
16
- value.is_a?(::Regexp) ? value.source : ::Regexp.compile(value).source
17
- end
18
-
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,40 +0,0 @@
1
- module Rasti
2
- class Form
3
- module Types
4
- class String
5
- class << self
6
-
7
- include Castable
8
-
9
- def [](format)
10
- Class.new(self) do
11
- @format = format.is_a?(::String) ? ::Regexp.new(format) : format
12
- end
13
- end
14
-
15
- def to_s
16
- name || "#{superclass.name}[#{format.inspect}]"
17
- end
18
- alias_method :inspect, :to_s
19
-
20
- private
21
-
22
- attr_reader :format
23
-
24
- def valid?(value)
25
- !value.nil? && value.respond_to?(:to_s) && valid_format?(value)
26
- end
27
-
28
- def valid_format?(value)
29
- format.nil? || !value.to_s.match(format).nil?
30
- end
31
-
32
- def transform(value)
33
- value.to_s
34
- end
35
-
36
- end
37
- end
38
- end
39
- end
40
- end
@@ -1,23 +0,0 @@
1
- module Rasti
2
- class Form
3
- module Types
4
- class Symbol
5
- class << self
6
-
7
- include Castable
8
-
9
- private
10
-
11
- def valid?(value)
12
- !value.nil? && (value.respond_to?(:to_sym) || value.respond_to?(:to_s))
13
- end
14
-
15
- def transform(value)
16
- value.respond_to?(:to_sym) ? value.to_sym : value.to_s.to_sym
17
- end
18
-
19
- end
20
- end
21
- end
22
- end
23
- end
@@ -1,40 +0,0 @@
1
- module Rasti
2
- class Form
3
- module Types
4
- class Time
5
-
6
- include Castable
7
-
8
- attr_reader :format
9
-
10
- def self.[](format)
11
- new format
12
- end
13
-
14
- def to_s
15
- "#{self.class}['#{format}']"
16
- end
17
- alias_method :inspect, :to_s
18
-
19
- private
20
-
21
- def initialize(format)
22
- @format = format
23
- end
24
-
25
- def valid?(value)
26
- value.is_a?(::String) || value.respond_to?(:to_time)
27
- end
28
-
29
- def transform(value)
30
- value.is_a?(::String) ? string_to_time(value) : value.to_time
31
- end
32
-
33
- def string_to_time(value)
34
- ::Time.strptime(value, format)
35
- end
36
-
37
- end
38
- end
39
- end
40
- end
@@ -1,7 +0,0 @@
1
- module Rasti
2
- class Form
3
- module Types
4
- UUID = String[/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}+$/]
5
- end
6
- end
7
- end
@@ -1,5 +0,0 @@
1
- module Rasti
2
- class Form
3
- VERSION = '3.1.2'
4
- end
5
- end
data/spec/form_spec.rb DELETED
@@ -1,411 +0,0 @@
1
- require 'minitest_helper'
2
-
3
- describe Rasti::Form do
4
-
5
- let(:point_class) { Rasti::Form[x: Rasti::Form::Types::Integer, y: Rasti::Form::Types::Integer] }
6
-
7
- let(:point_subclass) { Class.new point_class }
8
-
9
- def build_form(&block)
10
- Class.new(Rasti::Form) do
11
- class_eval(&block)
12
- end
13
- end
14
-
15
- describe 'Initialization' do
16
-
17
- it 'All attributes' do
18
- point = point_class.new x: 1, y: 2
19
- point.x.must_equal 1
20
- point.y.must_equal 2
21
- point.assigned?(:x).must_equal true
22
- point.assigned?(:y).must_equal true
23
- end
24
-
25
- it 'Some attributes' do
26
- point = point_class.new x: 1
27
- point.x.must_equal 1
28
- point.y.must_be_nil
29
- point.assigned?(:x).must_equal true
30
- point.assigned?(:y).must_equal false
31
- end
32
-
33
- it 'Whitout attributes' do
34
- point = point_class.new
35
- point.x.must_be_nil
36
- point.y.must_be_nil
37
- point.assigned?(:x).must_equal false
38
- point.assigned?(:y).must_equal false
39
- end
40
-
41
- it 'Invalid attributes' do
42
- error = proc { point_class.new z: 3 }.must_raise Rasti::Form::ValidationError
43
- error.message.must_equal "Validation errors:\n- z: [\"unexpected attribute\"]"
44
- end
45
-
46
- describe 'Casting' do
47
-
48
- it 'Attribute' do
49
- form = build_form do
50
- attribute :text, Rasti::Form::Types::String
51
- end
52
-
53
- f = form.new text: 123
54
- f.text.must_equal "123"
55
- end
56
-
57
- it 'Nested attributes' do
58
- form = build_form do
59
- attribute :range, Rasti::Form::Types::Form[min: Rasti::Form::Types::Integer, max: Rasti::Form::Types::Integer]
60
- end
61
-
62
- f = form.new range: {min: '1', max: '10'}
63
- f.range.min.must_equal 1
64
- f.range.max.must_equal 10
65
- end
66
-
67
- it 'Nested form' do
68
- range = build_form do
69
- attribute :min, Rasti::Form::Types::Integer
70
- attribute :max, Rasti::Form::Types::Integer
71
- end
72
-
73
- form = build_form do
74
- attribute :range, Rasti::Form::Types::Form[range]
75
- end
76
-
77
- f = form.new range: {min: '1', max: '10'}
78
- f.range.min.must_equal 1
79
- f.range.max.must_equal 10
80
- end
81
-
82
- it 'Invalid attributes' do
83
- form = build_form do
84
- attribute :boolean, Rasti::Form::Types::Boolean
85
- attribute :number, Rasti::Form::Types::Integer
86
- end
87
-
88
- error = proc { form.new boolean: 'x', number: 'y' }.must_raise Rasti::Form::ValidationError
89
- error.message.must_equal "Validation errors:\n- boolean: [\"Invalid cast: \'x\' -> Rasti::Form::Types::Boolean\"]\n- number: [\"Invalid cast: \'y\' -> Rasti::Form::Types::Integer\"]"
90
- end
91
-
92
- it 'Invalid nested attributes' do
93
- form = build_form do
94
- attribute :range, Rasti::Form::Types::Form[min: Rasti::Form::Types::Integer, max: Rasti::Form::Types::Integer]
95
- end
96
-
97
- error = proc { form.new range: {min: 'x', max: 'y'} }.must_raise Rasti::Form::ValidationError
98
- error.message.must_equal "Validation errors:\n- range.min: [\"Invalid cast: 'x' -> Rasti::Form::Types::Integer\"]\n- range.max: [\"Invalid cast: 'y' -> Rasti::Form::Types::Integer\"]"
99
- end
100
-
101
- it 'Invalid form attributes' do
102
- range = build_form do
103
- attribute :min, Rasti::Form::Types::Integer
104
- attribute :max, Rasti::Form::Types::Integer
105
- end
106
-
107
- form = build_form do
108
- attribute :range, Rasti::Form::Types::Form[range]
109
- end
110
-
111
- error = proc { form.new range: {min: 'x', max: 'y'} }.must_raise Rasti::Form::ValidationError
112
- error.message.must_equal "Validation errors:\n- range.min: [\"Invalid cast: 'x' -> Rasti::Form::Types::Integer\"]\n- range.max: [\"Invalid cast: 'y' -> Rasti::Form::Types::Integer\"]"
113
- end
114
-
115
- end
116
-
117
- end
118
-
119
- describe 'Defaults' do
120
-
121
- it 'Value' do
122
- form = build_form do
123
- attribute :text, Rasti::Form::Types::String, default: 'xyz'
124
- end
125
-
126
- f = form.new
127
- f.text.must_equal 'xyz'
128
- end
129
-
130
- it 'Block' do
131
- form = build_form do
132
- attribute :time_1, Rasti::Form::Types::Time['%F']
133
- attribute :time_2, Rasti::Form::Types::Time['%F'], default: ->(f) { f.time_1 }
134
- end
135
-
136
- f = form.new time_1: Time.now
137
- f.time_2.must_equal f.time_1
138
- end
139
-
140
- end
141
-
142
- describe 'Validations' do
143
-
144
- it 'Not error' do
145
- form = build_form do
146
- attribute :text, Rasti::Form::Types::String
147
-
148
- def validate
149
- assert_not_error :text do
150
- raise 'Invalid text' if text.nil?
151
- end
152
- end
153
- end
154
-
155
- proc { form.new text: 'text' }.must_be_silent
156
-
157
- error = proc { form.new }.must_raise Rasti::Form::ValidationError
158
- error.message.must_equal "Validation errors:\n- text: [\"Invalid text\"]"
159
- end
160
-
161
- it 'Required' do
162
- form = build_form do
163
- attribute :text, Rasti::Form::Types::String
164
-
165
- def validate
166
- assert_present :text
167
- end
168
- end
169
-
170
- proc { form.new text: 'text' }.must_be_silent
171
-
172
- error = proc { form.new }.must_raise Rasti::Form::ValidationError
173
- error.message.must_equal "Validation errors:\n- text: [\"not present\"]"
174
- end
175
-
176
- it 'Required when cast failed' do
177
- form = build_form do
178
- attribute :number, Rasti::Form::Types::Integer
179
-
180
- def validate
181
- assert_present :number
182
- end
183
- end
184
-
185
- proc { form.new number: 1 }.must_be_silent
186
-
187
- error = proc { form.new number: 'text' }.must_raise Rasti::Form::ValidationError
188
- error.message.must_equal "Validation errors:\n- number: [\"Invalid cast: 'text' -> Rasti::Form::Types::Integer\"]"
189
- end
190
-
191
- it 'Not required' do
192
- form = build_form do
193
- attribute :text, Rasti::Form::Types::String
194
-
195
- def validate
196
- assert_not_present :text
197
- end
198
- end
199
-
200
- proc { form.new }.must_be_silent
201
-
202
- error = proc { form.new text: 'text' }.must_raise Rasti::Form::ValidationError
203
- error.message.must_equal "Validation errors:\n- text: [\"is present\"]"
204
- end
205
-
206
- it 'Not empty string' do
207
- form = build_form do
208
- attribute :text, Rasti::Form::Types::String
209
-
210
- def validate
211
- assert_not_empty :text
212
- end
213
- end
214
-
215
- proc { form.new text: 'text' }.must_be_silent
216
-
217
- error = proc { form.new text: ' ' }.must_raise Rasti::Form::ValidationError
218
- error.message.must_equal "Validation errors:\n- text: [\"is empty\"]"
219
- end
220
-
221
- it 'Not empty array' do
222
- form = build_form do
223
- attribute :array, Rasti::Form::Types::Array[Rasti::Form::Types::String]
224
-
225
- def validate
226
- assert_not_empty :array
227
- end
228
- end
229
-
230
- proc { form.new array: ['text'] }.must_be_silent
231
-
232
- error = proc { form.new array: [] }.must_raise Rasti::Form::ValidationError
233
- error.message.must_equal "Validation errors:\n- array: [\"is empty\"]"
234
- end
235
-
236
- it 'Included in values list' do
237
- form = build_form do
238
- attribute :text, Rasti::Form::Types::String
239
-
240
- def validate
241
- assert_included_in :text, %w(value_1 value_2)
242
- end
243
- end
244
-
245
- proc { form.new text: 'value_1' }.must_be_silent
246
-
247
- error = proc { form.new text: 'xyz' }.must_raise Rasti::Form::ValidationError
248
- error.message.must_equal "Validation errors:\n- text: [\"not included in \'value_1\', \'value_2\'\"]"
249
- end
250
-
251
- it 'Time range' do
252
- form = build_form do
253
- attribute :from, Rasti::Form::Types::Time['%Y-%m-%d %H:%M:%S %Z']
254
- attribute :to, Rasti::Form::Types::Time['%Y-%m-%d %H:%M:%S %Z']
255
-
256
- def validate
257
- assert_time_range :from, :to
258
- end
259
- end
260
-
261
- from = Time.parse '2018-01-01 03:10:00'
262
- to = Time.parse '2018-01-01 15:30:00'
263
-
264
- proc { form.new from: from, to: to }.must_be_silent
265
-
266
- error = proc { form.new from: to.to_s, to: from.to_s }.must_raise Rasti::Form::ValidationError
267
- error.message.must_equal "Validation errors:\n- from: [\"invalid time range\"]"
268
- end
269
-
270
- it 'Nested form' do
271
- form = build_form do
272
- attribute :range, Rasti::Form::Types::Form[min: Rasti::Form::Types::Integer, max: Rasti::Form::Types::Integer]
273
-
274
- def validate
275
- assert_present 'range.min'
276
- assert_present 'range.max'
277
- end
278
- end
279
-
280
- proc { form.new range: {min: 1, max: 2} }.must_be_silent
281
-
282
- error = proc { form.new }.must_raise Rasti::Form::ValidationError
283
- error.message.must_equal "Validation errors:\n- range.min: [\"not present\"]\n- range.max: [\"not present\"]"
284
- end
285
-
286
- it 'Nested validation' do
287
- range = build_form do
288
- attribute :min, Rasti::Form::Types::Integer
289
- attribute :max, Rasti::Form::Types::Integer
290
-
291
- def validate
292
- assert :min, min < max, 'Min must be less than Max' if min && max
293
- end
294
- end
295
-
296
- form = build_form do
297
- attribute :range, Rasti::Form::Types::Form[range]
298
- end
299
-
300
- proc { form.new range: {min: 1, max: 2} }.must_be_silent
301
-
302
- error = proc { form.new range: {min: 2, max: 1} }.must_raise Rasti::Form::ValidationError
303
- error.message.must_equal "Validation errors:\n- range.min: [\"Min must be less than Max\"]"
304
- end
305
-
306
- end
307
-
308
- describe 'Comparable' do
309
-
310
- it 'Equivalency (==)' do
311
- point_1 = point_class.new x: 1, y: 2
312
- point_2 = point_subclass.new x: 1, y: 2
313
- point_3 = point_class.new x: 2, y: 1
314
-
315
- assert point_1 == point_2
316
- refute point_1 == point_3
317
- end
318
-
319
- it 'Equality (eql?)' do
320
- point_1 = point_class.new x: 1, y: 2
321
- point_2 = point_class.new x: 1, y: 2
322
- point_3 = point_subclass.new x: 1, y: 2
323
- point_4 = point_class.new x: 2, y: 1
324
-
325
- assert point_1.eql?(point_2)
326
- refute point_1.eql?(point_3)
327
- refute point_1.eql?(point_4)
328
- end
329
-
330
- it 'hash' do
331
- point_1 = point_class.new x: 1, y: 2
332
- point_2 = point_class.new x: 1, y: 2
333
- point_3 = point_subclass.new x: 1, y: 2
334
- point_4 = point_class.new x: 2, y: 1
335
-
336
- point_1.hash.must_equal point_2.hash
337
- point_1.hash.wont_equal point_3.hash
338
- point_1.hash.wont_equal point_4.hash
339
- end
340
-
341
- end
342
-
343
- describe 'Attributes' do
344
-
345
- let :address_class do
346
- Rasti::Form[
347
- street: Rasti::Form::Types::String,
348
- number: Rasti::Form::Types::Integer
349
- ]
350
- end
351
-
352
- let :contact_class do
353
- Rasti::Form[
354
- name: Rasti::Form::Types::String,
355
- age: Rasti::Form::Types::Integer,
356
- phones: Rasti::Form::Types::Hash[Rasti::Form::Types::Symbol, Rasti::Form::Types::Integer],
357
- addresses: Rasti::Form::Types::Array[Rasti::Form::Types::Form[address_class]],
358
- hobbies: Rasti::Form::Types::Array[Rasti::Form::Types::String]
359
- ]
360
- end
361
-
362
- let :attributes do
363
- {
364
- name: 'John',
365
- age: 24,
366
- phones: {
367
- office: 1234567890,
368
- house: 456456456
369
- },
370
- addresses: [
371
- {street: 'Lexington Avenue', number: 123},
372
- {street: 'Park Avenue', number: 456}
373
- ]
374
- }
375
- end
376
-
377
- it 'All (to_h)' do
378
- contact = contact_class.new attributes
379
-
380
- contact.attributes.must_equal attributes
381
- contact.to_h.must_equal attributes
382
- end
383
-
384
- it 'Only' do
385
- contact = contact_class.new attributes
386
-
387
- contact.attributes(only: [:name, :age]).must_equal name: attributes[:name],
388
- age: attributes[:age]
389
- end
390
-
391
- it 'Except' do
392
- contact = contact_class.new attributes
393
-
394
- contact.attributes(except: [:age, :addresses]).must_equal name: attributes[:name],
395
- phones: attributes[:phones]
396
- end
397
-
398
- end
399
-
400
- it 'to_s' do
401
- point_class.to_s.must_equal 'Rasti::Form[:x, :y]'
402
- point_class.new(x: '1', y: '2').to_s.must_equal '#<Rasti::Form[x: 1, y: 2]>'
403
- end
404
-
405
- it 'Subclass' do
406
- point = point_subclass.new x: 1, y: 2
407
- point.x.must_equal 1
408
- point.y.must_equal 2
409
- end
410
-
411
- end
@@ -1,54 +0,0 @@
1
- require 'minitest_helper'
2
-
3
- describe Rasti::Form::Types::Array do
4
-
5
- VALID_ARRAY = [1, '2', Time.now]
6
- INVALID_ARRAY = [nil, 1, 'text', :symbol, {a: 1, b: 2}, Object.new]
7
-
8
- it "#{VALID_ARRAY.inspect} -> #{VALID_ARRAY.map(&:to_i)}" do
9
- Rasti::Form::Types::Array[Rasti::Form::Types::Integer].cast(VALID_ARRAY).must_equal VALID_ARRAY.map(&:to_i)
10
- end
11
-
12
- it "#{VALID_ARRAY.inspect} -> #{VALID_ARRAY.map(&:to_s)}" do
13
- Rasti::Form::Types::Array[Rasti::Form::Types::String].cast(VALID_ARRAY).must_equal VALID_ARRAY.map(&:to_s)
14
- end
15
-
16
- INVALID_ARRAY.each do |value|
17
- it "#{value.inspect} -> CastError" do
18
- error = proc { Rasti::Form::Types::Array[Rasti::Form::Types::String].cast(value) }.must_raise Rasti::Form::CastError
19
- error.message.must_equal "Invalid cast: #{as_string(value)} -> Rasti::Form::Types::Array[Rasti::Form::Types::String]"
20
- end
21
- end
22
-
23
- describe 'Multi cast errors' do
24
-
25
- it 'Array of integers' do
26
- array = [1, 2 , 'a', 3, 'c', 4, nil]
27
- error = proc { Rasti::Form::Types::Array[Rasti::Form::Types::Integer].cast(array) }.must_raise Rasti::Form::MultiCastError
28
- error.errors.must_equal 3 => ["Invalid cast: 'a' -> Rasti::Form::Types::Integer"],
29
- 5 => ["Invalid cast: 'c' -> Rasti::Form::Types::Integer"],
30
- 7 => ["Invalid cast: nil -> Rasti::Form::Types::Integer"]
31
- error.message.must_equal "Invalid cast: [1, 2, \"a\", 3, \"c\", 4, nil] -> Rasti::Form::Types::Array[Rasti::Form::Types::Integer] - {\"3\":[\"Invalid cast: 'a' -> Rasti::Form::Types::Integer\"],\"5\":[\"Invalid cast: 'c' -> Rasti::Form::Types::Integer\"],\"7\":[\"Invalid cast: nil -> Rasti::Form::Types::Integer\"]}"
32
- end
33
-
34
- it 'Array of forms' do
35
- inner_form_class = Rasti::Form::Types::Form[x: Rasti::Form::Types::Integer, y: Rasti::Form::Types::Integer]
36
- form_class = Rasti::Form[points: Rasti::Form::Types::Array[inner_form_class]]
37
-
38
- error = proc do
39
- form_class.new points: [
40
- {x: 1, y: 2},
41
- {x: 'a', y: 2},
42
- {x: 1, y: 'b'},
43
- {x: 3, y: 4}
44
- ]
45
- end.must_raise Rasti::Form::ValidationError
46
-
47
- error.errors.must_equal 'points.2.x' => ["Invalid cast: 'a' -> Rasti::Form::Types::Integer"],
48
- 'points.3.y' => ["Invalid cast: 'b' -> Rasti::Form::Types::Integer"]
49
- error.message.must_equal "Validation errors:\n- points.2.x: [\"Invalid cast: 'a' -> Rasti::Form::Types::Integer\"]\n- points.3.y: [\"Invalid cast: 'b' -> Rasti::Form::Types::Integer\"]"
50
- end
51
-
52
- end
53
-
54
- end