definition 0.4.0 → 0.5.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.
- checksums.yaml +5 -5
- data/Changelog.md +11 -0
- data/README.md +53 -3
- data/lib/definition/conform_result.rb +17 -7
- data/lib/definition/dsl.rb +19 -0
- data/lib/definition/types/and.rb +1 -0
- data/lib/definition/types/keys.rb +13 -3
- data/lib/definition/types/type.rb +2 -0
- data/lib/definition/version.rb +1 -1
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2e17d7407705fa6a48ed0a3852b73abe91688fbe
|
4
|
+
data.tar.gz: 118a8ad2dc4663c8666a25b68614f89f8546ab16
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fe00bb7ab22dbc910ab68df1e054e5d15618d3e02d91a266c2f1fb07c48a4d95553c0071fbe3f5e3274d446d833491efd1753cca8d6ef625c25aa17747c129c6
|
7
|
+
data.tar.gz: 5cee41aff7c8c40cb54c533f6424046152fdcdfd9a971e17fc11209ec833b21e3834d77158c200430678d9683e9fcdc510aac0527838276fc502a74ab91b5451
|
data/Changelog.md
CHANGED
@@ -4,6 +4,17 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## [0.5.0] - 2019-03-28
|
8
|
+
### Added
|
9
|
+
- CoercibleValueObject Definition for better nesting of ValueObjects
|
10
|
+
- Nilable Definition as shortcut for nil OR some other definition
|
11
|
+
- Option for Keys Definition to ignore unexpected keys
|
12
|
+
### Fixed
|
13
|
+
- Error hash was missing some errors in a few cases
|
14
|
+
- `ConformResult.error_hash` was empty for Definitions without any `Keys` Definition
|
15
|
+
### Changed
|
16
|
+
- And Definition stops processing after the first failure
|
17
|
+
|
7
18
|
## [0.4.0] - 2019-03-22
|
8
19
|
### Added
|
9
20
|
- Added support for default values to Keys Definition
|
data/README.md
CHANGED
@@ -111,6 +111,28 @@ IntegerArray.new([1,2,"3"]) # => Definition::InvalidValueObjectError: Not all it
|
|
111
111
|
|
112
112
|
You can access the conform result object via `InvalidValueObjectError#conform_result`
|
113
113
|
|
114
|
+
#### Nesting value Objects
|
115
|
+
|
116
|
+
Value objects can be nested by either using the value object itself as type definition,
|
117
|
+
or by using the `CoercibleValueObject` Definition. The latter would convert input
|
118
|
+
hashes that conform with the value objects schema to an instance of the value object.
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
class IntegerArray < Definition::ValueObject
|
122
|
+
definition(Definition.Each(Definition.Type(Integer)))
|
123
|
+
end
|
124
|
+
|
125
|
+
class User < Definition::ValueObject
|
126
|
+
definition(Definition.Keys do
|
127
|
+
required :username, Definition.Type(String)
|
128
|
+
required :scores, Definition.CoercibleValueObject(IntegerArray)
|
129
|
+
end)
|
130
|
+
end
|
131
|
+
|
132
|
+
object = User.new(username: "John", scores: [1,2,3])
|
133
|
+
object.scores.class.name # => IntegerArray
|
134
|
+
```
|
135
|
+
|
114
136
|
### Conforming Hashes
|
115
137
|
|
116
138
|
Hashes can be conformed by using the `Keys` definition. It allows you to configure
|
@@ -130,6 +152,25 @@ Definition.Keys do
|
|
130
152
|
end
|
131
153
|
```
|
132
154
|
|
155
|
+
#### Ignoring unexpected keys
|
156
|
+
|
157
|
+
By default the `Keys` Definition does not conform with input hashes that contains
|
158
|
+
keys that are not defined in the Definition. You can set the `:ignore_extra_keys`
|
159
|
+
option to disable this.
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
schema = Definition.Keys do
|
163
|
+
option :ignore_extra_keys
|
164
|
+
|
165
|
+
required :title, Definition.NonEmptyString
|
166
|
+
optional :publication_date, Definition.Type(Time)
|
167
|
+
end
|
168
|
+
|
169
|
+
conform_result = schema.conform({title: "My first blog post", body: "Shortest one ever!", publication_date: Time.new})
|
170
|
+
conform_result.passed? # => true
|
171
|
+
conform_result.value # => {title: "My first blog post", publication_date: 2018-12-30 11:43:00 UTC}
|
172
|
+
```
|
173
|
+
|
133
174
|
### Validating types
|
134
175
|
|
135
176
|
This will validate that the value is of the specified type.
|
@@ -159,17 +200,17 @@ Definition.CoercibleType(Float).conform("0.1").value # => 0.1
|
|
159
200
|
### Combining multiple definitions with "And"
|
160
201
|
|
161
202
|
```ruby
|
162
|
-
Definition.And(definition1, definition2)
|
203
|
+
Definition.And(definition1, definition2, ...)
|
163
204
|
```
|
164
205
|
|
165
206
|
This definition will only conform if all definitions conform. The definitions will
|
166
207
|
be processed from left to right and the output of the previous will be the input
|
167
|
-
of the next.
|
208
|
+
of the next. Processing of the And-Definition stops as soon as one definition does not conform.
|
168
209
|
|
169
210
|
### Combining multiple definitions with "Or"
|
170
211
|
|
171
212
|
```ruby
|
172
|
-
Definition.Or(definition1, definition2)
|
213
|
+
Definition.Or(definition1, definition2, ...)
|
173
214
|
```
|
174
215
|
|
175
216
|
This definition will conform if at least one definition conforms. The definitions will
|
@@ -291,11 +332,20 @@ Definition.NonEmpty.conform({ a: 1 }) # => pass
|
|
291
332
|
Definition.Nil.conform(nil) # => pass
|
292
333
|
```
|
293
334
|
|
335
|
+
#### Boolean
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
Definition.Boolean.conform(tru) # => pass
|
339
|
+
```
|
340
|
+
|
294
341
|
#### All types
|
295
342
|
|
296
343
|
```ruby
|
297
344
|
Definition.Equal(5).conform(5) # => pass
|
298
345
|
Definition.Equal("foo").conform("foo") # => pass
|
346
|
+
|
347
|
+
Definition.Nilable(Definition.Type(String)).conform(nil) # => pass
|
348
|
+
Definition.Nilable(Definition.Type(String)).conform("foo") # => pass
|
299
349
|
```
|
300
350
|
|
301
351
|
### Examples
|
@@ -33,14 +33,14 @@ module Definition
|
|
33
33
|
def error_hash
|
34
34
|
{}.tap do |error_hash|
|
35
35
|
errors.each do |error|
|
36
|
-
|
36
|
+
path_hash = if error.error_path.empty?
|
37
|
+
{ "" => error }
|
38
|
+
else
|
39
|
+
error.error_path.reverse
|
40
|
+
.inject([error]) { |errors, key| { key => errors } }
|
41
|
+
end
|
37
42
|
|
38
|
-
path_hash
|
39
|
-
.inject([error]) { |errors, key| { key => errors } }
|
40
|
-
|
41
|
-
error_hash.deep_merge!(path_hash) do |_key, old, new|
|
42
|
-
old + new if old.is_a?(Array) && new.is_a?(Array) # concat arrays during deep_merge
|
43
|
-
end
|
43
|
+
merge_error_hash(error_hash, path_hash)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
end
|
@@ -51,6 +51,16 @@ module Definition
|
|
51
51
|
|
52
52
|
private
|
53
53
|
|
54
|
+
def merge_error_hash(hash, new_hash)
|
55
|
+
hash.deep_merge!(new_hash) do |_key, old, new|
|
56
|
+
if old.is_a?(Array) && new.is_a?(Hash) # Dont replace Hashes with arrays
|
57
|
+
new
|
58
|
+
elsif old.is_a?(Array) && new.is_a?(Array) # concat arrays during deep_merge
|
59
|
+
old + new
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
54
64
|
def find_next_parent_key_error(error)
|
55
65
|
current = error
|
56
66
|
loop do
|
data/lib/definition/dsl.rb
CHANGED
@@ -76,5 +76,24 @@ module Definition
|
|
76
76
|
def Boolean # rubocop:disable Style/MethodName
|
77
77
|
Types::Or.new(:boolean, Type(TrueClass), Type(FalseClass))
|
78
78
|
end
|
79
|
+
|
80
|
+
# Example:
|
81
|
+
# CoercibleValueObject(ValueObjectClass)
|
82
|
+
def CoercibleValueObject(klass) # rubocop:disable Style/MethodName
|
83
|
+
Types::Or.new(:coercible_value_object,
|
84
|
+
Definition.Type(klass), # If its of ther correct type already this will let it pass already
|
85
|
+
And(
|
86
|
+
klass, # First make sure that the input could be coerced to 'klass'
|
87
|
+
Lambda("value_object_coercion", context: { value_object_class: klass }) do |value|
|
88
|
+
conform_with(klass.new(value)) # Actually coerce the input to klass
|
89
|
+
end
|
90
|
+
))
|
91
|
+
end
|
92
|
+
|
93
|
+
# Example:
|
94
|
+
# Nilable(Definition.Type(Integer))
|
95
|
+
def Nilable(definition) # rubocop:disable Style/MethodName
|
96
|
+
Types::Or.new(:nullable, Nil(), definition)
|
97
|
+
end
|
79
98
|
end
|
80
99
|
end
|
data/lib/definition/types/and.rb
CHANGED
@@ -22,16 +22,26 @@ module Definition
|
|
22
22
|
def default(key, value)
|
23
23
|
defaults[key] = value
|
24
24
|
end
|
25
|
+
|
26
|
+
def option(option_name)
|
27
|
+
case option_name
|
28
|
+
when :ignore_extra_keys
|
29
|
+
self.ignore_extra_keys = true
|
30
|
+
else
|
31
|
+
raise "Option #{option_name} is not defined"
|
32
|
+
end
|
33
|
+
end
|
25
34
|
end
|
26
35
|
|
27
36
|
include Dsl
|
28
|
-
attr_accessor :required_definitions, :optional_definitions, :defaults
|
37
|
+
attr_accessor :required_definitions, :optional_definitions, :defaults, :ignore_extra_keys
|
29
38
|
|
30
|
-
def initialize(name, req: {}, opt: {}, defaults: {})
|
39
|
+
def initialize(name, req: {}, opt: {}, defaults: {}, options: {})
|
31
40
|
super(name)
|
32
41
|
self.required_definitions = req
|
33
42
|
self.optional_definitions = opt
|
34
43
|
self.defaults = defaults
|
44
|
+
self.ignore_extra_keys = options.fetch(:ignore_extra_keys, false)
|
35
45
|
end
|
36
46
|
|
37
47
|
def conform(value)
|
@@ -50,7 +60,7 @@ module Definition
|
|
50
60
|
end
|
51
61
|
|
52
62
|
def conform
|
53
|
-
add_extra_key_errors
|
63
|
+
add_extra_key_errors unless definition.ignore_extra_keys
|
54
64
|
add_missing_key_errors
|
55
65
|
values = conform_all_keys
|
56
66
|
|
data/lib/definition/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: definition
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dominik Goltermann
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -299,7 +299,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
299
299
|
version: '0'
|
300
300
|
requirements: []
|
301
301
|
rubyforge_project:
|
302
|
-
rubygems_version: 2.
|
302
|
+
rubygems_version: 2.2.2
|
303
303
|
signing_key:
|
304
304
|
specification_version: 4
|
305
305
|
summary: Simple and composable validation and coercion of data structures inspired
|