dry-validation 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +3 -0
- data/.rubocop.yml +16 -0
- data/.rubocop_todo.yml +7 -0
- data/.travis.yml +29 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +11 -0
- data/LICENSE +20 -0
- data/README.md +297 -0
- data/Rakefile +12 -0
- data/config/errors.yml +35 -0
- data/dry-validation.gemspec +25 -0
- data/examples/basic.rb +21 -0
- data/examples/nested.rb +30 -0
- data/examples/rule_ast.rb +33 -0
- data/lib/dry-validation.rb +1 -0
- data/lib/dry/validation.rb +12 -0
- data/lib/dry/validation/error.rb +43 -0
- data/lib/dry/validation/error_compiler.rb +116 -0
- data/lib/dry/validation/messages.rb +71 -0
- data/lib/dry/validation/predicate.rb +39 -0
- data/lib/dry/validation/predicate_set.rb +22 -0
- data/lib/dry/validation/predicates.rb +88 -0
- data/lib/dry/validation/result.rb +64 -0
- data/lib/dry/validation/rule.rb +125 -0
- data/lib/dry/validation/rule_compiler.rb +57 -0
- data/lib/dry/validation/schema.rb +74 -0
- data/lib/dry/validation/schema/definition.rb +15 -0
- data/lib/dry/validation/schema/key.rb +39 -0
- data/lib/dry/validation/schema/rule.rb +28 -0
- data/lib/dry/validation/schema/value.rb +31 -0
- data/lib/dry/validation/version.rb +5 -0
- data/rakelib/rubocop.rake +18 -0
- data/spec/fixtures/errors.yml +4 -0
- data/spec/integration/custom_error_messages_spec.rb +35 -0
- data/spec/integration/custom_predicates_spec.rb +57 -0
- data/spec/integration/validation_spec.rb +118 -0
- data/spec/shared/predicates.rb +31 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/unit/error_compiler_spec.rb +165 -0
- data/spec/unit/predicate_spec.rb +37 -0
- data/spec/unit/predicates/empty_spec.rb +38 -0
- data/spec/unit/predicates/eql_spec.rb +21 -0
- data/spec/unit/predicates/exclusion_spec.rb +35 -0
- data/spec/unit/predicates/filled_spec.rb +38 -0
- data/spec/unit/predicates/format_spec.rb +21 -0
- data/spec/unit/predicates/gt_spec.rb +40 -0
- data/spec/unit/predicates/gteq_spec.rb +40 -0
- data/spec/unit/predicates/inclusion_spec.rb +35 -0
- data/spec/unit/predicates/int_spec.rb +34 -0
- data/spec/unit/predicates/key_spec.rb +29 -0
- data/spec/unit/predicates/lt_spec.rb +40 -0
- data/spec/unit/predicates/lteq_spec.rb +40 -0
- data/spec/unit/predicates/max_size_spec.rb +49 -0
- data/spec/unit/predicates/min_size_spec.rb +49 -0
- data/spec/unit/predicates/nil_spec.rb +28 -0
- data/spec/unit/predicates/size_spec.rb +49 -0
- data/spec/unit/predicates/str_spec.rb +32 -0
- data/spec/unit/rule/each_spec.rb +20 -0
- data/spec/unit/rule/key_spec.rb +27 -0
- data/spec/unit/rule/set_spec.rb +32 -0
- data/spec/unit/rule/value_spec.rb +42 -0
- data/spec/unit/rule_compiler_spec.rb +86 -0
- metadata +230 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 6acd14c8b93a29eed518ef8934921cf35d0faf17
|
4
|
+
data.tar.gz: 83c9f549fb92f5ff4cf3f5c95ec8029e81c045d2
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b7411edfe3d00dbc645d9c48a7df9dd952bf36c01e59f1a55e39114e0a757afd6b4e66b57a07380b658a146b2757651f4a1b47d61f29565d464d2b7896cdb04f
|
7
|
+
data.tar.gz: 50201382e0ed26bcebcf19f1d9dafe2c7c3ee260c9654afdb3b6002ce7cbf4bc83ad549e6ffff9f6636ca1fb9aa6eaadea1371044a08e33425443768eef2e512
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Generated by `rubocop --auto-gen-config`
|
2
|
+
inherit_from: .rubocop_todo.yml
|
3
|
+
|
4
|
+
Metrics/LineLength:
|
5
|
+
Max: 100
|
6
|
+
|
7
|
+
Style/Documentation:
|
8
|
+
Enabled: false
|
9
|
+
|
10
|
+
Lint/HandleExceptions:
|
11
|
+
Exclude:
|
12
|
+
- rakelib/*.rake
|
13
|
+
|
14
|
+
Style/FileName:
|
15
|
+
Exclude:
|
16
|
+
- lib/dry-validation.rb
|
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2015-10-30 01:32:46 +0000 using RuboCop version 0.34.2.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
data/.travis.yml
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
language: ruby
|
2
|
+
sudo: false
|
3
|
+
cache: bundler
|
4
|
+
bundler_args: --without console
|
5
|
+
script:
|
6
|
+
- bundle exec rake spec
|
7
|
+
rvm:
|
8
|
+
- 2.0
|
9
|
+
- 2.1
|
10
|
+
- 2.2
|
11
|
+
- rbx-2
|
12
|
+
- jruby-9000
|
13
|
+
- ruby-head
|
14
|
+
- jruby-head
|
15
|
+
env:
|
16
|
+
global:
|
17
|
+
- JRUBY_OPTS='--dev -J-Xmx1024M'
|
18
|
+
matrix:
|
19
|
+
allow_failures:
|
20
|
+
- rvm: ruby-head
|
21
|
+
- rvm: jruby-head
|
22
|
+
notifications:
|
23
|
+
email: false
|
24
|
+
webhooks:
|
25
|
+
urls:
|
26
|
+
- https://webhooks.gitter.im/e/19098b4253a72c9796db
|
27
|
+
on_success: change # options: [always|never|change] default: always
|
28
|
+
on_failure: always # options: [always|never|change] default: always
|
29
|
+
on_start: false # default: false
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Ruby Object Mapper
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
9
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
10
|
+
subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
17
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
18
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
19
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
20
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
# dry-validation <a href="https://gitter.im/dryrb/chat" target="_blank">![Join the chat at https://gitter.im/dryrb/chat](https://badges.gitter.im/Join%20Chat.svg)</a>
|
2
|
+
|
3
|
+
<a href="https://rubygems.org/gems/dry-validation" target="_blank">![Gem Version](https://badge.fury.io/rb/dry-validation.svg)</a>
|
4
|
+
<a href="https://travis-ci.org/dryrb/dry-validation" target="_blank">![Build Status](https://travis-ci.org/dryrb/dry-validation.svg?branch=master)</a>
|
5
|
+
<a href="https://gemnasium.com/dryrb/dry-validation" target="_blank">![Dependency Status](https://gemnasium.com/dryrb/dry-validation.svg)</a>
|
6
|
+
<a href="https://codeclimate.com/github/dryrb/dry-validation" target="_blank">![Code Climate](https://codeclimate.com/github/dryrb/dry-validation/badges/gpa.svg)</a>
|
7
|
+
<a href="http://inch-ci.org/github/dryrb/dry-validation" target="_blank">![Documentation Status](http://inch-ci.org/github/dryrb/dry-validation.svg?branch=master&style=flat)</a>
|
8
|
+
|
9
|
+
Data validation library based on predicate logic and rule composition.
|
10
|
+
|
11
|
+
## Overview
|
12
|
+
|
13
|
+
Unlike other, well known, validation solutions in Ruby, `dry-validation` takes
|
14
|
+
a different approach and focuses a lot on explicitness, clarity and preciseness
|
15
|
+
of validation logic. It is designed to work with any data input, whether it's a
|
16
|
+
simple hash, an array or a complex object with deeply nested data.
|
17
|
+
|
18
|
+
It is based on a simple idea that each validation is encapsulated by a simple,
|
19
|
+
stateless predicate, that receives some input and returns either `true` or `false`.
|
20
|
+
|
21
|
+
Those predicates are encapsulated by `rules` which can be composed together using
|
22
|
+
`predicate logic`. This means you can use the common logic operators to build up
|
23
|
+
a validation `schema`.
|
24
|
+
|
25
|
+
It's very explicit, powerful and extendible.
|
26
|
+
|
27
|
+
Validations can be described with great precision, `dry-validation` eliminates
|
28
|
+
ambigious concepts like `presence` validation where we can't really say whether
|
29
|
+
some attribute or key is *missing* or it's just that the value is `nil`.
|
30
|
+
|
31
|
+
There's also the concept of type-safety, completely missing in other validation
|
32
|
+
libraries, which is quite important and useful. It means you can compose a validation
|
33
|
+
that does rely on the type of a given value. In example it makes no sense to validate
|
34
|
+
each element of an array when it turns out to be an empty string.
|
35
|
+
|
36
|
+
## The DSL
|
37
|
+
|
38
|
+
The core of `dry-validation` is rules composition and predicate logic. The DSL
|
39
|
+
is a simple front-end for that. It only allows you to define the rules by using
|
40
|
+
predicate identifiers. There are no magical options, conditionals and custom
|
41
|
+
validation blocks known from other libraries. The focus is on pure validation
|
42
|
+
logic.
|
43
|
+
|
44
|
+
## Examples
|
45
|
+
|
46
|
+
### Basic
|
47
|
+
|
48
|
+
Here's a basic example where we validate following things:
|
49
|
+
|
50
|
+
* The input *must have a key* called `:email`
|
51
|
+
* Provided the email key is present, its value *must be filled*
|
52
|
+
* The input *must have a key* called `:age`
|
53
|
+
* Provided the age key is present, its value *must be an integer* and it *must be greater than 18*
|
54
|
+
|
55
|
+
This can be easily expressed through the DSL:
|
56
|
+
|
57
|
+
``` ruby
|
58
|
+
require 'dry-validation'
|
59
|
+
|
60
|
+
class Schema < Dry::Validation::Schema
|
61
|
+
key(:email) { |email| email.filled? }
|
62
|
+
|
63
|
+
key(:age) do |age|
|
64
|
+
age.int? & age.gt?(18)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
schema = Schema.new
|
69
|
+
|
70
|
+
errors = schema.messages(email: 'jane@doe.org', age: 19)
|
71
|
+
|
72
|
+
puts errors.inspect
|
73
|
+
# []
|
74
|
+
|
75
|
+
errors = schema.messages(email: nil, age: 19)
|
76
|
+
|
77
|
+
puts errors.inspect
|
78
|
+
# [[:email, ["email must be filled"]]]
|
79
|
+
```
|
80
|
+
|
81
|
+
A couple of remarks:
|
82
|
+
|
83
|
+
* `key` assumes that we want to use the `:key?` predicate to check the existance of that key
|
84
|
+
* `age.gt?(18)` translates to calling a predicate like this: `schema[:gt?].(18, age)`
|
85
|
+
* `age.int? & age.gt?(18)` is a conjunction, so we don't bother about `gt?` unless `int?` returns `true`
|
86
|
+
* You can also use `|` for disjunction
|
87
|
+
* Schema object does not carry the input as its state, nor does it know how to access the input values, we
|
88
|
+
pass the input to `call` and get error set as the response
|
89
|
+
|
90
|
+
### Nested Hash
|
91
|
+
|
92
|
+
We are free to define validations for anything, including deeply nested structures:
|
93
|
+
|
94
|
+
``` ruby
|
95
|
+
require 'dry-validation'
|
96
|
+
|
97
|
+
class Schema < Dry::Validation::Schema
|
98
|
+
key(:address) do |address|
|
99
|
+
address.key(:city) do |city|
|
100
|
+
city.min_size?(3)
|
101
|
+
end
|
102
|
+
|
103
|
+
address.key(:street) do |street|
|
104
|
+
street.filled?
|
105
|
+
end
|
106
|
+
|
107
|
+
address.key(:country) do |country|
|
108
|
+
country.key(:name, &:filled?)
|
109
|
+
country.key(:code, &:filled?)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
schema = Schema.new
|
115
|
+
|
116
|
+
errors = schema.messages({})
|
117
|
+
|
118
|
+
puts errors.inspect
|
119
|
+
# [[:address, ["address is missing"]]]
|
120
|
+
|
121
|
+
errors = schema.messages(address: { city: 'NYC' })
|
122
|
+
|
123
|
+
puts errors.inspect
|
124
|
+
# [[:address, [[:street, ["street is missing"]], [:country, ["country is missing"]]]]]
|
125
|
+
```
|
126
|
+
|
127
|
+
### Defining Custom Predicates
|
128
|
+
|
129
|
+
You can simply define predicate methods on your schema object:
|
130
|
+
|
131
|
+
``` ruby
|
132
|
+
class Schema < Dry::Validation::Schema
|
133
|
+
key(:email) { |value| value.str? & value.email? }
|
134
|
+
|
135
|
+
def email?(value)
|
136
|
+
! /magical-regex-that-matches-emails/.match(value).nil?
|
137
|
+
end
|
138
|
+
end
|
139
|
+
```
|
140
|
+
|
141
|
+
You can also re-use a predicate container across multiple schemas:
|
142
|
+
|
143
|
+
``` ruby
|
144
|
+
module MyPredicates
|
145
|
+
include Dry::Validation::Predicates
|
146
|
+
|
147
|
+
predicate(:email?) do |input|
|
148
|
+
! /magical-regex-that-matches-emails/.match(value).nil?
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class Schema < Dry::Validation::Schema
|
153
|
+
configure do |config|
|
154
|
+
config.predicates = MyPredicates
|
155
|
+
end
|
156
|
+
|
157
|
+
key(:email) { |value| value.str? & value.email? }
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
## List of Built-In Predicates
|
162
|
+
|
163
|
+
* `empty?`
|
164
|
+
* `eql?`
|
165
|
+
* `exclusion?`
|
166
|
+
* `filled?`
|
167
|
+
* `format?`
|
168
|
+
* `gt?`
|
169
|
+
* `gteq?`
|
170
|
+
* `inclusion?`
|
171
|
+
* `int?`
|
172
|
+
* `key?`
|
173
|
+
* `lt?`
|
174
|
+
* `lteq?`
|
175
|
+
* `max_size?`
|
176
|
+
* `min_size?`
|
177
|
+
* `nil?`
|
178
|
+
* `size?`
|
179
|
+
* `str?`
|
180
|
+
|
181
|
+
## Error Messages
|
182
|
+
|
183
|
+
By default `dry-validation` comes with a set of pre-defined error messages for
|
184
|
+
every built-in predicate. They are defined in [a yaml file](https://github.com/dryrb/dry-validation/blob/master/config/errors.yml)
|
185
|
+
which is shipped with the gem.
|
186
|
+
|
187
|
+
You can provide your own messages and configure your schemas to use it like that:
|
188
|
+
|
189
|
+
``` ruby
|
190
|
+
class Schema < Dry::Validation::Schema
|
191
|
+
configure { |config| config.messages_file = '/path/to/my/errors.yml' }
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
195
|
+
You can also provide a namespace per-schema that will be used by default:
|
196
|
+
|
197
|
+
``` ruby
|
198
|
+
class Schema < Dry::Validation::Schema
|
199
|
+
configure { |config| config.namespace = :user }
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
Lookup rules:
|
204
|
+
|
205
|
+
``` yaml
|
206
|
+
filled?: "%{name} must be filled"
|
207
|
+
|
208
|
+
attributes:
|
209
|
+
email:
|
210
|
+
filled?: "the email is missing"
|
211
|
+
|
212
|
+
user:
|
213
|
+
filled?: "%{name} name cannot be blank"
|
214
|
+
|
215
|
+
attributes:
|
216
|
+
address:
|
217
|
+
filled?: "You gotta tell us where you live"
|
218
|
+
```
|
219
|
+
|
220
|
+
Given the yaml file above, messages lookup works as follows:
|
221
|
+
|
222
|
+
``` ruby
|
223
|
+
messages = Dry::Validation::Messages.load('/path/to/our/errors.yml')
|
224
|
+
|
225
|
+
messages.lookup(:filled?, :age) # => "age must be filled"
|
226
|
+
messages.lookup(:filled?, :address) # => "address must be filled"
|
227
|
+
messages.lookup(:filled?, :email) # => "the email is missing"
|
228
|
+
|
229
|
+
# with namespaced messages
|
230
|
+
user_messages = messages.namespaced(:user)
|
231
|
+
|
232
|
+
user_messages.lookup(:filled?, :age) # "age cannot be blank"
|
233
|
+
user_messages.lookup(:filled?, :address) # "You gotta tell us where you live"
|
234
|
+
```
|
235
|
+
|
236
|
+
By configuring `messages_file` and/or `namespace` in a schema, default messages
|
237
|
+
are going to be automatically merged with your overrides and/or namespaced.
|
238
|
+
|
239
|
+
## I18n Integration
|
240
|
+
|
241
|
+
Coming (very) soon...
|
242
|
+
|
243
|
+
## Rule AST
|
244
|
+
|
245
|
+
Internally, `dry-validation` uses a simple AST representation of rules and errors
|
246
|
+
to produce rule objects and error messages. If you would like to programatically
|
247
|
+
generate rules, it is a very simple process:
|
248
|
+
|
249
|
+
``` ruby
|
250
|
+
ast = [
|
251
|
+
[
|
252
|
+
:and,
|
253
|
+
[
|
254
|
+
[:key, [:age, [:predicate, [:key?, []]]]],
|
255
|
+
[
|
256
|
+
:and,
|
257
|
+
[
|
258
|
+
[:val, [:age, [:predicate, [:filled?, []]]]],
|
259
|
+
[:val, [:age, [:predicate, [:gt?, [18]]]]]
|
260
|
+
]
|
261
|
+
]
|
262
|
+
]
|
263
|
+
]
|
264
|
+
]
|
265
|
+
|
266
|
+
compiler = Dry::Validation::RuleCompiler.new(Dry::Validation::Predicates)
|
267
|
+
|
268
|
+
# compile an array of rule objects
|
269
|
+
rules = compiler.call(ast)
|
270
|
+
|
271
|
+
puts rules.inspect
|
272
|
+
# [
|
273
|
+
# #<Dry::Validation::Rule::Conjunction
|
274
|
+
# left=#<Dry::Validation::Rule::Key name=:age predicate=#<Dry::Validation::Predicate id=:key?>>
|
275
|
+
# right=#<Dry::Validation::Rule::Conjunction
|
276
|
+
# left=#<Dry::Validation::Rule::Value name=:age predicate=#<Dry::Validation::Predicate id=:filled?>>
|
277
|
+
# right=#<Dry::Validation::Rule::Value name=:age predicate=#<Dry::Validation::Predicate id=:gt?>>>>
|
278
|
+
# ]
|
279
|
+
|
280
|
+
# dump it back to ast
|
281
|
+
puts rules.map(&:to_ary).inspect
|
282
|
+
# [[:and, [:key, [:age, [:predicate, [:key?, [:age]]]]], [[:and, [:val, [:age, [:predicate, [:filled?, []]]]], [[:val, [:age, [:predicate, [:gt?, [18]]]]]]]]]]
|
283
|
+
```
|
284
|
+
|
285
|
+
Complete docs for the AST format are coming soon, for now please refer to
|
286
|
+
[this spec](https://github.com/dryrb/dry-validation/blob/master/spec/unit/rule_compiler_spec.rb).
|
287
|
+
|
288
|
+
## Status and Roadmap
|
289
|
+
|
290
|
+
This library is in a very early stage of development but you are encauraged to
|
291
|
+
try it out and provide feedback.
|
292
|
+
|
293
|
+
For planned features check out [the issues](https://github.com/dryrb/dry-validation/labels/feature).
|
294
|
+
|
295
|
+
## License
|
296
|
+
|
297
|
+
See `LICENSE` file.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
|
4
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
5
|
+
|
6
|
+
require 'rspec/core'
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
|
9
|
+
task default: :spec
|
10
|
+
|
11
|
+
desc 'Run all specs in spec directory'
|
12
|
+
RSpec::Core::RakeTask.new(:spec)
|
data/config/errors.yml
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
empty?: "%{name} cannot be empty"
|
2
|
+
|
3
|
+
exclusion?: "%{name} must not be one of: %{list}"
|
4
|
+
|
5
|
+
eql?: "%{name} must be equal to %{eql_value}"
|
6
|
+
|
7
|
+
filled?: "%{name} must be filled"
|
8
|
+
|
9
|
+
format?: "%{name} is in invalid format"
|
10
|
+
|
11
|
+
gt?: "%{name} must be greater than %{num} (%{value} was given)"
|
12
|
+
|
13
|
+
gteq?: "%{name} must be greater than or equal to %{num}"
|
14
|
+
|
15
|
+
inclusion?: "%{name} must be one of: %{list}"
|
16
|
+
|
17
|
+
int?: "%{name} must be an integer"
|
18
|
+
|
19
|
+
key?: "%{name} is missing"
|
20
|
+
|
21
|
+
lt?: "%{name} must be less than %{num} (%{value} was given)"
|
22
|
+
|
23
|
+
lteq?: "%{name} must be less than or equal to %{num}"
|
24
|
+
|
25
|
+
max_size?: "%{name} size cannot be greater than %{num}"
|
26
|
+
|
27
|
+
min_size?: "%{name} size cannot be less than %{num}"
|
28
|
+
|
29
|
+
nil?: "%{name} cannot be nil"
|
30
|
+
|
31
|
+
size?:
|
32
|
+
range: "%{name} size must be within %{left} - %{right}"
|
33
|
+
default: "%{name} size must be %{num}"
|
34
|
+
|
35
|
+
str?: "%{name} must be a string"
|