json_schema 0.0.7 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,5 @@
1
+ require "json"
2
+
1
3
  module JsonSchema
2
4
  class Schema
3
5
  @@copyable = []
@@ -13,33 +15,116 @@ module JsonSchema
13
15
  class_eval("def #{attr} ; !@#{attr}.nil? ? @#{attr} : #{default} ; end")
14
16
  end
15
17
 
18
+ def initialize
19
+ @clones = Set.new
20
+ end
21
+
16
22
  # Rather than a normal schema, the node may be a JSON Reference. In this
17
23
  # case, no other attributes will be filled in except for #parent.
18
- attr_copyable :reference
24
+ attr_accessor :reference
25
+
26
+ attr_copyable :expanded
19
27
 
20
- # the schema keeps a reference to the data it was initialized from for JSON
21
- # Pointer resolution
28
+ # A reference to the data which the Schema was initialized from. Used for
29
+ # resolving JSON Pointer references.
30
+ #
31
+ # Type: Hash
22
32
  attr_copyable :data
23
33
 
24
- # parent and children schemas
34
+ #
35
+ # Relations
36
+ #
37
+
38
+ # Parent Schema object. Child may come from any of `definitions`,
39
+ # `properties`, `anyOf`, etc.
40
+ #
41
+ # Type: Schema
25
42
  attr_copyable :parent
26
43
 
27
- # the normalize URI of this schema
44
+ # Collection of clones of this schema object, meaning all Schemas that were
45
+ # initialized after the original. Used for JSON Reference expansion. The
46
+ # only copy not present in this set is the original Schema object.
47
+ #
48
+ # Type: Set[Schema]
49
+ attr_copyable :clones
50
+
51
+ # The normalized URI of this schema. Note that child schemas inherit a URI
52
+ # from their parent unless they have one explicitly defined, so this is
53
+ # likely not a unique value in any given schema hierarchy.
54
+ #
55
+ # Type: String
28
56
  attr_copyable :uri
29
57
 
30
- # basic descriptors
58
+ #
59
+ # Metadata
60
+ #
61
+
62
+ # Alters resolution scope. This value is used along with the parent scope's
63
+ # URI to build a new address for this schema. Relative ID's will append to
64
+ # the parent, and absolute URI's will replace it.
65
+ #
66
+ # Type: String
31
67
  attr_copyable :id
68
+
69
+ # Short title of the schema.
70
+ #
71
+ # Type: String
32
72
  attr_copyable :title
73
+
74
+ # More detailed description of the schema.
75
+ #
76
+ # Type: String
33
77
  attr_copyable :description
78
+
79
+ # Default JSON value for this particular schema
80
+ #
81
+ # Type: [any]
34
82
  attr_copyable :default
35
83
 
36
- # validation: any
84
+ #
85
+ # Validation: Any
86
+ #
87
+
88
+ # A collection of subschemas of which data must validate against the full
89
+ # set of to be valid.
90
+ #
91
+ # Type: Array[Schema]
37
92
  attr_copyable :all_of
93
+
94
+ # A collection of subschemas of which data must validate against any schema
95
+ # in the set to be be valid.
96
+ #
97
+ # Type: Array[Schema]
38
98
  attr_copyable :any_of
99
+
100
+ # A collection of inlined subschemas. Standard convention is to subschemas
101
+ # here and reference them from elsewhere.
102
+ #
103
+ # Type: Hash[String => Schema]
39
104
  attr_copyable :definitions
105
+
106
+ # A collection of objects that must include the data for it to be valid.
107
+ #
108
+ # Type: Array
40
109
  attr_copyable :enum
110
+
111
+ # A collection of subschemas of which data must validate against exactly
112
+ # one of to be valid.
113
+ #
114
+ # Type: Array[Schema]
41
115
  attr_copyable :one_of
116
+
117
+
118
+ # A subschema which data must not validate against to be valid.
119
+ #
120
+ # Type: Schema
42
121
  attr_copyable :not
122
+
123
+ # An array of types that data is allowed to be. The spec allows this to be
124
+ # a string as well, but the parser will always normalize this to an array
125
+ # of strings.
126
+ #
127
+ # Type: Array[String]
43
128
  attr_copyable :type
44
129
 
45
130
  # validation: array
@@ -97,7 +182,7 @@ module JsonSchema
97
182
 
98
183
  # allow booleans to be access with question mark
99
184
  alias :additional_items? :additional_items
100
- alias :additional_properties? :additional_properties
185
+ alias :expanded? :expanded
101
186
  alias :max_exclusive? :max_exclusive
102
187
  alias :min_exclusive? :min_exclusive
103
188
  alias :read_only? :read_only
@@ -123,6 +208,53 @@ module JsonSchema
123
208
  true
124
209
  end
125
210
 
211
+ def inspect
212
+ str = inspect_schema
213
+ str = JSON.pretty_generate(str).gsub(/\\?"/, '') if str.is_a?(Hash)
214
+ "\#<JsonSchema::Schema #{str}>"
215
+ end
216
+
217
+ def inspect_schema
218
+ if reference
219
+ str = reference.to_s
220
+ str += " [EXPANDED]" if expanded?
221
+ str += " [CLONE]" if !original?
222
+ str
223
+ else
224
+ hash = {}
225
+ @@copyable.each do |copyable|
226
+ next if [:@clones, :@data, :@parent, :@uri].include?(copyable)
227
+ if value = instance_variable_get(copyable)
228
+ if value.is_a?(Array)
229
+ if !value.empty?
230
+ hash[copyable] = value.map { |v| inspect_value(v) }
231
+ end
232
+ elsif value.is_a?(Hash)
233
+ if !value.empty?
234
+ hash[copyable] =
235
+ Hash[*value.map { |k, v| [k, inspect_value(v)] }.flatten]
236
+ end
237
+ else
238
+ hash[copyable] = inspect_value(value)
239
+ end
240
+ end
241
+ end
242
+ hash
243
+ end
244
+ end
245
+
246
+ def inspect_value(value)
247
+ if value.is_a?(Schema)
248
+ value.inspect_schema
249
+ else
250
+ value.inspect
251
+ end
252
+ end
253
+
254
+ def original?
255
+ !clones.include?(self)
256
+ end
257
+
126
258
  def validate(data)
127
259
  validator = Validator.new(self)
128
260
  valid = validator.validate(data)
@@ -86,13 +86,25 @@ module JsonSchema
86
86
  end
87
87
 
88
88
  def validate_additional_properties(schema, data, errors)
89
- return true if schema.additional_properties?
90
- if (extra = data.keys - schema.properties.keys).empty?
91
- true
89
+ return true if schema.additional_properties == true
90
+
91
+ extra = data.keys - schema.properties.keys
92
+
93
+ # schema indicates that all properties not in `properties` should be
94
+ # validated according to subschema
95
+ if schema.additional_properties.is_a?(Schema)
96
+ extra.each do |key|
97
+ validate_data(schema.additional_properties, data[key], errors)
98
+ end
99
+ # boolean indicates whether additional properties are allowed
92
100
  else
93
- message = %{Extra keys in object: #{extra.sort.join(", ")}.}
94
- errors << SchemaError.new(schema, message)
95
- false
101
+ if extra.empty?
102
+ true
103
+ else
104
+ message = %{Extra keys in object: #{extra.sort.join(", ")}.}
105
+ errors << SchemaError.new(schema, message)
106
+ false
107
+ end
96
108
  end
97
109
  end
98
110
 
@@ -143,8 +155,10 @@ module JsonSchema
143
155
  data =~ IPV4_PATTERN
144
156
  when "ipv6"
145
157
  data =~ IPV6_PATTERN
158
+ when "regex"
159
+ Regexp.new(data) rescue false
146
160
  when "uri"
147
- data =~ URI.regexp
161
+ URI.parse(data) rescue false
148
162
  when "uuid"
149
163
  data =~ UUID_PATTERN
150
164
  end
@@ -347,6 +361,7 @@ module JsonSchema
347
361
  return true if schema.properties.empty?
348
362
  valid = true
349
363
  schema.properties.each do |key, subschema|
364
+
350
365
  if value = data[key]
351
366
  valid = strict_and valid, validate_data(subschema, value, errors)
352
367
  end
@@ -0,0 +1,168 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-04/hyper-schema#",
3
+ "id": "http://json-schema.org/draft-04/hyper-schema#",
4
+ "title": "JSON Hyper-Schema",
5
+ "allOf": [
6
+ {
7
+ "$ref": "http://json-schema.org/draft-04/schema#"
8
+ }
9
+ ],
10
+ "properties": {
11
+ "additionalItems": {
12
+ "anyOf": [
13
+ {
14
+ "type": "boolean"
15
+ },
16
+ {
17
+ "$ref": "#"
18
+ }
19
+ ]
20
+ },
21
+ "additionalProperties": {
22
+ "anyOf": [
23
+ {
24
+ "type": "boolean"
25
+ },
26
+ {
27
+ "$ref": "#"
28
+ }
29
+ ]
30
+ },
31
+ "dependencies": {
32
+ "additionalProperties": {
33
+ "anyOf": [
34
+ {
35
+ "$ref": "#"
36
+ },
37
+ {
38
+ "type": "array"
39
+ }
40
+ ]
41
+ }
42
+ },
43
+ "items": {
44
+ "anyOf": [
45
+ {
46
+ "$ref": "#"
47
+ },
48
+ {
49
+ "$ref": "#/definitions/schemaArray"
50
+ }
51
+ ]
52
+ },
53
+ "definitions": {
54
+ "additionalProperties": {
55
+ "$ref": "#"
56
+ }
57
+ },
58
+ "patternProperties": {
59
+ "additionalProperties": {
60
+ "$ref": "#"
61
+ }
62
+ },
63
+ "properties": {
64
+ "additionalProperties": {
65
+ "$ref": "#"
66
+ }
67
+ },
68
+ "allOf": {
69
+ "$ref": "#/definitions/schemaArray"
70
+ },
71
+ "anyOf": {
72
+ "$ref": "#/definitions/schemaArray"
73
+ },
74
+ "oneOf": {
75
+ "$ref": "#/definitions/schemaArray"
76
+ },
77
+ "not": {
78
+ "$ref": "#"
79
+ },
80
+
81
+ "links": {
82
+ "type": "array",
83
+ "items": {
84
+ "$ref": "#/definitions/linkDescription"
85
+ }
86
+ },
87
+ "fragmentResolution": {
88
+ "type": "string"
89
+ },
90
+ "media": {
91
+ "type": "object",
92
+ "properties": {
93
+ "type": {
94
+ "description": "A media type, as described in RFC 2046",
95
+ "type": "string"
96
+ },
97
+ "binaryEncoding": {
98
+ "description": "A content encoding scheme, as described in RFC 2045",
99
+ "type": "string"
100
+ }
101
+ }
102
+ },
103
+ "pathStart": {
104
+ "description": "Instances' URIs must start with this value for this schema to apply to them",
105
+ "type": "string",
106
+ "format": "uri"
107
+ }
108
+ },
109
+ "definitions": {
110
+ "schemaArray": {
111
+ "type": "array",
112
+ "items": {
113
+ "$ref": "#"
114
+ }
115
+ },
116
+ "linkDescription": {
117
+ "title": "Link Description Object",
118
+ "type": "object",
119
+ "required": [ "href", "rel" ],
120
+ "properties": {
121
+ "href": {
122
+ "description": "a URI template, as defined by RFC 6570, with the addition of the $, ( and ) characters for pre-processing",
123
+ "type": "string"
124
+ },
125
+ "rel": {
126
+ "description": "relation to the target resource of the link",
127
+ "type": "string"
128
+ },
129
+ "title": {
130
+ "description": "a title for the link",
131
+ "type": "string"
132
+ },
133
+ "targetSchema": {
134
+ "description": "JSON Schema describing the link target",
135
+ "$ref": "#"
136
+ },
137
+ "mediaType": {
138
+ "description": "media type (as defined by RFC 2046) describing the link target",
139
+ "type": "string"
140
+ },
141
+ "method": {
142
+ "description": "method for requesting the target of the link (e.g. for HTTP this might be \"GET\" or \"DELETE\")",
143
+ "type": "string"
144
+ },
145
+ "encType": {
146
+ "description": "The media type in which to submit data along with the request",
147
+ "type": "string",
148
+ "default": "application/json"
149
+ },
150
+ "schema": {
151
+ "description": "Schema describing the data to submit along with the request",
152
+ "$ref": "#"
153
+ }
154
+ }
155
+ }
156
+ },
157
+ "links": [
158
+ {
159
+ "rel": "self",
160
+ "href": "{+id}"
161
+ },
162
+ {
163
+ "rel": "full",
164
+ "href": "{+($ref)}"
165
+ }
166
+ ]
167
+ }
168
+
@@ -0,0 +1,150 @@
1
+ {
2
+ "id": "http://json-schema.org/draft-04/schema#",
3
+ "$schema": "http://json-schema.org/draft-04/schema#",
4
+ "description": "Core schema meta-schema",
5
+ "definitions": {
6
+ "schemaArray": {
7
+ "type": "array",
8
+ "minItems": 1,
9
+ "items": { "$ref": "#" }
10
+ },
11
+ "positiveInteger": {
12
+ "type": "integer",
13
+ "minimum": 0
14
+ },
15
+ "positiveIntegerDefault0": {
16
+ "allOf": [ { "$ref": "#/definitions/positiveInteger" }, { "default": 0 } ]
17
+ },
18
+ "simpleTypes": {
19
+ "enum": [ "array", "boolean", "integer", "null", "number", "object", "string" ]
20
+ },
21
+ "stringArray": {
22
+ "type": "array",
23
+ "items": { "type": "string" },
24
+ "minItems": 1,
25
+ "uniqueItems": true
26
+ }
27
+ },
28
+ "type": "object",
29
+ "properties": {
30
+ "id": {
31
+ "type": "string",
32
+ "format": "uri"
33
+ },
34
+ "$schema": {
35
+ "type": "string",
36
+ "format": "uri"
37
+ },
38
+ "title": {
39
+ "type": "string"
40
+ },
41
+ "description": {
42
+ "type": "string"
43
+ },
44
+ "default": {},
45
+ "multipleOf": {
46
+ "type": "number",
47
+ "minimum": 0,
48
+ "exclusiveMinimum": true
49
+ },
50
+ "maximum": {
51
+ "type": "number"
52
+ },
53
+ "exclusiveMaximum": {
54
+ "type": "boolean",
55
+ "default": false
56
+ },
57
+ "minimum": {
58
+ "type": "number"
59
+ },
60
+ "exclusiveMinimum": {
61
+ "type": "boolean",
62
+ "default": false
63
+ },
64
+ "maxLength": { "$ref": "#/definitions/positiveInteger" },
65
+ "minLength": { "$ref": "#/definitions/positiveIntegerDefault0" },
66
+ "pattern": {
67
+ "type": "string",
68
+ "format": "regex"
69
+ },
70
+ "additionalItems": {
71
+ "anyOf": [
72
+ { "type": "boolean" },
73
+ { "$ref": "#" }
74
+ ],
75
+ "default": {}
76
+ },
77
+ "items": {
78
+ "anyOf": [
79
+ { "$ref": "#" },
80
+ { "$ref": "#/definitions/schemaArray" }
81
+ ],
82
+ "default": {}
83
+ },
84
+ "maxItems": { "$ref": "#/definitions/positiveInteger" },
85
+ "minItems": { "$ref": "#/definitions/positiveIntegerDefault0" },
86
+ "uniqueItems": {
87
+ "type": "boolean",
88
+ "default": false
89
+ },
90
+ "maxProperties": { "$ref": "#/definitions/positiveInteger" },
91
+ "minProperties": { "$ref": "#/definitions/positiveIntegerDefault0" },
92
+ "required": { "$ref": "#/definitions/stringArray" },
93
+ "additionalProperties": {
94
+ "anyOf": [
95
+ { "type": "boolean" },
96
+ { "$ref": "#" }
97
+ ],
98
+ "default": {}
99
+ },
100
+ "definitions": {
101
+ "type": "object",
102
+ "additionalProperties": { "$ref": "#" },
103
+ "default": {}
104
+ },
105
+ "properties": {
106
+ "type": "object",
107
+ "additionalProperties": { "$ref": "#" },
108
+ "default": {}
109
+ },
110
+ "patternProperties": {
111
+ "type": "object",
112
+ "additionalProperties": { "$ref": "#" },
113
+ "default": {}
114
+ },
115
+ "dependencies": {
116
+ "type": "object",
117
+ "additionalProperties": {
118
+ "anyOf": [
119
+ { "$ref": "#" },
120
+ { "$ref": "#/definitions/stringArray" }
121
+ ]
122
+ }
123
+ },
124
+ "enum": {
125
+ "type": "array",
126
+ "minItems": 1,
127
+ "uniqueItems": true
128
+ },
129
+ "type": {
130
+ "anyOf": [
131
+ { "$ref": "#/definitions/simpleTypes" },
132
+ {
133
+ "type": "array",
134
+ "items": { "$ref": "#/definitions/simpleTypes" },
135
+ "minItems": 1,
136
+ "uniqueItems": true
137
+ }
138
+ ]
139
+ },
140
+ "allOf": { "$ref": "#/definitions/schemaArray" },
141
+ "anyOf": { "$ref": "#/definitions/schemaArray" },
142
+ "oneOf": { "$ref": "#/definitions/schemaArray" },
143
+ "not": { "$ref": "#" }
144
+ },
145
+ "dependencies": {
146
+ "exclusiveMaximum": [ "maximum" ],
147
+ "exclusiveMinimum": [ "minimum" ]
148
+ },
149
+ "default": {}
150
+ }