rasti-form 3.1.2 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/rasti/form.rb +35 -134
- data/lib/rasti/form/errors.rb +6 -52
- data/lib/rasti/form/validable.rb +5 -6
- data/rasti-form.gemspec +2 -12
- data/spec/coverage_helper.rb +1 -3
- data/spec/minitest_helper.rb +1 -7
- data/spec/validations_spec.rb +301 -0
- metadata +18 -47
- data/lib/rasti/form/castable.rb +0 -25
- data/lib/rasti/form/types/array.rb +0 -54
- data/lib/rasti/form/types/boolean.rb +0 -42
- data/lib/rasti/form/types/enum.rb +0 -48
- data/lib/rasti/form/types/float.rb +0 -33
- data/lib/rasti/form/types/form.rb +0 -40
- data/lib/rasti/form/types/hash.rb +0 -39
- data/lib/rasti/form/types/integer.rb +0 -21
- data/lib/rasti/form/types/io.rb +0 -23
- data/lib/rasti/form/types/regexp.rb +0 -23
- data/lib/rasti/form/types/string.rb +0 -40
- data/lib/rasti/form/types/symbol.rb +0 -23
- data/lib/rasti/form/types/time.rb +0 -40
- data/lib/rasti/form/types/uuid.rb +0 -7
- data/lib/rasti/form/version.rb +0 -5
- data/spec/form_spec.rb +0 -411
- data/spec/types/array_spec.rb +0 -54
- data/spec/types/boolean_spec.rb +0 -24
- data/spec/types/enum_spec.rb +0 -28
- data/spec/types/float_spec.rb +0 -18
- data/spec/types/form_spec.rb +0 -41
- data/spec/types/hash_spec.rb +0 -20
- data/spec/types/integer_spec.rb +0 -18
- data/spec/types/io_spec.rb +0 -18
- data/spec/types/regexp_spec.rb +0 -18
- data/spec/types/string_formatted_spec.rb +0 -20
- data/spec/types/string_spec.rb +0 -16
- data/spec/types/symbol_spec.rb +0 -16
- data/spec/types/time_spec.rb +0 -25
- data/spec/types/uuid_spec.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f480a81f5d023100e579147654b5026d29f6d00d43099756ec80304e96fbd71d
|
4
|
+
data.tar.gz: 31c790f10607e20335262383955d7919d33aa6f54aa4fe0d7a9caa75c1bc973c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83aaf5a613afd59afc64486b0228226dd9122e2096cdb38f8b8faad6fb07a19c8ac427bc677cc90bb804d63d2a39cef37b05f6215658ea1867d96c8e5ddf50ef
|
7
|
+
data.tar.gz: 55ba10cfb9a398999f928c5a803ad751c32989416c1bc2a164f105d436a46fe63ff75a47893e29436ec8cf85b93a51b49f09aa94e2a6ac8481ce6b628c39e4ab
|
data/lib/rasti/form.rb
CHANGED
@@ -1,171 +1,72 @@
|
|
1
|
-
require '
|
1
|
+
require 'rasti-model'
|
2
2
|
require 'multi_require'
|
3
3
|
|
4
4
|
module Rasti
|
5
|
-
class Form
|
5
|
+
class Form < Rasti::Model
|
6
6
|
|
7
7
|
extend MultiRequire
|
8
8
|
|
9
9
|
require_relative_pattern 'form/*'
|
10
|
-
require_relative_pattern 'form/types/*'
|
11
10
|
|
12
11
|
include Validable
|
13
12
|
|
14
|
-
|
13
|
+
def initialize(attributes={})
|
14
|
+
begin
|
15
|
+
super attributes
|
15
16
|
|
16
|
-
|
17
|
-
Class.new(self) do
|
18
|
-
attributes.each do |name, type, options={}|
|
19
|
-
attribute name, type, options
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def inherited(subclass)
|
25
|
-
subclass.instance_variable_set :@attributes, attributes.dup
|
26
|
-
end
|
27
|
-
|
28
|
-
def attribute(name, type, options={})
|
29
|
-
attributes[name.to_sym] = options.merge(type: type)
|
30
|
-
attr_reader name
|
31
|
-
end
|
17
|
+
cast_attributes!
|
32
18
|
|
33
|
-
|
34
|
-
|
35
|
-
|
19
|
+
rescue Rasti::Model::UnexpectedAttributesError => ex
|
20
|
+
ex.attributes.each do |attr_name|
|
21
|
+
errors[attr_name] << 'unexpected attribute'
|
22
|
+
end
|
36
23
|
|
37
|
-
|
38
|
-
|
39
|
-
|
24
|
+
rescue Rasti::Types::CompoundError => ex
|
25
|
+
ex.errors.each do |key, messages|
|
26
|
+
errors[key] += messages
|
27
|
+
end
|
40
28
|
|
41
|
-
def to_s
|
42
|
-
"#{name || self.superclass.name}[#{attribute_names.map(&:inspect).join(', ')}]"
|
43
29
|
end
|
44
|
-
alias_method :inspect, :to_s
|
45
30
|
|
46
|
-
end
|
47
|
-
|
48
|
-
def initialize(attrs={})
|
49
|
-
assign_attributes attrs
|
50
|
-
set_defaults
|
51
31
|
validate!
|
52
32
|
end
|
53
33
|
|
54
|
-
def
|
55
|
-
|
56
|
-
end
|
57
|
-
alias_method :inspect, :to_s
|
58
|
-
|
59
|
-
def attributes(options={})
|
60
|
-
attributes_filter = {only: assigned_attribute_names, except: []}.merge(options)
|
61
|
-
(attributes_filter[:only] - attributes_filter[:except]).each_with_object({}) do |name, hash|
|
62
|
-
hash[name] = serialize(read_attribute(name))
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
def to_h
|
67
|
-
attributes
|
68
|
-
end
|
69
|
-
|
70
|
-
def assigned?(name)
|
71
|
-
assigned_attribute_names.include? name
|
72
|
-
end
|
73
|
-
|
74
|
-
def ==(other)
|
75
|
-
other.kind_of?(self.class) && other.attributes == attributes
|
76
|
-
end
|
77
|
-
|
78
|
-
def eql?(other)
|
79
|
-
other.instance_of?(self.class) && other.attributes == attributes
|
80
|
-
end
|
81
|
-
|
82
|
-
def hash
|
83
|
-
[self.class, attributes].hash
|
34
|
+
def assigned?(attr_name)
|
35
|
+
assigned_attribute? attr_name.to_sym
|
84
36
|
end
|
85
37
|
|
86
38
|
private
|
87
39
|
|
88
|
-
def
|
89
|
-
|
90
|
-
attr_name
|
91
|
-
begin
|
92
|
-
if self.class.attributes.key? attr_name
|
93
|
-
write_attribute attr_name, value
|
94
|
-
else
|
95
|
-
errors[attr_name] << 'unexpected attribute'
|
96
|
-
end
|
97
|
-
|
98
|
-
rescue CastError => error
|
99
|
-
errors[attr_name] << error.message
|
100
|
-
|
101
|
-
rescue MultiCastError, ValidationError => error
|
102
|
-
error.errors.each do |inner_name, inner_errors|
|
103
|
-
inner_errors.each { |message| errors["#{attr_name}.#{inner_name}"] << message }
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
def set_defaults
|
110
|
-
(self.class.attribute_names - attributes.keys).each do |name|
|
111
|
-
if self.class.attributes[name].key? :default
|
112
|
-
value = self.class.attributes[name][:default]
|
113
|
-
write_attribute name, value.is_a?(Proc) ? value.call(self) : value
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def assigned_attribute_names
|
119
|
-
self.class.attribute_names & instance_variables.map { |v| v.to_s[1..-1].to_sym }
|
120
|
-
end
|
121
|
-
|
122
|
-
def serialize(value)
|
123
|
-
if value.kind_of? Array
|
124
|
-
value.map { |v| serialize v }
|
125
|
-
elsif value.kind_of? Form
|
126
|
-
value.attributes
|
127
|
-
else
|
128
|
-
value
|
40
|
+
def assert_present(attr_name)
|
41
|
+
if !errors.key?(attr_name)
|
42
|
+
assert attr_name, assigned?(attr_name) && !public_send(attr_name).nil?, 'not present'
|
129
43
|
end
|
44
|
+
rescue Types::Error
|
45
|
+
assert attr_name, false, 'not present'
|
130
46
|
end
|
131
47
|
|
132
|
-
def
|
133
|
-
|
48
|
+
def assert_not_present(attr_name)
|
49
|
+
assert attr_name, !assigned?(attr_name) || public_send(attr_name).nil?, 'is present'
|
50
|
+
rescue Types::Error
|
51
|
+
assert attr_name, false, 'is present'
|
134
52
|
end
|
135
53
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
def fetch(attribute)
|
142
|
-
attribute.to_s.split('.').inject(self) do |target, attr_name|
|
143
|
-
target.nil? ? nil : target.public_send(attr_name)
|
54
|
+
def assert_not_empty(attr_name)
|
55
|
+
if assert_present attr_name
|
56
|
+
value = public_send attr_name
|
57
|
+
assert attr_name, value.is_a?(String) ? !value.strip.empty? : !value.empty?, 'is empty'
|
144
58
|
end
|
145
59
|
end
|
146
60
|
|
147
|
-
def
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
def assert_not_present(attribute)
|
152
|
-
assert attribute, fetch(attribute).nil?, 'is present'
|
153
|
-
end
|
154
|
-
|
155
|
-
def assert_not_empty(attribute)
|
156
|
-
if assert_present attribute
|
157
|
-
value = fetch attribute
|
158
|
-
assert attribute, value.is_a?(String) ? !value.strip.empty? : !value.empty?, 'is empty'
|
61
|
+
def assert_included_in(attr_name, set)
|
62
|
+
if assert_present attr_name
|
63
|
+
assert attr_name, set.include?(public_send(attr_name)), "not included in #{set.map(&:inspect).join(', ')}"
|
159
64
|
end
|
160
65
|
end
|
161
66
|
|
162
|
-
def
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
def assert_included_in(attribute, set)
|
167
|
-
if assert_present attribute
|
168
|
-
assert attribute, set.include?(fetch(attribute)), "not included in #{set.map { |e| e.is_a?(::String) ? "'#{e}'" : e.inspect }.join(', ')}"
|
67
|
+
def assert_range(attr_name_from, attr_name_to)
|
68
|
+
if assert_present(attr_name_from) && assert_present(attr_name_to)
|
69
|
+
assert attr_name_from, public_send(attr_name_from) <= public_send(attr_name_to), 'invalid range'
|
169
70
|
end
|
170
71
|
end
|
171
72
|
|
data/lib/rasti/form/errors.rb
CHANGED
@@ -1,65 +1,19 @@
|
|
1
1
|
module Rasti
|
2
2
|
class Form
|
3
|
-
|
4
|
-
class CastError < StandardError
|
5
3
|
|
6
|
-
|
4
|
+
class ValidationError < Rasti::Types::CompoundError
|
7
5
|
|
8
|
-
|
9
|
-
@type = type
|
10
|
-
@value = value
|
11
|
-
end
|
12
|
-
|
13
|
-
def message
|
14
|
-
"Invalid cast: #{display_value} -> #{type}"
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def display_value
|
20
|
-
value.is_a?(::String) ? "'#{value}'" : value.inspect
|
21
|
-
end
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
|
26
|
-
class MultiCastError < StandardError
|
6
|
+
attr_reader :scope
|
27
7
|
|
28
|
-
attr_reader :type, :value, :errors
|
29
|
-
|
30
|
-
def initialize(type, value, errors)
|
31
|
-
@type = type
|
32
|
-
@value = value
|
33
|
-
@errors = errors
|
34
|
-
end
|
35
|
-
|
36
|
-
def message
|
37
|
-
"Invalid cast: #{display_value} -> #{type} - #{JSON.dump(errors)}"
|
38
|
-
end
|
39
|
-
|
40
|
-
def display_value
|
41
|
-
value.is_a?(::String) ? "'#{value}'" : value.inspect
|
42
|
-
end
|
43
|
-
|
44
|
-
end
|
45
|
-
|
46
|
-
class ValidationError < StandardError
|
47
|
-
|
48
|
-
attr_reader :scope, :errors
|
49
|
-
|
50
8
|
def initialize(scope, errors)
|
51
9
|
@scope = scope
|
52
|
-
|
10
|
+
super errors
|
53
11
|
end
|
54
12
|
|
55
|
-
|
56
|
-
lines = ['Validation errors:']
|
57
|
-
|
58
|
-
errors.each do |key, value|
|
59
|
-
lines << "- #{key}: #{value}"
|
60
|
-
end
|
13
|
+
private
|
61
14
|
|
62
|
-
|
15
|
+
def message_title
|
16
|
+
'Validation errors:'
|
63
17
|
end
|
64
18
|
|
65
19
|
end
|
data/lib/rasti/form/validable.rb
CHANGED
@@ -17,17 +17,16 @@ module Rasti
|
|
17
17
|
end
|
18
18
|
|
19
19
|
def assert(key, condition, message)
|
20
|
-
|
21
|
-
|
22
|
-
errors[key] << message
|
23
|
-
false
|
20
|
+
errors[key] << message unless condition
|
21
|
+
condition
|
24
22
|
end
|
25
23
|
|
26
24
|
def assert_not_error(key)
|
27
25
|
yield
|
28
26
|
true
|
29
|
-
rescue =>
|
30
|
-
|
27
|
+
rescue => ex
|
28
|
+
errors[key] << ex.message
|
29
|
+
false
|
31
30
|
end
|
32
31
|
|
33
32
|
end
|
data/rasti-form.gemspec
CHANGED
@@ -1,11 +1,6 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'rasti/form/version'
|
5
|
-
|
6
1
|
Gem::Specification.new do |spec|
|
7
2
|
spec.name = 'rasti-form'
|
8
|
-
spec.version =
|
3
|
+
spec.version = '4.0.0'
|
9
4
|
spec.authors = ['Gabriel Naiman']
|
10
5
|
spec.email = ['gabynaiman@gmail.com']
|
11
6
|
spec.summary = 'Forms validations and type casting'
|
@@ -19,6 +14,7 @@ Gem::Specification.new do |spec|
|
|
19
14
|
spec.require_paths = ['lib']
|
20
15
|
|
21
16
|
spec.add_runtime_dependency 'multi_require', '~> 1.0'
|
17
|
+
spec.add_runtime_dependency 'rasti-model', '~> 1.0'
|
22
18
|
|
23
19
|
spec.add_development_dependency 'rake', '~> 12.0'
|
24
20
|
spec.add_development_dependency 'minitest', '~> 5.0', '< 5.11'
|
@@ -27,10 +23,4 @@ Gem::Specification.new do |spec|
|
|
27
23
|
spec.add_development_dependency 'simplecov', '~> 0.12'
|
28
24
|
spec.add_development_dependency 'coveralls', '~> 0.8'
|
29
25
|
spec.add_development_dependency 'pry-nav', '~> 0.2'
|
30
|
-
|
31
|
-
if RUBY_VERSION < '2'
|
32
|
-
spec.add_development_dependency 'term-ansicolor', '~> 1.3.0'
|
33
|
-
spec.add_development_dependency 'tins', '~> 1.6.0'
|
34
|
-
spec.add_development_dependency 'json', '~> 1.8'
|
35
|
-
end
|
36
26
|
end
|
data/spec/coverage_helper.rb
CHANGED
data/spec/minitest_helper.rb
CHANGED
@@ -0,0 +1,301 @@
|
|
1
|
+
require 'minitest_helper'
|
2
|
+
|
3
|
+
describe Rasti::Form, 'Validations' do
|
4
|
+
|
5
|
+
def build_form(&block)
|
6
|
+
Class.new(Rasti::Form) do
|
7
|
+
class_eval(&block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def assert_validation_error(expected_errors, &block)
|
12
|
+
error = proc { block.call }.must_raise Rasti::Form::ValidationError
|
13
|
+
error.errors.must_equal expected_errors
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'Validation error message' do
|
17
|
+
scope = Object.new
|
18
|
+
errors = {
|
19
|
+
x: ['not present'],
|
20
|
+
y: ['error 1', 'error 2']
|
21
|
+
}
|
22
|
+
|
23
|
+
error = Rasti::Form::ValidationError.new scope, errors
|
24
|
+
|
25
|
+
error.scope.must_equal scope
|
26
|
+
error.errors.must_equal errors
|
27
|
+
error.message.must_equal "Validation errors:\n- x: [\"not present\"]\n- y: [\"error 1\", \"error 2\"]"
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'Invalid attributes' do
|
31
|
+
assert_validation_error(z: ['unexpected attribute']) do
|
32
|
+
Rasti::Form[:x, :y].new z: 3
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'Not error' do
|
37
|
+
form = build_form do
|
38
|
+
attribute :text, T::String
|
39
|
+
|
40
|
+
def validate
|
41
|
+
assert_not_error :text do
|
42
|
+
raise 'invalid text' unless assigned? :text
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
proc { form.new text: 'text' }.must_be_silent
|
48
|
+
|
49
|
+
assert_validation_error(text: ['invalid text']) do
|
50
|
+
form.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe 'Present' do
|
55
|
+
|
56
|
+
let :form do
|
57
|
+
build_form do
|
58
|
+
attribute :number, T::Integer
|
59
|
+
|
60
|
+
def validate
|
61
|
+
assert_present :number
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'Success' do
|
67
|
+
proc { form.new number: 1 }.must_be_silent
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'Not assigned' do
|
71
|
+
assert_validation_error(number: ['not present']) do
|
72
|
+
form.new
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'Nil' do
|
77
|
+
assert_validation_error(number: ['not present']) do
|
78
|
+
form.new number: nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'Invalid cast' do
|
83
|
+
assert_validation_error(number: ['Invalid cast: true -> Rasti::Types::Integer']) do
|
84
|
+
form.new number: true
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'Invalid nested cast' do
|
89
|
+
range = build_form do
|
90
|
+
attribute :min, T::Integer
|
91
|
+
attribute :max, T::Integer
|
92
|
+
|
93
|
+
def validate
|
94
|
+
assert_range :min, :max
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
form = build_form do
|
99
|
+
attribute :range, T::Model[range]
|
100
|
+
|
101
|
+
def validate
|
102
|
+
assert_present :range
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
assert_validation_error('range.max' => ['not present'], range: ['not present']) do
|
107
|
+
form.new range: {min: 1}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'With default' do
|
112
|
+
form = build_form do
|
113
|
+
attribute :number, T::Integer, default: 1
|
114
|
+
|
115
|
+
def validate
|
116
|
+
assert_present :number
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
proc { form.new }.must_be_silent
|
121
|
+
|
122
|
+
assert_validation_error(number: ['not present']) do
|
123
|
+
form.new number: nil
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
describe 'Not present' do
|
130
|
+
|
131
|
+
let :form do
|
132
|
+
range = build_form do
|
133
|
+
attribute :min, T::Integer
|
134
|
+
attribute :max, T::Integer
|
135
|
+
|
136
|
+
def validate
|
137
|
+
assert_range :min, :max
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
build_form do
|
142
|
+
attribute :range, T::Model[range]
|
143
|
+
|
144
|
+
def validate
|
145
|
+
assert_not_present :range
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'Success not assigned' do
|
151
|
+
proc { form.new }.must_be_silent
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'Success with nil' do
|
155
|
+
proc { form.new range: nil }.must_be_silent
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'Assigned' do
|
159
|
+
assert_validation_error(range: ['is present']) do
|
160
|
+
form.new range: {min: 1, max: 2}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'Invalid nested cast' do
|
165
|
+
assert_validation_error('range.max' => ['not present'], range: ['is present']) do
|
166
|
+
form.new range: {min: 1}
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
describe 'Not empty' do
|
173
|
+
|
174
|
+
it 'Must be present' do
|
175
|
+
form = build_form do
|
176
|
+
attribute :text, T::String
|
177
|
+
|
178
|
+
def validate
|
179
|
+
assert_not_empty :text
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
proc { form.new text: 'text' }.must_be_silent
|
184
|
+
|
185
|
+
assert_validation_error(text: ['not present']) do
|
186
|
+
form.new text: nil
|
187
|
+
end
|
188
|
+
|
189
|
+
assert_validation_error(text: ['not present']) do
|
190
|
+
form.new
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
it 'String' do
|
195
|
+
form = build_form do
|
196
|
+
attribute :text, T::String
|
197
|
+
|
198
|
+
def validate
|
199
|
+
assert_not_empty :text
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
proc { form.new text: 'text' }.must_be_silent
|
204
|
+
|
205
|
+
assert_validation_error(text: ['is empty']) do
|
206
|
+
form.new text: ' '
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'Array' do
|
211
|
+
form = build_form do
|
212
|
+
attribute :array, T::Array[T::String]
|
213
|
+
|
214
|
+
def validate
|
215
|
+
assert_not_empty :array
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
proc { form.new array: ['text'] }.must_be_silent
|
220
|
+
|
221
|
+
assert_validation_error(array: ['is empty']) do
|
222
|
+
form.new array: []
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'Hash' do
|
227
|
+
form = build_form do
|
228
|
+
attribute :hash, T::Hash[T::String, T::String]
|
229
|
+
|
230
|
+
def validate
|
231
|
+
assert_not_empty :hash
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
proc { form.new hash: {key: 'value'} }.must_be_silent
|
236
|
+
|
237
|
+
assert_validation_error(hash: ['is empty']) do
|
238
|
+
form.new hash: {}
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
end
|
243
|
+
|
244
|
+
it 'Included in values list' do
|
245
|
+
form = build_form do
|
246
|
+
attribute :text, T::String
|
247
|
+
|
248
|
+
def validate
|
249
|
+
assert_included_in :text, %w(value_1 value_2)
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
proc { form.new text: 'value_1' }.must_be_silent
|
254
|
+
|
255
|
+
assert_validation_error(text: ['not included in "value_1", "value_2"']) do
|
256
|
+
form.new text: 'xyz'
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
it 'Time range' do
|
261
|
+
form = build_form do
|
262
|
+
attribute :from, T::Time['%Y-%m-%d %H:%M:%S']
|
263
|
+
attribute :to, T::Time['%Y-%m-%d %H:%M:%S']
|
264
|
+
|
265
|
+
def validate
|
266
|
+
assert_range :from, :to
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
from = '2018-01-01 03:10:00'
|
271
|
+
to = '2018-01-01 15:30:00'
|
272
|
+
|
273
|
+
proc { form.new from: from, to: to }.must_be_silent
|
274
|
+
|
275
|
+
assert_validation_error(from: ['invalid range']) do
|
276
|
+
form.new from: to, to: from
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'Nested validation' do
|
281
|
+
range = build_form do
|
282
|
+
attribute :min, T::Integer
|
283
|
+
attribute :max, T::Integer
|
284
|
+
|
285
|
+
def validate
|
286
|
+
assert_range :min, :max
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
form = build_form do
|
291
|
+
attribute :range, T::Model[range]
|
292
|
+
end
|
293
|
+
|
294
|
+
proc { form.new range: {min: 1, max: 2} }.must_be_silent
|
295
|
+
|
296
|
+
assert_validation_error('range.min' => ['invalid range']) do
|
297
|
+
form.new range: {min: 2, max: 1}
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
end
|