lotus-validations 0.2.3 → 0.2.4
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/CHANGELOG.md +8 -0
- data/LICENSE.md +1 -1
- data/README.md +46 -1
- data/lib/lotus/validations.rb +14 -2
- data/lib/lotus/validations/attribute.rb +31 -31
- data/lib/lotus/validations/attribute_definer.rb +47 -3
- data/lib/lotus/validations/error.rb +40 -6
- data/lib/lotus/validations/errors.rb +6 -3
- data/lib/lotus/validations/validation_set.rb +2 -1
- data/lib/lotus/validations/validator.rb +9 -8
- data/lib/lotus/validations/version.rb +1 -1
- data/lotus-validations.gemspec +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddd486452556cb357a7742a46ce462e15ba9c206
|
4
|
+
data.tar.gz: 48fbf0ec796aaf18df2a2d138daed1c25f3335dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c25f6c2b8dd39d3d4cba3c0c7473c85f9572c23124d1e02cb5c354d145c4315262bbc576f6d99939048a1f58c6f68e4b6135542d33a843e8309f6f794c4ba771
|
7
|
+
data.tar.gz: 8bb02cb2c960d93b6e146cd61a4cd68fc5a0292188c8e61726a1e0bf7e09d5c3246e2d20de3adb2367756429e1bbbb4ad7750bfffd0544252cd6b4d94220e3b6
|
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,14 @@
|
|
1
1
|
# Lotus::Validations
|
2
2
|
Validations mixin for Ruby objects
|
3
3
|
|
4
|
+
## v0.2.4 - 2015-01-30
|
5
|
+
### Added
|
6
|
+
- [Steve Hodgkiss] Introduced `Lotus::Validations::Error#attribute_name`
|
7
|
+
- [Steve Hodgkiss] Nested validations
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- [Steve Hodgkiss] `Lotus::Validations::Error#name` returns the complete attribute name (Eg. `first_name` or `address.street`)
|
11
|
+
|
4
12
|
## v0.2.3 - 2015-01-12
|
5
13
|
### Added
|
6
14
|
- [Luca Guidi] Compatibility with Lotus::Entity
|
data/LICENSE.md
CHANGED
data/README.md
CHANGED
@@ -363,6 +363,51 @@ The other reason is that this isn't an effective way to ensure uniqueness of a v
|
|
363
363
|
|
364
364
|
Please read more at: [The Perils of Uniqueness Validations](http://robots.thoughtbot.com/the-perils-of-uniqueness-validations).
|
365
365
|
|
366
|
+
### Nested validations
|
367
|
+
|
368
|
+
Nested validations are handled with a nested block syntax.
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
class ShippingDetails
|
372
|
+
include Lotus::Validations
|
373
|
+
|
374
|
+
attribute :full_name, presence: true
|
375
|
+
|
376
|
+
attribute :address do
|
377
|
+
attribute :street, presence: true
|
378
|
+
attribute :city, presence: true
|
379
|
+
attribute :country, presence: true
|
380
|
+
attribute :postal_code, presence: true, format: /.../
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
validator = ShippingDetails.new
|
385
|
+
validator.valid? # => false
|
386
|
+
```
|
387
|
+
|
388
|
+
Bulk operations on errors are guaranteed by `#each`.
|
389
|
+
This method yields a **flatten collection of errors**.
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
validator.errors.each do |error|
|
393
|
+
error.name
|
394
|
+
# => on the first iteration it returns "full_name"
|
395
|
+
# => the second time it returns "address.street" and so on..
|
396
|
+
end
|
397
|
+
```
|
398
|
+
|
399
|
+
Errors for a specific attribute can be accessed via `#for`.
|
400
|
+
|
401
|
+
```ruby
|
402
|
+
error = validator.errors.for('full_name').first
|
403
|
+
error.name # => "full_name"
|
404
|
+
error.attribute_name # => "full_name"
|
405
|
+
|
406
|
+
error = validator.errors.for('address.street').first
|
407
|
+
error.name # => "address.street"
|
408
|
+
error.attribute_name # => "street"
|
409
|
+
```
|
410
|
+
|
366
411
|
### Composable validations
|
367
412
|
|
368
413
|
Validations can be reused via composition:
|
@@ -504,4 +549,4 @@ product.price # => 100
|
|
504
549
|
|
505
550
|
## Copyright
|
506
551
|
|
507
|
-
Copyright 2014-2015 Luca Guidi – Released under MIT License
|
552
|
+
Copyright © 2014-2015 Luca Guidi – Released under MIT License
|
data/lib/lotus/validations.rb
CHANGED
@@ -208,12 +208,24 @@ module Lotus
|
|
208
208
|
#
|
209
209
|
# @since 0.1.0
|
210
210
|
def valid?
|
211
|
-
|
212
|
-
@errors = validator.validate
|
211
|
+
validate
|
213
212
|
|
214
213
|
errors.empty?
|
215
214
|
end
|
216
215
|
|
216
|
+
# Validates the object.
|
217
|
+
#
|
218
|
+
# @return [Errors]
|
219
|
+
#
|
220
|
+
# @since 0.2.4
|
221
|
+
# @api private
|
222
|
+
#
|
223
|
+
# @see Lotus::Attribute#nested
|
224
|
+
def validate
|
225
|
+
validator = Validator.new(defined_validations, read_attributes, errors)
|
226
|
+
validator.validate
|
227
|
+
end
|
228
|
+
|
217
229
|
# Iterates thru the defined attributes and their values
|
218
230
|
#
|
219
231
|
# @param blk [Proc] a block
|
@@ -34,20 +34,30 @@ module Lotus
|
|
34
34
|
#
|
35
35
|
# @since 0.2.0
|
36
36
|
# @api private
|
37
|
-
def initialize(attributes, name, value, validations)
|
37
|
+
def initialize(attributes, name, value, validations, errors)
|
38
38
|
@attributes = attributes
|
39
39
|
@name = name
|
40
40
|
@value = value
|
41
41
|
@validations = validations
|
42
|
-
@errors =
|
42
|
+
@errors = errors
|
43
43
|
end
|
44
44
|
|
45
45
|
# @api private
|
46
46
|
# @since 0.2.0
|
47
47
|
def validate
|
48
|
-
|
49
|
-
|
50
|
-
|
48
|
+
presence
|
49
|
+
acceptance
|
50
|
+
|
51
|
+
return if skip?
|
52
|
+
|
53
|
+
format
|
54
|
+
inclusion
|
55
|
+
exclusion
|
56
|
+
size
|
57
|
+
confirmation
|
58
|
+
nested
|
59
|
+
|
60
|
+
@errors
|
51
61
|
end
|
52
62
|
|
53
63
|
# @api private
|
@@ -183,6 +193,21 @@ module Lotus
|
|
183
193
|
end
|
184
194
|
end
|
185
195
|
|
196
|
+
# Validates nested Lotus Validations objects
|
197
|
+
#
|
198
|
+
# @since 0.2.4
|
199
|
+
# @api private
|
200
|
+
def nested
|
201
|
+
_validate(__method__) do |validator|
|
202
|
+
errors = value.validate
|
203
|
+
errors.each do |error|
|
204
|
+
new_error = Error.new(error.attribute, error.validation, error.expected, error.actual, @name)
|
205
|
+
@errors.add new_error.attribute, new_error
|
206
|
+
end
|
207
|
+
true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
186
211
|
# @since 0.1.0
|
187
212
|
# @api private
|
188
213
|
def skip?
|
@@ -199,31 +224,6 @@ module Lotus
|
|
199
224
|
BlankValueChecker.new(@value).blank_value?
|
200
225
|
end
|
201
226
|
|
202
|
-
# Run the defined validations
|
203
|
-
#
|
204
|
-
# @since 0.2.0
|
205
|
-
# @api private
|
206
|
-
def _run_validations
|
207
|
-
presence
|
208
|
-
acceptance
|
209
|
-
|
210
|
-
return if skip?
|
211
|
-
|
212
|
-
format
|
213
|
-
inclusion
|
214
|
-
exclusion
|
215
|
-
size
|
216
|
-
confirmation
|
217
|
-
end
|
218
|
-
|
219
|
-
# @api private
|
220
|
-
# @since 0.2.0
|
221
|
-
def _with_cleared_errors
|
222
|
-
@errors.clear
|
223
|
-
yield
|
224
|
-
@errors.dup.tap {|_| @errors.clear }
|
225
|
-
end
|
226
|
-
|
227
227
|
# Reads an attribute from the validator.
|
228
228
|
#
|
229
229
|
# @since 0.2.0
|
@@ -238,7 +238,7 @@ module Lotus
|
|
238
238
|
# @api private
|
239
239
|
def _validate(validation)
|
240
240
|
if (validator = @validations[validation]) && !(yield validator)
|
241
|
-
@errors.
|
241
|
+
@errors.add(@name, Error.new(@name, validation, @validations.fetch(validation), @value))
|
242
242
|
end
|
243
243
|
end
|
244
244
|
end
|
@@ -253,9 +253,14 @@ module Lotus
|
|
253
253
|
#
|
254
254
|
# signup = Signup.new(password: 'short')
|
255
255
|
# signup.valid? # => false
|
256
|
-
def attribute(name, options = {})
|
257
|
-
|
258
|
-
|
256
|
+
def attribute(name, options = {}, &block)
|
257
|
+
if block_given?
|
258
|
+
define_nested_attribute(name, options, &block)
|
259
|
+
validates(name, {})
|
260
|
+
else
|
261
|
+
define_attribute(name, options)
|
262
|
+
validates(name, options)
|
263
|
+
end
|
259
264
|
end
|
260
265
|
|
261
266
|
# Set of user defined attributes
|
@@ -285,6 +290,16 @@ module Lotus
|
|
285
290
|
end
|
286
291
|
end
|
287
292
|
|
293
|
+
# @since 0.2.4
|
294
|
+
# @api private
|
295
|
+
def define_nested_attribute(name, options, &block)
|
296
|
+
nested_class = build_validation_class(&block)
|
297
|
+
define_lazy_reader(name, nested_class)
|
298
|
+
define_coerced_writer(name, nested_class)
|
299
|
+
defined_attributes.add(name.to_s)
|
300
|
+
validates(name, nested: true)
|
301
|
+
end
|
302
|
+
|
288
303
|
# @since 0.2.2
|
289
304
|
# @api private
|
290
305
|
def define_accessor(name, type)
|
@@ -320,6 +335,35 @@ module Lotus
|
|
320
335
|
@attributes.get(name)
|
321
336
|
end
|
322
337
|
end
|
338
|
+
|
339
|
+
# Defines a reader that will return a new instance of
|
340
|
+
# the given type if one is not already present
|
341
|
+
#
|
342
|
+
# @since 0.2.4
|
343
|
+
# @api private
|
344
|
+
def define_lazy_reader(name, type)
|
345
|
+
define_method(name) do
|
346
|
+
value = @attributes.get(name)
|
347
|
+
return value if value
|
348
|
+
|
349
|
+
type.new({}).tap do |val|
|
350
|
+
@attributes.set(name, val)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
# Creates a validation class and configures it with the
|
356
|
+
# given block.
|
357
|
+
#
|
358
|
+
# @since 0.2.4
|
359
|
+
# @api private
|
360
|
+
def build_validation_class(&block)
|
361
|
+
kls = Class.new do
|
362
|
+
include Lotus::Validations
|
363
|
+
end
|
364
|
+
kls.class_eval(&block)
|
365
|
+
kls
|
366
|
+
end
|
323
367
|
end
|
324
368
|
|
325
369
|
# Support for `Lotus::Entity`
|
@@ -4,12 +4,24 @@ module Lotus
|
|
4
4
|
#
|
5
5
|
# @since 0.1.0
|
6
6
|
class Error
|
7
|
+
# @since 0.2.4
|
8
|
+
# @api private
|
9
|
+
NAMESPACE_SEPARATOR = '.'.freeze
|
10
|
+
|
7
11
|
# The name of the attribute
|
8
12
|
#
|
9
13
|
# @return [Symbol] the name of the attribute
|
10
14
|
#
|
11
|
-
# @since 0.
|
12
|
-
|
15
|
+
# @since 0.2.4
|
16
|
+
#
|
17
|
+
# @see Lotus::Validations::Error#attribute
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# error = Error.new(:name, :presence, true, nil, 'author')
|
21
|
+
#
|
22
|
+
# error.attribute # => "author.name"
|
23
|
+
# error.attribute_name # => "name"
|
24
|
+
attr_reader :attribute_name
|
13
25
|
|
14
26
|
# The name of the validation
|
15
27
|
#
|
@@ -32,18 +44,40 @@ module Lotus
|
|
32
44
|
# @since 0.1.0
|
33
45
|
attr_reader :actual
|
34
46
|
|
47
|
+
# Returns the namespaced attribute name
|
48
|
+
#
|
49
|
+
# In cases where the error was pulled up from nested validators,
|
50
|
+
# `attribute` will be a namespaced string containing
|
51
|
+
# parent attribute names separated by a period.
|
52
|
+
#
|
53
|
+
# @since 0.1.0
|
54
|
+
#
|
55
|
+
# @see Lotus::Validations::Error#attribute_name
|
56
|
+
#
|
57
|
+
# @example
|
58
|
+
# error = Error.new(:name, :presence, true, nil, 'author')
|
59
|
+
#
|
60
|
+
# error.attribute # => "author.name"
|
61
|
+
# error.attribute_name # => "name"
|
62
|
+
attr_accessor :attribute
|
63
|
+
|
35
64
|
# Initialize a validation error
|
36
65
|
#
|
37
|
-
# @param
|
66
|
+
# @param attribute_name [Symbol] the name of the attribute
|
38
67
|
# @param validation [Symbol] the name of the validation
|
39
68
|
# @param expected [Object] the expected value
|
40
69
|
# @param actual [Object] the actual value
|
70
|
+
# @param namespace [String] the optional namespace
|
41
71
|
#
|
42
72
|
# @since 0.1.0
|
43
73
|
# @api private
|
44
|
-
def initialize(
|
45
|
-
@
|
46
|
-
|
74
|
+
def initialize(attribute_name, validation, expected, actual, namespace = nil)
|
75
|
+
@attribute_name = attribute_name.to_s
|
76
|
+
@validation = validation
|
77
|
+
@expected = expected
|
78
|
+
@actual = actual
|
79
|
+
@namespace = namespace
|
80
|
+
@attribute = [@namespace, attribute_name].compact.join(NAMESPACE_SEPARATOR)
|
47
81
|
end
|
48
82
|
|
49
83
|
# Check if self equals to `other`
|
@@ -15,7 +15,7 @@ module Lotus
|
|
15
15
|
# @since 0.1.0
|
16
16
|
# @api private
|
17
17
|
def initialize
|
18
|
-
@errors = Hash.new
|
18
|
+
@errors = Hash.new
|
19
19
|
end
|
20
20
|
|
21
21
|
# Check if the set is empty
|
@@ -94,7 +94,10 @@ module Lotus
|
|
94
94
|
#
|
95
95
|
# @see Lotus::Validations::Error
|
96
96
|
def add(attribute, *errors)
|
97
|
-
|
97
|
+
if errors.any?
|
98
|
+
@errors[attribute] ||= []
|
99
|
+
@errors[attribute].push(*errors)
|
100
|
+
end
|
98
101
|
end
|
99
102
|
|
100
103
|
# Return the errors for the given attribute
|
@@ -103,7 +106,7 @@ module Lotus
|
|
103
106
|
#
|
104
107
|
# @since 0.1.0
|
105
108
|
def for(attribute)
|
106
|
-
@errors[
|
109
|
+
@errors.fetch(attribute) { [] }
|
107
110
|
end
|
108
111
|
|
109
112
|
# Check if the current set of errors equals to the one who belongs to
|
@@ -5,23 +5,24 @@ module Lotus
|
|
5
5
|
# @since 0.2.2
|
6
6
|
# @api private
|
7
7
|
class Validator
|
8
|
-
def initialize(validation_set, attributes)
|
8
|
+
def initialize(validation_set, attributes, errors)
|
9
9
|
@validation_set = validation_set
|
10
10
|
@attributes = attributes
|
11
|
+
@errors = errors
|
11
12
|
end
|
12
13
|
|
13
14
|
# @since 0.2.2
|
14
15
|
# @api private
|
15
16
|
def validate
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
@errors.clear
|
18
|
+
@validation_set.each do |name, validations|
|
19
|
+
value = @attributes[name]
|
20
|
+
value = @attributes[name.to_s] if value.nil?
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
end
|
22
|
+
attribute = Attribute.new(@attributes, name, value, validations, @errors)
|
23
|
+
attribute.validate
|
24
24
|
end
|
25
|
+
@errors
|
25
26
|
end
|
26
27
|
end
|
27
28
|
end
|
data/lotus-validations.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
spec.required_ruby_version = '>= 2.0.0'
|
21
21
|
|
22
|
-
spec.add_dependency 'lotus-utils', '~> 0.3', '>= 0.3.
|
22
|
+
spec.add_dependency 'lotus-utils', '~> 0.3', '>= 0.3.4'
|
23
23
|
|
24
24
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
25
25
|
spec.add_development_dependency 'minitest', '~> 5'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lotus-validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Luca Guidi
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-01-
|
12
|
+
date: 2015-01-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: lotus-utils
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
version: '0.3'
|
21
21
|
- - ">="
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0.3.
|
23
|
+
version: 0.3.4
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -30,7 +30,7 @@ dependencies:
|
|
30
30
|
version: '0.3'
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.3.
|
33
|
+
version: 0.3.4
|
34
34
|
- !ruby/object:Gem::Dependency
|
35
35
|
name: bundler
|
36
36
|
requirement: !ruby/object:Gem::Requirement
|