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,129 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
module Saphyr
|
|
4
|
+
|
|
5
|
+
# Base class used to define a validator.
|
|
6
|
+
class Validator
|
|
7
|
+
|
|
8
|
+
class << self
|
|
9
|
+
attr_accessor :cache_config, :proc_idx
|
|
10
|
+
|
|
11
|
+
def config()
|
|
12
|
+
self.cache_config ||= Saphyr::Schema.new
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# ----- Proxy all following method calls to Saphyr::Schema instance
|
|
16
|
+
def strict(value)
|
|
17
|
+
config.strict value
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def root(value)
|
|
21
|
+
config.root value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def field(name, type, **opts)
|
|
25
|
+
if config.root_array? and name != :_root_
|
|
26
|
+
raise Saphyr::Error.new "Can only define ':_root_' field when root is ':array'"
|
|
27
|
+
end
|
|
28
|
+
if name == :_root_ and type != :array
|
|
29
|
+
raise Saphyr::Error.new "Field ':_root_' must be of type ':array'"
|
|
30
|
+
end
|
|
31
|
+
config.field name, type, **opts
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def schema(name, &block)
|
|
35
|
+
config.schema name, &block
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def conditional(cond, &block)
|
|
39
|
+
#
|
|
40
|
+
# TODO: 'cond' must be a method Symbol or a proc / lambda
|
|
41
|
+
#
|
|
42
|
+
method = cond
|
|
43
|
+
if cond.is_a? Proc
|
|
44
|
+
m = "_proc_#{self.internal_proc_index}".to_sym
|
|
45
|
+
self.send :define_method, m, cond
|
|
46
|
+
method = m
|
|
47
|
+
self.proc_idx += 1
|
|
48
|
+
end
|
|
49
|
+
config.conditional method, &block
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def cast(field, method)
|
|
53
|
+
if method.is_a? Proc
|
|
54
|
+
m = "_proc_#{self.internal_proc_index}".to_sym
|
|
55
|
+
self.send :define_method, m, method
|
|
56
|
+
method = m
|
|
57
|
+
self.proc_idx += 1
|
|
58
|
+
end
|
|
59
|
+
config.cast field, method
|
|
60
|
+
end
|
|
61
|
+
# ----- / Proxy
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def internal_proc_index()
|
|
66
|
+
self.proc_idx ||= 0
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def initialize()
|
|
71
|
+
@proc_idx = 0
|
|
72
|
+
@ctx = nil
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get the validator configuration (ie: the attached schema)
|
|
76
|
+
# @return [Saphyr::Schema]
|
|
77
|
+
def get_config()
|
|
78
|
+
self.class.config
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Find a local schema
|
|
82
|
+
# @param name [Symbol] The schema name.
|
|
83
|
+
# @return [Saphyr::Schema]
|
|
84
|
+
def find_schema(name)
|
|
85
|
+
self.class.config.find_schema name
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Get the parsed JSON data.
|
|
89
|
+
# @return [Hash]
|
|
90
|
+
def data()
|
|
91
|
+
return nil if @ctx.nil?
|
|
92
|
+
@ctx.data
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# -----
|
|
96
|
+
|
|
97
|
+
# Validate an already parsed JSON document.
|
|
98
|
+
# @param [Hash | Array] The data to validate.
|
|
99
|
+
# @return [Boolean] Wheter the validation was successful or failed.
|
|
100
|
+
def validate(data)
|
|
101
|
+
@ctx = Saphyr::Engine::Context.new [self], get_config, data, nil, '//'
|
|
102
|
+
engine = Saphyr::Engine.new @ctx
|
|
103
|
+
engine.validate
|
|
104
|
+
@ctx.errors.size == 0
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Parse and validate a JSON document.
|
|
108
|
+
# @param json [String] The JSON document.
|
|
109
|
+
# @return [Boolean] Wheter the validation was successful or failed.
|
|
110
|
+
def parse_and_validate(json)
|
|
111
|
+
validate JSON.parse(json)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Get the validation errors
|
|
115
|
+
# @return [Array] An array of errors.
|
|
116
|
+
def errors()
|
|
117
|
+
return [] if @ctx.nil?
|
|
118
|
+
@ctx.errors
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Get a field from the data to validate.
|
|
122
|
+
# @param [String | Symbol] The field name
|
|
123
|
+
# @return The field value
|
|
124
|
+
def get(field)
|
|
125
|
+
data = @ctx.data_to_validate
|
|
126
|
+
data[field.to_s]
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
data/lib/saphyr.rb
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#efrozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "saphyr/version"
|
|
4
|
+
|
|
5
|
+
# Saphyr is a libray used to validate JSON data using a schema defined with a DSL.
|
|
6
|
+
#
|
|
7
|
+
# == Usage example :
|
|
8
|
+
#
|
|
9
|
+
# Say you have a JSON literal string or already parsed 'data' like that :
|
|
10
|
+
#
|
|
11
|
+
# json = '{"id": 3465, "name": "Bob"}'
|
|
12
|
+
#
|
|
13
|
+
# data = {
|
|
14
|
+
# "id" => 3465,
|
|
15
|
+
# "name" => "Bob",
|
|
16
|
+
# }
|
|
17
|
+
#
|
|
18
|
+
# Define a Validator with a specific schema :
|
|
19
|
+
#
|
|
20
|
+
# class ItemValidator < Saphyr::Validator
|
|
21
|
+
# schema do
|
|
22
|
+
# field :id, :integer, gte: 1, lt: 32000
|
|
23
|
+
# field :name, :string, min: 2, max: 50
|
|
24
|
+
# end
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# Validate data :
|
|
28
|
+
#
|
|
29
|
+
# v = ItemValidator.new
|
|
30
|
+
# if v.validate data
|
|
31
|
+
# puts "Validation : SUCCESS", "\n"
|
|
32
|
+
# else
|
|
33
|
+
# puts "Validation : FAILED", "\n"
|
|
34
|
+
# Saphyr::Helpers::Format.errors_to_text v.errors
|
|
35
|
+
# end
|
|
36
|
+
#
|
|
37
|
+
# Or :
|
|
38
|
+
#
|
|
39
|
+
# v = ItemValidator.new
|
|
40
|
+
# if v.parse_and_validate json
|
|
41
|
+
# puts "Validation : SUCCESS", "\n"
|
|
42
|
+
# data = v.data # Get back the parsed json data
|
|
43
|
+
# else
|
|
44
|
+
# puts "Validation : FAILED", "\n"
|
|
45
|
+
# Saphyr::Helpers::Format.errors_to_text v.errors
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
module Saphyr
|
|
49
|
+
class Error < StandardError; end
|
|
50
|
+
|
|
51
|
+
class << self
|
|
52
|
+
# The global configuration.
|
|
53
|
+
# @return [Saphyr::Config]
|
|
54
|
+
attr_accessor :config
|
|
55
|
+
|
|
56
|
+
# This method is part of the DSL used to register global field type or schema.
|
|
57
|
+
# @param block [Block] This block must use the followingDSL methods:
|
|
58
|
+
# 'field_type' and 'schema'
|
|
59
|
+
# @api public
|
|
60
|
+
def register(&block)
|
|
61
|
+
self.config ||= Config.new
|
|
62
|
+
self.config.instance_eval &block
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Get a specific global schema.
|
|
66
|
+
# @param name [Symbol] The name of the schema
|
|
67
|
+
# @return [Saphyr::Schema]
|
|
68
|
+
# @raise [Saphyr::Error] If schema does not exists.
|
|
69
|
+
# @api public
|
|
70
|
+
def global_schema(name)
|
|
71
|
+
self.config.get_schema name
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# This class is used to encapsulate global settings (field types, schema).
|
|
77
|
+
# @api private
|
|
78
|
+
class Config
|
|
79
|
+
attr_reader :field_types, :schemas
|
|
80
|
+
|
|
81
|
+
def initialize()
|
|
82
|
+
@schemas = {}
|
|
83
|
+
@field_types = {
|
|
84
|
+
array: Saphyr::Fields::ArrayField,
|
|
85
|
+
schema: Saphyr::Fields::SchemaField,
|
|
86
|
+
}
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# ---------------------------------------------------- DSL
|
|
90
|
+
|
|
91
|
+
# This method is part of the DSL and used to register global field type
|
|
92
|
+
# and global schemas.
|
|
93
|
+
#
|
|
94
|
+
# @param name [String, Symbol] The field type name
|
|
95
|
+
# @param klass [Class] The class used to handle the field type
|
|
96
|
+
def field_type(name, klass)
|
|
97
|
+
raise Saphyr::Error.new "Cannot overwrite ':array' field" if name == :array
|
|
98
|
+
raise Saphyr::Error.new "Cannot overwrite ':schema' field" if name == :schema
|
|
99
|
+
@field_types[name] = klass
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# This method is part of the DSL used to configure global settings.
|
|
103
|
+
# Register a new global schema.
|
|
104
|
+
#
|
|
105
|
+
# == Parameters:
|
|
106
|
+
# name::
|
|
107
|
+
# The name of the schema
|
|
108
|
+
# can be `Symbol` or `String`.
|
|
109
|
+
# &block::
|
|
110
|
+
# The block evaluated by the DSL.
|
|
111
|
+
def schema(name, &block)
|
|
112
|
+
schema = Saphyr::Schema.new
|
|
113
|
+
schema.instance_eval &block
|
|
114
|
+
@schemas[name] = schema
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# -------------------------------------------------- / DSL
|
|
118
|
+
|
|
119
|
+
# Instanciate a registered field type.
|
|
120
|
+
# @param type [Symbol] The type name use to register the field type
|
|
121
|
+
# @param opts [Hash] A hash of options to pass to the field type instance.
|
|
122
|
+
# @return [Field Type Object] An instance of the field type.
|
|
123
|
+
def instanciate_field_type(type, opts={})
|
|
124
|
+
klass = @field_types[type]
|
|
125
|
+
raise Saphyr::Error.new "Unknown field : #{type}" if klass.nil?
|
|
126
|
+
Object.const_get(klass.name).new opts
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Get a specific global schema given his name.
|
|
130
|
+
# @param name [Symbol] The schema name
|
|
131
|
+
# @return [Saphyr::Schema]
|
|
132
|
+
# @raise [Saphyr::Error] If schema does not exists.
|
|
133
|
+
def get_schema(name)
|
|
134
|
+
raise Saphyr::Error.new "Unknown schema : #{name}" unless @schemas.key? name
|
|
135
|
+
@schemas[name]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
#
|
|
142
|
+
# Required files
|
|
143
|
+
#
|
|
144
|
+
require_relative './saphyr/asserts'
|
|
145
|
+
require_relative './saphyr/fields'
|
|
146
|
+
require_relative './saphyr/schema'
|
|
147
|
+
require_relative './saphyr/validator'
|
|
148
|
+
require_relative './saphyr/engine'
|
|
149
|
+
require_relative './saphyr/helpers'
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
#
|
|
153
|
+
# Register default field types.
|
|
154
|
+
#
|
|
155
|
+
Saphyr.register do
|
|
156
|
+
field_type :string, Saphyr::Fields::StringField
|
|
157
|
+
field_type :integer, Saphyr::Fields::IntegerField
|
|
158
|
+
field_type :float, Saphyr::Fields::FloatField
|
|
159
|
+
field_type :boolean, Saphyr::Fields::BooleanField
|
|
160
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# How To Define a Schema
|
|
2
|
+
|
|
3
|
+
A validation schema is basicly a class inheriting
|
|
4
|
+
from `Saphyr::Validator` and using the DSL to describe the schema.
|
|
5
|
+
|
|
6
|
+
```ruby
|
|
7
|
+
data = {
|
|
8
|
+
"name" => 'my item',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
class ItemValidator < Saphyr::Validator
|
|
12
|
+
# Declaring the 'name' field into the validation schema:
|
|
13
|
+
|
|
14
|
+
field :name, :string, min: 5, max: 50
|
|
15
|
+
# | | |-------------|
|
|
16
|
+
# | | |
|
|
17
|
+
# name type options (depending on the type)
|
|
18
|
+
|
|
19
|
+
# The field name can be a Sring or Symbol:
|
|
20
|
+
field 'name', :string, min: 5, max: 50
|
|
21
|
+
end
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
By default the `Saphyr` library provide many field types, like: `:integer`,
|
|
25
|
+
`:float`, `:string`, `:array` ...
|
|
26
|
+
|
|
27
|
+
_(You can also define your own custom field type, we'll see that later)_
|
|
28
|
+
|
|
29
|
+
Example of some types:
|
|
30
|
+
|
|
31
|
+
```ruby
|
|
32
|
+
data = {
|
|
33
|
+
"id" => 721, # Integer: > 0
|
|
34
|
+
"type" => 2, # Integer: possible value 1, 2 or 3
|
|
35
|
+
"price" => 27.60, # Float: > 0
|
|
36
|
+
"name" => 'my item', # String: 5 >= len <= 50
|
|
37
|
+
"active" => true, # Boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class ItemValidator < Saphyr::Validator
|
|
41
|
+
field :id, :integer, gt: 0
|
|
42
|
+
field :type, :integer, in: [1, 2, 3]
|
|
43
|
+
field :price, :float, gt: 0
|
|
44
|
+
field :name, :string, min: 5, max: 50
|
|
45
|
+
field :active, :boolean
|
|
46
|
+
end
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Each `Validator` can embed local schemas used to describe the structure of data:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
data = {
|
|
53
|
+
"id" => 721,
|
|
54
|
+
"name" => 'my item',
|
|
55
|
+
"timestamps" => {
|
|
56
|
+
"created_at" => 1669215446, # Unix timestamp
|
|
57
|
+
"modified_at" => 1670943462,
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
class ItemValidator < Saphyr::Validator
|
|
62
|
+
schema :timestamp do
|
|
63
|
+
field :created_at, :integer, gt: 0
|
|
64
|
+
field :modified_at, :integer, gt: 0
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
field :id, :integer, gt: 0
|
|
68
|
+
field :name, :string, min: 5, max: 50
|
|
69
|
+
field :timestamps, :schema, name: :timestamp
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The `:timestamp` schema is local to the validator and cannot be accessed by other
|
|
74
|
+
validators.
|
|
75
|
+
|
|
76
|
+
If you need to share a schema between many validators then you can declare it globally:
|
|
77
|
+
|
|
78
|
+
```ruby
|
|
79
|
+
Saphyr.register do
|
|
80
|
+
schema :timestamp do
|
|
81
|
+
field :created_at, :integer, gt: 0
|
|
82
|
+
field :modified_at, :integer, gt: 0
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class ItemValidator < Saphyr::Validator
|
|
87
|
+
# ....
|
|
88
|
+
field :timestamps, :schema, name: :timestamp
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class PostValidator < Saphyr::Validator
|
|
92
|
+
# ....
|
|
93
|
+
field :timestamps, :schema, name: :timestamp
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## When root is an array
|
|
98
|
+
|
|
99
|
+
By default validator root are set to `:object`, but this can be customized.
|
|
100
|
+
|
|
101
|
+
In this case, only one virtual field must be defined : `:_root_` and it must be of type `:array`
|
|
102
|
+
|
|
103
|
+
Example with `:of_type` :
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
data = ['fr', 'en', 'es']
|
|
107
|
+
|
|
108
|
+
class ItemValidator < Saphyr::Validator
|
|
109
|
+
root :array
|
|
110
|
+
|
|
111
|
+
field :_root_, :array, min: 2, max: 5, of_type: :string, opts: {len: 2}
|
|
112
|
+
end
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Example with `:of_schema` :
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
data = [
|
|
119
|
+
{ "id" => 12, "label" => "tag1" },
|
|
120
|
+
{ "id" => 15, "label" => "tag2" },
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
class ItemValidator < Saphyr::Validator
|
|
124
|
+
root :array
|
|
125
|
+
|
|
126
|
+
schema :tag do
|
|
127
|
+
field :id, :integer, gt: 0
|
|
128
|
+
field :label, :string, min: 2, max: 30
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
field :_root_, :array, min: 2, max: 4, of_schema: :tag
|
|
132
|
+
end
|
|
133
|
+
```
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# An Overview of Library Field Types
|
|
2
|
+
|
|
3
|
+
By default the `Saphyr` library is including many field types.
|
|
4
|
+
|
|
5
|
+
## Common options
|
|
6
|
+
|
|
7
|
+
All field type have the common `:required`, `:nullable`.
|
|
8
|
+
|
|
9
|
+
## String
|
|
10
|
+
|
|
11
|
+
Authorized options for the `:string` type: `[:eq, :len, :min, :max, :in, :regexp]`
|
|
12
|
+
|
|
13
|
+
Here is an example with all possible options for `:string` type:
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
class MyValidator < Saphyr::Validator
|
|
17
|
+
field :name, :string
|
|
18
|
+
field :name, :string, eq: 'v1.1'
|
|
19
|
+
field :name, :string, min: 5, max: 50
|
|
20
|
+
field :name, :string, max: 50
|
|
21
|
+
field :name, :string, len: 15
|
|
22
|
+
field :name, :string, len: 15, regexp: /^[a-f0-9]+$/
|
|
23
|
+
field :name, :string, regexp: /^[A-Z0-9]{15}$/
|
|
24
|
+
field :name, :string, in: ['jpg', 'png', 'gif']
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
field :location, :string, required: false, min: 10
|
|
28
|
+
field :info, :string, nullable: true, max: 1024
|
|
29
|
+
end
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
- If you use `:eq` option then you cannot use any of the other options
|
|
33
|
+
- If you use `:len` option then you cannot use `:min` and `:max` options
|
|
34
|
+
- If you use `:in` option then you cannot use any of the other options
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## Integer
|
|
38
|
+
|
|
39
|
+
Authorized options for the `:integer` type: `[:eq, :gt, :gte, :lt, :lte, :in]`
|
|
40
|
+
|
|
41
|
+
Here is an example with all possible options for `:integer` type:
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
class MyValidator < Saphyr::Validator
|
|
45
|
+
field :count, :integer
|
|
46
|
+
field :count, :integer, eq: 'v1.1'
|
|
47
|
+
field :count, :integer, gt: 0
|
|
48
|
+
field :count, :integer, lt: 50
|
|
49
|
+
field :count, :integer, gte: 5, lte: 50
|
|
50
|
+
field :count, :integer, in: ['jpg', 'png', 'gif']
|
|
51
|
+
|
|
52
|
+
field :count, :integer, required: false, gte: 10
|
|
53
|
+
field :count, :integer, nullable: true, lte: 1024
|
|
54
|
+
end
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
- If you use `:eq` option then you cannot use any of the other options
|
|
58
|
+
- If you use `:in` option then you cannot use any of the other options
|
|
59
|
+
|
|
60
|
+
## Float
|
|
61
|
+
|
|
62
|
+
Authorized options for the `:float` type: `[:eq, :gt, :gte, :lt, :lte, :in]`
|
|
63
|
+
|
|
64
|
+
Here is an example with all possible options for `:float` type:
|
|
65
|
+
|
|
66
|
+
```ruby
|
|
67
|
+
class MyValidator < Saphyr::Validator
|
|
68
|
+
field :price, :float
|
|
69
|
+
field :price, :float, eq: 15.1
|
|
70
|
+
field :price, :float, gt: 0
|
|
71
|
+
field :price, :float, lt: 50
|
|
72
|
+
field :price, :float, gte: 5, lte: 50
|
|
73
|
+
field :price, :float, in: ['jpg', 'png', 'gif']
|
|
74
|
+
|
|
75
|
+
field :price, :float, required: false, gte: 10
|
|
76
|
+
field :price, :float, nullable: true, lte: 1024
|
|
77
|
+
end
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
- If you use `:eq` option then you cannot use any of the other options
|
|
81
|
+
- If you use `:in` option then you cannot use any of the other options
|
|
82
|
+
|
|
83
|
+
## Boolean
|
|
84
|
+
|
|
85
|
+
Authorized options for the `:boolean` type: `[:eq]`
|
|
86
|
+
|
|
87
|
+
Here is an example with all possible options for `:boolean` type:
|
|
88
|
+
|
|
89
|
+
```ruby
|
|
90
|
+
class MyValidator < Saphyr::Validator
|
|
91
|
+
field :active, :boolean
|
|
92
|
+
field :active, :boolean, eq: true
|
|
93
|
+
field :active, :boolean, eq: false
|
|
94
|
+
|
|
95
|
+
field :active, :boolean, required: false
|
|
96
|
+
field :active, :boolean, nullable: true
|
|
97
|
+
end
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Array
|
|
101
|
+
|
|
102
|
+
Authorized options for the `:array` type: `[:len, :min, :max, :of_type, :of_schema, :opts]`
|
|
103
|
+
|
|
104
|
+
The `:array` type is little bit different than the other types.
|
|
105
|
+
|
|
106
|
+
The following options `[:len, :min, :max]` are for the array size then you must define
|
|
107
|
+
the type of the array element, this is where `:of_type` and `:of_schema` options
|
|
108
|
+
take place.
|
|
109
|
+
|
|
110
|
+
- One of this option is required: `:of_type` and `:of_schema`
|
|
111
|
+
- If you use `:len` option then you cannot use `:min`, `:max` options
|
|
112
|
+
|
|
113
|
+
### Example with `of_type`:
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
data = {
|
|
117
|
+
'tags' => ['code', 'ruby', 'json']
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
class MyValidator < Saphyr::Validator
|
|
121
|
+
field :tags, :array, of_type: :string, opts: {max: 50}
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
class MyValidator < Saphyr::Validator
|
|
125
|
+
field :tags, :array, min: 1, max: 10, of_type: :string, opts: {max: 50}
|
|
126
|
+
# |-------------| |-------------|
|
|
127
|
+
# | |
|
|
128
|
+
# Size of array must be: 1 >= s <= 10 |
|
|
129
|
+
# |
|
|
130
|
+
# This 'opts' are for the element of array, ie: 'string'
|
|
131
|
+
end
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Example with `of_schema`:
|
|
135
|
+
|
|
136
|
+
- When using `:of_schema` then `:opts` cannot be used
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
data = {
|
|
140
|
+
'code' => 'AGF30',
|
|
141
|
+
'tags' => [
|
|
142
|
+
{'id' => 234, 'label' => 'ruby'},
|
|
143
|
+
{'id' => 567, 'label' => 'elixir'}
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
class MyValidator < Saphyr::Validator
|
|
148
|
+
schema :tag do
|
|
149
|
+
field :id, :integer, gt: 0
|
|
150
|
+
field :label, :string, min: 5, max: 30
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
field :code, :string, min: 5, max: 10
|
|
154
|
+
field :tags, :array, of_schema: :tag
|
|
155
|
+
end
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Schema
|
|
159
|
+
|
|
160
|
+
The schema field type does not have any option.
|
|
161
|
+
|
|
162
|
+
It is used to describe the structure hierarchy of the document. The referenced schema
|
|
163
|
+
can local to the validator or global to `Saphyr` library.
|
|
164
|
+
|
|
165
|
+
To find the schema, we first search it in the validator, if there isn't the named schema in
|
|
166
|
+
the validtor then you search it in the global `Saphyr` registry.
|
|
167
|
+
_(and if not found an exception is raised)_
|
|
168
|
+
|
|
169
|
+
Using a local validator schema:
|
|
170
|
+
|
|
171
|
+
```ruby
|
|
172
|
+
data = {
|
|
173
|
+
# ....
|
|
174
|
+
"timestamps": {
|
|
175
|
+
"created_at": 1669215446, # Unix timestamp
|
|
176
|
+
"modified_at": 1670943462
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
class ItemValidator < Saphyr::Validator
|
|
181
|
+
schema :timestamp do
|
|
182
|
+
field :created_at, :integer, gt: 0
|
|
183
|
+
field :modified_at, :integer, gt: 0
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# ....
|
|
187
|
+
field :timestamps, :schema, name: :timestamp
|
|
188
|
+
end
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Using a global schema:
|
|
192
|
+
|
|
193
|
+
```ruby
|
|
194
|
+
Saphyr.register do
|
|
195
|
+
schema :timestamp do
|
|
196
|
+
field :created_at, :integer, gt: 0
|
|
197
|
+
field :modified_at, :integer, gt: 0
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
class ItemValidator < Saphyr::Validator
|
|
202
|
+
# ....
|
|
203
|
+
field :timestamps, :schema, name: :timestamp
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
class PostValidator < Saphyr::Validator
|
|
207
|
+
# ....
|
|
208
|
+
field :timestamps, :schema, name: :timestamp
|
|
209
|
+
end
|
|
210
|
+
```
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# How to Use Strict Mode
|
|
2
|
+
|
|
3
|
+
By default the library is setup as strict mode, this mean that all fields from schema or
|
|
4
|
+
data must be described.
|
|
5
|
+
|
|
6
|
+
```ruby
|
|
7
|
+
data = {
|
|
8
|
+
"name" => 'my item',
|
|
9
|
+
"info" => 'Lipsum', # Not defined in schema
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
class ItemValidator < Saphyr::Validator
|
|
13
|
+
field :id, :integer, gt: 0 # Not existing in data
|
|
14
|
+
field :name, :string, min: 5, max: 50
|
|
15
|
+
end
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
when trying to validate the data, we will get 2 errors:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
v = ItemValidator.new
|
|
22
|
+
v.validate data
|
|
23
|
+
puts "Validation : FAILED", "\n"
|
|
24
|
+
Saphyr::Helpers::Format.errors_to_text validator.errors
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Output:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
Validation : FAILED
|
|
31
|
+
|
|
32
|
+
path: //id
|
|
33
|
+
- type: strict_mode:missing_in_data
|
|
34
|
+
- data: {:field=>"id"}
|
|
35
|
+
- msg: Missing fields in data
|
|
36
|
+
|
|
37
|
+
path: //info
|
|
38
|
+
- type: strict_mode:missing_in_schema
|
|
39
|
+
- data: {:field=>"info"}
|
|
40
|
+
- msg: Missing fields in schema
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Disabling the strict mode
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
class ItemValidator < Saphyr::Validator
|
|
47
|
+
strict false
|
|
48
|
+
|
|
49
|
+
field :id, :integer, gt: 0
|
|
50
|
+
field :name, :string, min: 5, max: 50
|
|
51
|
+
end
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
In this case the validation wil succeed.
|
|
55
|
+
|
|
56
|
+
### Combining the ':required' option with strict mode
|
|
57
|
+
|
|
58
|
+
```ruby
|
|
59
|
+
|
|
60
|
+
class ItemValidator < Saphyr::Validator
|
|
61
|
+
field :id, :integer, gt: 0
|
|
62
|
+
field :name, :string, min: 5, max: 50
|
|
63
|
+
field :info, :string, min: 5, max: 50, required: false
|
|
64
|
+
end
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This will ensure that the following data example are both valid:
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
data1 = {
|
|
71
|
+
"id" => 235,
|
|
72
|
+
"name" => 'my item',
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
data2 = {
|
|
76
|
+
"id" => 235,
|
|
77
|
+
"name" => 'my item',
|
|
78
|
+
"info" => 'Lipsum ...',
|
|
79
|
+
}
|
|
80
|
+
```
|