kharon 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/validate.rb +15 -0
- data/lib/validator.rb +272 -0
- data/spec/lib/validator_spec.rb +437 -0
- data/spec/spec_helper.rb +14 -0
- metadata +64 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c25325fecde2ed9bb57cc60983980dba2b50f700
|
4
|
+
data.tar.gz: 46a7a17a5075d66e9552eb083d8428edb428147f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e5ed5dc943275d41234da3c1c36507968b11df77dd0403e2553b2362a6959561990231cc155b0d572f586fffffc2c1265b0a67eb623957fb4b6c8c3296bdc5b
|
7
|
+
data.tar.gz: c6a47f792e0644c6bae66269352d167c4b770f043a133e5481dacd6a8ce1eebe02390abbe7606315645bda4f2f629fbcc64c4dad7f01ea036c30b2f73a432083
|
data/lib/validate.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module Charon
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
# Validates the datas passed as parameter with a Phenix::Validator and the given instructions.
|
5
|
+
# @param [Hash] the parameters to validate with the given instructions.
|
6
|
+
# @param [Proc] the instructions to apply on the validator.
|
7
|
+
# @return [Hash] the validated and filtered datas.
|
8
|
+
def validate(datas, &block)
|
9
|
+
validator = Phenix::Validator.new(datas)
|
10
|
+
validator.instance_eval(&block)
|
11
|
+
return validator.filtered
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
end
|
data/lib/validator.rb
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
require "pry"
|
2
|
+
|
3
|
+
module Charon
|
4
|
+
# The validator uses aquarium as an AOP DSL to provide "before" and "after" joint point to its main methods.
|
5
|
+
# @author Vincent Courtois <vincent.courtois@mycar-innovations.com>
|
6
|
+
class Validator
|
7
|
+
include Aquarium::DSL
|
8
|
+
|
9
|
+
# @!attribute [r] datas
|
10
|
+
# @return The datas to filter, they shouldn't be modified to guarantee their integrity.
|
11
|
+
attr_reader :datas
|
12
|
+
|
13
|
+
# @!attribute [rw] filtered
|
14
|
+
# @return The filtered datas are the datas after they have been filtered (renamed keys for example) by the validator.
|
15
|
+
attr_accessor :filtered
|
16
|
+
|
17
|
+
# Constructor of the classe, receiving the datas to validate and filter.
|
18
|
+
# @param [Hash] datas the datas to validate in the validator.
|
19
|
+
def initialize(datas)
|
20
|
+
@datas = datas
|
21
|
+
@filtered = Hash.new
|
22
|
+
end
|
23
|
+
|
24
|
+
# @!group Public_interface
|
25
|
+
|
26
|
+
# Checks if the given key is an integer or not.
|
27
|
+
# @param [Object] key the key about which verify the type.
|
28
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
29
|
+
def integer(key, options = {})
|
30
|
+
match?(key, /\A\d+\Z/) ? store(key, ->(item){item.to_i}, options) : raise_type_error(key, "Integer")
|
31
|
+
end
|
32
|
+
|
33
|
+
# Checks if the given key is a numeric or not.
|
34
|
+
# @param [Object] key the key about which verify the type.
|
35
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
36
|
+
def numeric(key, options = {})
|
37
|
+
match?(key, /\A([+-]?\d+)([,.](\d+))?\Z/) ? store(key, ->(item){item.sub(/,/, ".").to_f}, options) : raise_type_error(key, "Numeric")
|
38
|
+
end
|
39
|
+
|
40
|
+
# Checks if the given key is a not-empty string or not.
|
41
|
+
# @param [Object] key the key about which verify the type.
|
42
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
43
|
+
def text(key, options = {})
|
44
|
+
is_typed?(key, String) ? store(key, ->(item){item.to_s}, options) : raise_type_error(key, "String")
|
45
|
+
end
|
46
|
+
|
47
|
+
# Doesn't check the type of the key and let it pass without modification.
|
48
|
+
# @param [Object] key the key about which verify the type.
|
49
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
50
|
+
def any(key, options = {})
|
51
|
+
store(key, ->(item){item}, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Checks if the given key is a datetime or not.
|
55
|
+
# @param [Object] key the key about which verify the type.
|
56
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
57
|
+
def datetime(key, options = {})
|
58
|
+
begin; store(key, ->(item){DateTime.parse(item.to_s)} , options); rescue; raise_type_error(key, "DateTime"); end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Checks if the given key is a date or not.
|
62
|
+
# @param [Object] key the key about which verify the type.
|
63
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
64
|
+
def date(key, options = {})
|
65
|
+
begin; store(key, ->(item){Date.parse(item.to_s)}, options); rescue; raise_type_error(key, "Date"); end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Checks if the given key is an array or not.
|
69
|
+
# @param [Object] key the key about which verify the type.
|
70
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
71
|
+
def array(key, options = {})
|
72
|
+
is_typed?(key, Array) ? store(key, ->(item){item.to_a}, options) : raise_type_error(key, "Array")
|
73
|
+
end
|
74
|
+
|
75
|
+
# Checks if the given key is a hash or not.
|
76
|
+
# @param [Object] key the key about which verify the type.
|
77
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
78
|
+
def hash(key, options = {})
|
79
|
+
is_typed?(key, Hash) ? store(key, ->(item){item.to_h}, options) : raise_type_error(key, "Hash")
|
80
|
+
end
|
81
|
+
|
82
|
+
# Checks if the given key is a boolean or not.
|
83
|
+
# @param [Object] key the key about which verify the type.
|
84
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
85
|
+
def boolean(key, options = {})
|
86
|
+
match?(key, /(true)|(false)/) ? store(key, ->(item){to_boolean(item)}, options) : raise_type_error(key, "Numeric")
|
87
|
+
end
|
88
|
+
|
89
|
+
# Checks if the given key is a SSID for a MongoDB object or not.
|
90
|
+
# @param [Object] key the key about which verify the type.
|
91
|
+
# @param [Hash] options a hash of options passed to this method (see documentation to know which options pass).
|
92
|
+
def ssid(key, options = {})
|
93
|
+
match?(key, /^[0-9a-fA-F]{24}$/) ? store(key, ->(item){BSON::ObjectId.from_string(item.to_s)}, options) : raise_type_error(key, "Moped::BSON::ObjectId")
|
94
|
+
end
|
95
|
+
|
96
|
+
# @!endgroup Public_interface
|
97
|
+
|
98
|
+
# @!group Advices
|
99
|
+
|
100
|
+
# Before advice checking for "required", "dependency", and "dependencies" options.
|
101
|
+
before calls_to: self.instance_methods(false), exclude_methods: [:initialize, :new, :required, :dependency, :dependencies] do |joint_point, validator, *args|
|
102
|
+
unless !defined?(args[1]) or args[1].nil? or args[1].empty?
|
103
|
+
validator.required(args[0]) if (args[1].has_key?(:required) and args[1][:required] == true)
|
104
|
+
if args[1].has_key?(:dependencies)
|
105
|
+
validator.dependencies(args[0], args[1][:dependencies])
|
106
|
+
elsif args[1].has_key?(:dependency)
|
107
|
+
validator.dependency(args[0], args[1][:dependency])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# After advice checking in numerics if limits are given, and if there are, if they are respected.
|
113
|
+
after calls_to: [:integer, :numeric] do |joint_point, validator, *args|
|
114
|
+
unless !defined?(args[1]) or args[1].nil? or args[1].empty?
|
115
|
+
if(args[1].has_key?(:between))
|
116
|
+
validator.check_min_value(args[0], args[1][:between][0])
|
117
|
+
validator.check_max_value(args[0], args[1][:between][1])
|
118
|
+
else
|
119
|
+
validator.check_min_value(args[0], args[1][:min]) if(args[1].has_key?(:min))
|
120
|
+
validator.check_max_value(args[0], args[1][:max]) if(args[1].has_key?(:max))
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# After advcie for all methods, checking the "in" and "equals" options.
|
126
|
+
after calls_to: self.instance_methods(false), exclude_methods: [:initialize, :new, :required, :dependency, :dependencies] do |joint_point, validator, *args|
|
127
|
+
unless !defined?(args[1]) or args[1].nil? or args[1].empty?
|
128
|
+
if(args[1].has_key?(:in))
|
129
|
+
validator.in_array?(args[0], args[1][:in])
|
130
|
+
elsif(args[1].has_key?(:equals))
|
131
|
+
validator.equals_to?(args[0], args[1][:equals])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# After advice for hashes, checking the "has_keys" and "contains" options.
|
137
|
+
after calls_to: [:hash] do |joint_point, validator, *args|
|
138
|
+
unless !defined?(args[1]) or args[1].nil? or args[1].empty?
|
139
|
+
validator.has_keys?(args[0], args[1][:has_keys]) if(args[1].has_key?(:has_keys))
|
140
|
+
validator.contains?(args[0], validator.datas[args[0]].values, args[1][:contains]) if(args[1].has_key?(:contains))
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# After advice for arrays, checking the "contains" option.
|
145
|
+
after calls_to: [:array] do |joint_point, validator, *args|
|
146
|
+
unless !defined?(args[1]) or args[1].nil? or args[1].empty?
|
147
|
+
validator.contains?(args[0], validator.datas[args[0]], args[1][:contains]) if(args[1].has_key?(:contains))
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# @!endgroup Advices
|
152
|
+
|
153
|
+
# Checks if a required key is present in provided datas.
|
154
|
+
# @param [Object] key the key of which check the presence.
|
155
|
+
# @raise [ArgumentError] if the key is not present.
|
156
|
+
def required(key)
|
157
|
+
raise ArgumentError.new("The key #{key} is required and not provided.") unless @datas.has_key?(key)
|
158
|
+
end
|
159
|
+
|
160
|
+
# Syntaxic sugar used to chack several dependencies at once.
|
161
|
+
# @param [Object] key the key needing another key to properly work.
|
162
|
+
# @param [Object] dependency the key needed by another key for it to properly work.
|
163
|
+
# @raise [ArgumentError] if the required dependencies are not present.
|
164
|
+
# @see self#check_dependency the associated singular method.
|
165
|
+
def dependencies(key, dependencies)
|
166
|
+
dependencies.each { |dependency| dependency(key, dependency) }
|
167
|
+
end
|
168
|
+
|
169
|
+
# Checks if a dependency is respected. A dependency is a key openly needed by another key.
|
170
|
+
# @param [Object] key the key needing another key to properly work.
|
171
|
+
# @param [Object] dependency the key needed by another key for it to properly work.
|
172
|
+
# @raise [ArgumentError] if the required dependency is not present.
|
173
|
+
def dependency(key, dependency)
|
174
|
+
raise ArgumentError.new("The key #{key} needs the key #{dependency} but it was not provided.") unless @datas.has_key?(dependency)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Checks if the value associated with the given key is greater than the given minimum value.
|
178
|
+
# @param [Object] key the key associated with the value to compare.
|
179
|
+
# @param [Numeric] min_value the required minimum value.
|
180
|
+
# @raise [ArgumentError] if the initial value is strictly lesser than the minimum value.
|
181
|
+
def check_min_value(key, min_value)
|
182
|
+
raise ArgumentError.new("The key #{key} was supposed to be greater or equal than #{min_value}, the value was #{datas[key]}") unless datas[key].to_i >= min_value.to_i
|
183
|
+
end
|
184
|
+
|
185
|
+
# Checks if the value associated with the given key is lesser than the given maximum value.
|
186
|
+
# @param [Object] key the key associated with the value to compare.
|
187
|
+
# @param [Numeric] max_value the required maximum value.
|
188
|
+
# @raise [ArgumentError] if the initial value is strictly greater than the minimum value.
|
189
|
+
def check_max_value(key, max_value)
|
190
|
+
raise ArgumentError.new("The key #{key} was supposed to be lesser or equal than #{max_value}, the value was #{datas[key]}") unless datas[key].to_i <= max_value.to_i
|
191
|
+
end
|
192
|
+
|
193
|
+
# Checks if the value associated with the given key is included in the given array of values.
|
194
|
+
# @param [Object] key the key associated with the value to check.
|
195
|
+
# @param [Array] values the values in which the initial value should be contained.
|
196
|
+
# @raise [ArgumentError] if the initial value is not included in the given possible values.
|
197
|
+
def in_array?(key, values)
|
198
|
+
raise ArgumentError.new("The key #{key} was supposed to be in [#{values.join(", ")}], the value was #{datas[key]}") unless (values.empty? or values.include?(datas[key]))
|
199
|
+
end
|
200
|
+
|
201
|
+
# Checks if the value associated with the given key is equal to the given value.
|
202
|
+
# @param [Object] key the key associated with the value to check.
|
203
|
+
# @param [Object] value the values with which the initial value should be compared.
|
204
|
+
# @raise [ArgumentError] if the initial value is not equal to the given value.
|
205
|
+
def equals_to?(key, value)
|
206
|
+
raise ArgumentError.new("The key #{key} was supposed to equal than #{value}, the value was #{datas[key]}") unless datas[key] == value
|
207
|
+
end
|
208
|
+
|
209
|
+
# Checks if the value associated with the given key has the given required keys.
|
210
|
+
# @param [Object] key the key associated with the value to check.
|
211
|
+
# @param [Array] required_keys the keys that the initial Hash typed value should contain.
|
212
|
+
# @raise [ArgumentError] if the initial value has not each and every one of the given keys.
|
213
|
+
def has_keys?(key, required_keys)
|
214
|
+
raise ArgumentError.new("The key #{key} was supposed to contains keys [#{required_keys.join(", ")}]") if (datas[key].keys & required_keys) != required_keys
|
215
|
+
end
|
216
|
+
|
217
|
+
# Checks if the value associated with the given key has the given required values.
|
218
|
+
# @param [Object] key the key associated with the value to check.
|
219
|
+
# @param [Array] required_keys the values that the initial Enumerable typed value should contain.
|
220
|
+
# @raise [ArgumentError] if the initial value has not each and every one of the given values.
|
221
|
+
def contains?(key, values, required_values)
|
222
|
+
raise ArgumentError.new("The key #{key} was supposed to contains values [#{required_values.join(", ")}]") if (values & required_values) != required_values
|
223
|
+
end
|
224
|
+
|
225
|
+
private
|
226
|
+
|
227
|
+
# Check if the value associated with the given key matches the given regular expression.
|
228
|
+
# @param [Object] key the key of the value to compare with the given regexp.
|
229
|
+
# @param [Regexp] regex the regex with which match the initial value.
|
230
|
+
# @return [Boolean] true if the initial value matches the regex, false if not.
|
231
|
+
def match?(key, regex)
|
232
|
+
return (!datas.has_key?(key) or datas[key].to_s.match(regex))
|
233
|
+
end
|
234
|
+
|
235
|
+
# Check if the value associated with the given key is typed with the given type, or with a type inheriting from it.
|
236
|
+
# @param [Object] key the key of the value to check the type from.
|
237
|
+
# @param [Class] type the type with which check the initial value.
|
238
|
+
# @return [Boolean] true if the initial value is from the right type, false if not.
|
239
|
+
def is_typed?(key, type)
|
240
|
+
return (!datas.has_key?(key) or datas[key].kind_of?(type))
|
241
|
+
end
|
242
|
+
|
243
|
+
# Tries to store the associated key in the filtered key, transforming it with the given process.
|
244
|
+
# @param [Object] key the key associated with the value to store in the filtered datas.
|
245
|
+
# @param [Proc] process a process (lambda) to execute on the initial value. Must contain strictly one argument.
|
246
|
+
# @param [Hash] options the options applied to the initial value. Only the option "rename" is checked and executed here.
|
247
|
+
def store(key, process, options = {})
|
248
|
+
unless (options.has_key?(:extract) and options[:extract] == false)
|
249
|
+
if datas.has_key?(key)
|
250
|
+
value = ((options.has_key?(:cast) and options[:cast] == false) ? datas[key] : process.call(datas[key]))
|
251
|
+
options.has_key?(:rename) ? (@filtered[options[:rename]] = value) : (@filtered[key] = value)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# Raises a type error with a generic message.
|
257
|
+
# @param [Object] key the key associated from the value triggering the error.
|
258
|
+
# @param [Class] type the expected type, not respected by the initial value.
|
259
|
+
# @raise [ArgumentError] the chosen type error.
|
260
|
+
def raise_type_error(key, type)
|
261
|
+
raise ArgumentError.new("The key {key} was supposed to be an instance of #{type}, #{key.class} found.")
|
262
|
+
end
|
263
|
+
|
264
|
+
# Transforms a given value in a boolean.
|
265
|
+
# @param [Object] value the value to transform into a boolean.
|
266
|
+
# @return [Boolean] true if the value was true, 1 or yes, false if not.
|
267
|
+
def to_boolean(value)
|
268
|
+
["true", "1", "yes"].include?(value.to_s) ? true : false
|
269
|
+
end
|
270
|
+
|
271
|
+
end
|
272
|
+
end
|
@@ -0,0 +1,437 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require './lib/validator.rb'
|
3
|
+
|
4
|
+
shared_examples "options" do |process|
|
5
|
+
context ":rename" do
|
6
|
+
it "correctly renames a key when the value is valid" do
|
7
|
+
validator = Charon::Validator.new(valid_datas)
|
8
|
+
validator.send(process, valid_datas.keys.first, rename: :another_name)
|
9
|
+
expect(validator.filtered[:another_name]).to eq(valid_filtered[valid_datas.keys.first])
|
10
|
+
end
|
11
|
+
|
12
|
+
it "correctly doesn't rename a key when the value is invalid" do
|
13
|
+
validator = Charon::Validator.new(invalid_datas)
|
14
|
+
expect(->{validator.send(process, invalid_datas.keys.first, rename: :another_name)}).to raise_error(ArgumentError)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context ":dependency" do
|
19
|
+
it "succeeds when a dependency is given as a key and respected" do
|
20
|
+
validator = Charon::Validator.new(valid_datas.merge({dep: "anything"}))
|
21
|
+
validator.send(process, valid_datas.keys.first, dependency: :dep)
|
22
|
+
expect(validator.filtered).to eq(valid_filtered)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "fails when a dependency is not respected" do
|
26
|
+
validator = Charon::Validator.new(valid_datas)
|
27
|
+
expect(->{validator.send(process, valid_datas.keys.first, dependency: :another_key_not_existing)}).to raise_error(ArgumentError)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context ":dependencies" do
|
32
|
+
it "succeeds when dependencies are given as an array and respected" do
|
33
|
+
validator = Charon::Validator.new(valid_datas.merge({dep1: "something", dep2: "something else"}))
|
34
|
+
validator.send(process, valid_datas.keys.first, dependencies: [:dep1, :dep2])
|
35
|
+
expect(validator.filtered).to eq(valid_filtered)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "fails when one of the dependencies is not respected" do
|
39
|
+
validator = Charon::Validator.new(valid_datas.merge({dep1: "anything"}))
|
40
|
+
expect(->{validator.send(process, valid_datas.keys.first, dependencies: [:dep1, :dep2])}).to raise_error(ArgumentError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context ":required" do
|
45
|
+
it "succeeds when a not required key is not given, but filters nothing" do
|
46
|
+
validator = Charon::Validator.new(valid_datas)
|
47
|
+
validator.send(process, :not_required_key)
|
48
|
+
expect(validator.filtered).to be_empty
|
49
|
+
end
|
50
|
+
|
51
|
+
it "suceeds when a key has a required option to false, and is not given, but filters nothing" do
|
52
|
+
validator = Charon::Validator.new(valid_datas)
|
53
|
+
validator.send(process, :not_in_hash, required: false)
|
54
|
+
expect(validator.filtered).to be_empty
|
55
|
+
end
|
56
|
+
|
57
|
+
it "fails when a required key is not given" do
|
58
|
+
validator = Charon::Validator.new(valid_datas)
|
59
|
+
expect(->{validator.send(process, :not_in_hash, required: true)}).to raise_error(ArgumentError)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context ":in" do
|
64
|
+
it "succeeds when the value is effectively in the possible values" do
|
65
|
+
validator = Charon::Validator.new(valid_datas)
|
66
|
+
validator.send(process, valid_datas.keys.first, :in => [valid_datas[valid_datas.keys.first], "another random data"])
|
67
|
+
expect(validator.filtered).to eq(valid_filtered)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "succeeds if there are no values" do
|
71
|
+
validator = Charon::Validator.new(valid_datas)
|
72
|
+
validator.send(process, valid_datas.keys.first, :in => [])
|
73
|
+
expect(validator.filtered).to eq(valid_filtered)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "fails if the value is not in the possible values" do
|
77
|
+
validator = Charon::Validator.new(valid_datas)
|
78
|
+
expect(->{validator.send(process, valid_datas.keys.first, :in => ["anything but the value", "another impossible thing"])}).to raise_error(ArgumentError)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context ":equals" do
|
83
|
+
it "succeeds when the value is equal to the given value" do
|
84
|
+
validator = Charon::Validator.new(valid_datas)
|
85
|
+
validator.send(process, valid_datas.keys.first, :equals => valid_datas[valid_datas.keys.first])
|
86
|
+
expect(validator.filtered).to eq(valid_filtered)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "fails if the value is not equal to the given value" do
|
90
|
+
validator = Charon::Validator.new(valid_datas)
|
91
|
+
expect(->{validator.send(process, valid_datas.keys.first, :equals => "anything but the given value")}).to raise_error(ArgumentError)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context ":extract" do
|
96
|
+
it "etracts the data when given at true" do
|
97
|
+
validator = Charon::Validator.new(valid_datas)
|
98
|
+
validator.send(process, valid_datas.keys.first, :extract => false)
|
99
|
+
expect(validator.filtered).to eq({})
|
100
|
+
end
|
101
|
+
|
102
|
+
it "doesn't extract the data when given at false" do
|
103
|
+
validator = Charon::Validator.new(valid_datas)
|
104
|
+
validator.send(process, valid_datas.keys.first, :extract => true)
|
105
|
+
expect(validator.filtered).to eq(valid_filtered)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context ":cast" do
|
110
|
+
it "casts the data when given at true" do
|
111
|
+
validator = Charon::Validator.new(valid_datas)
|
112
|
+
validator.send(process, valid_datas.keys.first, :cast => true)
|
113
|
+
expect(validator.filtered).to eq(valid_filtered)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "doesn't cast the data when given at false" do
|
117
|
+
validator = Charon::Validator.new(valid_datas)
|
118
|
+
validator.send(process, valid_datas.keys.first, :cast => false)
|
119
|
+
expect(validator.filtered).to eq(valid_datas)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
shared_examples "type checker" do |type, process|
|
125
|
+
it "succeeds when given an instance of #{type}" do
|
126
|
+
validator = Charon::Validator.new(valid_datas)
|
127
|
+
validator.send(process, valid_datas.keys.first)
|
128
|
+
expect(validator.filtered).to eq(valid_filtered)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "fails when given something else than an instance of #{type}" do
|
132
|
+
validator = Charon::Validator.new(invalid_datas)
|
133
|
+
expect(->{validator.send(process, invalid_datas.keys.first)}).to raise_error(ArgumentError)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
shared_examples "min/max checker" do |process, key, transformation|
|
138
|
+
let(:value) { valid_datas[key].send(transformation) }
|
139
|
+
let(:validator) { Charon::Validator.new(valid_datas) }
|
140
|
+
|
141
|
+
context ":min" do
|
142
|
+
it "succeeds when a min option is given, and the value is strictly greater than it" do
|
143
|
+
validator.send(process, key, {min: value-1})
|
144
|
+
expect(validator.filtered).to eq(valid_filtered)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "succeeds when a min option is given, and the value is equal to it" do
|
148
|
+
validator.send(process, key, {min: value})
|
149
|
+
expect(validator.filtered).to eq(valid_filtered)
|
150
|
+
end
|
151
|
+
|
152
|
+
it "fails when a min option is given, but not respected" do
|
153
|
+
expect(->{validator.send(process, key, {min: value+1})}).to raise_error(ArgumentError)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context ":max" do
|
158
|
+
it "succeeds when a max option is given, and the value is strictly lesser than it" do
|
159
|
+
validator.send(process, key, {max: value+1})
|
160
|
+
expect(validator.filtered).to eq(valid_filtered)
|
161
|
+
end
|
162
|
+
|
163
|
+
it "succeeds when a max option is given, and the value is equal to it" do
|
164
|
+
validator.send(process, key, {max: value})
|
165
|
+
expect(validator.filtered).to eq(valid_filtered)
|
166
|
+
end
|
167
|
+
|
168
|
+
it "fails when a max option is given, but not respected" do
|
169
|
+
expect(->{validator.send(process, key, {max: value-1})}).to raise_error(ArgumentError)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
context ":between" do
|
174
|
+
|
175
|
+
it "succeeds when a between option is given, and respected" do
|
176
|
+
validator.send(process, key, {between: [value-1, value+1]})
|
177
|
+
expect(validator.filtered).to eq(valid_filtered)
|
178
|
+
end
|
179
|
+
|
180
|
+
it "fails when a max between option is given, but the value is strictly lesser" do
|
181
|
+
expect(->{validator.send(process, key, {between: [value+1, value+2]})}).to raise_error(ArgumentError)
|
182
|
+
end
|
183
|
+
|
184
|
+
it "fails when a max between option is given, but the value is strictly greater" do
|
185
|
+
expect(->{validator.send(process, key, {between: [value-2, value-1]})}).to raise_error(ArgumentError)
|
186
|
+
end
|
187
|
+
|
188
|
+
it "fails when a max between option is given, but the value is equal to the inferior limit" do
|
189
|
+
validator.send(process, key, {between: [value, value+1]})
|
190
|
+
expect(validator.filtered).to eq(valid_filtered)
|
191
|
+
end
|
192
|
+
|
193
|
+
it "fails when a max between option is given, but the value is equal to the superio limit" do
|
194
|
+
validator.send(process, key, {between: [value-1, value]})
|
195
|
+
expect(validator.filtered).to eq(valid_filtered)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
shared_examples "contains option" do |process, key|
|
201
|
+
context ":contains" do
|
202
|
+
it "succeeds if all values are contained" do
|
203
|
+
validator = Charon::Validator.new(valid_datas)
|
204
|
+
validator.send(process, key, contains: ["val1", "val2"])
|
205
|
+
expect(validator.filtered).to eq(valid_filtered)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "fails if only some values are contained" do
|
209
|
+
validator = Charon::Validator.new(valid_datas)
|
210
|
+
expect(->{validator.send(process, key, contains: ["val1", "val3"])}).to raise_error(ArgumentError)
|
211
|
+
end
|
212
|
+
|
213
|
+
it "fails if none of the values are contained" do
|
214
|
+
validator = Charon::Validator.new(valid_datas)
|
215
|
+
expect(->{validator.send(process, key, contains: ["val3", "val4"])}).to raise_error(ArgumentError)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
describe "Validator" do
|
221
|
+
context "integer" do
|
222
|
+
let(:valid_datas) { {is_an_integer: "1000"} }
|
223
|
+
let(:valid_filtered) { {is_an_integer: 1000} }
|
224
|
+
let(:invalid_datas) { {is_not_an_integer: "something else"} }
|
225
|
+
|
226
|
+
it "succeeds when given an integer" do
|
227
|
+
validator = Charon::Validator.new(valid_datas)
|
228
|
+
validator.integer(:is_an_integer)
|
229
|
+
expect(validator.filtered).to eq(valid_filtered)
|
230
|
+
end
|
231
|
+
|
232
|
+
it "fails when given a float" do
|
233
|
+
validator = Charon::Validator.new({is_not_an_integer: 1000.5})
|
234
|
+
expect(->{validator.integer(:is_not_an_integer)}).to raise_error(ArgumentError)
|
235
|
+
end
|
236
|
+
|
237
|
+
it "fails when not given a numeric" do
|
238
|
+
validator = Charon::Validator.new(invalid_datas)
|
239
|
+
expect(->{validator.integer(:is_not_an_integer)}).to raise_error(ArgumentError)
|
240
|
+
end
|
241
|
+
|
242
|
+
context "options" do
|
243
|
+
include_examples "options", :integer
|
244
|
+
include_examples "min/max checker", :integer, :is_an_integer, :to_i
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
context "numeric" do
|
249
|
+
let(:valid_datas) { {is_a_double: "1000.5"} }
|
250
|
+
let(:valid_filtered) { {is_a_double: 1000.5} }
|
251
|
+
let(:invalid_datas) { {is_not_a_numeric: "something else"} }
|
252
|
+
|
253
|
+
it "succeeds when given an integer" do
|
254
|
+
validator = Charon::Validator.new({is_an_integer: "1000"})
|
255
|
+
validator.numeric(:is_an_integer)
|
256
|
+
expect(validator.filtered).to eq({is_an_integer: 1000})
|
257
|
+
end
|
258
|
+
|
259
|
+
it "succeeds when given an decimal number with a dot" do
|
260
|
+
validator = Charon::Validator.new(valid_datas)
|
261
|
+
validator.numeric(:is_a_double)
|
262
|
+
expect(validator.filtered).to eq(valid_filtered)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "succeeds when given an decimal number with a comma" do
|
266
|
+
validator = Charon::Validator.new({is_a_double: "1000,5"})
|
267
|
+
validator.numeric(:is_a_double)
|
268
|
+
expect(validator.filtered).to eq(valid_filtered)
|
269
|
+
end
|
270
|
+
|
271
|
+
it "fails when not given a numeric" do
|
272
|
+
validator = Charon::Validator.new(invalid_datas)
|
273
|
+
expect(->{validator.integer(:is_not_a_numeric)}).to raise_error(ArgumentError)
|
274
|
+
end
|
275
|
+
|
276
|
+
context "options" do
|
277
|
+
include_examples "options", :numeric
|
278
|
+
include_examples "min/max checker", :numeric, :is_a_double, :to_f
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
context "text" do
|
283
|
+
let(:valid_datas) { {is_a_string: "something"} }
|
284
|
+
let(:valid_filtered) { {is_a_string: "something"} }
|
285
|
+
let(:invalid_datas) { {is_not_a_string: 1000} }
|
286
|
+
|
287
|
+
include_examples "type checker", "String", :text
|
288
|
+
|
289
|
+
context "options" do
|
290
|
+
include_examples "options", :text
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
context "datetime" do
|
295
|
+
let(:date_time) { DateTime.new }
|
296
|
+
let(:valid_datas) { {is_a_datetime: date_time.to_s} }
|
297
|
+
let(:valid_filtered) { {is_a_datetime: date_time} }
|
298
|
+
let(:invalid_datas) { {is_not_a_datetime: "something else"} }
|
299
|
+
|
300
|
+
it "succeeds when given a valid datetime as a string" do
|
301
|
+
validator = Charon::Validator.new(valid_datas)
|
302
|
+
validator.datetime(:is_a_datetime)
|
303
|
+
expect(validator.filtered).to eq(valid_filtered)
|
304
|
+
end
|
305
|
+
|
306
|
+
it "succeeds when given a valid datetime as a DateTime Object" do
|
307
|
+
validator = Charon::Validator.new(valid_filtered)
|
308
|
+
validator.datetime(:is_a_datetime)
|
309
|
+
expect(validator.filtered).to eq(valid_filtered)
|
310
|
+
end
|
311
|
+
|
312
|
+
it "fails when given something else than a valid datetime" do
|
313
|
+
validator = Charon::Validator.new(invalid_datas)
|
314
|
+
expect(->{validator.datetime(:is_not_a_datetime)}).to raise_error(ArgumentError)
|
315
|
+
end
|
316
|
+
|
317
|
+
context "options" do
|
318
|
+
include_examples "options", :datetime
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
context "date" do
|
323
|
+
let(:date) { Date.new }
|
324
|
+
let(:valid_datas) { {is_a_date: date.to_s} }
|
325
|
+
let(:valid_filtered) { {is_a_date: date} }
|
326
|
+
let(:invalid_datas) { {is_not_a_date: "something else"} }
|
327
|
+
|
328
|
+
it "succeeds when given a valid date as a string" do
|
329
|
+
validator = Charon::Validator.new(valid_datas)
|
330
|
+
validator.date(:is_a_date)
|
331
|
+
expect(validator.filtered).to eq(valid_filtered)
|
332
|
+
end
|
333
|
+
|
334
|
+
it "succeeds when given a valid date as a Date Object" do
|
335
|
+
validator = Charon::Validator.new(valid_filtered)
|
336
|
+
validator.date(:is_a_date)
|
337
|
+
expect(validator.filtered).to eq(valid_filtered)
|
338
|
+
end
|
339
|
+
|
340
|
+
it "fails when given something else than a valid date" do
|
341
|
+
validator = Charon::Validator.new(invalid_datas)
|
342
|
+
expect(->{validator.date(:is_not_a_date)}).to raise_error(ArgumentError)
|
343
|
+
end
|
344
|
+
|
345
|
+
context "options" do
|
346
|
+
include_examples "options", :date
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
context "array" do
|
351
|
+
let(:valid_datas) { {is_an_array: ["val1", "val2"]} }
|
352
|
+
let(:valid_filtered) { {is_an_array: ["val1", "val2"]} }
|
353
|
+
let(:invalid_datas) { {is_not_an_array: 1000} }
|
354
|
+
|
355
|
+
include_examples "type checker", "Array", :array
|
356
|
+
|
357
|
+
context "options" do
|
358
|
+
include_examples "options", :array
|
359
|
+
include_examples "contains option", :array, :is_an_array
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
context "hash" do
|
364
|
+
let(:valid_datas) { {is_a_hash: {key1: "val1", key2: "val2"}} }
|
365
|
+
let(:valid_filtered) { {is_a_hash: {key1: "val1", key2: "val2"}} }
|
366
|
+
let(:invalid_datas) { {is_not_a_hash: 1000} }
|
367
|
+
|
368
|
+
include_examples "type checker", "Hash", :hash
|
369
|
+
|
370
|
+
context "options" do
|
371
|
+
include_examples "options", :hash
|
372
|
+
include_examples "contains option", :hash, :is_a_hash
|
373
|
+
|
374
|
+
context ":has_keys" do
|
375
|
+
it "succeeds if all keys are contained in the hash" do
|
376
|
+
validator = Charon::Validator.new(valid_datas)
|
377
|
+
validator.hash(:is_a_hash, has_keys: [:key1, :key2])
|
378
|
+
expect(validator.filtered).to eq(valid_filtered)
|
379
|
+
end
|
380
|
+
|
381
|
+
it "fails if not all keys are given in the hash" do
|
382
|
+
validator = Charon::Validator.new(valid_datas)
|
383
|
+
expect(->{validator.hash(:is_a_hash, has_keys: [:key1, :key3])}).to raise_error(ArgumentError)
|
384
|
+
end
|
385
|
+
|
386
|
+
it "fails if no keys are contained in the hash" do
|
387
|
+
validator = Charon::Validator.new(valid_datas)
|
388
|
+
expect(->{validator.hash(:is_a_hash, has_keys: [:key3, :key4])}).to raise_error(ArgumentError)
|
389
|
+
end
|
390
|
+
end
|
391
|
+
end
|
392
|
+
end
|
393
|
+
|
394
|
+
context "boolean" do
|
395
|
+
let(:valid_datas) { {is_a_boolean: "true"} }
|
396
|
+
let(:valid_filtered) { {is_a_boolean: true} }
|
397
|
+
let(:invalid_datas) { {is_not_a_boolean: "anything else"} }
|
398
|
+
|
399
|
+
it "succeeds when given a boolean" do
|
400
|
+
validator = Charon::Validator.new(valid_datas)
|
401
|
+
validator.boolean(:is_a_boolean)
|
402
|
+
expect(validator.filtered).to eq(valid_filtered)
|
403
|
+
end
|
404
|
+
|
405
|
+
it "fails when not given a boolean" do
|
406
|
+
validator = Charon::Validator.new(invalid_datas)
|
407
|
+
expect(->{validator.boolean(:is_not_a_boolean)}).to raise_error(ArgumentError)
|
408
|
+
end
|
409
|
+
|
410
|
+
context "options" do
|
411
|
+
include_examples "options", :boolean
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
context "ssid" do
|
416
|
+
let(:valid_ssid) { BSON::ObjectId.new }
|
417
|
+
let(:valid_datas) { {is_a_ssid: valid_ssid.to_s} }
|
418
|
+
let(:valid_filtered) { {is_a_ssid: valid_ssid} }
|
419
|
+
let(:invalid_datas) { {is_not_a_ssid: "anything else"} }
|
420
|
+
|
421
|
+
it "succeeds when given a valid SSID" do
|
422
|
+
validator = Charon::Validator.new(valid_datas)
|
423
|
+
validator.ssid(:is_a_ssid)
|
424
|
+
expect(validator.filtered).to eq(valid_filtered)
|
425
|
+
end
|
426
|
+
|
427
|
+
it "fails when not given a SSID" do
|
428
|
+
validator = Charon::Validator.new(invalid_datas)
|
429
|
+
expect(->{validator.ssid(:is_not_a_ssid)}).to raise_error(ArgumentError)
|
430
|
+
end
|
431
|
+
|
432
|
+
context "options" do
|
433
|
+
include_examples "options", :ssid
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# Sets the environment variable to test before loading all the files.
|
2
|
+
ENV['RACK_ENV'] = 'test'
|
3
|
+
|
4
|
+
require 'rack/test'
|
5
|
+
require 'mocha/api'
|
6
|
+
require 'aquarium'
|
7
|
+
require 'moped'
|
8
|
+
|
9
|
+
module RSpec
|
10
|
+
configure do |configuration|
|
11
|
+
# Configuration rspec to use mocha as mocking API.
|
12
|
+
configuration.mock_with :mocha
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kharon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Vincent Courtois
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: aquarium
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.5.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.5.1
|
27
|
+
description: Charon let you pass or not pass depending if you meet the criterias for
|
28
|
+
this... Or not.
|
29
|
+
email: vincent.courtois@mycar-innovations.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/validator.rb
|
35
|
+
- lib/validate.rb
|
36
|
+
- spec/spec_helper.rb
|
37
|
+
- spec/lib/validator_spec.rb
|
38
|
+
homepage: https://rubygems.org/gems/kharon
|
39
|
+
licenses:
|
40
|
+
- Apache License 2
|
41
|
+
metadata: {}
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.1.0
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
requirements: []
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 2.1.11
|
59
|
+
signing_key:
|
60
|
+
specification_version: 4
|
61
|
+
summary: Ruby Hash validator
|
62
|
+
test_files:
|
63
|
+
- spec/spec_helper.rb
|
64
|
+
- spec/lib/validator_spec.rb
|