ratatouille 1.2.6 → 1.3.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.
- data/CHANGELOG.md +29 -3
- data/README.md +40 -183
- data/autotest/discover.rb +1 -0
- data/lib/ratatouille/array.rb +71 -32
- data/lib/ratatouille/hash.rb +160 -73
- data/lib/ratatouille/ratifier.rb +5 -1
- data/lib/ratatouille/version.rb +1 -1
- data/ratatouille.gemspec +1 -0
- data/spec/lib/ratatouille/hash_spec.rb +103 -9
- metadata +20 -5
data/CHANGELOG.md
CHANGED
@@ -1,25 +1,46 @@
|
|
1
|
+
## 1.3.0 (API Change!)
|
2
|
+
|
3
|
+
### Updates
|
4
|
+
|
5
|
+
* All Methods have been modified to accept optional hashes as arguments
|
6
|
+
* This is to provide better flexibility with future versions of the API.
|
7
|
+
* NOTE: As such, some methods have been updated to conform to this new call format. See README for details.
|
8
|
+
* Optional key, **:unwrap_block** supported by all methods.
|
9
|
+
* See README for details.
|
10
|
+
|
1
11
|
## 1.2.6
|
2
12
|
|
13
|
+
### Updates
|
14
|
+
|
3
15
|
* CHANGELOG added
|
4
16
|
* required\_key logic moved to given\_key and triggered with a :required => true option
|
5
17
|
* required\_key now calls given_key with :required => true
|
6
18
|
|
7
19
|
## 1.2.5
|
8
20
|
|
9
|
-
|
21
|
+
### Corrections
|
22
|
+
|
23
|
+
* required\_key fixed so that block behaves like given\_key block, by diving into the
|
24
|
+
context of the key value
|
10
25
|
|
11
26
|
## 1.2.4
|
12
27
|
|
13
|
-
|
28
|
+
### Updates
|
29
|
+
|
30
|
+
* **required\_key** added to HashMethods
|
14
31
|
* Additional Tests
|
15
32
|
|
16
33
|
## 1.2.2
|
17
34
|
|
18
|
-
|
35
|
+
### Updates
|
36
|
+
|
37
|
+
* **is\_a?** method added to HashMethods
|
19
38
|
* Additional Tests
|
20
39
|
|
21
40
|
## 1.2.0
|
22
41
|
|
42
|
+
### Updates
|
43
|
+
|
23
44
|
* Added Ratatouille::Ratifier name attribute for use with dynamically changing scoped errors.
|
24
45
|
* The current Ratifier name (in scope of the validation block) will be appended to errors created
|
25
46
|
in the direct context of that block.
|
@@ -28,12 +49,17 @@
|
|
28
49
|
|
29
50
|
## 1.1.1
|
30
51
|
|
52
|
+
### Corrections
|
53
|
+
|
31
54
|
* choice\_of validation errors show semantically correct key names (Symbols, Strings, etc.)
|
32
55
|
* choice\_of tweaked/fixed to perform proper validation on list of keys
|
33
56
|
|
34
57
|
## 1.0.0
|
35
58
|
|
36
59
|
* Not to be mistaken by 0.1.0
|
60
|
+
|
61
|
+
### Updates
|
62
|
+
|
37
63
|
* Plethora of tests added
|
38
64
|
* Refactoring Code
|
39
65
|
* Organization and Documentation
|
data/README.md
CHANGED
@@ -5,6 +5,7 @@ DSL for validation of complex Hashes
|
|
5
5
|
## Travis CI Status:
|
6
6
|
[](http://travis-ci.org/CITguy/ratatouille)
|
7
7
|
|
8
|
+
|
8
9
|
## Installation
|
9
10
|
|
10
11
|
Add this line to your application's Gemfile:
|
@@ -21,6 +22,11 @@ Or install it yourself as:
|
|
21
22
|
|
22
23
|
|
23
24
|
|
25
|
+
## Information
|
26
|
+
|
27
|
+
Specific uses and syntax can be found in the documentation of each module. The information defined
|
28
|
+
here is common information amongst the entirety of the ratatouille gem.
|
29
|
+
|
24
30
|
## Blocks
|
25
31
|
|
26
32
|
All of the given methods accept a block for validation and will not progress into the block if the core method logic does not validate.
|
@@ -35,223 +41,72 @@ This will change when using *given\_key*.
|
|
35
41
|
|
36
42
|
### name
|
37
43
|
|
38
|
-
Within a block, the name method provides the name of the scope.
|
39
|
-
|
44
|
+
Within a block, the **name** method provides the name of the scope.
|
45
|
+
This can be used in your custom validation messages and is already prepended to the beginning
|
46
|
+
of every validation error.
|
40
47
|
|
41
48
|
|
42
|
-
## Usage
|
43
49
|
|
44
|
-
|
50
|
+
## Custom Validation
|
45
51
|
|
52
|
+
**Return to this section after reading the remaining sections.**
|
46
53
|
|
47
|
-
|
54
|
+
Custom validation can take place using the following methods to generate custom validation logic that cannot be satisfied with the existing methods.
|
48
55
|
|
49
|
-
|
56
|
+
You should use the **validation\_error** method to add your own errors to the Ratifier object.
|
50
57
|
|
51
58
|
|
52
|
-
###
|
59
|
+
### validation\_error
|
53
60
|
|
54
|
-
|
61
|
+
Used to insert validation error message into the Ratifier object.
|
55
62
|
|
56
|
-
*
|
57
|
-
* Changes *ratifiable\_object* to *key value* (original scope will return on block exit)
|
63
|
+
* Scope name prepended to every validation error
|
58
64
|
|
59
65
|
|
60
66
|
#### Syntax
|
61
67
|
|
62
|
-
|
63
|
-
|
64
|
-
# validation for ratifiable_object[:foo]
|
65
|
-
end
|
66
|
-
```
|
67
|
-
|
68
|
-
#### Example
|
69
|
-
|
70
|
-
See **choice\_of** for an example.
|
71
|
-
|
72
|
-
|
73
|
-
### choice\_of
|
74
|
-
|
75
|
-
Meant to be used to validate that X number of given key choices exist in the Hash.
|
76
|
-
|
77
|
-
* Number of Choices must be less than size of key choice array
|
78
|
-
* Can be used without a block
|
79
|
-
* Works well with **given\_key()**.
|
80
|
-
|
81
|
-
|
82
|
-
#### Syntax
|
68
|
+
It is also possible to set the context of an error by passing in a second argument.
|
69
|
+
However, it defaults to the root of the current ratifiable\_object ('/').
|
83
70
|
|
84
71
|
```ruby
|
85
|
-
|
86
|
-
|
87
|
-
end
|
88
|
-
```
|
89
|
-
|
90
|
-
|
91
|
-
#### Example
|
92
|
-
|
93
|
-
```ruby
|
94
|
-
r = ratify({:foo => "bar", :bar => "biz"}) do
|
95
|
-
choice_of(1, :foo, :bar) do
|
96
|
-
# Validation, provided :foo OR :bar exists
|
97
|
-
end
|
98
|
-
end
|
99
|
-
r.valid? #=> false (:foo OR :bar must be defined, NOT BOTH)
|
100
|
-
|
101
|
-
r = ratify({:foo => "bar", :bar => "biz"}) do
|
102
|
-
choice_of(2, :foo, :bar, :biz) do
|
103
|
-
# Validation, provided 2 of the following exist: :foo, :bar, :biz
|
104
|
-
|
105
|
-
given_key(:foo) do
|
106
|
-
# in context of ratifiable_object[:foo]
|
107
|
-
end
|
108
|
-
|
109
|
-
given_key(:bar) do
|
110
|
-
# in context of ratifiable_object[:bar]
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
r.valid? #=> true (a choice of 2 items from [:foo, :bar, :biz] is satisfied)
|
115
|
-
|
116
|
-
r = ratify({:foo => "bar", :bar => "biz"}) do
|
117
|
-
choice_of(2, :foo, :bar) do
|
118
|
-
# Validation ...
|
119
|
-
end
|
120
|
-
end
|
121
|
-
r.valid? #=> false (you might as well use required_keys)
|
72
|
+
validation_error("This is an error")
|
73
|
+
validation_error("This is an error", "current_context")
|
122
74
|
```
|
123
75
|
|
124
76
|
|
125
|
-
### required\_keys
|
126
|
-
|
127
|
-
Used to ensure that the list of keys exist in the Hash.
|
128
|
-
|
129
|
-
* Block is optional
|
130
|
-
|
131
|
-
|
132
|
-
#### Syntax
|
133
77
|
|
134
|
-
|
135
|
-
# Validate that the keys exist and perform validation if they do
|
136
|
-
required_keys(:foo, :bar) do
|
137
|
-
# Validation provided that :foo and :bar exist
|
138
|
-
end
|
78
|
+
## Universal Options
|
139
79
|
|
140
|
-
|
141
|
-
required_keys(:foo, :bar)
|
142
|
-
```
|
80
|
+
### :unwrap\_block
|
143
81
|
|
82
|
+
This optional key, when set to true, will skip the wrapped validation provided in the called
|
83
|
+
method and run the validations contained in its given block (if any). This is useful
|
84
|
+
if previous validation results dictate further validation logic.
|
144
85
|
|
145
86
|
#### Example
|
146
87
|
|
147
|
-
|
148
|
-
|
149
|
-
required_keys(:foo, :bar)
|
150
|
-
end
|
151
|
-
r.valid? #=> true
|
152
|
-
|
153
|
-
r = ratify({:foo => "bar"}) do
|
154
|
-
required_keys(:foo, :bar)
|
155
|
-
end
|
156
|
-
r.valid? #=> false
|
157
|
-
```
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
### required\_key
|
162
|
-
|
163
|
-
Used to ensure given key exists in the Hash.
|
164
|
-
|
165
|
-
* Eliminates the need to perform "given\_key" methods within a "required\_keys" block.
|
166
|
-
* Evaluates an optional block in context of key value (same as given\_key)
|
167
|
-
|
168
|
-
|
169
|
-
#### Syntax
|
88
|
+
*A choice of :bar or :biz is required only if :foo is 'green',
|
89
|
+
otherwise :bar and :biz should be validated if they are present.*
|
170
90
|
|
171
91
|
```ruby
|
172
|
-
|
92
|
+
ratify({:foo => "red"}) do
|
173
93
|
required_key(:foo) do
|
174
|
-
|
94
|
+
unwrap_choice = true
|
95
|
+
unwrap_choice = false if ratifiable_object == 'green'
|
175
96
|
end
|
176
97
|
|
177
|
-
#
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
### is\_empty
|
184
|
-
|
185
|
-
* Self-explanatory
|
186
|
-
* Block is optional
|
187
|
-
|
188
|
-
```ruby
|
189
|
-
r = ratify({:foo => "bar"}) do
|
190
|
-
is_empty # validation continues
|
191
|
-
is_empty do
|
192
|
-
# validation in block is never performed
|
98
|
+
# Because :foo is 'red', choice_of logic will be ignored and the block will be entered.
|
99
|
+
choice_of(:key_list => [:bar, :biz], :unwrap_block => unwrap_choice) do
|
100
|
+
given_key(:bar) do
|
101
|
+
# :bar validation
|
193
102
|
end
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
r = ratify({}) do
|
198
|
-
is_empty # validation continues even if not empty
|
199
|
-
is_empty do
|
200
|
-
# validation continues only if ratifiable_object is empty
|
201
|
-
end
|
202
|
-
end
|
203
|
-
r.valid? #=> true
|
204
|
-
```
|
205
|
-
|
206
|
-
|
207
|
-
### is\_not\_empty
|
208
|
-
|
209
|
-
* Self-explanatory
|
210
|
-
* Block is optional
|
211
|
-
|
212
|
-
```ruby
|
213
|
-
r = ratify({:foo => "bar"}) do
|
214
|
-
is_not_empty # validation continues even if empty
|
215
|
-
is_not_empty do
|
216
|
-
# validation continues unless ratifiable_object is empty
|
103
|
+
given_key(:biz) do
|
104
|
+
# :biz validation
|
217
105
|
end
|
218
106
|
end
|
219
|
-
r.valid? #=> true
|
220
107
|
|
221
|
-
|
222
|
-
|
223
|
-
is_not_empty do
|
224
|
-
# validation in block is never performed
|
225
|
-
end
|
226
|
-
end
|
227
|
-
r.valid? #=> false
|
228
|
-
```
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
## Custom Validation
|
233
|
-
|
234
|
-
**Return to this section after reading the remaining sections.**
|
235
|
-
|
236
|
-
Custom validation can take place using the following methods to generate custom validation logic that cannot be satisfied with the existing methods.
|
237
|
-
|
238
|
-
You should use the **validation\_error** method to add your own errors to the Ratifier object.
|
239
|
-
|
240
|
-
|
241
|
-
### validation\_error
|
242
|
-
|
243
|
-
Used to insert validation error message into the Ratifier object.
|
244
|
-
|
245
|
-
* Scope name prepended to every validation error
|
246
|
-
|
247
|
-
|
248
|
-
#### Syntax
|
249
|
-
|
250
|
-
It is also possible to set the context of an error by passing in a second argument. However, it defaults to the root of the current ratifiable\_object ('/').
|
251
|
-
|
252
|
-
```ruby
|
253
|
-
validation_error("This is an error")
|
254
|
-
validation_error("This is an error", "current_context")
|
108
|
+
# If :foo were 'green', choice_of logic would be performed before entering the block.
|
109
|
+
end
|
255
110
|
```
|
256
111
|
|
257
112
|
|
@@ -277,6 +132,8 @@ It is also possible to set the context of an error by passing in a second argume
|
|
277
132
|
r.valid? # => false
|
278
133
|
```
|
279
134
|
|
135
|
+
|
136
|
+
|
280
137
|
## Contributing
|
281
138
|
|
282
139
|
1. Fork it
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
data/lib/ratatouille/array.rb
CHANGED
@@ -2,11 +2,19 @@ module Ratatouille
|
|
2
2
|
|
3
3
|
# Module used to provide Array-specific validation methods
|
4
4
|
module ArrayMethods
|
5
|
+
|
6
|
+
# @param [Hash] options
|
7
|
+
# @option options [Boolean] :unwrap_block (false)
|
8
|
+
# Perform block validation only -- skip is_empty validation logic.
|
9
|
+
# Useless unless block provided
|
5
10
|
# @return [void]
|
6
|
-
def is_empty(&block)
|
7
|
-
unless
|
8
|
-
|
9
|
-
|
11
|
+
def is_empty(options={}, &block)
|
12
|
+
unless options.fetch(:unwrap_block, false) == true
|
13
|
+
# Wrapped Validation
|
14
|
+
unless @ratifiable_object.empty?
|
15
|
+
validation_error("not empty")
|
16
|
+
return
|
17
|
+
end
|
10
18
|
end
|
11
19
|
|
12
20
|
instance_eval(&block) if block_given?
|
@@ -15,11 +23,18 @@ module Ratatouille
|
|
15
23
|
end#is_empty
|
16
24
|
|
17
25
|
|
26
|
+
# @param [Hash] options
|
27
|
+
# @option options [Boolean] :unwrap_block (false)
|
28
|
+
# Perform block validation only -- skip is_not_empty validation logic.
|
29
|
+
# Useless unless block provided
|
18
30
|
# @return [void]
|
19
|
-
def is_not_empty(&block)
|
20
|
-
|
21
|
-
|
22
|
-
|
31
|
+
def is_not_empty(options={}, &block)
|
32
|
+
unless options.fetch(:unwrap_block, false) == true
|
33
|
+
# Wrapped Validation
|
34
|
+
if @ratifiable_object.empty?
|
35
|
+
validation_error("empty")
|
36
|
+
return
|
37
|
+
end
|
23
38
|
end
|
24
39
|
|
25
40
|
instance_eval(&block) if block_given?
|
@@ -31,9 +46,16 @@ module Ratatouille
|
|
31
46
|
# Define Minimum Length of Array
|
32
47
|
#
|
33
48
|
# @param [Integer] min_size
|
49
|
+
# @param [Hash] options
|
50
|
+
# @option options [Boolean] :unwrap_block (false)
|
51
|
+
# Perform block validation only -- skip min_length validation logic.
|
52
|
+
# Useless unless block provided
|
34
53
|
# @return [void]
|
35
|
-
def min_length(min_size=0, &block)
|
36
|
-
|
54
|
+
def min_length(min_size=0, options={}, &block)
|
55
|
+
unless options.fetch(:unwrap_block, false) == true
|
56
|
+
# Wrapped Validation
|
57
|
+
return unless valid_min_length?(min_size)
|
58
|
+
end
|
37
59
|
|
38
60
|
instance_eval(&block) if block_given?
|
39
61
|
rescue Exception => e
|
@@ -43,10 +65,17 @@ module Ratatouille
|
|
43
65
|
|
44
66
|
# Define Maximum Length of Array
|
45
67
|
#
|
46
|
-
# @param [Integer]
|
68
|
+
# @param [Integer] max_size
|
69
|
+
# @param [Hash] options
|
70
|
+
# @option options [Boolean] :unwrap_block (false)
|
71
|
+
# Perform block validation only -- skip max_length validation logic.
|
72
|
+
# Useless unless block provided
|
47
73
|
# @return [void]
|
48
|
-
def max_length(max_size=0, &block)
|
49
|
-
|
74
|
+
def max_length(max_size=0, options={}, &block)
|
75
|
+
unless options.fetch(:unwrap_block, false) == true
|
76
|
+
# Wrapped Validation
|
77
|
+
return unless valid_max_length?(max_size)
|
78
|
+
end
|
50
79
|
|
51
80
|
instance_eval(&block) if block_given?
|
52
81
|
rescue Exception => e
|
@@ -58,26 +87,33 @@ module Ratatouille
|
|
58
87
|
#
|
59
88
|
# @param [Integer] min_size
|
60
89
|
# @param [Integer] max_size
|
90
|
+
# @param [Hash] options
|
91
|
+
# @option options [Boolean] :unwrap_block (false)
|
92
|
+
# Perform block validation only -- skip length_between validation logic.
|
93
|
+
# Useless unless block provided
|
61
94
|
# @return [void]
|
62
|
-
def length_between(min_size=0, max_size=nil, &block)
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
95
|
+
def length_between(min_size=0, max_size=nil, options={}, &block)
|
96
|
+
unless options.fetch(:unwrap_block, false) == true
|
97
|
+
# Wrapped Validation
|
98
|
+
return unless valid_min_length?(min_size)
|
99
|
+
|
100
|
+
if max_size.nil?
|
101
|
+
if min_size == 1
|
102
|
+
validation_error("Consider using is_not_empty")
|
103
|
+
return
|
104
|
+
end
|
105
|
+
else
|
106
|
+
return unless valid_max_length?(max_size)
|
107
|
+
|
108
|
+
if max_size == 0 && min_size == 0
|
109
|
+
validation_error("Consider using is_empty")
|
110
|
+
return
|
111
|
+
end
|
112
|
+
|
113
|
+
unless max_size > min_size
|
114
|
+
validation_error("max_size must be greater than min_size")
|
115
|
+
return
|
116
|
+
end
|
81
117
|
end
|
82
118
|
end
|
83
119
|
|
@@ -85,8 +121,10 @@ module Ratatouille
|
|
85
121
|
rescue Exception => e
|
86
122
|
validation_error("#{e.message}")
|
87
123
|
end#length_between
|
124
|
+
|
88
125
|
private
|
89
126
|
|
127
|
+
# @note Supporting Method
|
90
128
|
# @return [Boolean]
|
91
129
|
def valid_min_length?(min_size)
|
92
130
|
unless min_size.to_i >= 0
|
@@ -104,6 +142,7 @@ module Ratatouille
|
|
104
142
|
return false
|
105
143
|
end
|
106
144
|
|
145
|
+
# @note Supporting Method
|
107
146
|
# @return [Boolean]
|
108
147
|
def valid_max_length?(max_size)
|
109
148
|
unless max_size.to_i >= 0
|
data/lib/ratatouille/hash.rb
CHANGED
@@ -1,18 +1,38 @@
|
|
1
1
|
module Ratatouille
|
2
2
|
|
3
3
|
# Module used to provide Hash-specific validation methods
|
4
|
+
#
|
5
|
+
# All of the Hash methods perform validation on the *ratifiable_object* defined in
|
6
|
+
# scope of their given block.
|
4
7
|
module HashMethods
|
5
|
-
# Runs validation in block against object for the given key.
|
8
|
+
# Runs validation in block against object for the given key. It is used to
|
9
|
+
# scope its given block to the key value. Useful to reduce the need to
|
10
|
+
# explicitly namespace nested keys in *ratifiable_object*.
|
11
|
+
#
|
12
|
+
# * This method doesn't perform any validation and must be used with a
|
13
|
+
# block to get any use out of it.
|
14
|
+
# * Changes *ratifiable_object* to *key value* (original scope will
|
15
|
+
# return on block exit.
|
6
16
|
#
|
7
17
|
# @param [String, Symbol] key
|
8
18
|
# @param [Hash] options
|
19
|
+
# @option options [String, Symbol] :name (key.to_s)
|
20
|
+
# Name of ratifiable_object for use with validation error messages.
|
21
|
+
# @option options [Boolean] :required (false)
|
22
|
+
# Ensure that ratifiable_object has the given key
|
23
|
+
# @option options [Boolean] :unwrap_block (false)
|
24
|
+
# Perform block validation only -- skip is_empty validation logic.
|
25
|
+
# Useless unless block provided
|
9
26
|
def given_key(key, options={}, &block)
|
10
27
|
options[:name] = options.fetch(:name, (Symbol === key ? ":#{key}" : key.to_s) )
|
11
28
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
29
|
+
unless options.fetch(:unwrap_block, false) == true
|
30
|
+
# Wrapped Validation
|
31
|
+
if options.fetch(:required, false) == true
|
32
|
+
unless @ratifiable_object.has_key?(key)
|
33
|
+
validation_error("Missing key #{key.inspect}")
|
34
|
+
return
|
35
|
+
end
|
16
36
|
end
|
17
37
|
end
|
18
38
|
|
@@ -25,11 +45,20 @@ module Ratatouille
|
|
25
45
|
end#given_key
|
26
46
|
|
27
47
|
|
48
|
+
# Self-explanatory
|
49
|
+
#
|
50
|
+
# @param [Hash] options
|
51
|
+
# @option options [Boolean] :unwrap_block (false)
|
52
|
+
# Perform block validation only -- skip is_empty validation logic.
|
53
|
+
# Useless unless block provided
|
28
54
|
# @return [void]
|
29
|
-
def is_empty(&block)
|
30
|
-
unless
|
31
|
-
|
32
|
-
|
55
|
+
def is_empty(options={}, &block)
|
56
|
+
unless options.fetch(:unwrap_block, false) == true
|
57
|
+
# Wrapped Validation
|
58
|
+
unless @ratifiable_object.empty?
|
59
|
+
validation_error("not empty")
|
60
|
+
return
|
61
|
+
end
|
33
62
|
end
|
34
63
|
|
35
64
|
instance_eval(&block) if block_given?
|
@@ -38,11 +67,20 @@ module Ratatouille
|
|
38
67
|
end#is_empty
|
39
68
|
|
40
69
|
|
70
|
+
# Self-explanatory
|
71
|
+
#
|
72
|
+
# @param [Hash] options
|
73
|
+
# @option options [Boolean] :unwrap_block (false)
|
74
|
+
# Perform block validation only -- skip is_not_empty validation logic.
|
75
|
+
# Useless unless block provided
|
41
76
|
# @return [void]
|
42
|
-
def is_not_empty(&block)
|
43
|
-
|
44
|
-
|
45
|
-
|
77
|
+
def is_not_empty(options={}, &block)
|
78
|
+
unless options.fetch(:unwrap_block, false) == true
|
79
|
+
# Wrapped Validation
|
80
|
+
if @ratifiable_object.empty?
|
81
|
+
validation_error("empty")
|
82
|
+
return
|
83
|
+
end
|
46
84
|
end
|
47
85
|
|
48
86
|
instance_eval(&block) if block_given?
|
@@ -51,29 +89,49 @@ module Ratatouille
|
|
51
89
|
end#is_not_empty
|
52
90
|
|
53
91
|
|
54
|
-
# Provide a list of keys that must be present in the Hash to validate.
|
55
|
-
# an error will be added.
|
92
|
+
# Provide a list of keys that must be present in the Hash to validate.
|
93
|
+
# Otherwise, an error will be added.
|
94
|
+
#
|
95
|
+
# * block is optional
|
56
96
|
#
|
57
|
-
#
|
97
|
+
# *NOTE:* Due to the addition of the optional hash in 1.3.0, required\_keys has been modified
|
98
|
+
# considerably and will break compatibility with previous versions of Ratatouille code.
|
99
|
+
#
|
100
|
+
# @example pre 1.3.0 vs 1.3.0+
|
101
|
+
# # Old Way
|
102
|
+
# required_keys(:foo, :bar) { validation_here }
|
103
|
+
# # New Way
|
104
|
+
# required_keys(:key_list => [:foo, :bar]) { validation_here }
|
105
|
+
#
|
106
|
+
# @param [Hash] options
|
107
|
+
# @option options [Array] :key_list ([]) Required Keys
|
108
|
+
# @option options [Boolean] :unwrap_block (false)
|
109
|
+
# Perform block validation only -- skip required_keys validation logic.
|
110
|
+
# Useless unless block provided
|
58
111
|
# @return [void]
|
59
|
-
def required_keys(
|
60
|
-
|
112
|
+
def required_keys(options={}, &block)
|
113
|
+
unless options.fetch(:unwrap_block, false) == true
|
114
|
+
req_keys = options.fetch(:key_list, [])
|
61
115
|
|
62
|
-
|
63
|
-
|
64
|
-
return
|
65
|
-
end
|
116
|
+
# Wrapped Validation
|
117
|
+
common_keys = (@ratifiable_object.keys & req_keys)
|
66
118
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
119
|
+
if @ratifiable_object.empty?
|
120
|
+
validation_error("Cannot find required keys")
|
121
|
+
return
|
122
|
+
end
|
123
|
+
|
124
|
+
if req_keys.nil? || req_keys.empty?
|
125
|
+
validation_error("No required keys given to compare against.")
|
126
|
+
return
|
127
|
+
end
|
71
128
|
|
72
|
-
|
73
|
-
|
74
|
-
|
129
|
+
unless common_keys.size == req_keys.size
|
130
|
+
(req_keys - common_keys).each do |missed|
|
131
|
+
validation_error("Missing #{missed.inspect}")
|
132
|
+
end
|
133
|
+
return
|
75
134
|
end
|
76
|
-
return
|
77
135
|
end
|
78
136
|
|
79
137
|
instance_eval(&block) if block_given?
|
@@ -82,10 +140,15 @@ module Ratatouille
|
|
82
140
|
end#required_keys
|
83
141
|
|
84
142
|
|
85
|
-
# Perform validation on a key that must be present in the Hash to validate.
|
86
|
-
# an error will be added.
|
143
|
+
# Perform validation on a single key that must be present in the Hash to validate.
|
144
|
+
# Otherwise, an error will be added.
|
145
|
+
#
|
146
|
+
# * Eliminates the need to perform given_key methods within a required_keys block.
|
147
|
+
# * Evaluates an optional block in context of key value (same as given_key)
|
87
148
|
#
|
88
149
|
# @param key Required Key
|
150
|
+
# @param [Hash] options
|
151
|
+
# @option options [Boolean] :required (true) Used to call given_key.
|
89
152
|
# @return [void]
|
90
153
|
def required_key(key, options={}, &block)
|
91
154
|
options[:required] = true
|
@@ -96,57 +159,81 @@ module Ratatouille
|
|
96
159
|
end
|
97
160
|
|
98
161
|
|
99
|
-
# Provide a list of keys to choose from and a choice size
|
162
|
+
# Provide a list of keys to choose from and a choice size.
|
100
163
|
# When the Hash does not contain at least 'choice_size' keys of the key
|
101
164
|
# list provided, an error will be added.
|
102
165
|
#
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
166
|
+
# *NOTE:* Due to the addition of the optional hash in version 1.3.0, choice\_of has been modified
|
167
|
+
# considerably and will break compatibility with previous versions of Ratatouille code.
|
168
|
+
#
|
169
|
+
# @example pre 1.3.0 vs 1.3.0+
|
170
|
+
# # Old Way
|
171
|
+
# choice_of(1, :foo, :bar) { validation_here }
|
172
|
+
# # New Way
|
173
|
+
# choice_of(:key_list => [:foo, :bar]) { validation_here }
|
174
|
+
#
|
175
|
+
# # Old Way
|
176
|
+
# choice_of(2, :foo, :bar, :biz) { validation_here }
|
177
|
+
# # New Way
|
178
|
+
# choice_of(:choice_size => 2, :key_list => [:foo, :bar, :biz]) { validation_here }
|
179
|
+
#
|
180
|
+
# @param [Hash] options
|
181
|
+
# @option options [Integer] :choice_size (1) Number of choices required
|
182
|
+
# @option options [Array] :key_list ([]) Keys to choose from
|
183
|
+
# @option options [Boolean] :unwrap_block (false)
|
184
|
+
# Perform block validation only -- skip choice_of validation logic.
|
185
|
+
# Useless unless block provided
|
107
186
|
# @return [void]
|
108
|
-
def choice_of(
|
109
|
-
|
110
|
-
validation_error("choice_of requires a key list to choose from")
|
111
|
-
return
|
112
|
-
end
|
113
|
-
key_list.flatten!
|
187
|
+
def choice_of(options, &block)
|
188
|
+
options ||= {}
|
114
189
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
190
|
+
unless options.fetch(:unwrap_block, false) == true
|
191
|
+
choice_size = options.fetch(:choice_size, 1)
|
192
|
+
key_list = options.fetch(:key_list, [])
|
193
|
+
|
194
|
+
# Wrapped Validation
|
195
|
+
if key_list.nil? || key_list.empty?
|
196
|
+
validation_error("choice_of requires a key list to choose from")
|
120
197
|
return
|
121
198
|
end
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
199
|
+
key_list.flatten!
|
200
|
+
|
201
|
+
# I can work with a non-zero integer or any object that responds
|
202
|
+
case choice_size
|
203
|
+
when Integer
|
204
|
+
unless choice_size > 0
|
205
|
+
validation_error("choice_of requires a positive integer for choice size")
|
206
|
+
return
|
207
|
+
end
|
208
|
+
else
|
209
|
+
unless choice_size.respond_to?(:to_i)
|
210
|
+
validation_error("choice_of requires an object that responds to :to_i for choice size")
|
211
|
+
return
|
212
|
+
end
|
213
|
+
choice_size = choice_size.to_i
|
126
214
|
end
|
127
|
-
choice_size = choice_size.to_i
|
128
|
-
end
|
129
215
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
216
|
+
unless choice_size > 0
|
217
|
+
validation_error("choice size for choice_of must be positive non-zero number")
|
218
|
+
return
|
219
|
+
end
|
134
220
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
221
|
+
unless key_list.size > choice_size
|
222
|
+
validation_error("Key list size for 'choice_of' should be larger than choice size. Consider using required_keys instead.")
|
223
|
+
return
|
224
|
+
end
|
225
|
+
|
226
|
+
common_keys = (@ratifiable_object.keys & key_list)
|
227
|
+
unless common_keys.size == choice_size
|
228
|
+
choices = key_list.collect{|a|
|
229
|
+
case a
|
230
|
+
when Symbol then ":#{a}"
|
231
|
+
when String then "#{a}"
|
232
|
+
end
|
233
|
+
}
|
234
|
+
validation_error("Require #{choice_size} of the following: #{choices.join(', ')}")
|
235
|
+
return
|
236
|
+
end
|
150
237
|
end
|
151
238
|
|
152
239
|
instance_eval(&block) if block_given?
|
data/lib/ratatouille/ratifier.rb
CHANGED
@@ -25,6 +25,9 @@ module Ratatouille
|
|
25
25
|
@errors.freeze
|
26
26
|
end#initialize
|
27
27
|
|
28
|
+
# Alias method (much shorter to type)
|
29
|
+
alias :ro :ratifiable_object
|
30
|
+
|
28
31
|
|
29
32
|
# Name of instance
|
30
33
|
#
|
@@ -118,7 +121,8 @@ module Ratatouille
|
|
118
121
|
end#errors_array
|
119
122
|
|
120
123
|
|
121
|
-
#
|
124
|
+
# Method to check if ratifiable_object matches given class.
|
125
|
+
# Will not validate without class.
|
122
126
|
#
|
123
127
|
# @param [Class] klass
|
124
128
|
def is_a?(klass=nil, &block)
|
data/lib/ratatouille/version.rb
CHANGED
data/ratatouille.gemspec
CHANGED
@@ -14,6 +14,7 @@ Gem::Specification.new do |gem|
|
|
14
14
|
|
15
15
|
gem.add_development_dependency "rspec", ">= 2.4.0"
|
16
16
|
gem.add_development_dependency "rake"
|
17
|
+
gem.add_development_dependency "autotest"
|
17
18
|
|
18
19
|
gem.files = `git ls-files`.split($\)
|
19
20
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -9,6 +9,16 @@ describe "Ratatouille::HashMethods" do
|
|
9
9
|
it "should be valid for empty Hash" do
|
10
10
|
RatifierTest.new({}){ is_empty }.should be_valid
|
11
11
|
end
|
12
|
+
|
13
|
+
describe "when :unwrap_block is true" do
|
14
|
+
it "should be valid for non-empty Hash" do
|
15
|
+
RatifierTest.new({:bar => 'biz'}){
|
16
|
+
is_empty(:unwrap_block => true) do
|
17
|
+
# Nothing to validate, wrapper ignored.
|
18
|
+
end
|
19
|
+
}.should be_valid
|
20
|
+
end
|
21
|
+
end
|
12
22
|
end
|
13
23
|
|
14
24
|
describe "is_not_empty" do
|
@@ -19,47 +29,113 @@ describe "Ratatouille::HashMethods" do
|
|
19
29
|
it "should not be valid for empty hash" do
|
20
30
|
RatifierTest.new({}){ is_not_empty }.should_not be_valid
|
21
31
|
end
|
32
|
+
|
33
|
+
describe "when :unwrap_block is true" do
|
34
|
+
it "should be valid for empty hash" do
|
35
|
+
RatifierTest.new({}){
|
36
|
+
is_not_empty(:unwrap_block => true) do
|
37
|
+
# Nothing to validate, wrapper ignored
|
38
|
+
end
|
39
|
+
}.should be_valid
|
40
|
+
end
|
41
|
+
end
|
22
42
|
end
|
23
43
|
|
24
44
|
describe "choice_of" do
|
45
|
+
it "should be valid with no :choice_size given" do
|
46
|
+
RatifierTest.new({:foo => "bar"}) {
|
47
|
+
choice_of(:key_list => [:foo, :bar])
|
48
|
+
}.should be_valid
|
49
|
+
end
|
50
|
+
|
25
51
|
it "should be invalid if key list is empty" do
|
26
|
-
RatifierTest.new({}) {
|
52
|
+
RatifierTest.new({}) {
|
53
|
+
choice_of(:choice_size => 1, :key_list => [])
|
54
|
+
}.should_not be_valid
|
27
55
|
end
|
28
56
|
|
29
57
|
it "should be invalid if choice size less than 1" do
|
30
|
-
RatifierTest.new({}) {
|
58
|
+
RatifierTest.new({}) {
|
59
|
+
choice_of(:choice_size => 0, :key_list => [:foo])
|
60
|
+
}.should_not be_valid
|
31
61
|
end
|
32
62
|
|
33
63
|
it "should be invalid if choice list is not 1 more than choice size" do
|
34
|
-
RatifierTest.new({}) {
|
64
|
+
RatifierTest.new({}) {
|
65
|
+
choice_of(:choice_size => 1, :key_list => [:foo])
|
66
|
+
}.should_not be_valid
|
35
67
|
end
|
36
68
|
|
37
69
|
it "should be valid when given hash has 1 key in a choice list of 2 or more" do
|
38
|
-
RatifierTest.new({:foo => "bar"}){
|
70
|
+
RatifierTest.new({:foo => "bar"}){
|
71
|
+
choice_of(:key_list => [:foo, :bar])
|
72
|
+
}.should be_valid
|
39
73
|
end
|
40
74
|
|
41
75
|
it "should be valid when given hash has 2 keys in choice list of 3 or more" do
|
42
76
|
RatifierTest.new({:foo => "foo", :bar => "bar"}){
|
43
|
-
choice_of(2, [:foo, :bar, :biz])
|
77
|
+
choice_of(:choice_size => 2, :key_list => [:foo, :bar, :biz])
|
44
78
|
}.should be_valid
|
45
79
|
|
46
80
|
RatifierTest.new({:foo => "foo", :bar => "bar"}){
|
47
|
-
choice_of(2, [:foo, :bar, :biz, :bang])
|
81
|
+
choice_of(:choice_size => 2, :key_list => [:foo, :bar, :biz, :bang])
|
48
82
|
}.should be_valid
|
49
83
|
end
|
84
|
+
|
85
|
+
describe "when :unwrap_block is true" do
|
86
|
+
it "should be valid when used on empty Hash" do
|
87
|
+
RatifierTest.new({}){
|
88
|
+
choice_of(:key_list => [:foo, :bar], :unwrap_block => true) do
|
89
|
+
# Nothing to validate, wrapper ignored
|
90
|
+
end
|
91
|
+
}.should be_valid
|
92
|
+
|
93
|
+
RatifierTest.new({}){
|
94
|
+
choice_of(:key_list => [:foo, :bar]) do
|
95
|
+
# Nothing to validate, wrapper ignored
|
96
|
+
end
|
97
|
+
}.should_not be_valid
|
98
|
+
end
|
99
|
+
end
|
50
100
|
end
|
51
101
|
|
52
102
|
describe "required_keys" do
|
53
103
|
it "should be valid if Hash contains all required keys" do
|
54
|
-
RatifierTest.new({:foo => "foo"}) {
|
104
|
+
RatifierTest.new({:foo => "foo"}) {
|
105
|
+
required_keys(:key_list => [:foo, :bar])
|
106
|
+
}.should_not be_valid
|
107
|
+
|
108
|
+
RatifierTest.new({:foo => "foo", :bar => "bar"}) {
|
109
|
+
required_keys(:key_list => [:foo, :bar])
|
110
|
+
}.should be_valid
|
55
111
|
end
|
56
112
|
|
57
113
|
it "should be invalid if Hash is empty and key list is not" do
|
58
|
-
RatifierTest.new({}) {
|
114
|
+
RatifierTest.new({}) {
|
115
|
+
required_keys(:key_list => [:foo])
|
116
|
+
}.should_not be_valid
|
59
117
|
end
|
60
118
|
|
61
119
|
it "should be invalid if Hash does not contain ALL keys in key list" do
|
62
|
-
RatifierTest.new({:foo => "foo"}) {
|
120
|
+
RatifierTest.new({:foo => "foo"}) {
|
121
|
+
required_keys(:key_list => [:foo, :bar])
|
122
|
+
}.should_not be_valid
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "when :unwrap_block is true" do
|
126
|
+
it "should be valid when used on empty Hash" do
|
127
|
+
RatifierTest.new({}){
|
128
|
+
required_keys(:key_list => [:foo, :bar], :unwrap_block => true) do
|
129
|
+
# Nothing to validate, wrapper ignored
|
130
|
+
end
|
131
|
+
}.should be_valid
|
132
|
+
|
133
|
+
RatifierTest.new({}){
|
134
|
+
required_keys(:key_list => [:foo, :bar]) do
|
135
|
+
# Nothing to validate, wrapper ignored
|
136
|
+
end
|
137
|
+
}.should_not be_valid
|
138
|
+
end
|
63
139
|
end
|
64
140
|
end
|
65
141
|
|
@@ -87,6 +163,16 @@ describe "Ratatouille::HashMethods" do
|
|
87
163
|
end
|
88
164
|
n.should == ":foo"
|
89
165
|
end
|
166
|
+
|
167
|
+
describe "when :unwrap_block is true" do
|
168
|
+
it "should be valid when used on empty Hash" do
|
169
|
+
RatifierTest.new({}){
|
170
|
+
required_key(:foo, :unwrap_block => true) do
|
171
|
+
# Nothing to validate, wrapper ignored
|
172
|
+
end
|
173
|
+
}.should be_valid
|
174
|
+
end
|
175
|
+
end
|
90
176
|
end
|
91
177
|
|
92
178
|
describe "given_key" do
|
@@ -108,6 +194,14 @@ describe "Ratatouille::HashMethods" do
|
|
108
194
|
n.should == "None"
|
109
195
|
end
|
110
196
|
|
197
|
+
it "block should respond to :ro" do
|
198
|
+
responded = false
|
199
|
+
RatifierTest.new({:foo => "bar"}, :name => "Outer") do
|
200
|
+
responded = self.respond_to?(:ro)
|
201
|
+
end
|
202
|
+
responded.should be_true
|
203
|
+
end
|
204
|
+
|
111
205
|
it "should not change the outer scope's name" do
|
112
206
|
o = n = ""
|
113
207
|
RatifierTest.new({:foo => "bar"}) do
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ratatouille
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 1.
|
8
|
+
- 3
|
9
|
+
- 0
|
10
|
+
version: 1.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ryan Johnson
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-04-
|
18
|
+
date: 2012-04-20 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: rspec
|
@@ -47,6 +47,20 @@ dependencies:
|
|
47
47
|
version: "0"
|
48
48
|
type: :development
|
49
49
|
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
51
|
+
name: autotest
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 3
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
type: :development
|
63
|
+
version_requirements: *id003
|
50
64
|
description: DSL for validating complex hashes
|
51
65
|
email:
|
52
66
|
- rhino.citguy@gmail.com
|
@@ -65,6 +79,7 @@ files:
|
|
65
79
|
- LICENSE
|
66
80
|
- README.md
|
67
81
|
- Rakefile
|
82
|
+
- autotest/discover.rb
|
68
83
|
- lib/ratatouille.rb
|
69
84
|
- lib/ratatouille/array.rb
|
70
85
|
- lib/ratatouille/hash.rb
|