saphyr 0.4.0.beta
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/CHANGELOG +7 -0
- data/LICENSE +21 -0
- data/lib/saphyr/asserts/base_assert.rb +58 -0
- data/lib/saphyr/asserts/error_constants.rb +28 -0
- data/lib/saphyr/asserts/numeric_assert.rb +66 -0
- data/lib/saphyr/asserts/size_assert.rb +54 -0
- data/lib/saphyr/asserts/string_assert.rb +21 -0
- data/lib/saphyr/asserts.rb +15 -0
- data/lib/saphyr/engine.rb +220 -0
- data/lib/saphyr/fields/array_field.rb +80 -0
- data/lib/saphyr/fields/boolean_field.rb +20 -0
- data/lib/saphyr/fields/field_base.rb +235 -0
- data/lib/saphyr/fields/float_field.rb +49 -0
- data/lib/saphyr/fields/integer_field.rb +49 -0
- data/lib/saphyr/fields/schema_field.rb +26 -0
- data/lib/saphyr/fields/string_field.rb +41 -0
- data/lib/saphyr/fields.rb +13 -0
- data/lib/saphyr/helpers/format.rb +106 -0
- data/lib/saphyr/helpers.rb +7 -0
- data/lib/saphyr/schema.rb +73 -0
- data/lib/saphyr/validator.rb +129 -0
- data/lib/saphyr/version.rb +5 -0
- data/lib/saphyr.rb +160 -0
- data/rdoc/01_Define_Schema.md +133 -0
- data/rdoc/02_Field_Types.md +210 -0
- data/rdoc/03_Strict_Mode.md +80 -0
- metadata +106 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
module Saphyr
|
|
2
|
+
module Fields
|
|
3
|
+
|
|
4
|
+
class FieldBase
|
|
5
|
+
# Prefix use to format error code
|
|
6
|
+
# @note You must Override this class constants in your field type class
|
|
7
|
+
PREFIX = 'base'
|
|
8
|
+
|
|
9
|
+
include Saphyr::Asserts::ErrorConstants
|
|
10
|
+
include Saphyr::Asserts::BaseAssert
|
|
11
|
+
include Saphyr::Asserts::SizeAssert
|
|
12
|
+
include Saphyr::Asserts::NumericAssert
|
|
13
|
+
include Saphyr::Asserts::StringAssert
|
|
14
|
+
|
|
15
|
+
# A hash containing the options of the field.
|
|
16
|
+
attr_reader :opts
|
|
17
|
+
|
|
18
|
+
# Every field type has the +:required+ and +:nullable+ options.
|
|
19
|
+
# @api private
|
|
20
|
+
DEFAULT_OPT_VALUES = {required: true, nullable: false}.freeze
|
|
21
|
+
# @api private
|
|
22
|
+
DEFAULT_OPTS = DEFAULT_OPT_VALUES.keys.freeze
|
|
23
|
+
|
|
24
|
+
# NOTE:
|
|
25
|
+
# Override following constants to manage options
|
|
26
|
+
|
|
27
|
+
# List of authorized options.
|
|
28
|
+
# @note Override this class constant if you want to use this feature.
|
|
29
|
+
AUTHORIZED_OPTIONS = []
|
|
30
|
+
|
|
31
|
+
# List of required options.
|
|
32
|
+
# @note Override this class constant if you want to use this feature.
|
|
33
|
+
REQUIRED_OPTIONS = []
|
|
34
|
+
|
|
35
|
+
# Require one and only of the listed options.
|
|
36
|
+
# @note Override this class constant if you want to use this feature.
|
|
37
|
+
REQUIRED_ONE_OF_OPTIONS = []
|
|
38
|
+
|
|
39
|
+
# Definition of exclusive options
|
|
40
|
+
# @note Override this class constant if you want to use this feature.
|
|
41
|
+
EXCLUSIVE_OPTIONS = []
|
|
42
|
+
|
|
43
|
+
# List of options where value must not be equals to another option.
|
|
44
|
+
# (ex: min == max)
|
|
45
|
+
# @note Override this class constant if you want to use this feature.
|
|
46
|
+
NOT_EQUALS_OPTIONS = []
|
|
47
|
+
|
|
48
|
+
# List of options where value must not be superior to another option.
|
|
49
|
+
# (ex: lt > gt)
|
|
50
|
+
# @note Override this class constant if you want to use this feature.
|
|
51
|
+
NOT_SUP_OPTIONS = []
|
|
52
|
+
|
|
53
|
+
# List of options where value must not be superior or equals to another option.
|
|
54
|
+
# (ex: lt >= gt)
|
|
55
|
+
# @note Override this class constant if you want to use this feature.
|
|
56
|
+
NOT_SUP_OR_EQUALS_OPTIONS = []
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def initialize(opts={})
|
|
60
|
+
if opts.key? :required
|
|
61
|
+
unless assert_boolean opts[:required]
|
|
62
|
+
raise Saphyr::Error.new "Option ':required' must be a Boolean"
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
if opts.key? :nullable
|
|
66
|
+
unless assert_boolean opts[:nullable]
|
|
67
|
+
raise Saphyr::Error.new "Option ':nullable' must be a Boolean"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
unless authorized_options.size == 0
|
|
72
|
+
opts.keys.each do |opt|
|
|
73
|
+
next if opt == :required or opt == :nullable
|
|
74
|
+
unless authorized_options.include? opt
|
|
75
|
+
raise Saphyr::Error.new "Unauthorized option: #{opt}"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
required_options.each do |opt|
|
|
81
|
+
unless opts.include? opt
|
|
82
|
+
raise Saphyr::Error.new "Missing required option: '#{opt}'"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
unless required_one_of_options.size == 0
|
|
87
|
+
status, selected = false, nil
|
|
88
|
+
required_one_of_options.each do |opt|
|
|
89
|
+
if opts.include? opt
|
|
90
|
+
if status
|
|
91
|
+
raise Saphyr::Error.new "You can't provide both options at same time: '#{selected}' and '#{opt}'"
|
|
92
|
+
end
|
|
93
|
+
status = true
|
|
94
|
+
selected = opt
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
unless status
|
|
98
|
+
raise Saphyr::Error.new "You must provide one of the following options: '#{required_one_of_options.to_s}'"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
exclusive_options.each do |data|
|
|
103
|
+
opt, excluded = data
|
|
104
|
+
if opts.include? opt
|
|
105
|
+
if excluded.first == :_all_
|
|
106
|
+
excluded = authorized_options - [opt]
|
|
107
|
+
end
|
|
108
|
+
unless opts.keys.intersection(excluded).size == 0
|
|
109
|
+
raise Saphyr::Error.new "You can't use #{excluded.to_s} options, if you use #{opt.to_s} options, if you use : :#{opt}"
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
not_equals_options.each do |data|
|
|
115
|
+
opt1, opt2 = data
|
|
116
|
+
if opts.include? opt1 and opts.include? opt2
|
|
117
|
+
if opts[opt1] == opts[opt2]
|
|
118
|
+
raise Saphyr::Error.new "Option '#{opt1} cannot be > to '#{opt2}'"
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
not_sup_options.each do |data|
|
|
124
|
+
opt1, opt2 = data
|
|
125
|
+
if opts.include? opt1 and opts.include? opt2
|
|
126
|
+
if opts[opt1] > opts[opt2]
|
|
127
|
+
raise Saphyr::Error.new "Option '#{opt1} cannot be > to '#{opt2}'"
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
not_sup_or_equals_options.each do |data|
|
|
133
|
+
opt1, opt2 = data
|
|
134
|
+
if opts.include? opt1 and opts.include? opt2
|
|
135
|
+
if opts[opt1] >= opts[opt2]
|
|
136
|
+
raise Saphyr::Error.new "Option '#{opt1} cannot be >= to '#{opt2}'"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
@opts = DEFAULT_OPT_VALUES.merge opts
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# -----
|
|
145
|
+
|
|
146
|
+
# Get the +PREFIX+ setting.
|
|
147
|
+
# @return [String] The prefix
|
|
148
|
+
def prefix
|
|
149
|
+
self.class::PREFIX
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# -----
|
|
153
|
+
|
|
154
|
+
# Get the +AUTHORIZED_OPTIONS+ options
|
|
155
|
+
# @return [Array]
|
|
156
|
+
def authorized_options
|
|
157
|
+
self.class::AUTHORIZED_OPTIONS
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Get the +REQUIRED_OPTIONS+ options
|
|
161
|
+
# @return [Array]
|
|
162
|
+
def required_options
|
|
163
|
+
self.class::REQUIRED_OPTIONS
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Get the +REQUIRED_ONE_OF_OPTIONS+ options
|
|
167
|
+
# @return [Array]
|
|
168
|
+
def required_one_of_options
|
|
169
|
+
self.class::REQUIRED_ONE_OF_OPTIONS
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Get the +EXCLUSIVE_OPTIONS+ options
|
|
173
|
+
# @return [Array]
|
|
174
|
+
def exclusive_options
|
|
175
|
+
self.class::EXCLUSIVE_OPTIONS
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Get the +NOT_EQUALS_OPTIONS+ options
|
|
179
|
+
# @return [Array]
|
|
180
|
+
def not_equals_options
|
|
181
|
+
self.class::NOT_EQUALS_OPTIONS
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Get the +NOT_SUP_OPTIONS+ options
|
|
185
|
+
# @return [Array]
|
|
186
|
+
def not_sup_options
|
|
187
|
+
self.class::NOT_SUP_OPTIONS
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Get the +NOT_SUP_OR_EQUALS_OPTIONS+ options
|
|
191
|
+
# @return [Array]
|
|
192
|
+
def not_sup_or_equals_options
|
|
193
|
+
self.class::NOT_SUP_OR_EQUALS_OPTIONS
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# -----
|
|
197
|
+
|
|
198
|
+
# Format the error code with the field prefix.
|
|
199
|
+
# @param code [String] The error code.
|
|
200
|
+
# @return [String] The formatted error code.
|
|
201
|
+
def err(code)
|
|
202
|
+
prefix + ':' + code
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Is the field required?
|
|
206
|
+
def required?
|
|
207
|
+
@opts[:required]
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Is the field nullable?
|
|
211
|
+
def nullable?
|
|
212
|
+
@opts[:nullable]
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# -----
|
|
216
|
+
|
|
217
|
+
# Check if the field value is valid.
|
|
218
|
+
# @param ctx [Saphyr::Engine::Context] The engine context.
|
|
219
|
+
# @param name [String] The field name.
|
|
220
|
+
# @param value [String] The field value.
|
|
221
|
+
def validate(ctx, name, value)
|
|
222
|
+
# NOTE: Nullable is handle by the engine.
|
|
223
|
+
errors = []
|
|
224
|
+
do_validate ctx, name, value, errors
|
|
225
|
+
errors
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
private
|
|
229
|
+
|
|
230
|
+
def do_validate(ctx, name, value, errors)
|
|
231
|
+
raise Saphyr::Error.new 'Not implemented!'
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Saphyr
|
|
2
|
+
module Fields
|
|
3
|
+
|
|
4
|
+
# The +float+ field type
|
|
5
|
+
#
|
|
6
|
+
# Allowed options are: +:eq, :gt, :gte, :lt, :lte, :in+.
|
|
7
|
+
class FloatField < FieldBase
|
|
8
|
+
PREFIX = 'float'
|
|
9
|
+
|
|
10
|
+
AUTHORIZED_OPTIONS = [:eq, :gt, :gte, :lt, :lte, :in]
|
|
11
|
+
|
|
12
|
+
EXCLUSIVE_OPTIONS = [
|
|
13
|
+
[ :eq, [:_all_] ],
|
|
14
|
+
[ :in, [:_all_] ],
|
|
15
|
+
[ :gt, [:gte] ],
|
|
16
|
+
[ :lt, [:lte] ],
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# Cannot have: lte == gte, use +:eq+ instead
|
|
20
|
+
NOT_EQUALS_OPTIONS = [
|
|
21
|
+
[:lte, :gte],
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
# Cannot have: gte > lte
|
|
25
|
+
NOT_SUP_OPTIONS = [
|
|
26
|
+
[:gte, :lte],
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Cannot have: gt >= lt ... and so on
|
|
30
|
+
NOT_SUP_OR_EQUALS_OPTIONS = [
|
|
31
|
+
[:gt, :lt],
|
|
32
|
+
[:gt, :lte],
|
|
33
|
+
[:gte, :lt],
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def do_validate(ctx, name, value, errors)
|
|
39
|
+
return unless assert_class Float, value, errors
|
|
40
|
+
assert_eq @opts[:eq], value, errors
|
|
41
|
+
assert_numeric_gt @opts[:gt], value, errors
|
|
42
|
+
assert_numeric_gte @opts[:gte], value, errors
|
|
43
|
+
assert_numeric_lt @opts[:lt], value, errors
|
|
44
|
+
assert_numeric_lte @opts[:lte], value, errors
|
|
45
|
+
assert_in @opts[:in], value, errors
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Saphyr
|
|
2
|
+
module Fields
|
|
3
|
+
|
|
4
|
+
# The +integer+ field type
|
|
5
|
+
#
|
|
6
|
+
# Allowed options are: +:eq, :gt, :gte, :lt, :lte, :in+.
|
|
7
|
+
class IntegerField < FieldBase
|
|
8
|
+
PREFIX = 'integer'
|
|
9
|
+
|
|
10
|
+
AUTHORIZED_OPTIONS = [:eq, :gt, :gte, :lt, :lte, :in]
|
|
11
|
+
|
|
12
|
+
EXCLUSIVE_OPTIONS = [
|
|
13
|
+
[ :eq, [:_all_] ],
|
|
14
|
+
[ :in, [:_all_] ],
|
|
15
|
+
[ :gt, [:gte] ],
|
|
16
|
+
[ :lt, [:lte] ],
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
# Cannot have: lte == gte, use +:eq+ instead
|
|
20
|
+
NOT_EQUALS_OPTIONS = [
|
|
21
|
+
[:lte, :gte],
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
# Cannot have: gte > lte
|
|
25
|
+
NOT_SUP_OPTIONS = [
|
|
26
|
+
[:gte, :lte],
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
# Cannot have: gt >= lt ... and so on
|
|
30
|
+
NOT_SUP_OR_EQUALS_OPTIONS = [
|
|
31
|
+
[:gt, :lt],
|
|
32
|
+
[:gt, :lte],
|
|
33
|
+
[:gte, :lt],
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def do_validate(ctx, name, value, errors)
|
|
39
|
+
return unless assert_class Integer, value, errors
|
|
40
|
+
assert_eq @opts[:eq], value, errors
|
|
41
|
+
assert_numeric_gt @opts[:gt], value, errors
|
|
42
|
+
assert_numeric_gte @opts[:gte], value, errors
|
|
43
|
+
assert_numeric_lt @opts[:lt], value, errors
|
|
44
|
+
assert_numeric_lte @opts[:lte], value, errors
|
|
45
|
+
assert_in @opts[:in], value, errors
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Saphyr
|
|
2
|
+
module Fields
|
|
3
|
+
|
|
4
|
+
class SchemaField < FieldBase
|
|
5
|
+
PREFIX = 'schema'
|
|
6
|
+
|
|
7
|
+
AUTHORIZED_OPTIONS = [:name]
|
|
8
|
+
|
|
9
|
+
REQUIRED_OPTIONS = [:name]
|
|
10
|
+
|
|
11
|
+
private
|
|
12
|
+
|
|
13
|
+
def do_validate(ctx, name, value, errors)
|
|
14
|
+
# Find the schema
|
|
15
|
+
schema_name = @opts[:name]
|
|
16
|
+
schema = ctx.find_schema schema_name # Raise exceeption if not found
|
|
17
|
+
|
|
18
|
+
# Create derived engine to handle schema and data fragment.
|
|
19
|
+
field_path = ctx.get_path name
|
|
20
|
+
new_ctx = ctx.derive schema, value, field_path
|
|
21
|
+
engine = Saphyr::Engine.new new_ctx
|
|
22
|
+
engine.validate
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module Saphyr
|
|
2
|
+
module Fields
|
|
3
|
+
|
|
4
|
+
# The +string+ field type
|
|
5
|
+
#
|
|
6
|
+
# Allowed options are: +:eq, :len, :min, :max, :in, :regexp+.
|
|
7
|
+
class StringField < FieldBase
|
|
8
|
+
PREFIX = 'string'
|
|
9
|
+
|
|
10
|
+
AUTHORIZED_OPTIONS = [:eq, :len, :min, :max, :in, :regexp]
|
|
11
|
+
|
|
12
|
+
EXCLUSIVE_OPTIONS = [
|
|
13
|
+
[ :eq, [:_all_] ],
|
|
14
|
+
[ :len, [:eq, :min, :max, :in] ],
|
|
15
|
+
[ :in, [:_all_] ],
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
# Cannot have: min == max, use +:len+ instead
|
|
19
|
+
NOT_EQUALS_OPTIONS = [
|
|
20
|
+
[:min, :max],
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
# Cannot have: min > max
|
|
24
|
+
NOT_SUP_OPTIONS = [
|
|
25
|
+
[:min, :max],
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def do_validate(ctx, name, value, errors)
|
|
31
|
+
return unless assert_class String, value, errors
|
|
32
|
+
assert_eq @opts[:eq], value, errors
|
|
33
|
+
assert_size_len @opts[:len], value, errors
|
|
34
|
+
assert_size_min @opts[:min], value, errors
|
|
35
|
+
assert_size_max @opts[:max], value, errors
|
|
36
|
+
assert_in @opts[:in], value, errors
|
|
37
|
+
assert_string_regexp @opts[:regexp], value, errors
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Saphyr
|
|
2
|
+
module Fields
|
|
3
|
+
require_relative './fields/field_base'
|
|
4
|
+
|
|
5
|
+
require_relative './fields/array_field'
|
|
6
|
+
require_relative './fields/schema_field'
|
|
7
|
+
|
|
8
|
+
require_relative './fields/string_field'
|
|
9
|
+
require_relative './fields/integer_field'
|
|
10
|
+
require_relative './fields/float_field'
|
|
11
|
+
require_relative './fields/boolean_field'
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
module Saphyr
|
|
2
|
+
module Helpers
|
|
3
|
+
class Format
|
|
4
|
+
class << self
|
|
5
|
+
def errors_to_text errors
|
|
6
|
+
errors.each do |error|
|
|
7
|
+
type = error[:type]
|
|
8
|
+
puts "path: #{error[:path]}"
|
|
9
|
+
errs = error[:errors]
|
|
10
|
+
errs.each do |err|
|
|
11
|
+
puts " - type: #{err[:type]}"
|
|
12
|
+
puts " - data: #{err[:data].to_s}"
|
|
13
|
+
puts " - msg: #{make_msg err}"
|
|
14
|
+
end
|
|
15
|
+
puts ''
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def make_msg(err)
|
|
20
|
+
type = err[:type]
|
|
21
|
+
data = err[:data]
|
|
22
|
+
|
|
23
|
+
# ------------------------------------
|
|
24
|
+
# BASE
|
|
25
|
+
# ------------------------------------
|
|
26
|
+
# eq
|
|
27
|
+
if type.end_with? 'eq'
|
|
28
|
+
return "Expecting value to be equals to: #{data[:eq]}, got: #{data[:_val]}"
|
|
29
|
+
end
|
|
30
|
+
# class
|
|
31
|
+
if type.end_with? 'type'
|
|
32
|
+
return "Expecting type '#{data[:type]}', got: #{data[:got]}"
|
|
33
|
+
end
|
|
34
|
+
# in
|
|
35
|
+
if type.end_with? 'in'
|
|
36
|
+
return "Expecting value to be in: #{data[:in].to_s}, got: #{data[:_val]}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# ------------------------------------
|
|
40
|
+
# Numeric
|
|
41
|
+
# ------------------------------------
|
|
42
|
+
# gt
|
|
43
|
+
if type.end_with? 'gt'
|
|
44
|
+
return "Expecting value to be > #{data[:gt]}, got: #{data[:_val]}"
|
|
45
|
+
end
|
|
46
|
+
# gte
|
|
47
|
+
if type.end_with? 'gte'
|
|
48
|
+
return "Expecting value to be >= #{data[:gte]}, got: #{data[:_val]}"
|
|
49
|
+
end
|
|
50
|
+
# lt
|
|
51
|
+
if type.end_with? 'lt'
|
|
52
|
+
return "Expecting value to be < #{data[:lt]}, got: #{data[:_val]}"
|
|
53
|
+
end
|
|
54
|
+
# lte
|
|
55
|
+
if type.end_with? 'lte'
|
|
56
|
+
return "Expecting value to be <= #{data[:lte]}, got: #{data[:_val]}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# ------------------------------------
|
|
60
|
+
# Size
|
|
61
|
+
# ------------------------------------
|
|
62
|
+
# len
|
|
63
|
+
if type.end_with? 'len'
|
|
64
|
+
return "Expecting size to be equals to: #{data[:len]}, got: #{data[:_val].size}"
|
|
65
|
+
end
|
|
66
|
+
# min
|
|
67
|
+
if type.end_with? 'min'
|
|
68
|
+
return "Expecting size to be >= #{data[:min]}, got: #{data[:_val].size}"
|
|
69
|
+
end
|
|
70
|
+
# max
|
|
71
|
+
if type.end_with? 'max'
|
|
72
|
+
return "Expecting size to be <= #{data[:max]}, got: #{data[:_val].size}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# ------------------------------------
|
|
76
|
+
# String
|
|
77
|
+
# ------------------------------------
|
|
78
|
+
# regexp
|
|
79
|
+
if type.end_with? 'regexp'
|
|
80
|
+
return "Value failed to match regexp: #{data[:regexp].to_s}, got: #{data[:_val]}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# ------------------------------------
|
|
84
|
+
# Strict mode
|
|
85
|
+
# ------------------------------------
|
|
86
|
+
if type.end_with? 'missing_in_data'
|
|
87
|
+
return 'Missing fields in data'
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
if type.end_with? 'missing_in_schema'
|
|
91
|
+
return 'Missing fields in schema'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# ------------------------------------
|
|
95
|
+
# Not Nullable
|
|
96
|
+
# ------------------------------------
|
|
97
|
+
if type == 'not-nullable'
|
|
98
|
+
return 'Not nullable'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
'unknown'
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Saphyr
|
|
2
|
+
|
|
3
|
+
# This class is used to encapsulate schema definition.
|
|
4
|
+
#
|
|
5
|
+
class Schema
|
|
6
|
+
attr_reader :strict, :root, :fields, :schemas, :conditionals, :casts
|
|
7
|
+
|
|
8
|
+
def initialize()
|
|
9
|
+
@strict, @root = true, :object # Default values
|
|
10
|
+
@fields, @schemas, @conditionals, @casts = {}, {}, [], {}
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# ---------------------------------------------------- DSL
|
|
14
|
+
def strict(value)
|
|
15
|
+
@strict = value # TODO: Validate bollean
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def root(value)
|
|
19
|
+
@root = value # TODO : Validate symbol -> :object or :array
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def field(name, type, **opts)
|
|
23
|
+
# TODO : What if field 'name' already exists?
|
|
24
|
+
name_s = name.to_s # Convert Symbol to string
|
|
25
|
+
# Raise exceptions if field type is not found
|
|
26
|
+
@fields[name_s] = Saphyr.config.instanciate_field_type type, opts
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def schema(name, &block)
|
|
30
|
+
schema = Saphyr::Schema.new
|
|
31
|
+
schema.instance_eval &block
|
|
32
|
+
# TODO : What if schema 'name' already exists?
|
|
33
|
+
@schemas[name] = schema
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def conditional(cond, &block)
|
|
37
|
+
cond = cond.to_sym if cond.is_a? String
|
|
38
|
+
if not cond.is_a?(Proc) and not cond.is_a?(Symbol)
|
|
39
|
+
raise Saphyr::Error.new "Bad condition, must a Proc or Symbol"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
schema = Saphyr::Schema.new
|
|
43
|
+
schema.instance_eval &block
|
|
44
|
+
@conditionals << [cond, schema]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def cast(field, method)
|
|
48
|
+
method = method.to_s if method.is_a? Symbol
|
|
49
|
+
if not method.is_a?(Proc) and not method.is_a?(String)
|
|
50
|
+
raise Saphyr::Error.new "Bad method, must a Proc or Symbol | String"
|
|
51
|
+
end
|
|
52
|
+
@casts.store field.to_s, method
|
|
53
|
+
end
|
|
54
|
+
# -------------------------------------------------- / DSL
|
|
55
|
+
|
|
56
|
+
# Find local schema definition
|
|
57
|
+
def find_schema(name)
|
|
58
|
+
@schemas[name]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def strict?
|
|
62
|
+
@strict
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def root_array?
|
|
66
|
+
@root == :array
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def root_object?
|
|
70
|
+
@root == :object
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|