schemacop 1.0.2 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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.