kharon 0.0.2
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.
- 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
|