ratatouille 1.2.6 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
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
- * required\_key fixed so that block behaves like given\_key block, by diving into the context of the key value
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
- * required\_key added to HashMethods
28
+ ### Updates
29
+
30
+ * **required\_key** added to HashMethods
14
31
  * Additional Tests
15
32
 
16
33
  ## 1.2.2
17
34
 
18
- * is\_a? method added to HashMethods
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
  [![Build Status](https://secure.travis-ci.org/CITguy/ratatouille.png?branch=master)](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. This can be used in your custom validation messages.
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
- All of the following methods perform validation on the *ratifiable\_object* defined in scope of the block.
50
+ ## Custom Validation
45
51
 
52
+ **Return to this section after reading the remaining sections.**
46
53
 
47
- ### is\_a?
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
- Method to check if ratifiable\_object matches given class. Will not validate without a given class.
56
+ You should use the **validation\_error** method to add your own errors to the Ratifier object.
50
57
 
51
58
 
52
- ### given\_key
59
+ ### validation\_error
53
60
 
54
- This method is used to scope its given block to the key value. Useful to reduce the need to explicitly namespace nested keys in *ratifiable\_object*.
61
+ Used to insert validation error message into the Ratifier object.
55
62
 
56
- * **This method doesn't perform any validation and must be used with a block to get any use out of it.**
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
- ```ruby
63
- given_key(:foo) do
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
- choice_of(number_of_choices, key_choice_array) do
86
- # Validation provided number of choices is satisfied
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
- ```ruby
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
- # Validate that the keys exist
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
- ```ruby
148
- r = ratify({:foo => "bar", :bar => "biz"}) do
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
- # Validate that the keys exist and perform validation if they do
92
+ ratify({:foo => "red"}) do
173
93
  required_key(:foo) do
174
- # Validation provided that :foo exists in Hash
94
+ unwrap_choice = true
95
+ unwrap_choice = false if ratifiable_object == 'green'
175
96
  end
176
97
 
177
- # Validate that the keys exist
178
- required_key(:foo)
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
- end
195
- r.valid? #=> false
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
- r = ratify({}) do
222
- is_not_empty # validation continues
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" }
@@ -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 @ratifiable_object.empty?
8
- validation_error("not empty")
9
- return
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
- if @ratifiable_object.empty?
21
- validation_error("empty")
22
- return
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
- return unless valid_min_length?(min_size)
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] min_size
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
- return unless valid_max_length?(max_size)
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
- return unless valid_min_length?(min_size)
64
-
65
- if max_size.nil?
66
- if min_size == 1
67
- validation_error("Consider using is_not_empty")
68
- return
69
- end
70
- else
71
- return unless valid_max_length?(max_size)
72
-
73
- if max_size == 0 && min_size == 0
74
- validation_error("Consider using is_empty")
75
- return
76
- end
77
-
78
- unless max_size > min_size
79
- validation_error("max_size must be greater than min_size")
80
- return
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
@@ -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
- if options.fetch(:required, false) == true
13
- unless @ratifiable_object.has_key?(key)
14
- validation_error("Missing key #{key.inspect}")
15
- return
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 @ratifiable_object.empty?
31
- validation_error("not empty")
32
- return
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
- if @ratifiable_object.empty?
44
- validation_error("empty")
45
- return
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. Otherwise,
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
- # @param [Array] args Array required keys
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(*req_keys, &block)
60
- common_keys = (@ratifiable_object.keys & req_keys)
112
+ def required_keys(options={}, &block)
113
+ unless options.fetch(:unwrap_block, false) == true
114
+ req_keys = options.fetch(:key_list, [])
61
115
 
62
- if @ratifiable_object.empty?
63
- validation_error("Cannot find required keys")
64
- return
65
- end
116
+ # Wrapped Validation
117
+ common_keys = (@ratifiable_object.keys & req_keys)
66
118
 
67
- if req_keys.nil? || req_keys.empty?
68
- validation_error("No required keys given to compare against.")
69
- return
70
- end
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
- unless common_keys.size == req_keys.size
73
- (req_keys - common_keys).each do |missed|
74
- validation_error("Missing #{missed.inspect}")
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. Otherwise,
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 (default 1).
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
- # @param [Integer] choice_size
104
- # @param [Array] args
105
- # Array of symbols and/or strings to denote the choices of keys.
106
- # All other values are ignored.
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(choice_size=1, *key_list, &block)
109
- if key_list.nil? || key_list.empty?
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
- # I can work with a non-zero integer or any object that responds
116
- case choice_size
117
- when Integer
118
- unless choice_size > 0
119
- validation_error("choice_of requires a positive integer for choice size")
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
- else
123
- unless choice_size.respond_to?(:to_i)
124
- validation_error("choice_of requires an object that responds to :to_i for choice size")
125
- return
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
- unless choice_size > 0
131
- validation_error("choice size for choice_of must be positive non-zero number")
132
- return
133
- end
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
- unless key_list.size > choice_size
136
- validation_error("Key list size for 'choice_of' should be larger than choice size. Consider using required_keys instead.")
137
- return
138
- end
139
-
140
- common_keys = (@ratifiable_object.keys & key_list)
141
- unless common_keys.size == choice_size
142
- choices = key_list.collect{|a|
143
- case a
144
- when Symbol then ":#{a}"
145
- when String then "#{a}"
146
- end
147
- }
148
- validation_error("Require #{choice_size} of the following: #{choices.join(', ')}")
149
- return
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?
@@ -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
- # Validate against ratifiable_object class
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)
@@ -1,4 +1,4 @@
1
1
  module Ratatouille
2
2
  # Gem Version
3
- VERSION = "1.2.6"
3
+ VERSION = "1.3.0"
4
4
  end
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({}) { choice_of(1, []) }.should_not be_valid
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({}) { choice_of(0, [:foo]) }.should_not be_valid
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({}) { choice_of(1, [:foo]) }.should_not be_valid
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"}){ choice_of(1, [:foo, :bar]) }.should be_valid
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"}) { required_keys(:foo, :bar) }.should_not be_valid
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({}) { required_keys(:foo) }.should_not be_valid
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"}) { required_keys(:foo, :bar) }.should_not be_valid
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: 19
4
+ hash: 27
5
5
  prerelease:
6
6
  segments:
7
7
  - 1
8
- - 2
9
- - 6
10
- version: 1.2.6
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-16 00:00:00 Z
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