ratatouille 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ *.swp
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm --create use 1.8.7@ratatouille
data/.travis-yml ADDED
@@ -0,0 +1,8 @@
1
+ script: "rake"
2
+ language: ruby
3
+ rvm:
4
+ - 1.8.7
5
+ - 1.9.2
6
+ - ree
7
+ - rbx
8
+ - jruby
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ 'lib/**/*.rb' - '*.md'
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ratatouille.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Ryan Johnson
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,249 @@
1
+ # Ratatouille [![Build Status](http://travis-ci.org/CITguy/ratatouille.png)](http://travis-ci.org/CITguy/ratatouille)
2
+
3
+ DSL for validation of complex Hashes
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'ratatouille'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install ratatouille
18
+
19
+
20
+
21
+ ## Blocks
22
+
23
+ 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.
24
+ However, some methods may be used without a block and validation will progress whether or not the method logic validates.
25
+
26
+
27
+ ### ratifiable\_object
28
+
29
+ Within a block, the ratifiable\_object method provides the object that is to be validated against.
30
+ This will change when using *given\_key*.
31
+
32
+
33
+
34
+ ## Usage
35
+
36
+ All of the following methods perform validation on the *ratifiable\_object* defined in scope of the block.
37
+
38
+
39
+ ### given\_key
40
+
41
+ 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*.
42
+
43
+ * **This method doesn't perform any validation and must be used with a block to get any use out of it.**
44
+ * Changes *ratifiable\_object* to *key value* (original scope will return on block exit)
45
+
46
+
47
+ #### Syntax
48
+
49
+ ```ruby
50
+ given_key(:foo) do
51
+ # validation for ratifiable_object[:foo]
52
+ end
53
+ ```
54
+
55
+ #### Example
56
+
57
+ See **choice\_of** for an example.
58
+
59
+
60
+ ### choice\_of
61
+
62
+ Meant to be used to validate that X number of given key choices exist in the Hash.
63
+
64
+ * Number of Choices must be less than size of key choice array
65
+ * Can be used without a block
66
+ * Works well with **given\_key()**.
67
+
68
+
69
+ #### Syntax
70
+
71
+ ```ruby
72
+ choice_of(number_of_choices, key_choice_array) do
73
+ # Validation provided number of choices is satisfied
74
+ end
75
+ ```
76
+
77
+
78
+ #### Example
79
+
80
+ ```ruby
81
+ r = ratify({:foo => "bar", :bar => "biz"}) do
82
+ choice_of(1, :foo, :bar) do
83
+ # Validation, provided :foo OR :bar exists
84
+ end
85
+ end
86
+ r.valid? #=> false (:foo OR :bar must be defined, NOT BOTH)
87
+
88
+ r = ratify({:foo => "bar", :bar => "biz"}) do
89
+ choice_of(2, :foo, :bar, :biz) do
90
+ # Validation, provided 2 of the following exist: :foo, :bar, :biz
91
+
92
+ given_key(:foo) do
93
+ # in context of ratifiable_object[:foo]
94
+ end
95
+
96
+ given_key(:bar) do
97
+ # in context of ratifiable_object[:bar]
98
+ end
99
+ end
100
+ end
101
+ r.valid? #=> true (a choice of 2 items from [:foo, :bar, :biz] is satisfied)
102
+
103
+ r = ratify({:foo => "bar", :bar => "biz"}) do
104
+ choice_of(2, :foo, :bar) do
105
+ # Validation ...
106
+ end
107
+ end
108
+ r.valid? #=> false (you might as well use required_keys)
109
+ ```
110
+
111
+
112
+ ### required\_keys
113
+
114
+ Used to ensure that the list of keys exist in the Hash.
115
+
116
+ * Block is optional
117
+
118
+
119
+ #### Syntax
120
+
121
+ ```ruby
122
+ # Validate that the keys exist and perform validation if they do
123
+ required_keys(:foo, :bar) do
124
+ # Validation provided that :foo and :bar exist
125
+ end
126
+
127
+ # Validate that the keys exist
128
+ required_keys(:foo, :bar)
129
+ ```
130
+
131
+
132
+ #### Example
133
+
134
+ ```ruby
135
+ r = ratify({:foo => "bar", :bar => "biz"}) do
136
+ required_keys(:foo, :bar)
137
+ end
138
+ r.valid? #=> true
139
+
140
+ r = ratify({:foo => "bar"}) do
141
+ required_keys(:foo, :bar)
142
+ end
143
+ r.valid? #=> false
144
+ ```
145
+
146
+
147
+
148
+ ### is\_empty
149
+
150
+ * Self-explanatory
151
+ * Block is optional
152
+
153
+ ```ruby
154
+ r = ratify({:foo => "bar"}) do
155
+ is_empty # validation continues
156
+ is_empty do
157
+ # validation in block is never performed
158
+ end
159
+ end
160
+ r.valid? #=> false
161
+
162
+ r = ratify({}) do
163
+ is_empty # validation continues even if not empty
164
+ is_empty do
165
+ # validation continues only if ratifiable_object is empty
166
+ end
167
+ end
168
+ r.valid? #=> true
169
+ ```
170
+
171
+
172
+ ### is\_not\_empty
173
+
174
+ * Self-explanatory
175
+ * Block is optional
176
+
177
+ ```ruby
178
+ r = ratify({:foo => "bar"}) do
179
+ is_not_empty # validation continues even if empty
180
+ is_not_empty do
181
+ # validation continues unless ratifiable_object is empty
182
+ end
183
+ end
184
+ r.valid? #=> true
185
+
186
+ r = ratify({}) do
187
+ is_not_empty # validation continues
188
+ is_not_empty do
189
+ # validation in block is never performed
190
+ end
191
+ end
192
+ r.valid? #=> false
193
+ ```
194
+
195
+
196
+
197
+ ## Custom Validation
198
+
199
+ **Return to this section after reading the remaining sections.**
200
+
201
+ Custom validation can take place using the following methods to generate custom validation logic that cannot be satisfied with the existing methods.
202
+
203
+ You should use the **validation\_error** method to add your own errors to the Ratifier object.
204
+
205
+
206
+ ### validation\_error
207
+
208
+ Used to insert validation error message into the Ratifier object.
209
+
210
+
211
+ #### Syntax
212
+
213
+ 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 ('/').
214
+
215
+ ```ruby
216
+ validation_error("This is an error")
217
+ validation_error("This is an error", "current_context")
218
+ ```
219
+
220
+
221
+
222
+ ## Advanced Example
223
+
224
+ ```ruby
225
+ include Ratatouille
226
+ r = ratify({:foo => {:bar => {:biz => "bang"}}}) do
227
+ is_not_empty
228
+ given_key(:foo) do
229
+ validation_error(":foo error")
230
+ given_key(:bar) do
231
+ validation_error(":bar error")
232
+ given_key(:biz) do
233
+ if ratifiable_object == "bang"
234
+ validation_error("should be 'shoot'")
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end
240
+ r.valid? # => false
241
+ ```
242
+
243
+ ## Contributing
244
+
245
+ 1. Fork it
246
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
247
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
248
+ 4. Push to the branch (`git push origin my-new-feature`)
249
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler"
3
+ Bundler::GemHelper.install_tasks
4
+
5
+ require "rspec/core/rake_task"
6
+
7
+ RSpec::Core::RakeTask.new do |t|
8
+ t.rspec_opts = %w(-c)
9
+ end
10
+
11
+ task :default => :spec
12
+ task :test => :spec
@@ -0,0 +1,120 @@
1
+ module Ratatouille
2
+
3
+ # Module used to provide Array-specific validation methods
4
+ module ArrayMethods
5
+ # @return [void]
6
+ def is_empty(&block)
7
+ unless @ratifiable_object.empty?
8
+ validation_error("Array is not empty")
9
+ return
10
+ end
11
+
12
+ instance_eval(&block) if block_given?
13
+ end#is_empty
14
+
15
+
16
+ # @return [void]
17
+ def is_not_empty(&block)
18
+ if @ratifiable_object.empty?
19
+ validation_error("Array is empty")
20
+ return
21
+ end
22
+
23
+ instance_eval(&block) if block_given?
24
+ end#is_not_empty
25
+
26
+
27
+ # Define Minimum Length of Array
28
+ #
29
+ # @param [Integer] min_size
30
+ # @return [void]
31
+ def min_length(min_size=0, &block)
32
+ return unless valid_min_length?(min_size)
33
+
34
+ instance_eval(&block) if block_given?
35
+ rescue Exception => e
36
+ validation_error("#{e.message}")
37
+ end#min_length
38
+
39
+
40
+ # Define Maximum Length of Array
41
+ #
42
+ # @param [Integer] min_size
43
+ # @return [void]
44
+ def max_length(max_size=0, &block)
45
+ return unless valid_max_length?(max_size)
46
+
47
+ instance_eval(&block) if block_given?
48
+ rescue Exception => e
49
+ validation_error("#{e.message}")
50
+ end#max_length
51
+
52
+
53
+ # Define length range of Array (inclusive)
54
+ #
55
+ # @param [Integer] min_size
56
+ # @param [Integer] max_size
57
+ # @return [void]
58
+ def length_between(min_size=0, max_size=nil, &block)
59
+ return unless valid_min_length?(min_size)
60
+
61
+ if max_size.nil?
62
+ if min_size == 1
63
+ validation_error("Consider using is_not_empty")
64
+ return
65
+ end
66
+ else
67
+ return unless valid_max_length?(max_size)
68
+
69
+ if max_size == 0 && min_size == 0
70
+ validation_error("Consider using is_empty")
71
+ return
72
+ end
73
+
74
+ unless max_size > min_size
75
+ validation_error("max_size must be greater than min_size")
76
+ return
77
+ end
78
+ end
79
+
80
+ instance_eval(&block) if block_given?
81
+ end#length_between
82
+ private
83
+
84
+ # @return [Boolean]
85
+ def valid_min_length?(min_size)
86
+ unless min_size.to_i >= 0
87
+ validation_error("min_length must be a number greater than or equal to 0")
88
+ return false
89
+ end
90
+
91
+ unless @ratifiable_object.size >= min_size.to_i
92
+ validation_error("Array length must be #{min_size} or more")
93
+ return false
94
+ end
95
+ return true
96
+ rescue Exception => e
97
+ validation_error("#{e.message}")
98
+ return false
99
+ end
100
+
101
+ # @return [Boolean]
102
+ def valid_max_length?(max_size)
103
+ unless max_size.to_i >= 0
104
+ validation_error("max_size must be a number greater than or equal to 0")
105
+ return false
106
+ end
107
+
108
+ if @ratifiable_object.size > max_size.to_i
109
+ validation_error("Array length must be less than #{max_size.to_i}")
110
+ return false
111
+ end
112
+
113
+ return true
114
+ rescue Exception => e
115
+ validation_error("#{e.message}")
116
+ return false
117
+ end
118
+ end#ArrayMethods
119
+
120
+ end
@@ -0,0 +1,108 @@
1
+ module Ratatouille
2
+
3
+ # Module used to provide Hash-specific validation methods
4
+ module HashMethods
5
+ # Runs validation in block against object for the given key.
6
+ #
7
+ # @param [String, Symbol] key
8
+ def given_key(key, &block)
9
+ if @ratifiable_object.has_key?(key) && block_given?
10
+ child_object = Ratatouille::Ratifier.new(@ratifiable_object[key], &block)
11
+ @errors[key] = child_object.errors unless child_object.valid?
12
+ end
13
+ end#given_key
14
+
15
+
16
+ # @return [void]
17
+ def is_empty(&block)
18
+ unless @ratifiable_object.empty?
19
+ validation_error("Hash is not empty")
20
+ return
21
+ end
22
+
23
+ instance_eval(&block) if block_given?
24
+ end#is_empty
25
+
26
+
27
+ # @return [void]
28
+ def is_not_empty(&block)
29
+ if @ratifiable_object.empty?
30
+ validation_error("Hash is empty")
31
+ return
32
+ end
33
+
34
+ instance_eval(&block) if block_given?
35
+ end#is_not_empty
36
+
37
+
38
+ # Provide a list of keys that must be present in the Hash to validate. Otherwise,
39
+ # an error will be added.
40
+ #
41
+ # @param [Array] args Array required keys
42
+ # @return [void]
43
+ def required_keys(*req_keys, &block)
44
+ common_keys = (@ratifiable_object.keys & req_keys)
45
+
46
+ if @ratifiable_object.empty?
47
+ validation_error("Cannot find required keys in empty hash.")
48
+ return
49
+ end
50
+
51
+ if req_keys.nil? || req_keys.empty?
52
+ validation_error("No required keys given to compare Hash against.")
53
+ return
54
+ end
55
+
56
+ unless common_keys.size == req_keys.size
57
+ (req_keys - common_keys).each do |missed|
58
+ case missed
59
+ when Symbol then validation_error("Missing :#{missed}")
60
+ when String then validation_error("Missing #{missed}")
61
+ when respond_to?(:to_s)
62
+ validation_error("Missing #{missed.to_s}")
63
+ end
64
+ end
65
+ return
66
+ end
67
+
68
+ instance_eval(&block) if block_given?
69
+ end#required_keys
70
+
71
+
72
+ # Provide a list of keys to choose from and a choice size (default 1).
73
+ # When the Hash does not contain at least 'choice_size' keys of the key
74
+ # list provided, an error will be added.
75
+ #
76
+ # @param [Integer] choice_size
77
+ # @param [Array] args
78
+ # Array of symbols and/or strings to denote the choices of keys.
79
+ # All other values are ignored.
80
+ # @return [void]
81
+ def choice_of(choice_size=1, *key_list, &block)
82
+ unless key_list.nil? || key_list.empty?
83
+ validation_error("choice_of requires a key list to choose from")
84
+ return
85
+ end
86
+
87
+ unless choice_size =~ /\d+/ && choice_size > 0
88
+ validation_error("choice_of requires a positive integer for choice size")
89
+ return
90
+ end
91
+
92
+ unless key_list.size > choice_size
93
+ validation_error("Key list size for 'choice_of' should be larger than choice size. Consider using required_keys instead.")
94
+ return
95
+ end
96
+
97
+ common_keys = (@ratifiable_object.keys & key_list)
98
+ unless common_keys.size == choice_size
99
+ choices = key_list.collect{|a| String === a || Symbol === a}
100
+ validation_error("Require #{choice_size} of the following: #{choices.join(', ')}")
101
+ return
102
+ end
103
+
104
+ instance_eval(&block) if block_given?
105
+ end#choice_of
106
+ end#HashMethods
107
+
108
+ end
@@ -0,0 +1,6 @@
1
+ # MonkeyPatch NilClass to return true for empty?
2
+ class NilClass
3
+ def empty?
4
+ return true
5
+ end
6
+ end
@@ -0,0 +1,98 @@
1
+ module Ratatouille
2
+
3
+ # Ratifier acts as a clean room in which to perform validations.
4
+ class Ratifier
5
+ attr_reader :errors
6
+ attr_reader :ratifiable_object
7
+
8
+ # A new instance of Ratifier
9
+ # @param [Hash, Array] obj Object to validate
10
+ def initialize(obj, options={}, &block)
11
+ @errors = { "/" => [] }
12
+ @ratifiable_object = obj
13
+
14
+ case obj
15
+ when Hash then extend Ratatouille::HashMethods
16
+ when Array then extend Ratatouille::ArrayMethods
17
+ end
18
+
19
+ instance_eval( &block ) if block_given?
20
+
21
+ cleanup_errors
22
+
23
+ @errors.freeze
24
+ end#initialize
25
+
26
+
27
+ # Add validation error. Useful for custom validations.
28
+ # @param [String] str
29
+ # @param [String] context
30
+ # @return [void]
31
+ def validation_error(err_in, context="/")
32
+ case err_in
33
+ when String
34
+ return if err_in.blank?
35
+ @errors[context] = [] unless @errors[context]
36
+ @errors[context] << err_in
37
+ end
38
+ rescue Exception => e
39
+ @errors["/"] << e.message
40
+ end#validation_error
41
+
42
+
43
+ # Does the object pass all validation logic?
44
+ #
45
+ # @return [Boolean]
46
+ def valid?
47
+ @errors.empty?
48
+ end#valid?
49
+
50
+
51
+ # If there are no errors in the errors hash, empty it out.
52
+ #
53
+ # @return [void]
54
+ def cleanup_errors
55
+ @errors = {} if errors_array.empty?
56
+ rescue Exception => e
57
+ @errors["/"] << e.message
58
+ end#cleanup_errors
59
+
60
+
61
+ # @param [Hash] hsh Hash to act upon.
62
+ # @return [Array]
63
+ def errors_array(hsh = @errors)
64
+ return [] unless Hash === hsh
65
+ all_errs = []
66
+
67
+ hsh.each_pair do |k,v|
68
+ pair_errs = case v
69
+ when Hash then errors_array(v)
70
+ when Array then v
71
+ else []
72
+ end
73
+
74
+ nsed_errs = pair_errs.collect do |e|
75
+ split_err = e.split("|")
76
+
77
+ ctxt, err = "", e
78
+ ctxt, err = split_err if split_err.size == 2
79
+
80
+ case k
81
+ when String
82
+ ctxt = "#{k}#{ctxt}" unless k == '/'
83
+ when Symbol
84
+ ctxt = ":#{k}#{ctxt}"
85
+ end
86
+
87
+ "/#{ctxt}|#{err}"
88
+ end
89
+
90
+ all_errs << nsed_errs
91
+ all_errs.flatten!
92
+ end
93
+
94
+ return all_errs
95
+ end#errors_array
96
+ end#Ratifier
97
+
98
+ end
@@ -0,0 +1,6 @@
1
+ # Monkey Patch String class
2
+ class String
3
+ def blank?
4
+ return self.chomp.empty?
5
+ end
6
+ end
@@ -0,0 +1,4 @@
1
+ module Ratatouille
2
+ # Gem Version
3
+ VERSION = "0.1.0"
4
+ end
@@ -0,0 +1,18 @@
1
+ require "ratatouille/version"
2
+
3
+ require "ratatouille/ratifier"
4
+ require "ratatouille/nilclass"
5
+ require "ratatouille/hash"
6
+ require "ratatouille/array"
7
+ require "ratatouille/string"
8
+
9
+ # Module to provide DSL for validation of complex Hashes
10
+ module Ratatouille
11
+
12
+ # @param [Hash, Array] obj Object to validate
13
+ # @return [Validatable::Ratifier]
14
+ def ratify(obj, &block)
15
+ Ratatouille::Ratifier.new(obj, &block)
16
+ end#ratify
17
+
18
+ end
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/ratatouille/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "ratatouille"
6
+ gem.version = Ratatouille::VERSION
7
+ gem.authors = ["Ryan Johnson"]
8
+ gem.email = ["rhino.citguy@gmail.com"]
9
+ gem.homepage = "http://github.com/CITguy/#{gem.name}"
10
+ gem.summary = %q{DSL for validating complex hashes}
11
+ gem.description = %q{DSL for validating complex hashes}
12
+
13
+ gem.rubyforge_project = 'ratatouille'
14
+
15
+ gem.add_development_dependency "rspec", ">= 2.4.0"
16
+ gem.add_development_dependency "rake"
17
+
18
+ gem.files = `git ls-files`.split($\)
19
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
20
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
21
+ gem.require_paths = ["lib"]
22
+ end
@@ -0,0 +1,111 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Ratatouille::ArrayMethods" do
4
+ [ :is_empty,
5
+ :is_not_empty,
6
+ :min_length,
7
+ :max_length,
8
+ :length_between
9
+ ].each do |m|
10
+ it "block context should respond to #{m}" do
11
+ r = nil
12
+ RatifierTest.new([]) { r = self }
13
+ r.should respond_to m
14
+ end
15
+ end
16
+
17
+ describe "is_empty" do
18
+ it "should not be valid for non-empty array" do
19
+ RatifierTest.new(['bar']){ is_empty }.should_not be_valid
20
+ end
21
+
22
+ it "should be valid for empty array" do
23
+ RatifierTest.new([]){ is_empty }.should be_valid
24
+ end
25
+ end
26
+
27
+ describe "is_not_empty" do
28
+ it "should be valid for non-empty array" do
29
+ RatifierTest.new(['bar']){ is_not_empty }.should be_valid
30
+ end
31
+
32
+ it "should not be valid for empty array" do
33
+ RatifierTest.new([]){ is_not_empty }.should_not be_valid
34
+ end
35
+ end
36
+
37
+ describe "length_between" do
38
+ describe "for empty array" do
39
+ before(:each) do
40
+ @arr = []
41
+ end
42
+
43
+ it "should be valid with 0 min length and any positive, non-zero max_length" do
44
+ RatifierTest.new(@arr) { length_between(0) }.should be_valid
45
+ RatifierTest.new(@arr) { length_between(0, 0) }.should_not be_valid
46
+ RatifierTest.new(@arr) { length_between(0, 1) }.should be_valid
47
+ end
48
+
49
+ it "should be invalid with 1 min_length and any max_length" do
50
+ RatifierTest.new(@arr) { length_between(1) }.should_not be_valid
51
+ RatifierTest.new(@arr) { length_between(1,1) }.should_not be_valid
52
+ RatifierTest.new(@arr) { length_between(1,2) }.should_not be_valid
53
+ end
54
+ end
55
+
56
+ describe "for Array with 2 elements" do
57
+ before(:each) do
58
+ @arr = ['foo', 'bar']
59
+ end
60
+
61
+ it "should be valid with 1 min_length and any max_length above 1" do
62
+ RatifierTest.new(@arr) { length_between(1,1) }.should_not be_valid
63
+ RatifierTest.new(@arr) { length_between(1,2) }.should be_valid
64
+ end
65
+
66
+ it "should be invalid with 0 min_length and any max_length less than 2" do
67
+ RatifierTest.new(@arr) { length_between(0,0) }.should_not be_valid
68
+ RatifierTest.new(@arr) { length_between(0,1) }.should_not be_valid
69
+ RatifierTest.new(@arr) { length_between(0,2) }.should be_valid
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "max_length" do
75
+ it "should be valid for proper length array with integer argument" do
76
+ RatifierTest.new([]) { max_length(1) }.should be_valid
77
+ RatifierTest.new(['foo']) { max_length(1) }.should be_valid
78
+
79
+ RatifierTest.new(['foo']) { max_length(0) }.should_not be_valid
80
+ end
81
+
82
+ it "should be invalid for non-numeric length" do
83
+ RatifierTest.new([]) { max_length({}) }.should_not be_valid
84
+ end
85
+
86
+ it "should be valid for properly transformed float values" do
87
+ RatifierTest.new(['foo']) { max_length(0.3) }.should_not be_valid
88
+ RatifierTest.new(['foo']) { max_length(1.3) }.should be_valid
89
+ RatifierTest.new(['foo', 'bar']) { max_length(1.3) }.should_not be_valid
90
+ end
91
+ end
92
+
93
+ describe "min_length" do
94
+ it "should be valid for proper length array with integer argument" do
95
+ RatifierTest.new([]) { min_length(0) }.should be_valid
96
+ RatifierTest.new([]) { min_length(1) }.should_not be_valid
97
+
98
+ RatifierTest.new(['foo']) { min_length(0) }.should be_valid
99
+ RatifierTest.new(['foo']) { min_length(1) }.should be_valid
100
+ end
101
+
102
+ it "should be invalid for non-numeric length" do
103
+ RatifierTest.new([]) { min_length({}) }.should_not be_valid
104
+ end
105
+
106
+ it "should be valid for properly transformed float values" do
107
+ RatifierTest.new([]) { min_length(0.3) }.should be_valid
108
+ RatifierTest.new([]) { min_length(1.3) }.should_not be_valid
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Ratatouille::HashMethods" do
4
+ describe "is_empty" do
5
+ it "should be invalid for non-empty Hash" do
6
+ RatifierTest.new({:bar => 'biz'}){ is_empty }.should_not be_valid
7
+ end
8
+
9
+ it "should be valid for empty Hash" do
10
+ RatifierTest.new({}){ is_empty }.should be_valid
11
+ end
12
+ end
13
+
14
+ describe "is_not_empty" do
15
+ it "should be valid for non-empty hash" do
16
+ RatifierTest.new({:bar => "biz"}){ is_not_empty }.should be_valid
17
+ end
18
+
19
+ it "should not be valid for empty hash" do
20
+ RatifierTest.new({}){ is_not_empty }.should_not be_valid
21
+ end
22
+ end
23
+
24
+ describe "choice_of" do
25
+ it "should be invalid if key list is empty" do
26
+ RatifierTest.new({}) { choice_of(1, []) }.should_not be_valid
27
+ end
28
+
29
+ it "should be invalid if choice size less than 1" do
30
+ RatifierTest.new({}) { choice_of(0, [:foo]) }.should_not be_valid
31
+ end
32
+
33
+ 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
35
+ end
36
+ end
37
+
38
+ describe "required_keys" do
39
+ it "should be valid if Has contains all required keys" do
40
+ RatifierTest.new({:foo => "foo"}) { required_keys(:foo, :bar) }.should_not be_valid
41
+ end
42
+
43
+ it "should be invalid if hash is empty and key list is not" do
44
+ RatifierTest.new({}) { required_keys(:foo) }.should_not be_valid
45
+ end
46
+
47
+ it "should be invalid if Hash does not contain ALL keys in key list" do
48
+ RatifierTest.new({:foo => "foo"}) { required_keys(:foo, :bar) }.should_not be_valid
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe NilClass do
4
+ describe "empty?" do
5
+ it "should return true" do
6
+ nil.should be_empty
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+
3
+ describe Ratatouille::Ratifier do
4
+
5
+ it "should be valid on instantiation of new object" do
6
+ e = RatifierTest.new({})
7
+ e.should be_valid
8
+ end
9
+
10
+ it "errors should contain one key within block of new instance" do
11
+ x = {}
12
+ e = RatifierTest.new({}){ x = @errors }
13
+ x.keys.size.should == 1
14
+ x.keys.should == ['/']
15
+ x['/'].should be_empty
16
+ end
17
+
18
+ describe "validation_error" do
19
+ it "should create an error when called within a Ratifier block" do
20
+ test = RatifierTest.new(Object.new) do
21
+ validation_error("some error")
22
+ end
23
+ test.errors_array.should have(1).String
24
+
25
+ test = RatifierTest.new({}) do
26
+ validation_error("some error")
27
+ validation_error("another error")
28
+ end
29
+ test.errors_array.should have(2).String
30
+ end
31
+
32
+ it "should not create an error when called with a non-string argument" do
33
+ test = RatifierTest.new({}) do
34
+ validation_error({})
35
+ end
36
+ test.errors_array.should be_empty
37
+ end
38
+
39
+ it "should add the error to the '/' context by default" do
40
+ test = RatifierTest.new({}) do
41
+ # NO ERRORS
42
+ end
43
+ test.errors['/'].should be_empty
44
+
45
+ test = RatifierTest.new({}) do
46
+ validation_error("foo")
47
+ end
48
+ test.errors['/'].should have(1).String
49
+ end
50
+
51
+ it "should add an error to an explicit context (even if it doesn't exist)" do
52
+ ctxt = "foo"
53
+ test = RatifierTest.new({}) do
54
+ # NO ERRORS
55
+ end
56
+ test.errors[ctxt].should be_nil
57
+
58
+ test = RatifierTest.new({}) do
59
+ validation_error("broken", ctxt)
60
+ end
61
+ test.errors[ctxt].should have(1).String
62
+ end
63
+ end
64
+
65
+ describe "valid?" do
66
+ it "should be true if errors is empty?" do
67
+ test = RatifierTest.new({}) do
68
+ # No Validation = Valid Object
69
+ end
70
+ test.valid?.should be_true
71
+ end
72
+ end
73
+
74
+ describe "instance variables" do
75
+ before(:each) do
76
+ @test = RatifierTest.new({})
77
+ end
78
+
79
+ describe "ratifiable_object" do
80
+ it "should raise error if modification is attempted" do
81
+ Proc.new { @test.ratifiable_object = {} }.should raise_error NoMethodError
82
+ end
83
+ end
84
+
85
+ describe "errors" do
86
+ it "should raise error if modification is attempted" do
87
+ Proc.new { @test.errors = {} }.should raise_error NoMethodError
88
+ Proc.new { @test.errors.delete("/") }.should raise_error TypeError
89
+ end
90
+
91
+ it "should be empty on valid object" do
92
+ ratifier = RatifierTest.new({:foo => "bar"}) do
93
+ # No Validations = Valid Object
94
+ end
95
+ ratifier.errors.should be_empty
96
+ end
97
+
98
+ it "should not be empty on invalid object" do
99
+ ratifier = RatifierTest.new({:foo => "bar"}) { is_empty }
100
+ ratifier.errors.should_not be_empty
101
+ end
102
+ end
103
+
104
+ describe "errors_array" do
105
+ it "should be empty on new Ratifier" do
106
+ @test.errors_array.should be_empty
107
+ end
108
+
109
+ it "should be empty on valid object" do
110
+ ratifier = RatifierTest.new({}) do
111
+ # No Validations = Valid Object
112
+ end
113
+ ratifier.errors_array.should be_empty
114
+ end
115
+
116
+ it "should have at least one String item for an invalid object" do
117
+ test = RatifierTest.new({:foo => "bar"}){ is_empty }
118
+ test.errors_array.should_not be_empty
119
+ test.errors_array.should have_at_least(1).String
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ include Ratatouille
4
+
5
+ describe Ratatouille do
6
+
7
+ describe "ratify" do
8
+ it "should return a Ratatouille::Ratifier object" do
9
+ ratify({}).should be_a Ratatouille::Ratifier
10
+ end
11
+
12
+ it "should evaluate block in context of a Ratatouille::Ratifier object" do
13
+ o = nil
14
+ ratify({}) { o = self }
15
+ o.should be_a Ratatouille::Ratifier
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,9 @@
1
+ require 'rspec'
2
+ require 'ratatouille'
3
+
4
+ RSpec.configure do |config|
5
+ config.color_enabled = true
6
+ end
7
+
8
+ class RatifierTest < Ratatouille::Ratifier
9
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ratatouille
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Ryan Johnson
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-04-14 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 31
29
+ segments:
30
+ - 2
31
+ - 4
32
+ - 0
33
+ version: 2.4.0
34
+ type: :development
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ description: DSL for validating complex hashes
51
+ email:
52
+ - rhino.citguy@gmail.com
53
+ executables: []
54
+
55
+ extensions: []
56
+
57
+ extra_rdoc_files: []
58
+
59
+ files:
60
+ - .gitignore
61
+ - .rvmrc
62
+ - .travis-yml
63
+ - .yardopts
64
+ - Gemfile
65
+ - LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - lib/ratatouille.rb
69
+ - lib/ratatouille/array.rb
70
+ - lib/ratatouille/hash.rb
71
+ - lib/ratatouille/nilclass.rb
72
+ - lib/ratatouille/ratifier.rb
73
+ - lib/ratatouille/string.rb
74
+ - lib/ratatouille/version.rb
75
+ - ratatouille.gemspec
76
+ - spec/lib/ratatouille/array_spec.rb
77
+ - spec/lib/ratatouille/hash_spec.rb
78
+ - spec/lib/ratatouille/nilclass_spec.rb
79
+ - spec/lib/ratatouille/ratifier_spec.rb
80
+ - spec/lib/ratatouille_spec.rb
81
+ - spec/spec_helper.rb
82
+ homepage: http://github.com/CITguy/ratatouille
83
+ licenses: []
84
+
85
+ post_install_message:
86
+ rdoc_options: []
87
+
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ hash: 3
96
+ segments:
97
+ - 0
98
+ version: "0"
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ hash: 3
105
+ segments:
106
+ - 0
107
+ version: "0"
108
+ requirements: []
109
+
110
+ rubyforge_project: ratatouille
111
+ rubygems_version: 1.8.10
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: DSL for validating complex hashes
115
+ test_files:
116
+ - spec/lib/ratatouille/array_spec.rb
117
+ - spec/lib/ratatouille/hash_spec.rb
118
+ - spec/lib/ratatouille/nilclass_spec.rb
119
+ - spec/lib/ratatouille/ratifier_spec.rb
120
+ - spec/lib/ratatouille_spec.rb
121
+ - spec/spec_helper.rb
122
+ has_rdoc: