jschematic 0.0.1
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.
- 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
|