json_schemer 1.0.3 → 2.4.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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +7 -8
  3. data/CHANGELOG.md +96 -0
  4. data/Gemfile.lock +23 -14
  5. data/README.md +343 -20
  6. data/json_schemer.gemspec +8 -3
  7. data/lib/json_schemer/configuration.rb +31 -0
  8. data/lib/json_schemer/content.rb +18 -0
  9. data/lib/json_schemer/draft201909/meta.rb +320 -0
  10. data/lib/json_schemer/draft201909/vocab/applicator.rb +104 -0
  11. data/lib/json_schemer/draft201909/vocab/core.rb +45 -0
  12. data/lib/json_schemer/draft201909/vocab.rb +31 -0
  13. data/lib/json_schemer/draft202012/meta.rb +364 -0
  14. data/lib/json_schemer/draft202012/vocab/applicator.rb +382 -0
  15. data/lib/json_schemer/draft202012/vocab/content.rb +52 -0
  16. data/lib/json_schemer/draft202012/vocab/core.rb +160 -0
  17. data/lib/json_schemer/draft202012/vocab/format_annotation.rb +23 -0
  18. data/lib/json_schemer/draft202012/vocab/format_assertion.rb +23 -0
  19. data/lib/json_schemer/draft202012/vocab/meta_data.rb +30 -0
  20. data/lib/json_schemer/draft202012/vocab/unevaluated.rb +104 -0
  21. data/lib/json_schemer/draft202012/vocab/validation.rb +290 -0
  22. data/lib/json_schemer/draft202012/vocab.rb +105 -0
  23. data/lib/json_schemer/draft4/meta.rb +161 -0
  24. data/lib/json_schemer/draft4/vocab/validation.rb +38 -0
  25. data/lib/json_schemer/draft4/vocab.rb +18 -0
  26. data/lib/json_schemer/draft6/meta.rb +172 -0
  27. data/lib/json_schemer/draft6/vocab.rb +16 -0
  28. data/lib/json_schemer/draft7/meta.rb +183 -0
  29. data/lib/json_schemer/draft7/vocab/validation.rb +69 -0
  30. data/lib/json_schemer/draft7/vocab.rb +30 -0
  31. data/lib/json_schemer/errors.rb +1 -0
  32. data/lib/json_schemer/format/duration.rb +23 -0
  33. data/lib/json_schemer/format/json_pointer.rb +18 -0
  34. data/lib/json_schemer/format.rb +127 -106
  35. data/lib/json_schemer/keyword.rb +56 -0
  36. data/lib/json_schemer/location.rb +25 -0
  37. data/lib/json_schemer/openapi.rb +38 -0
  38. data/lib/json_schemer/openapi30/document.rb +1672 -0
  39. data/lib/json_schemer/openapi30/meta.rb +34 -0
  40. data/lib/json_schemer/openapi30/vocab/base.rb +18 -0
  41. data/lib/json_schemer/openapi30/vocab.rb +12 -0
  42. data/lib/json_schemer/openapi31/document.rb +1557 -0
  43. data/lib/json_schemer/openapi31/meta.rb +136 -0
  44. data/lib/json_schemer/openapi31/vocab/base.rb +127 -0
  45. data/lib/json_schemer/openapi31/vocab.rb +18 -0
  46. data/lib/json_schemer/output.rb +56 -0
  47. data/lib/json_schemer/resources.rb +24 -0
  48. data/lib/json_schemer/result.rb +242 -0
  49. data/lib/json_schemer/schema.rb +433 -0
  50. data/lib/json_schemer/version.rb +1 -1
  51. data/lib/json_schemer.rb +235 -32
  52. metadata +119 -18
  53. data/lib/json_schemer/schema/base.rb +0 -677
  54. data/lib/json_schemer/schema/draft4.json +0 -149
  55. data/lib/json_schemer/schema/draft4.rb +0 -44
  56. data/lib/json_schemer/schema/draft6.json +0 -155
  57. data/lib/json_schemer/schema/draft6.rb +0 -25
  58. data/lib/json_schemer/schema/draft7.json +0 -172
  59. data/lib/json_schemer/schema/draft7.rb +0 -32
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft4
4
+ module Vocab
5
+ module Validation
6
+ class Type < Draft202012::Vocab::Validation::Type
7
+ def self.valid_integer?(instance)
8
+ instance.is_a?(Integer)
9
+ end
10
+ end
11
+
12
+ class ExclusiveMaximum < Keyword
13
+ def error(formatted_instance_location:, **)
14
+ "number at #{formatted_instance_location} is greater than or equal to `maximum`"
15
+ end
16
+
17
+ def validate(instance, instance_location, keyword_location, _context)
18
+ maximum = schema.parsed.fetch('maximum').parsed
19
+ valid = !instance.is_a?(Numeric) || !value || !maximum || instance < maximum
20
+ result(instance, instance_location, keyword_location, valid)
21
+ end
22
+ end
23
+
24
+ class ExclusiveMinimum < Keyword
25
+ def error(formatted_instance_location:, **)
26
+ "number at #{formatted_instance_location} is less than or equal to `minimum`"
27
+ end
28
+
29
+ def validate(instance, instance_location, keyword_location, _context)
30
+ minimum = schema.parsed.fetch('minimum').parsed
31
+ valid = !instance.is_a?(Numeric) || !value || !minimum || instance > minimum
32
+ result(instance, instance_location, keyword_location, valid)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft4
4
+ module Vocab
5
+ ALL = Draft6::Vocab::ALL.dup
6
+ ALL.transform_keys! { |key| key == '$id' ? 'id' : key }
7
+ ALL.delete('contains')
8
+ ALL.delete('propertyNames')
9
+ ALL.delete('const')
10
+ ALL.delete('examples')
11
+ ALL.merge!(
12
+ 'type' => Validation::Type,
13
+ 'exclusiveMaximum' => Validation::ExclusiveMaximum,
14
+ 'exclusiveMinimum' => Validation::ExclusiveMinimum
15
+ )
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft6
4
+ BASE_URI = URI('http://json-schema.org/draft-06/schema#')
5
+ FORMATS = Draft7::FORMATS.dup
6
+ FORMATS.delete('date')
7
+ FORMATS.delete('time')
8
+ FORMATS.delete('idn-email')
9
+ FORMATS.delete('idn-hostname')
10
+ FORMATS.delete('iri')
11
+ FORMATS.delete('iri-reference')
12
+ FORMATS.delete('relative-json-pointer')
13
+ FORMATS.delete('regex')
14
+ CONTENT_ENCODINGS = Draft7::CONTENT_ENCODINGS
15
+ CONTENT_MEDIA_TYPES = Draft7::CONTENT_MEDIA_TYPES
16
+ SCHEMA = {
17
+ '$schema' => 'http://json-schema.org/draft-06/schema#',
18
+ '$id' => 'http://json-schema.org/draft-06/schema#',
19
+ 'title' => 'Core schema meta-schema',
20
+ 'definitions' => {
21
+ 'schemaArray' => {
22
+ 'type' => 'array',
23
+ 'minItems' => 1,
24
+ 'items' => { '$ref' => '#' }
25
+ },
26
+ 'nonNegativeInteger' => {
27
+ 'type' => 'integer',
28
+ 'minimum' => 0
29
+ },
30
+ 'nonNegativeIntegerDefault0' => {
31
+ 'allOf' => [
32
+ { '$ref' => '#/definitions/nonNegativeInteger' },
33
+ { 'default' => 0 }
34
+ ]
35
+ },
36
+ 'simpleTypes' => {
37
+ 'enum' => [
38
+ 'array',
39
+ 'boolean',
40
+ 'integer',
41
+ 'null',
42
+ 'number',
43
+ 'object',
44
+ 'string'
45
+ ]
46
+ },
47
+ 'stringArray' => {
48
+ 'type' => 'array',
49
+ 'items' => { 'type' => 'string' },
50
+ 'uniqueItems' => true,
51
+ 'default' => []
52
+ }
53
+ },
54
+ 'type' => ['object', 'boolean'],
55
+ 'properties' => {
56
+ '$id' => {
57
+ 'type' => 'string',
58
+ 'format' => 'uri-reference'
59
+ },
60
+ '$schema' => {
61
+ 'type' => 'string',
62
+ 'format' => 'uri'
63
+ },
64
+ '$ref' => {
65
+ 'type' => 'string',
66
+ 'format' => 'uri-reference'
67
+ },
68
+ 'title' => {
69
+ 'type' => 'string'
70
+ },
71
+ 'description' => {
72
+ 'type' => 'string'
73
+ },
74
+ 'default' => {},
75
+ 'examples' => {
76
+ 'type' => 'array',
77
+ 'items' => {}
78
+ },
79
+ 'multipleOf' => {
80
+ 'type' => 'number',
81
+ 'exclusiveMinimum' => 0
82
+ },
83
+ 'maximum' => {
84
+ 'type' => 'number'
85
+ },
86
+ 'exclusiveMaximum' => {
87
+ 'type' => 'number'
88
+ },
89
+ 'minimum' => {
90
+ 'type' => 'number'
91
+ },
92
+ 'exclusiveMinimum' => {
93
+ 'type' => 'number'
94
+ },
95
+ 'maxLength' => { '$ref' => '#/definitions/nonNegativeInteger' },
96
+ 'minLength' => { '$ref' => '#/definitions/nonNegativeIntegerDefault0' },
97
+ 'pattern' => {
98
+ 'type' => 'string',
99
+ 'format' => 'regex'
100
+ },
101
+ 'additionalItems' => { '$ref' => '#' },
102
+ 'items' => {
103
+ 'anyOf' => [
104
+ { '$ref' => '#' },
105
+ { '$ref' => '#/definitions/schemaArray' }
106
+ ],
107
+ 'default' => {}
108
+ },
109
+ 'maxItems' => { '$ref' => '#/definitions/nonNegativeInteger' },
110
+ 'minItems' => { '$ref' => '#/definitions/nonNegativeIntegerDefault0' },
111
+ 'uniqueItems' => {
112
+ 'type' => 'boolean',
113
+ 'default' => false
114
+ },
115
+ 'contains' => { '$ref' => '#' },
116
+ 'maxProperties' => { '$ref' => '#/definitions/nonNegativeInteger' },
117
+ 'minProperties' => { '$ref' => '#/definitions/nonNegativeIntegerDefault0' },
118
+ 'required' => { '$ref' => '#/definitions/stringArray' },
119
+ 'additionalProperties' => { '$ref' => '#' },
120
+ 'definitions' => {
121
+ 'type' => 'object',
122
+ 'additionalProperties' => { '$ref' => '#' },
123
+ 'default' => {}
124
+ },
125
+ 'properties' => {
126
+ 'type' => 'object',
127
+ 'additionalProperties' => { '$ref' => '#' },
128
+ 'default' => {}
129
+ },
130
+ 'patternProperties' => {
131
+ 'type' => 'object',
132
+ 'additionalProperties' => { '$ref' => '#' },
133
+ 'propertyNames' => { 'format' => 'regex' },
134
+ 'default' => {}
135
+ },
136
+ 'dependencies' => {
137
+ 'type' => 'object',
138
+ 'additionalProperties' => {
139
+ 'anyOf' => [
140
+ { '$ref' => '#' },
141
+ { '$ref' => '#/definitions/stringArray' }
142
+ ]
143
+ }
144
+ },
145
+ 'propertyNames' => { '$ref' => '#' },
146
+ 'const' => {},
147
+ 'enum' => {
148
+ 'type' => 'array',
149
+ 'minItems' => 1,
150
+ 'uniqueItems' => true
151
+ },
152
+ 'type' => {
153
+ 'anyOf' => [
154
+ { '$ref' => '#/definitions/simpleTypes' },
155
+ {
156
+ 'type' => 'array',
157
+ 'items' => { '$ref' => '#/definitions/simpleTypes' },
158
+ 'minItems' => 1,
159
+ 'uniqueItems' => true
160
+ }
161
+ ]
162
+ },
163
+ 'format' => { 'type' => 'string' },
164
+ 'allOf' => { '$ref' => '#/definitions/schemaArray' },
165
+ 'anyOf' => { '$ref' => '#/definitions/schemaArray' },
166
+ 'oneOf' => { '$ref' => '#/definitions/schemaArray' },
167
+ 'not' => { '$ref' => '#' }
168
+ },
169
+ 'default' => {}
170
+ }
171
+ end
172
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft6
4
+ module Vocab
5
+ ALL = Draft7::Vocab::ALL.dup
6
+ ALL.delete('$comment')
7
+ ALL.delete('if')
8
+ ALL.delete('then')
9
+ ALL.delete('else')
10
+ ALL.delete('readOnly')
11
+ ALL.delete('writeOnly')
12
+ ALL.delete('contentMediaType')
13
+ ALL.delete('contentEncoding')
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft7
4
+ BASE_URI = URI('http://json-schema.org/draft-07/schema#')
5
+ FORMATS = Draft201909::FORMATS.dup
6
+ FORMATS.delete('duration')
7
+ FORMATS.delete('uuid')
8
+ CONTENT_ENCODINGS = Draft201909::CONTENT_ENCODINGS
9
+ CONTENT_MEDIA_TYPES = Draft201909::CONTENT_MEDIA_TYPES
10
+ SCHEMA = {
11
+ '$schema' => 'http://json-schema.org/draft-07/schema#',
12
+ '$id' => 'http://json-schema.org/draft-07/schema#',
13
+ 'title' => 'Core schema meta-schema',
14
+ 'definitions' => {
15
+ 'schemaArray' => {
16
+ 'type' => 'array',
17
+ 'minItems' => 1,
18
+ 'items' => { '$ref' => '#' }
19
+ },
20
+ 'nonNegativeInteger' => {
21
+ 'type' => 'integer',
22
+ 'minimum' => 0
23
+ },
24
+ 'nonNegativeIntegerDefault0' => {
25
+ 'allOf' => [
26
+ { '$ref' => '#/definitions/nonNegativeInteger' },
27
+ { 'default' => 0 }
28
+ ]
29
+ },
30
+ 'simpleTypes' => {
31
+ 'enum' => [
32
+ 'array',
33
+ 'boolean',
34
+ 'integer',
35
+ 'null',
36
+ 'number',
37
+ 'object',
38
+ 'string'
39
+ ]
40
+ },
41
+ 'stringArray' => {
42
+ 'type' => 'array',
43
+ 'items' => { 'type' => 'string' },
44
+ 'uniqueItems' => true,
45
+ 'default' => []
46
+ }
47
+ },
48
+ 'type' => ['object', 'boolean'],
49
+ 'properties' => {
50
+ '$id' => {
51
+ 'type' => 'string',
52
+ 'format' => 'uri-reference'
53
+ },
54
+ '$schema' => {
55
+ 'type' => 'string',
56
+ 'format' => 'uri'
57
+ },
58
+ '$ref' => {
59
+ 'type' => 'string',
60
+ 'format' => 'uri-reference'
61
+ },
62
+ '$comment' => {
63
+ 'type' => 'string'
64
+ },
65
+ 'title' => {
66
+ 'type' => 'string'
67
+ },
68
+ 'description' => {
69
+ 'type' => 'string'
70
+ },
71
+ 'default' => true,
72
+ 'readOnly' => {
73
+ 'type' => 'boolean',
74
+ 'default' => false
75
+ },
76
+ 'writeOnly' => {
77
+ 'type' => 'boolean',
78
+ 'default' => false
79
+ },
80
+ 'examples' => {
81
+ 'type' => 'array',
82
+ 'items' => true
83
+ },
84
+ 'multipleOf' => {
85
+ 'type' => 'number',
86
+ 'exclusiveMinimum' => 0
87
+ },
88
+ 'maximum' => {
89
+ 'type' => 'number'
90
+ },
91
+ 'exclusiveMaximum' => {
92
+ 'type' => 'number'
93
+ },
94
+ 'minimum' => {
95
+ 'type' => 'number'
96
+ },
97
+ 'exclusiveMinimum' => {
98
+ 'type' => 'number'
99
+ },
100
+ 'maxLength' => { '$ref' => '#/definitions/nonNegativeInteger' },
101
+ 'minLength' => { '$ref' => '#/definitions/nonNegativeIntegerDefault0' },
102
+ 'pattern' => {
103
+ 'type' => 'string',
104
+ 'format' => 'regex'
105
+ },
106
+ 'additionalItems' => { '$ref' => '#' },
107
+ 'items' => {
108
+ 'anyOf' => [
109
+ { '$ref' => '#' },
110
+ { '$ref' => '#/definitions/schemaArray' }
111
+ ],
112
+ 'default' => true
113
+ },
114
+ 'maxItems' => { '$ref' => '#/definitions/nonNegativeInteger' },
115
+ 'minItems' => { '$ref' => '#/definitions/nonNegativeIntegerDefault0' },
116
+ 'uniqueItems' => {
117
+ 'type' => 'boolean',
118
+ 'default' => false
119
+ },
120
+ 'contains' => { '$ref' => '#' },
121
+ 'maxProperties' => { '$ref' => '#/definitions/nonNegativeInteger' },
122
+ 'minProperties' => { '$ref' => '#/definitions/nonNegativeIntegerDefault0' },
123
+ 'required' => { '$ref' => '#/definitions/stringArray' },
124
+ 'additionalProperties' => { '$ref' => '#' },
125
+ 'definitions' => {
126
+ 'type' => 'object',
127
+ 'additionalProperties' => { '$ref' => '#' },
128
+ 'default' => {}
129
+ },
130
+ 'properties' => {
131
+ 'type' => 'object',
132
+ 'additionalProperties' => { '$ref' => '#' },
133
+ 'default' => {}
134
+ },
135
+ 'patternProperties' => {
136
+ 'type' => 'object',
137
+ 'additionalProperties' => { '$ref' => '#' },
138
+ 'propertyNames' => { 'format' => 'regex' },
139
+ 'default' => {}
140
+ },
141
+ 'dependencies' => {
142
+ 'type' => 'object',
143
+ 'additionalProperties' => {
144
+ 'anyOf' => [
145
+ { '$ref' => '#' },
146
+ { '$ref' => '#/definitions/stringArray' }
147
+ ]
148
+ }
149
+ },
150
+ 'propertyNames' => { '$ref' => '#' },
151
+ 'const' => true,
152
+ 'enum' => {
153
+ 'type' => 'array',
154
+ 'items' => true,
155
+ 'minItems' => 1,
156
+ 'uniqueItems' => true
157
+ },
158
+ 'type' => {
159
+ 'anyOf' => [
160
+ { '$ref' => '#/definitions/simpleTypes' },
161
+ {
162
+ 'type' => 'array',
163
+ 'items' => { '$ref' => '#/definitions/simpleTypes' },
164
+ 'minItems' => 1,
165
+ 'uniqueItems' => true
166
+ }
167
+ ]
168
+ },
169
+ 'format' => { 'type' => 'string' },
170
+ 'contentMediaType' => { 'type' => 'string' },
171
+ 'contentEncoding' => { 'type' => 'string' },
172
+ 'if' => { '$ref' => '#' },
173
+ 'then' => { '$ref' => '#' },
174
+ 'else' => { '$ref' => '#' },
175
+ 'allOf' => { '$ref' => '#/definitions/schemaArray' },
176
+ 'anyOf' => { '$ref' => '#/definitions/schemaArray' },
177
+ 'oneOf' => { '$ref' => '#/definitions/schemaArray' },
178
+ 'not' => { '$ref' => '#' }
179
+ },
180
+ 'default' => true
181
+ }
182
+ end
183
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft7
4
+ module Vocab
5
+ module Validation
6
+ class Ref < Draft202012::Vocab::Core::Ref
7
+ def self.exclusive?
8
+ true
9
+ end
10
+ end
11
+
12
+ class AdditionalItems < Keyword
13
+ def error(formatted_instance_location:, **)
14
+ "array items at #{formatted_instance_location} do not match `additionalItems` schema"
15
+ end
16
+
17
+ def parse
18
+ subschema(value)
19
+ end
20
+
21
+ def validate(instance, instance_location, keyword_location, context)
22
+ items = schema.parsed['items']&.parsed
23
+
24
+ if !instance.is_a?(Array) || !items.is_a?(Array) || items.size >= instance.size
25
+ return result(instance, instance_location, keyword_location, true)
26
+ end
27
+
28
+ offset = items.size
29
+
30
+ nested = instance.slice(offset..-1).map.with_index do |item, index|
31
+ parsed.validate_instance(item, join_location(instance_location, (offset + index).to_s), keyword_location, context)
32
+ end
33
+
34
+ result(instance, instance_location, keyword_location, nested.all?(&:valid), nested, :annotation => nested.any?)
35
+ end
36
+ end
37
+
38
+ class ContentEncoding < Draft202012::Vocab::Content::ContentEncoding
39
+ def error(formatted_instance_location:, **)
40
+ "string at #{formatted_instance_location} could not be decoded using encoding: #{value}"
41
+ end
42
+
43
+ def validate(instance, instance_location, keyword_location, _context)
44
+ return result(instance, instance_location, keyword_location, true) unless instance.is_a?(String)
45
+
46
+ valid, annotation = parsed.call(instance)
47
+
48
+ result(instance, instance_location, keyword_location, valid, :annotation => annotation)
49
+ end
50
+ end
51
+
52
+ class ContentMediaType < Draft202012::Vocab::Content::ContentMediaType
53
+ def error(formatted_instance_location:, **)
54
+ "string at #{formatted_instance_location} could not be parsed using media type: #{value}"
55
+ end
56
+
57
+ def validate(instance, instance_location, keyword_location, context)
58
+ return result(instance, instance_location, keyword_location, true) unless instance.is_a?(String)
59
+
60
+ decoded_instance = context.adjacent_results[ContentEncoding]&.annotation || instance
61
+ valid, annotation = parsed.call(decoded_instance)
62
+
63
+ result(instance, instance_location, keyword_location, valid, :annotation => annotation)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Draft7
4
+ module Vocab
5
+ ALL = Draft201909::Vocab::CORE.dup
6
+ ALL.delete('$recursiveAnchor')
7
+ ALL.delete('$recursiveRef')
8
+ ALL.delete('$vocabulary')
9
+ ALL.delete('$anchor')
10
+ ALL.delete('$defs')
11
+ ALL.merge!(Draft201909::Vocab::APPLICATOR)
12
+ ALL.delete('dependentSchemas')
13
+ ALL.delete('unevaluatedItems')
14
+ ALL.delete('unevaluatedProperties')
15
+ ALL.merge!(Draft201909::Vocab::VALIDATION)
16
+ ALL.delete('dependentRequired')
17
+ ALL.delete('maxContains')
18
+ ALL.delete('minContains')
19
+ ALL.merge!(Draft202012::Vocab::FORMAT_ANNOTATION)
20
+ ALL.merge!(Draft201909::Vocab::META_DATA)
21
+ ALL.delete('deprecated')
22
+ ALL.merge!(
23
+ '$ref' => Validation::Ref,
24
+ 'additionalItems' => Validation::AdditionalItems,
25
+ 'contentEncoding' => Validation::ContentEncoding,
26
+ 'contentMediaType' => Validation::ContentMediaType
27
+ )
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  # Based on code from @robacarp found in issue 48:
2
3
  # https://github.com/davishmcclurg/json_schemer/issues/48
3
4
  #
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Format
4
+ module Duration
5
+ # https://datatracker.ietf.org/doc/html/rfc3339#appendix-A
6
+ DUR_SECOND = '\d+S' # dur-second = 1*DIGIT "S"
7
+ DUR_MINUTE = "\\d+M(#{DUR_SECOND})?" # dur-minute = 1*DIGIT "M" [dur-second]
8
+ DUR_HOUR = "\\d+H(#{DUR_MINUTE})?" # dur-hour = 1*DIGIT "H" [dur-minute]
9
+ DUR_TIME = "T(#{DUR_HOUR}|#{DUR_MINUTE}|#{DUR_SECOND})" # dur-time = "T" (dur-hour / dur-minute / dur-second)
10
+ DUR_DAY = '\d+D' # dur-day = 1*DIGIT "D"
11
+ DUR_WEEK = '\d+W' # dur-week = 1*DIGIT "W"
12
+ DUR_MONTH = "\\d+M(#{DUR_DAY})?" # dur-month = 1*DIGIT "M" [dur-day]
13
+ DUR_YEAR = "\\d+Y(#{DUR_MONTH})?" # dur-year = 1*DIGIT "Y" [dur-month]
14
+ DUR_DATE = "(#{DUR_DAY}|#{DUR_MONTH}|#{DUR_YEAR})(#{DUR_TIME})?" # dur-date = (dur-day / dur-month / dur-year) [dur-time]
15
+ DURATION = "P(#{DUR_DATE}|#{DUR_TIME}|#{DUR_WEEK})" # duration = "P" (dur-date / dur-time / dur-week)
16
+ DURATION_REGEX = /\A#{DURATION}\z/
17
+
18
+ def valid_duration?(data)
19
+ DURATION_REGEX.match?(data)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+ module JSONSchemer
3
+ module Format
4
+ module JSONPointer
5
+ JSON_POINTER_REGEX_STRING = '(\/([^~\/]|~[01])*)*'
6
+ JSON_POINTER_REGEX = /\A#{JSON_POINTER_REGEX_STRING}\z/.freeze
7
+ RELATIVE_JSON_POINTER_REGEX = /\A(0|[1-9]\d*)(#|#{JSON_POINTER_REGEX_STRING})?\z/.freeze
8
+
9
+ def valid_json_pointer?(data)
10
+ JSON_POINTER_REGEX.match?(data)
11
+ end
12
+
13
+ def valid_relative_json_pointer?(data)
14
+ RELATIVE_JSON_POINTER_REGEX.match?(data)
15
+ end
16
+ end
17
+ end
18
+ end