schemacop 1.0.2 → 2.0.0

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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -1
  3. data/.rubocop.yml +59 -1
  4. data/CHANGELOG.md +10 -0
  5. data/README.md +389 -199
  6. data/Rakefile +2 -0
  7. data/VERSION +1 -1
  8. data/doc/Schemacop.html +41 -130
  9. data/doc/Schemacop/ArrayValidator.html +329 -0
  10. data/doc/Schemacop/BooleanValidator.html +145 -0
  11. data/doc/Schemacop/Collector.html +535 -0
  12. data/doc/Schemacop/Exceptions.html +39 -39
  13. data/doc/Schemacop/Exceptions/InvalidSchemaError.html +124 -0
  14. data/doc/Schemacop/Exceptions/ValidationError.html +124 -0
  15. data/doc/Schemacop/FieldNode.html +409 -0
  16. data/doc/Schemacop/FloatValidator.html +158 -0
  17. data/doc/Schemacop/HashValidator.html +263 -0
  18. data/doc/Schemacop/IntegerValidator.html +158 -0
  19. data/doc/Schemacop/NilValidator.html +145 -0
  20. data/doc/Schemacop/Node.html +1426 -0
  21. data/doc/Schemacop/NodeResolver.html +242 -0
  22. data/doc/Schemacop/NodeSupportingField.html +590 -0
  23. data/doc/Schemacop/NodeSupportingType.html +614 -0
  24. data/doc/Schemacop/NodeWithBlock.html +289 -0
  25. data/doc/Schemacop/NumberValidator.html +232 -0
  26. data/doc/Schemacop/ObjectValidator.html +288 -0
  27. data/doc/Schemacop/RootNode.html +171 -0
  28. data/doc/Schemacop/Schema.html +697 -0
  29. data/doc/Schemacop/StringValidator.html +295 -0
  30. data/doc/ScopedEnv.html +351 -0
  31. data/doc/_index.html +190 -47
  32. data/doc/class_list.html +24 -31
  33. data/doc/css/full_list.css +32 -31
  34. data/doc/css/style.css +244 -91
  35. data/doc/file.README.html +428 -232
  36. data/doc/file_list.html +26 -30
  37. data/doc/frames.html +7 -16
  38. data/doc/index.html +428 -232
  39. data/doc/inheritance.graphml +524 -0
  40. data/doc/inheritance.pdf +825 -0
  41. data/doc/js/app.js +106 -77
  42. data/doc/js/full_list.js +170 -135
  43. data/doc/method_list.html +494 -38
  44. data/doc/top-level-namespace.html +36 -36
  45. data/lib/schemacop.rb +22 -7
  46. data/lib/schemacop/collector.rb +34 -0
  47. data/lib/schemacop/exceptions.rb +2 -8
  48. data/lib/schemacop/field_node.rb +26 -0
  49. data/lib/schemacop/node.rb +127 -0
  50. data/lib/schemacop/node_resolver.rb +14 -0
  51. data/lib/schemacop/node_supporting_field.rb +62 -0
  52. data/lib/schemacop/node_supporting_type.rb +112 -0
  53. data/lib/schemacop/node_with_block.rb +16 -0
  54. data/lib/schemacop/root_node.rb +4 -0
  55. data/lib/schemacop/schema.rb +61 -0
  56. data/lib/schemacop/scoped_env.rb +18 -0
  57. data/lib/schemacop/validator/array_validator.rb +30 -0
  58. data/lib/schemacop/validator/boolean_validator.rb +5 -0
  59. data/lib/schemacop/validator/float_validator.rb +5 -0
  60. data/lib/schemacop/validator/hash_validator.rb +18 -0
  61. data/lib/schemacop/validator/integer_validator.rb +5 -0
  62. data/lib/schemacop/validator/nil_validator.rb +5 -0
  63. data/lib/schemacop/validator/number_validator.rb +19 -0
  64. data/lib/schemacop/validator/object_validator.rb +21 -0
  65. data/lib/schemacop/validator/string_validator.rb +37 -0
  66. data/schemacop.gemspec +7 -5
  67. data/test/custom_check_test.rb +86 -0
  68. data/test/custom_if_test.rb +95 -0
  69. data/test/nil_dis_allow_test.rb +41 -0
  70. data/test/short_forms_test.rb +316 -0
  71. data/test/test_helper.rb +6 -0
  72. data/test/types_test.rb +83 -0
  73. data/test/validator_array_test.rb +97 -0
  74. data/test/validator_boolean_test.rb +15 -0
  75. data/test/validator_float_test.rb +57 -0
  76. data/test/validator_hash_test.rb +71 -0
  77. data/test/validator_integer_test.rb +46 -0
  78. data/test/validator_nil_test.rb +13 -0
  79. data/test/validator_number_test.rb +60 -0
  80. data/test/validator_object_test.rb +87 -0
  81. data/test/validator_string_test.rb +76 -0
  82. metadata +78 -14
  83. data/doc/Schemacop/Exceptions/Base.html +0 -127
  84. data/doc/Schemacop/Exceptions/InvalidSchema.html +0 -141
  85. data/doc/Schemacop/Exceptions/Validation.html +0 -142
  86. data/doc/Schemacop/MethodValidation.html +0 -120
  87. data/doc/Schemacop/MethodValidation/ClassMethods.html +0 -196
  88. data/doc/Schemacop/Validator.html +0 -254
  89. data/lib/schemacop/validator.rb +0 -145
  90. data/test/schemacop_validator_test.rb +0 -257
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 05947ea28e58709d2fbdbb5407b1783fae64ddc8
4
- data.tar.gz: f33a9bacaf59ad149f29e809ff3417c86a2fc3c8
3
+ metadata.gz: 57fd66ee6db934d9c0c2026d2d9dab992d777b01
4
+ data.tar.gz: 27cda8b63a0962684c0f815145aa5e3fba9f3f6c
5
5
  SHA512:
6
- metadata.gz: 1a23f0191e0b8954cb9b0debe38067a3fb33921b52023c76b2d68b854d2447db13619f36e3184e705e951ac90a02cfcac71f2d50090a284ffd060ef04228d109
7
- data.tar.gz: 5698d5dac2965b9f453715bb66a584cabeff4875b707c1cb3ca4f675cc4f33f5059c3c9fb163c2fb0f6c0833dc7a9e5d7e01862272283fda995aae96609e682f
6
+ metadata.gz: 9fe380ead2ad62aa1d12171a49e1ca7d3ff3019d3c4839448818e724892e2d611702146cea0f12a5671a04db2ad98345a252da60d5aa8e9412e551112a2eb635
7
+ data.tar.gz: 3deefe749765e7e662f5878175ec9f1cb3aaebbd892c5d45ba0dd9b044bb6fd12baafd2ee22c13a5dc13e18d226ccd51d214b3830d96d2c15d0efe581c0a40ef
data/.gitignore CHANGED
@@ -13,4 +13,5 @@ tmtags
13
13
  /spec/reports
14
14
  /.yardoc
15
15
  .idea
16
- /*.gem
16
+ /*.gem
17
+ tags
data/.rubocop.yml CHANGED
@@ -1,15 +1,43 @@
1
1
  AllCops:
2
+ TargetRubyVersion: 2.3
3
+
2
4
  Exclude:
3
5
  - 'local/**/*'
4
6
  - 'vendor/**/*'
5
7
  - 'tmp/**/*'
6
- - '*.gemspec'
8
+ - 'target/**/*'
9
+ - 'log/**/*'
10
+ - 'db/schema.rb'
11
+ - 'locale/translations.rb'
12
+ - 'lib/scratch.rb'
7
13
 
8
14
  DisplayCopNames: true
9
15
 
16
+ Style/FrozenStringLiteralComment:
17
+ Enabled: false
18
+
19
+ Style/SignalException:
20
+ EnforcedStyle: only_fail
21
+
22
+ Style/ConditionalAssignment:
23
+ Enabled: false
24
+
25
+ Style/IndentArray:
26
+ EnforcedStyle: consistent
27
+
10
28
  Metrics/MethodLength:
11
29
  Enabled: false
12
30
 
31
+ Metrics/ClassLength:
32
+ Enabled: false
33
+
34
+ Metrics/ModuleLength:
35
+ Enabled: false
36
+
37
+ Metrics/ParameterLists:
38
+ Max: 5
39
+ CountKeywordArgs: false
40
+
13
41
  Metrics/AbcSize:
14
42
  Enabled: False
15
43
 
@@ -22,6 +50,12 @@ Metrics/PerceivedComplexity:
22
50
  Metrics/LineLength:
23
51
  Max: 160
24
52
 
53
+ Metrics/BlockNesting:
54
+ Enabled: false
55
+
56
+ Metrics/BlockLength:
57
+ Enabled: false
58
+
25
59
  Style/IfUnlessModifier:
26
60
  Enabled: false
27
61
 
@@ -31,6 +65,9 @@ Style/Documentation:
31
65
  Style/RedundantReturn:
32
66
  Enabled: false
33
67
 
68
+ Style/AsciiComments:
69
+ Enabled: false
70
+
34
71
  Style/GuardClause:
35
72
  Enabled: false
36
73
 
@@ -40,3 +77,24 @@ Style/ClassAndModuleChildren:
40
77
  SupportedStyles:
41
78
  - nested
42
79
  - compact
80
+ # Checks the style of children definitions at classes and modules.
81
+ #
82
+ # Basically there are two different styles:
83
+ #
84
+ # `nested` - have each child on a separate line
85
+ # class Foo
86
+ # class Bar
87
+ # end
88
+ # end
89
+ #
90
+ # `compact` - combine definitions as much as possible
91
+ # class Foo::Bar
92
+ # end
93
+ #
94
+ # The compact style is only forced, for classes / modules with one child.
95
+
96
+ Style/NumericPredicate:
97
+ Enabled: false
98
+
99
+ Style/FormatString:
100
+ Enabled: false
data/CHANGELOG.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # Change log
2
2
 
3
+ <!--
3
4
  ## master (unreleased)
4
5
 
5
6
  ### New features
@@ -7,6 +8,15 @@
7
8
  ### Bug fixes
8
9
 
9
10
  ### Changes
11
+ -->
12
+
13
+ ## 2.0.0 (2017-05-15)
14
+
15
+ ### Changes
16
+
17
+ * Completely rewritten the schema specification, breaking backwards
18
+ compatibility with version 1.x
19
+ * Added tons of unit tests
10
20
 
11
21
  ## 1.0.1 (2016-06-07)
12
22
 
data/README.md CHANGED
@@ -1,29 +1,49 @@
1
1
  [![Build Status](https://travis-ci.org/sitrox/schemacop.svg?branch=master)](https://travis-ci.org/sitrox/schemacop)
2
2
  [![Gem Version](https://badge.fury.io/rb/schemacop.svg)](https://badge.fury.io/rb/schemacop)
3
3
 
4
-
5
4
  # Schemacop
6
5
 
6
+ This is the README for Schemacop version 2, which **breaks backwards
7
+ compatibility** with version 1.
8
+
7
9
  Schemacop validates ruby structures consisting of nested hashes and arrays
8
- against simple schema definitions.
10
+ against schema definitions described by a simple DSL.
11
+
12
+ Examples:
9
13
 
10
- Example:
14
+ ```ruby
15
+ schema = Schema.new do
16
+ req :naming, :hash do
17
+ opt :first_name, :string
18
+ req :last_name, :string
19
+ end
20
+ opt! :age, :integer, min: 18
21
+ req? :password do
22
+ type :string, check: proc { |pw| pw.include?('*') }
23
+ type :integer
24
+ end
25
+ end
26
+
27
+ schema.validate!(
28
+ naming: { first_name: 'John',
29
+ last_name: 'Doe' },
30
+ age: 34,
31
+ password: 'my*pass'
32
+ )
33
+ ```
11
34
 
12
35
  ```ruby
13
- schema = {
14
- type: :hash,
15
- hash: {
16
- first_name: :string,
17
- last_name: :string
18
- }
19
- }
20
-
21
- data = {
22
- first_name: 'John',
23
- last_name: 'Doe'
24
- }
25
-
26
- Schemacop.validate!(schema, data)
36
+ schema2 = Schema.new do
37
+ req :description,
38
+ :string,
39
+ if: proc { |str| str.start_with?('Abstract: ') },
40
+ max: 35,
41
+ check: proc { |str| !str.end_with?('.') }
42
+ req :description, :string, min: 35
43
+ end
44
+
45
+ schema2.validate!(description: 'Abstract: a short description')
46
+ schema2.validate!(description: 'Since this is no abstract, we expect it to be longer.')
27
47
  ```
28
48
 
29
49
  ## Installation
@@ -34,289 +54,459 @@ To install the **Schemacop** gem:
34
54
  $ gem install schemacop
35
55
  ```
36
56
 
37
- To install it using `bundler` (recommended for any application), add it
38
- to your `Gemfile`:
57
+ To install it using `bundler` (recommended for any application), add it to your
58
+ `Gemfile`:
39
59
 
40
60
  ```ruby
41
61
  gem 'schemacop'
42
62
  ```
43
63
 
44
- ## Basic usage
64
+ ## Basics
45
65
 
46
- Schemacop's interface is very simple:
66
+ Since there is no explicit typing in Ruby, it can be hard to make sure that a
67
+ method is recieving exactly the right kind of data it needs. The idea of this
68
+ gem is to define a schema at boot time that will validate the data being passed
69
+ around at runtime. Those two steps look as follows:
70
+
71
+ At boot time:
47
72
 
48
73
  ```ruby
49
- Schemacop.validate!(schema, data)
74
+ my_schema = Schema.new do
75
+ # Your specification goes here
76
+ end
50
77
  ```
51
78
 
52
- It will throw an exception if either the schema is wrong or the given data does
53
- not comply with the schema. See section *Exceptions* for more information.
79
+ At runtime:
80
+
81
+ ```ruby
82
+ my_shema.validate!(
83
+ # Your data goes here
84
+ )
85
+ ```
54
86
 
55
- ## Defining schemas
87
+ `validate!` will fail if the data given to it does not match what was specified
88
+ in the schema.
56
89
 
57
- Schemacop can validate recursive structures of arrays nested into hashes and
58
- vice-versa. 'Leaf-nodes' can be of any data type, but their internal structure
59
- is not validated.
90
+ ### Type lines vs. Field lines
60
91
 
61
- Schema definitions are always a hash, even if they specify an array. Each level
62
- of a definition hash has to define a type.
92
+ Schemacop uses a DSL (domain-specific language) to let you describe your
93
+ schemas. We distinguish between two kinds of identifiers:
63
94
 
64
- You can specify any type, but only the types `:hash` and `:array` allow you to
65
- specify a sub structure.
95
+ - Field Lines: We call a key-value pair (like the contents of a hash) a *field*.
96
+ A field line typically starts with the keyword `req` (for a required field) or
97
+ `opt` (for an optional field).
66
98
 
67
- ### Defining hashes
99
+ - Type Lines: Those start with the keyword `type` and specify the data type to
100
+ be accepted with a corresponding symbol (e.g. `:integer` or `:boolean`). You
101
+ can have multiple Type Lines for a Field Line in order to indicate that the
102
+ field's value can be of one of the specified types.
68
103
 
69
- Once a level is defined as a hash (`type: :hash`), you can provide the key
70
- `hash` which in turn specifies the keys contained in that hash:
104
+ If you don't use any short forms, a schema definition would be something like
105
+ this:
71
106
 
72
107
  ```ruby
73
- {
74
- type: :hash,
75
- hash: {
76
- first_name: { type: :string },
77
- last_name: { type: :string }
78
- }
79
- }
108
+ s = Schema.new do
109
+ type :integer
110
+ type :hash do
111
+ req 'name' do
112
+ type :boolean
113
+ end
114
+ end
115
+ end
80
116
  ```
81
117
 
82
- If you don't provide the `:hash` key, the hash won't be validated (other than
83
- the verification that it really is a hash):
118
+ The above schema would accept either an integer or a hash with exactly one field
119
+ with key 'present' of type String and value of type Boolean (either TrueClass or
120
+ FalseClass).
84
121
 
85
- ```ruby
86
- { type: :hash }
87
- ```
122
+ We will see Type and Field lines in more detail below.
123
+
124
+ ### `validate` vs `validate!` vs `valid?`
125
+
126
+ The method `validate` will return a `Collector` object that contains all
127
+ validation errors (if any), whereas `validate!` will accumulate all violations
128
+ and finally throw an exception describing them.
129
+
130
+ For simply querying the validity of some data, use the methods `valid?` or
131
+ `invalid?`.
132
+
133
+ ## Schemacop's DSL
134
+
135
+ In this section, we will ignore [short forms](#short-forms) and explicitly
136
+ write out everything.
137
+
138
+ Inside the block given at the schema instantiation (`Schema.new do ... end`),
139
+ the following kinds of method calls are allowed (where the outermost must be a
140
+ Type Line):
88
141
 
89
- Hash definitions can be nested deeply:
142
+ ### Type Line
143
+
144
+ A Type Line always starts with the identifier `type` and specifies a possible
145
+ data type for a given field (if inside a Field Line) or the given data structure
146
+ (if directly below the schema instantiation).
147
+
148
+ Type Lines are generally of the form
90
149
 
91
150
  ```ruby
92
- {
93
- type: :hash,
94
- hash: {
95
- name: {
96
- type: :hash,
97
- hash: {
98
- first_name: { type: :string },
99
- last_name: { type: :string }
100
- }
101
- }
102
- }
103
- }
151
+ type :my_type, option_1: value_1, ..., option_n: value_n
104
152
  ```
105
153
 
106
- ### Defining arrays
154
+ where `:my_type` is a supported symbol (see section [Types](#types) below for
155
+ supported types).
156
+
157
+ #### General options
158
+
159
+ Some types support specific options that allow additional checks on the nature
160
+ of the data (such as the `min` option for type `:number`). The following options
161
+ are supported by all types:
162
+
163
+ ##### Option `if`
164
+
165
+ This option takes a proc (or a lambda) as value. The proc will be called when
166
+ checking whether or not the data being analyzed fits a certain type. The data is
167
+ given to the proc, which has to return either true or false. If it returns true,
168
+ the type of the given data is considered correct and the data will be validated
169
+ if further options are given.
170
+
171
+ Note that the proc in `if` will only get called if the type (`:my_type` from
172
+ above) fits the data already. You can use the option `if` in order to say: "Even
173
+ if the data is of type `:my_type`, I consider it having the wrong type if my
174
+ proc returns false."
175
+
176
+ Consider a scenario in which you want to have the following rule set:
177
+
178
+ - Only integers may be given
179
+ - Odd integers must be no larger than 15
180
+ - No limitations for even integers
107
181
 
108
- When you define a level as an array (`type: :array`), you can provide further
109
- specification of the array's contents uby supplying the key `:array`:
182
+ The corresponding schema would look as follows:
110
183
 
111
184
  ```ruby
112
- {
113
- type: :array,
114
- array: {
115
- type: :string
116
- }
117
- }
185
+ Schma.new do
186
+ type :integer, if: proc { |data| data.odd? }, max: 15
187
+ type :integer
188
+ end
118
189
  ```
119
190
 
120
- This example would define an array of strings.
191
+ Here, the first type line will only accept odd numbers and the option `max: 15`
192
+ provided by the `:integer` validator will discard numbers higher than 15.
121
193
 
122
- Arrays can nest hashes and vice-versa:
194
+ Since the first line only accepts odd numbers, it doesn't apply for even numbers
195
+ (due to the proc given to `if` they are considered to be of the wrong type) and
196
+ control falls through to the second type line accepting any integer.
197
+
198
+ ##### Option `check`
199
+
200
+ This option allows you to perform arbitrary custom checks for a given data type.
201
+ Just like `if`, `check` takes a proc or lambda as a value, but it runs *after*
202
+ the type checking, meaning that it only gets executed if the data has the right
203
+ type and the proc in `if` (if any) has returned true.
204
+
205
+ The proc passed to the `check` option is given the data being analyzed. It is to
206
+ return true if the data passes the custom check. If it returns false, Schemacop
207
+ considers the data to be invalid.
208
+
209
+ The following example illustrates the use of the option `check`: Consider a
210
+ scenario in which you want the following rule set:
211
+
212
+ - Data must be of type String
213
+ - The string must be longer than 5 characters
214
+ - The second character must be an 'r'
215
+
216
+ The corresponding schema would look as follows:
123
217
 
124
218
  ```ruby
125
- {
126
- type: :array,
127
- array: {
128
- type: :string
129
- }
130
- }
219
+ Schema.new do
220
+ type :string, min: 5, check: proc { |data| data[1] == 'r'}
221
+ end
131
222
  ```
132
223
 
133
- If you don't provide the `:array` key, the array contents won't be validated:
224
+ The above Type Line has type `:string` and two options (`min` and `check`). The
225
+ option `min` is supported by the `:string` validator (covered later).
226
+
227
+ ### Field Line
228
+
229
+ Inside a Type Line of type `:hash` or `Hash`, you may specify an arbitrary
230
+ number of field lines (one for each key-value pair you want to be in the hash).
231
+
232
+ Field Lines start with one of the following six identifiers: `req`, `req?`,
233
+ `req!`, `opt`, `opt?` or `opt!`:
234
+
235
+ - The suffix `-!` means that the field must not be nil.
236
+
237
+ - The suffix `-?` means that the field may be nil.
238
+
239
+ - The prefix `req-` denotes a required field (validation fails if the given data
240
+ hash doesn't define it). `req` is a shorthand notation for `req!` (meaning
241
+ that by default, a required field cannot be nil).
242
+
243
+ - The prefix `opt-` denotes an optional field. `opt` is a shorthand notation for
244
+ `opt?` (meaning that by default, an optional field may be nil).
245
+
246
+ To summarize:
247
+
248
+ - `req` or `req!`: required and non-nil
249
+ - `req?`: required but may be nil
250
+ - `opt` or `opt?`: optional and may be nil
251
+ - `opt!`: optional but non-nil
252
+
253
+ You then pass a block with a single or multiple Type Lines to the field.
254
+
255
+ Example: The following schema defines a hash that has a required non-nil field
256
+ of type String under the key `:name` (of type Symbol) and an optional but
257
+ non-nil field of type Integer or Date under the key `:age`.
134
258
 
135
259
  ```ruby
136
- { type: :array }
260
+ Schema.new do
261
+ type :hash do
262
+ req :name do
263
+ type :string
264
+ end
265
+ opt! :age do
266
+ type :integer
267
+ type :object, classes: Date
268
+ end
269
+ end
270
+ end
137
271
  ```
138
272
 
273
+ You might find the notation cumbersome, and you'd be right to say so. Luckily
274
+ there are plenty of short forms available which we will see below.
275
+
139
276
  ## Types
140
277
 
141
- For each level in your schema, you can specify the type in one of the following
142
- manors:
278
+ The following types are supported by Schemacop:
279
+ <!-- TODO: Test the following statement: (you can easily extend them by writing
280
+ another `your_validator.rb` class under `validator/`): -->
281
+
282
+ * `:boolean` accepts a Ruby TrueClass or FalseClass instance.
283
+
284
+ * `:integer` accepts a Ruby Integer.
285
+
286
+ - supported options: `min`, `max` (lower / upper bound)
287
+
288
+ * `:float` accepts a Ruby Float.
289
+
290
+ - supported options: `min`, `max` (lower / upper bound)
291
+
292
+ * `:number` accepts a Ruby Integer or Float.
293
+
294
+ - supported options: `min`, `max` (lower / upper bound)
143
295
 
144
- - A ruby class:
296
+ * `:string` accepts a Ruby String.
145
297
 
146
- ```ruby
147
- { type: String }
148
- ```
298
+ - supported options: `min`, `max` (bounds for string length)
149
299
 
150
- - A type alias (see {Schemacop::Validator::TYPE_ALIASES} for a full list of
151
- available type aliasses):
300
+ * `:object` accepts an arbitrary Ruby object (any object if no option is given).
152
301
 
153
- ```ruby
154
- { type: :boolean }
155
- ```
302
+ - supported option: `classes`: Ruby class (or an array of them) that will be
303
+ the only recognized filters. Unlike other options, this one affects not the
304
+ validation but the type recognition, meaning that you can have multiple Type
305
+ Lines with different `classes` option for the same field, each having its
306
+ own validation (e.g. through the option `check`).
156
307
 
157
- - A list of ruby classes or type aliases:
308
+ * `:array` accepts a Ruby Array.
158
309
 
159
- ```ruby
160
- { type: [String, :integer] }
161
- ```
310
+ - supported options: `min`, `max` (bounds for array size) and `nil`: TODO
162
311
 
163
- When specifying more than one type, it is validated that the given data
164
- structure matches *one* of the given types.
312
+ - accepts a block with an arbitrary number of Type Lines.
165
313
 
166
- If you specify both `:array` and `:hash` in such a type array, you can provide
167
- a specification for both `array` and `hash` types:
314
+ - TODO no lookahead for different arrays, see
315
+ validator_array_test#test_multiple_arrays
168
316
 
169
- ```ruby
170
- {
171
- type: [:array, :hash],
172
- array: {
173
- type: :string
174
- },
175
- hash: {
176
- first_name: :string
177
- }
178
- }
179
- ```
317
+ * `:hash` accepts a Ruby Hash.
180
318
 
181
- It will then determine which specification to use based on the actual data.
319
+ - accepts a block with an arbitrary number of Field Lines.
182
320
 
183
- ## Null and required
321
+ * `:nil`: accepts a Ruby NilClass instance. If you want to allow `nil` as a
322
+ value in a field, see above for the usage of the suffixes `-!` and `-?` for
323
+ Field Lines.
184
324
 
185
- Using the optional parameters `required` and `null`, you can control whether a
186
- specific substructure must be provided (`required`) and if it can be `nil`
187
- (`null`).
325
+ All types support the options `if` and `check` (see the section about Type Lines
326
+ above).
188
327
 
189
- These two parameters can be combined in any way.
328
+ ## Short forms
190
329
 
191
- ### Required validation
330
+ For convenience, the following short forms may be used (and combined if
331
+ possible).
192
332
 
193
- When validating with `required = false`, it means that the whole key can be
194
- omitted. As an example:
333
+ ### Passing a type to a Field Line or schema
334
+
335
+ Instead of adding a Type Line in the block of a Field Line, you can omit `do
336
+ type ... end` and directly write the type after the key of the field.
337
+
338
+ Note that when using this short form, you may not give a block to the Field
339
+ Line.
195
340
 
196
341
  ```ruby
197
- # Successfully validates data hash: {}
198
- {
199
- type: :hash,
200
- hash: {
201
- first_name: { type: :string, required: false }
202
- }
203
- }
342
+ # Long form
343
+ req :name do
344
+ type :string, min: 2, max: 5
345
+ end
346
+
347
+ # Short form
348
+ req :name, :string, min: 2, max: 5
204
349
  ```
205
350
 
206
- ### Null validation
351
+ This means that the value under the key `:name` of type Symbol must be a String
352
+ containing 2 to 5 characters.
207
353
 
208
- When validating with `null = true`, the key must still be present, but it can
209
- also be `nil`.
354
+ The short form also works in the schema instantiation:
210
355
 
211
356
  ```ruby
212
- # Successfully validates data hash: { first_name: nil }
213
- {
214
- type: :hash,
215
- hash: {
216
- first_name: { type: :string, null: false }
217
- }
218
- }
357
+ # Long form
358
+ Schema.new do
359
+ type :string, min: 2, max: 5
360
+ end
361
+
362
+ # Short form
363
+ Schema.new(:string, min: 2, max: 5)
219
364
  ```
220
365
 
221
- ## Allowed values
366
+ This means that the data given to the schema must be a String that is between 2
367
+ and 5 characters long.
368
+
369
+ ### Passing multiple types at once
222
370
 
223
- For any level, you can optionally specify an array of values that are allowed.
371
+ You can specify several types at once by putting them in an array.
224
372
 
225
- For example:
373
+ Note that when using this short form, you may not give any options.
226
374
 
227
375
  ```ruby
228
- {
229
- type: :hash,
230
- hash: {
231
- category: { type: :integer, allowed_values: [1, 2, 3] }
232
- }
233
- }
376
+ # Long form
377
+ opt! :age do
378
+ type :string
379
+ type :integer
380
+ type :boolean
381
+ end
382
+
383
+ # Short form
384
+ opt! :age do
385
+ type [:string, :integer, :boolean]
386
+ end
234
387
  ```
235
388
 
236
- ## Shortcuts
389
+ Combined with previous short form:
237
390
 
238
- ### Type shortcut
391
+ ```ruby
392
+ opt! :age, [:string, :integer, :boolean]
393
+ ```
239
394
 
240
- If you'd just like to define a type for a level but don't need to supply any
241
- additional information, you can just skip passing an extra hash and just pass
242
- the type instead.
395
+ This also works in the schema instantiation:
243
396
 
244
- For example, the following
397
+ ```ruby
398
+ Schema.new([:string, :integer, :boolean])
399
+ ```
400
+
401
+ This means that the schema will validate any data of type String, Integer,
402
+ TrueClass or FalseClass.
403
+
404
+ ### Omitting the Type Line in a Field Line
405
+
406
+ If you don't specify the type of a field, it will default to `:object` with no
407
+ options, meaning that the field will accept any kind of data:
245
408
 
246
409
  ```ruby
247
- {
248
- type: :array,
249
- array: {
250
- type: :string
251
- }
252
- }
410
+ # Long form
411
+ req? :child do
412
+ type :object
413
+ end
414
+
415
+ # Short form
416
+ req? :child
253
417
  ```
254
418
 
255
- can also be written as:
419
+ ### Omitting the Type Line in schema instantiation
420
+
421
+ If you don't give a Type Line to a schema, it will accept data of type Hash.
422
+ Therefore, if you validate Hashes only, you can omit the Type Line and directly
423
+ write Field Lines in the schema instantiation:
256
424
 
257
425
  ```ruby
258
- {
259
- type: :array,
260
- array: :string
261
- }
426
+ # Long form
427
+ Schema.new do
428
+ type :hash do
429
+ req :name do
430
+ # ...
431
+ end
432
+ end
433
+ end
434
+
435
+ # Short form
436
+ Schema.new do
437
+ req :name do
438
+ # ...
439
+ end
440
+ end
262
441
  ```
263
442
 
264
- ### Quick hash and array
443
+ ### Shortform for subtypes
265
444
 
266
- When specifying a level as hash or array and you're further specifying the
267
- hashe's fields or the array's content types, you can omit the `type` key.
445
+ In case of nested arrays, you can group all Type Lines to a single one.
268
446
 
269
- For example, the following
447
+ Note that any options or block passed to the grouped Type Line will be given to
448
+ the innermost (last) type.
270
449
 
271
450
  ```ruby
272
- {
273
- type: :array,
274
- array: {
275
- type: :string
276
- }
277
- }
451
+ # Long form
452
+ type :array do
453
+ type :integer, min: 3
454
+ end
455
+
456
+ # Short form
457
+ type :array, :integer, min: 3
278
458
  ```
279
459
 
280
- can also be written as:
460
+ A more complex example:
461
+
462
+ Long form:
281
463
 
282
464
  ```ruby
283
- {
284
- array: :string
285
- }
465
+ Schema.new do
466
+ type :hash do
467
+ req 'nutrition' do
468
+ type :array do
469
+ type :array do
470
+ type :hash, check: proc { |h| h.member?(:food) || h.member?(:drink) } do
471
+ opt! :food do
472
+ type :object
473
+ end
474
+ opt! :drink do
475
+ type :object
476
+ end
477
+ end
478
+ end
479
+ end
480
+ end
481
+ end
482
+ end
286
483
  ```
287
484
 
288
- ## Example schema
485
+ Short form (with this short form others from above):
289
486
 
290
487
  ```ruby
291
- {
292
- hash: {
293
- id: [Integer, String],
294
- name: :string,
295
- meta: {
296
- hash: {
297
- groups: { array: :integer },
298
- birthday: Date,
299
- comment: {
300
- type: :string,
301
- required: false,
302
- null: true
303
- },
304
- ar_object: User
305
- }
306
- }
307
- },
308
- }
488
+ Schema.new do
489
+ req 'nutrition', :array, :array, :hash, check: proc { |h| h.member?(:food) || h.member?(:drink) } do
490
+ opt! :food
491
+ opt! :drink
492
+ end
493
+ end
309
494
  ```
310
495
 
496
+ This example accepts a hash with exactly one String key 'nutrition' with value
497
+ of type Array with children of type Array with children of type Hash in which at
498
+ least one of the Symbol keys `:food` and `:drink` (with any non-nil value type)
499
+ is present.
500
+
311
501
  ## Exceptions
312
502
 
313
503
  Schemacop will throw one of the following checked exceptions:
314
504
 
315
- * {Schemacop::Exceptions::InvalidSchema}
505
+ * {Schemacop::Exceptions::InvalidSchemaError}
316
506
 
317
507
  This exception is thrown when the given schema definition format is invalid.
318
508
 
319
- * {Schemacop::Exceptions::Validation}
509
+ * {Schemacop::Exceptions::ValidationError}
320
510
 
321
511
  This exception is thrown when the given data does not comply with the given
322
512
  schema definition.
@@ -336,9 +526,9 @@ Schemacop will throw one of the following checked exceptions:
336
526
  ## Contributors
337
527
 
338
528
  Thanks to [Rubocop](https://github.com/bbatsov/rubocop) for great inspiration
339
- concerning their name and the structure of their README file. And special thanks to
340
- [SubGit](http://www.subgit.com/) for their great open source licensing.
529
+ concerning their name and the structure of their README file. And special thanks
530
+ to [SubGit](http://www.subgit.com/) for their great open source licensing.
341
531
 
342
532
  ## Copyright
343
533
 
344
- Copyright (c) 2016 Sitrox. See `LICENSE` for further details.
534
+ Copyright (c) 2017 Sitrox. See `LICENSE` for further details.