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 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