jschematic 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +40 -0
- data/LICENSE +20 -0
- data/README.md +48 -0
- data/Rakefile +10 -0
- data/cucumber.yml +1 -0
- data/features/default.feature +72 -0
- data/features/dependencies.feature +73 -0
- data/features/enum.feature +26 -0
- data/features/format.feature +40 -0
- data/features/id.feature +75 -0
- data/features/items.feature +90 -0
- data/features/min_length_max_length.feature +20 -0
- data/features/minimum_maximum.feature +29 -0
- data/features/pattern.feature +6 -0
- data/features/pattern_properties.feature +18 -0
- data/features/properties.feature +124 -0
- data/features/required.feature +42 -0
- data/features/step_definitions/jschematic_steps.rb +62 -0
- data/features/support/env.rb +6 -0
- data/features/type.feature +76 -0
- data/jschematic.gemspec +22 -0
- data/lib/jschematic/attributes/additional_items.rb +35 -0
- data/lib/jschematic/attributes/additional_properties.rb +28 -0
- data/lib/jschematic/attributes/dependencies.rb +26 -0
- data/lib/jschematic/attributes/enum.rb +18 -0
- data/lib/jschematic/attributes/exclusive_maximum.rb +21 -0
- data/lib/jschematic/attributes/exclusive_minimum.rb +21 -0
- data/lib/jschematic/attributes/format.rb +56 -0
- data/lib/jschematic/attributes/items.rb +24 -0
- data/lib/jschematic/attributes/max_items.rb +18 -0
- data/lib/jschematic/attributes/max_length.rb +18 -0
- data/lib/jschematic/attributes/maximum.rb +22 -0
- data/lib/jschematic/attributes/min_items.rb +18 -0
- data/lib/jschematic/attributes/min_length.rb +18 -0
- data/lib/jschematic/attributes/minimum.rb +22 -0
- data/lib/jschematic/attributes/pattern.rb +18 -0
- data/lib/jschematic/attributes/pattern_properties.rb +23 -0
- data/lib/jschematic/attributes/properties.rb +38 -0
- data/lib/jschematic/attributes/required.rb +28 -0
- data/lib/jschematic/attributes/type.rb +61 -0
- data/lib/jschematic/attributes/unique_items.rb +18 -0
- data/lib/jschematic/attributes.rb +28 -0
- data/lib/jschematic/element.rb +25 -0
- data/lib/jschematic/errors.rb +34 -0
- data/lib/jschematic/schema.rb +77 -0
- data/lib/jschematic/validation_error.rb +13 -0
- data/lib/jschematic.rb +13 -0
- data/spec/jschematic/attributes/enum_spec.rb +14 -0
- data/spec/jschematic/attributes/max_items_spec.rb +15 -0
- data/spec/jschematic/attributes/min_items_spec.rb +15 -0
- data/spec/jschematic/attributes/minimum_maximum_spec.rb +33 -0
- data/spec/jschematic/attributes/required_spec.rb +29 -0
- data/spec/jschematic/attributes/type_spec.rb +63 -0
- data/spec/jschematic/attributes_spec.rb +12 -0
- data/spec/jschematic/errors_spec.rb +43 -0
- data/spec/jschematic/schema_spec.rb +58 -0
- data/spec/spec_helper.rb +16 -0
- metadata +199 -0
@@ -0,0 +1,124 @@
|
|
1
|
+
Feature: Core Schema: properties & additionalProperties
|
2
|
+
|
3
|
+
Scenario: instance property values must conform to schema property definitions
|
4
|
+
When the schema is:
|
5
|
+
"""
|
6
|
+
{
|
7
|
+
"properties": {
|
8
|
+
"name": { "type": "string" }
|
9
|
+
}
|
10
|
+
}
|
11
|
+
"""
|
12
|
+
Then '{ "name": "Felizberto" }' is valid JSON
|
13
|
+
And '{ "color": "red" }' is valid JSON
|
14
|
+
But '{ "name": 12345" }' is not valid JSON
|
15
|
+
|
16
|
+
Scenario: With extra attribute
|
17
|
+
When the schema is:
|
18
|
+
"""
|
19
|
+
{
|
20
|
+
"properties": {
|
21
|
+
"age": { "maximum": 25 }
|
22
|
+
}
|
23
|
+
}
|
24
|
+
"""
|
25
|
+
Then '{ "age": 25, "color": "red" }' is valid JSON
|
26
|
+
But '{ "age": 26, "color": "red" }' is not valid JSON
|
27
|
+
|
28
|
+
Scenario: deeply nested
|
29
|
+
When the schema is:
|
30
|
+
"""
|
31
|
+
{
|
32
|
+
"properties": {
|
33
|
+
"attributes": {
|
34
|
+
"properties": {
|
35
|
+
"attribute": {
|
36
|
+
"type": "string"
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
}
|
41
|
+
}
|
42
|
+
"""
|
43
|
+
Then this is valid JSON:
|
44
|
+
"""
|
45
|
+
{
|
46
|
+
"attributes": {
|
47
|
+
"attribute": "value"
|
48
|
+
}
|
49
|
+
}
|
50
|
+
"""
|
51
|
+
But this is not valid JSON:
|
52
|
+
"""
|
53
|
+
{
|
54
|
+
"attributes": {
|
55
|
+
"attribute": 2112
|
56
|
+
}
|
57
|
+
}
|
58
|
+
"""
|
59
|
+
|
60
|
+
Scenario: additionalProperties is false
|
61
|
+
When the schema is:
|
62
|
+
"""
|
63
|
+
{
|
64
|
+
"properties": {
|
65
|
+
"name": { "type": "string" },
|
66
|
+
"age": { "type": "integer" }
|
67
|
+
},
|
68
|
+
|
69
|
+
"additionalProperties": false
|
70
|
+
}
|
71
|
+
"""
|
72
|
+
Then this is valid JSON:
|
73
|
+
"""
|
74
|
+
{
|
75
|
+
"name": "Felizberto Albi",
|
76
|
+
"age": 24
|
77
|
+
}
|
78
|
+
"""
|
79
|
+
But this is not valid JSON:
|
80
|
+
"""
|
81
|
+
{
|
82
|
+
"name": "Felizberto Albi",
|
83
|
+
"age": 24,
|
84
|
+
"color": "red"
|
85
|
+
}
|
86
|
+
"""
|
87
|
+
|
88
|
+
Scenario: additionalProperties defined by a schema
|
89
|
+
When the schema is:
|
90
|
+
"""
|
91
|
+
{
|
92
|
+
"properties": {
|
93
|
+
"name": { "type": "string" },
|
94
|
+
"age": { "type": "integer" }
|
95
|
+
},
|
96
|
+
|
97
|
+
"additionalProperties": {
|
98
|
+
"type": "string"
|
99
|
+
}
|
100
|
+
}
|
101
|
+
"""
|
102
|
+
Then this is valid JSON:
|
103
|
+
"""
|
104
|
+
{
|
105
|
+
"name": "Felizberto Albi",
|
106
|
+
"age": 24,
|
107
|
+
"color": "red"
|
108
|
+
}
|
109
|
+
"""
|
110
|
+
But this is not valid JSON:
|
111
|
+
"""
|
112
|
+
{
|
113
|
+
"name": "Felizberto Albi",
|
114
|
+
"age": 24,
|
115
|
+
"number": 2112
|
116
|
+
}
|
117
|
+
"""
|
118
|
+
|
119
|
+
Scenario: when true
|
120
|
+
TODO: duh
|
121
|
+
Scenario: neither boolean nor schema
|
122
|
+
TODO: should fail loudly: move into spec
|
123
|
+
Scenario: empty instance
|
124
|
+
TODO: find out what the behavior here should be
|
@@ -0,0 +1,42 @@
|
|
1
|
+
Feature: Core schema: required
|
2
|
+
Scenario: default false
|
3
|
+
When the schema is:
|
4
|
+
"""
|
5
|
+
{
|
6
|
+
"properties": {
|
7
|
+
"color": {
|
8
|
+
"type": "string"
|
9
|
+
}
|
10
|
+
}
|
11
|
+
}
|
12
|
+
"""
|
13
|
+
Then '{}' is valid JSON
|
14
|
+
|
15
|
+
When the schema is:
|
16
|
+
"""
|
17
|
+
{
|
18
|
+
"properties": {
|
19
|
+
"color": {
|
20
|
+
"type": "string",
|
21
|
+
"required": false
|
22
|
+
}
|
23
|
+
}
|
24
|
+
}
|
25
|
+
"""
|
26
|
+
Then '{}' is valid JSON
|
27
|
+
|
28
|
+
Scenario: true
|
29
|
+
When the schema is:
|
30
|
+
"""
|
31
|
+
{
|
32
|
+
"properties": {
|
33
|
+
"color": {
|
34
|
+
"type": "string",
|
35
|
+
"required": true
|
36
|
+
}
|
37
|
+
}
|
38
|
+
}
|
39
|
+
"""
|
40
|
+
Then '{ "color": "red" }' is valid JSON
|
41
|
+
But '{}' is not valid JSON
|
42
|
+
And '{ "age": 24 }' is not valid JSON
|
@@ -0,0 +1,62 @@
|
|
1
|
+
When /^the schema is '(.+)'$/ do |schema|
|
2
|
+
@schema = parse(schema)
|
3
|
+
end
|
4
|
+
|
5
|
+
When "the schema is:" do |schema|
|
6
|
+
@schema = parse(schema)
|
7
|
+
end
|
8
|
+
|
9
|
+
Then /^'(.+)' is valid JSON$/ do |json|
|
10
|
+
assert_valid(parse(json), @schema)
|
11
|
+
end
|
12
|
+
|
13
|
+
Then /^'(.+)' is not valid JSON$/ do |json|
|
14
|
+
assert_invalid(parse(json), @schema)
|
15
|
+
end
|
16
|
+
|
17
|
+
Then "this is valid JSON:" do |json|
|
18
|
+
assert_valid(parse(json), @schema)
|
19
|
+
end
|
20
|
+
|
21
|
+
Then "this is not valid JSON:" do |json|
|
22
|
+
assert_invalid(parse(json), @schema)
|
23
|
+
end
|
24
|
+
|
25
|
+
Then "these are valid JSON:" do |instances|
|
26
|
+
instances.raw.each do |row|
|
27
|
+
assert_valid(parse(row[0]), @schema)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
Then "these are not valid JSON:" do |instances|
|
32
|
+
instances.raw.each do |row|
|
33
|
+
assert_invalid(parse(row[0]), @schema)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Then /^the id of "(.+)" is "(.+)"$/ do |title, uri|
|
38
|
+
schema = build_schema.find{ |el| el.title == title }
|
39
|
+
[schema.title, schema.id.to_s].should == [title, uri]
|
40
|
+
end
|
41
|
+
|
42
|
+
module JschematicWorld
|
43
|
+
def parse(json)
|
44
|
+
Yajl::Parser.parse(json)
|
45
|
+
rescue Yajl::ParseError => e
|
46
|
+
raise "Parsing '#{json}' failed with #{e.to_s}"
|
47
|
+
end
|
48
|
+
|
49
|
+
def assert_valid(json, raw_schema)
|
50
|
+
Jschematic.validate(json, raw_schema).should be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
def assert_invalid(json, raw_schema)
|
54
|
+
Jschematic.validate(json, raw_schema).should be_false
|
55
|
+
end
|
56
|
+
|
57
|
+
def build_schema
|
58
|
+
@_schema ||= Jschematic::Schema.new(@schema)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
World(JschematicWorld)
|
@@ -0,0 +1,76 @@
|
|
1
|
+
Feature: Core Schema: type
|
2
|
+
|
3
|
+
Scenario: string
|
4
|
+
When the schema is '{ "type": "string" }'
|
5
|
+
Then '"hello, world"' is valid JSON
|
6
|
+
|
7
|
+
Scenario: number
|
8
|
+
When the schema is '{ "type": "number" }'
|
9
|
+
Then '3.14159' is valid JSON
|
10
|
+
And '-123.41235' is valid JSON
|
11
|
+
And '2112' is valid JSON
|
12
|
+
But '"1234"' is not valid JSON
|
13
|
+
|
14
|
+
Scenario: integer
|
15
|
+
When the schema is '{ "type": "integer" }'
|
16
|
+
Then '2112' is valid JSON
|
17
|
+
And '-12' is valid JSON
|
18
|
+
But '3.14159' is not valid JSON
|
19
|
+
|
20
|
+
Scenario: boolean
|
21
|
+
When the schema is '{ "type": "boolean" }'
|
22
|
+
Then 'true' is valid JSON
|
23
|
+
And 'false' is valid JSON
|
24
|
+
|
25
|
+
Scenario: object
|
26
|
+
When the schema is '{ "type": "object" }'
|
27
|
+
Then this is valid JSON:
|
28
|
+
"""
|
29
|
+
{ "person": "felizberto" }
|
30
|
+
"""
|
31
|
+
|
32
|
+
Scenario: array
|
33
|
+
When the schema is '{ "type": "array" }'
|
34
|
+
Then this is valid JSON:
|
35
|
+
"""
|
36
|
+
["foo", "bar", "baz"]
|
37
|
+
"""
|
38
|
+
|
39
|
+
Scenario: null
|
40
|
+
When the schema is '{ "type": "null" }'
|
41
|
+
Then 'null' is valid JSON
|
42
|
+
|
43
|
+
Scenario: any
|
44
|
+
When the schema is '{ "type": "any" }'
|
45
|
+
Then these are valid JSON:
|
46
|
+
| "Felizberto" |
|
47
|
+
| 3.1415 |
|
48
|
+
| 2112 |
|
49
|
+
| true |
|
50
|
+
| { "age": 24 } |
|
51
|
+
| ["foo", "bar"] |
|
52
|
+
| null |
|
53
|
+
|
54
|
+
Scenario: union
|
55
|
+
When the schema is '{ "type": ["string", "number"] }'
|
56
|
+
Then '"Felizberto"' is valid JSON
|
57
|
+
And '2112' is valid JSON
|
58
|
+
But 'true' is not valid JSON
|
59
|
+
|
60
|
+
Scenario: union allowing null values
|
61
|
+
When the schema is '{ "type": ["string", "null"] }'
|
62
|
+
Then 'null' is valid JSON
|
63
|
+
|
64
|
+
Scenario: union with schema element
|
65
|
+
When the schema is:
|
66
|
+
"""
|
67
|
+
{
|
68
|
+
"type": [
|
69
|
+
"string",
|
70
|
+
{ "type": "integer", "maximum": 2112 }
|
71
|
+
]
|
72
|
+
}
|
73
|
+
"""
|
74
|
+
Then '"Felizberto"' is valid JSON
|
75
|
+
And '2112' is valid JSON
|
76
|
+
But '2113' is not valid JSON
|
data/jschematic.gemspec
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'jschematic'
|
3
|
+
s.version = '0.0.1'
|
4
|
+
s.authors = ["Mike Sassak"]
|
5
|
+
s.description = "JSON Schema v3 Validator"
|
6
|
+
s.summary = "jschematic #{s.version}"
|
7
|
+
s.email = "msassak@gmail.com"
|
8
|
+
s.homepage = "https://github.com/msassak/jschematic"
|
9
|
+
|
10
|
+
s.add_dependency 'addressable'
|
11
|
+
|
12
|
+
s.add_development_dependency 'cucumber'
|
13
|
+
s.add_development_dependency 'rspec'
|
14
|
+
s.add_development_dependency 'yajl-ruby'
|
15
|
+
|
16
|
+
s.rubygems_version = "1.3.7"
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
|
19
|
+
s.extra_rdoc_files = ["LICENSE", "README.md"]
|
20
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
21
|
+
s.require_path = "lib"
|
22
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class AdditionalItems
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(allowed, &block)
|
9
|
+
case items = block.call("items")
|
10
|
+
when Array
|
11
|
+
@allowed = allowed
|
12
|
+
@tuple_types_count = items.length
|
13
|
+
else
|
14
|
+
# TODO spec: additionalItems applies only with tuple-typing, so
|
15
|
+
# even if it is set to false we allow anything... should probably raise
|
16
|
+
# but the I-D is silent on the proper behavior in that case.
|
17
|
+
@allowed = true
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def accepts?(instance)
|
22
|
+
return true if TrueClass === @allowed
|
23
|
+
|
24
|
+
case @allowed
|
25
|
+
when FalseClass
|
26
|
+
(instance.length == @tuple_types_count) || fail_validation!("#{@tuple_types_count} items", "#{instance.length} items")
|
27
|
+
when Hash
|
28
|
+
schema = Schema.new(@allowed)
|
29
|
+
additional = instance[@tuple_types_count..-1]
|
30
|
+
additional.all?{ |item| schema.accepts?(item) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class AdditionalProperties
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
# TODO: rename value to allowed
|
9
|
+
def initialize(value, &block)
|
10
|
+
@value = value
|
11
|
+
@properties = block.call("properties").keys
|
12
|
+
end
|
13
|
+
|
14
|
+
def accepts?(instance)
|
15
|
+
case @value
|
16
|
+
when FalseClass
|
17
|
+
(@properties == instance.keys) || fail_validation!(@properties, instance.keys)
|
18
|
+
when Hash
|
19
|
+
schema = Schema.new(@value)
|
20
|
+
additional = instance.select{ |attribute, value| !@properties.include?(attribute) }
|
21
|
+
additional.all? do |attribute, value|
|
22
|
+
schema.accepts?(value)
|
23
|
+
end || fail_validation!(@value, instance)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class Dependencies
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(dependencies)
|
9
|
+
@dependencies = dependencies
|
10
|
+
end
|
11
|
+
|
12
|
+
def accepts?(instance)
|
13
|
+
instance.keys.all? do |property|
|
14
|
+
case deps = @dependencies[property]
|
15
|
+
when String, Array
|
16
|
+
[deps].flatten.all?{ |req| instance.keys.include?(req) }
|
17
|
+
when Hash
|
18
|
+
Schema.new(deps).accepts?(instance)
|
19
|
+
else
|
20
|
+
true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class Enum
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(enum)
|
9
|
+
raise "Enum requires an Array of possible values" unless Array === enum
|
10
|
+
@enum = enum
|
11
|
+
end
|
12
|
+
|
13
|
+
def accepts?(instance)
|
14
|
+
@enum.any?{ |e| e == instance } || fail_validation!("one of #{@enum}", instance)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class ExclusiveMaximum
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(enabled, &block)
|
9
|
+
@enabled = enabled
|
10
|
+
@maximum = block.call("maximum") if block_given?
|
11
|
+
raise "'exclusiveMaximum' depends on 'maximum'" unless @maximum
|
12
|
+
end
|
13
|
+
|
14
|
+
def accepts?(actual)
|
15
|
+
if @enabled
|
16
|
+
(actual < @maximum) || fail_validation!("< #{@maximum}", actual)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class ExclusiveMinimum
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(enabled, &block)
|
9
|
+
@enabled = enabled
|
10
|
+
@minimum = block.call("minimum") if block_given?
|
11
|
+
raise "'exclusiveMinimum' depends on 'minimum'" unless @minimum
|
12
|
+
end
|
13
|
+
|
14
|
+
def accepts?(actual)
|
15
|
+
if @enabled
|
16
|
+
(actual > @minimum) || fail_validation!("> #{@minimum}", actual)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'ipaddr'
|
2
|
+
require 'addressable/uri'
|
3
|
+
|
4
|
+
module Jschematic
|
5
|
+
module Attributes
|
6
|
+
module Format
|
7
|
+
def self.new(format)
|
8
|
+
case format
|
9
|
+
when "uri"
|
10
|
+
Uri.new
|
11
|
+
when "ip-address", "ipv6"
|
12
|
+
Ip.new(format)
|
13
|
+
else
|
14
|
+
NullFormat.new
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Uri
|
19
|
+
include Jschematic::Element
|
20
|
+
|
21
|
+
def accepts?(uri)
|
22
|
+
Addressable::URI.parse(uri)
|
23
|
+
rescue Addressable::URI::InvalidURIError
|
24
|
+
false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Ip
|
29
|
+
include Jschematic::Element
|
30
|
+
|
31
|
+
def initialize(version)
|
32
|
+
@method = case version
|
33
|
+
when "ip-address"
|
34
|
+
:ipv4?
|
35
|
+
when "ipv6"
|
36
|
+
:ipv6?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def accepts?(addr)
|
41
|
+
IPAddr.new(addr).send(@method)
|
42
|
+
rescue ArgumentError
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class NullFormat
|
48
|
+
include Jschematic::Element
|
49
|
+
|
50
|
+
def accepts?(instance)
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class Items
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(schema)
|
9
|
+
@schema = schema
|
10
|
+
end
|
11
|
+
|
12
|
+
def accepts?(instance)
|
13
|
+
case @schema
|
14
|
+
when Hash
|
15
|
+
instance.all?{ |item| Schema.new(@schema).accepts?(item) }
|
16
|
+
when Array
|
17
|
+
@schema.zip(instance).all?{ |schema, item| Schema.new(schema).accepts?(item) }
|
18
|
+
# TODO: There is a bug here similar to the one in the Union type;
|
19
|
+
# the error reported does not mention the failure is w/i an item attribute
|
20
|
+
end || fail_validation!(@schema, instance)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class MaxItems
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(max_items)
|
9
|
+
@max_items = max_items
|
10
|
+
end
|
11
|
+
|
12
|
+
def accepts?(instance)
|
13
|
+
return true unless Array === instance
|
14
|
+
(instance.length <= @max_items) || fail_validation!("at most #{@max_items}", instance.length)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class MinLength
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(length)
|
9
|
+
@length = length
|
10
|
+
end
|
11
|
+
|
12
|
+
def accepts?(instance)
|
13
|
+
return true unless String === instance
|
14
|
+
(instance.length >= @length) || fail_validation!("minimum length of #{@length}", instance.length)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class Maximum
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
attr_reader :maximum
|
9
|
+
|
10
|
+
def initialize(maximum)
|
11
|
+
@maximum = maximum
|
12
|
+
end
|
13
|
+
|
14
|
+
def accepts?(number)
|
15
|
+
return true unless maximum
|
16
|
+
return true unless (number.kind_of?(Integer) || number.kind_of?(Float))
|
17
|
+
|
18
|
+
(number <= maximum) || fail_validation!("<= #{@maximum}", number)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class MinItems
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(min_items)
|
9
|
+
@min_items = min_items
|
10
|
+
end
|
11
|
+
|
12
|
+
def accepts?(instance)
|
13
|
+
return true unless Array === instance
|
14
|
+
(instance.length >= @min_items) || fail_validation!("at least #{@min_items}", instance.length)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'jschematic/element'
|
2
|
+
|
3
|
+
module Jschematic
|
4
|
+
module Attributes
|
5
|
+
class MaxLength
|
6
|
+
include Jschematic::Element
|
7
|
+
|
8
|
+
def initialize(length)
|
9
|
+
@length = length
|
10
|
+
end
|
11
|
+
|
12
|
+
def accepts?(instance)
|
13
|
+
return true unless String === instance
|
14
|
+
(instance.length <= @length) || fail_validation!("maximum length of #{@length}", instance.length)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|