schemurai 2.2.0 → 2.2.2
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.
- checksums.yaml +4 -4
- data/lib/schemurai/evaluator.rb +5 -11
- data/lib/schemurai/unique_items.rb +66 -0
- data/lib/schemurai/version.rb +1 -1
- data/lib/schemurai/vm/evaluator.rb +7 -29
- data/sig/schemurai.rbs +16 -73
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 163dbc9593611cce4f28183766bd5e17a08387a54883c17a502a198eb010ad36
|
|
4
|
+
data.tar.gz: aa3d5f90db227194179023b31043cf36b4597e4af5f1e2209b2846e9c36f9044
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f5ba215dde91e1bf364ff6ebbbebb7ea40b284e3908cfcb578d48aad2174c34e5cf6d9fd121947bb54f8b2cf0fad626e629ed94d6b4a52ab41ba84ad7dfbc55
|
|
7
|
+
data.tar.gz: 4efa97250aad095e414eb230310ae580bbd029b834aa11322cab8da0b19df7a1fb6703240ee0439fd4abe788fcfa47c918ac92806bef174d38d0110bb35cc934
|
data/lib/schemurai/evaluator.rb
CHANGED
|
@@ -4,10 +4,13 @@ require "json"
|
|
|
4
4
|
require "base64"
|
|
5
5
|
require_relative "evaluation"
|
|
6
6
|
require_relative "error_message"
|
|
7
|
+
require_relative "unique_items"
|
|
7
8
|
|
|
8
9
|
module Schemurai
|
|
9
10
|
module Internal
|
|
10
11
|
class Evaluator
|
|
12
|
+
include UniqueItems
|
|
13
|
+
|
|
11
14
|
MISSING_SEGMENT = Object.new.freeze
|
|
12
15
|
|
|
13
16
|
def backend = :ruby
|
|
@@ -226,11 +229,7 @@ module Schemurai
|
|
|
226
229
|
length = value.length
|
|
227
230
|
return false if schema.key?("maxItems") && length > schema["maxItems"]
|
|
228
231
|
return false if schema.key?("minItems") && length < schema["minItems"]
|
|
229
|
-
if schema["uniqueItems"]
|
|
230
|
-
value.each_with_index do |item, index|
|
|
231
|
-
return false if value[0...index].any? { |previous| json_equal?(previous, item) }
|
|
232
|
-
end
|
|
233
|
-
end
|
|
232
|
+
return false if schema["uniqueItems"] && !unique_items?(value)
|
|
234
233
|
|
|
235
234
|
prefix_items = schema["prefixItems"]
|
|
236
235
|
if prefix_items.is_a?(Array)
|
|
@@ -555,12 +554,7 @@ module Schemurai
|
|
|
555
554
|
limit(schema, "maxItems", value.length) { |actual, expected| actual <= expected }
|
|
556
555
|
limit(schema, "minItems", value.length) { |actual, expected| actual >= expected }
|
|
557
556
|
|
|
558
|
-
if schema["uniqueItems"]
|
|
559
|
-
duplicate = value.each_with_index.any? do |item, index|
|
|
560
|
-
value[0...index].any? { |previous| json_equal?(previous, item) }
|
|
561
|
-
end
|
|
562
|
-
add_error("uniqueItems") { ErrorMessage.unique_items } if duplicate
|
|
563
|
-
end
|
|
557
|
+
add_error("uniqueItems") { ErrorMessage.unique_items } if schema["uniqueItems"] && !unique_items?(value)
|
|
564
558
|
|
|
565
559
|
prefix_items = schema["prefixItems"]
|
|
566
560
|
if prefix_items.is_a?(Array)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Schemurai
|
|
4
|
+
module Internal
|
|
5
|
+
module UniqueItems
|
|
6
|
+
private def unique_items?(values)
|
|
7
|
+
# Avoid building an index for small arrays and early duplicates.
|
|
8
|
+
if values.length <= 16
|
|
9
|
+
values.each_with_index do |item, index|
|
|
10
|
+
index.times { |previous| return false if json_equal?(values[previous], item) }
|
|
11
|
+
end
|
|
12
|
+
return true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
buckets = {}
|
|
16
|
+
# Store a single index until a collision needs a bucket array.
|
|
17
|
+
index = 0
|
|
18
|
+
while index < values.length
|
|
19
|
+
item = values[index]
|
|
20
|
+
fingerprint = json_fingerprint(item)
|
|
21
|
+
if (bucket = buckets[fingerprint])
|
|
22
|
+
if bucket.is_a?(Integer)
|
|
23
|
+
return false if json_equal?(values[bucket], item)
|
|
24
|
+
|
|
25
|
+
buckets[fingerprint] = [bucket, index]
|
|
26
|
+
else
|
|
27
|
+
return false if bucket.any? { |previous| json_equal?(values[previous], item) }
|
|
28
|
+
|
|
29
|
+
bucket << index
|
|
30
|
+
end
|
|
31
|
+
else
|
|
32
|
+
buckets[fingerprint] = index
|
|
33
|
+
end
|
|
34
|
+
index += 1
|
|
35
|
+
end
|
|
36
|
+
true
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private def json_fingerprint(value)
|
|
40
|
+
case value
|
|
41
|
+
when Numeric
|
|
42
|
+
# JSON numbers compare across Integer/Float. Converting integral
|
|
43
|
+
# floats to integers also preserves precision for large integers.
|
|
44
|
+
return value.hash if value.is_a?(Integer)
|
|
45
|
+
|
|
46
|
+
number = value.to_f
|
|
47
|
+
(number.finite? && number == number.to_i) ? number.to_i.hash : number.hash
|
|
48
|
+
when Array
|
|
49
|
+
# Mask before multiplication to keep intermediate values immediate
|
|
50
|
+
# integers; hash each step to retain order without temporary arrays.
|
|
51
|
+
fingerprint = value.class.hash
|
|
52
|
+
value.each { |item| fingerprint = (((fingerprint & 0x1fffffff) * 31) ^ json_fingerprint(item)).hash }
|
|
53
|
+
fingerprint
|
|
54
|
+
when Hash
|
|
55
|
+
# Object key order is immaterial. Hash collisions are resolved using
|
|
56
|
+
# json_equal?, so this fingerprint never decides equality itself.
|
|
57
|
+
entries = 0
|
|
58
|
+
value.each_pair { |key, item| entries ^= (((key.hash & 0x1fffffff) * 31) ^ json_fingerprint(item)).hash }
|
|
59
|
+
(value.class.hash ^ value.length ^ entries).hash
|
|
60
|
+
else
|
|
61
|
+
value.hash
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
data/lib/schemurai/version.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "base64"
|
|
|
4
4
|
require "json"
|
|
5
5
|
require_relative "../evaluation"
|
|
6
6
|
require_relative "../error_message"
|
|
7
|
+
require_relative "../unique_items"
|
|
7
8
|
require_relative "compiler"
|
|
8
9
|
|
|
9
10
|
module Schemurai
|
|
@@ -49,6 +50,8 @@ module Schemurai
|
|
|
49
50
|
private_constant :EvaluationBuffer
|
|
50
51
|
|
|
51
52
|
class Evaluator
|
|
53
|
+
include Internal::UniqueItems
|
|
54
|
+
|
|
52
55
|
MISSING_SEGMENT = Object.new.freeze
|
|
53
56
|
DECIMAL_CACHE_LIMIT = 16
|
|
54
57
|
|
|
@@ -552,17 +555,7 @@ module Schemurai
|
|
|
552
555
|
length = value.length
|
|
553
556
|
return false if rules.max_items && length > rules.max_items
|
|
554
557
|
return false if rules.min_items && length < rules.min_items
|
|
555
|
-
if rules.unique
|
|
556
|
-
index = 1
|
|
557
|
-
while index < length
|
|
558
|
-
previous_index = 0
|
|
559
|
-
while previous_index < index
|
|
560
|
-
return false if json_equal?(value[previous_index], value[index])
|
|
561
|
-
previous_index += 1
|
|
562
|
-
end
|
|
563
|
-
index += 1
|
|
564
|
-
end
|
|
565
|
-
end
|
|
558
|
+
return false if rules.unique && !unique_items?(value)
|
|
566
559
|
|
|
567
560
|
if (prefix_items = rules.prefix_items)
|
|
568
561
|
prefix_items.each_with_index do |child, index|
|
|
@@ -616,25 +609,10 @@ module Schemurai
|
|
|
616
609
|
|
|
617
610
|
add_error("minItems") { Internal::ErrorMessage.size("minItems", rules.min_items, value.length) }
|
|
618
611
|
end
|
|
619
|
-
if rules.unique
|
|
620
|
-
|
|
621
|
-
index = 1
|
|
622
|
-
while index < value.length && !duplicate
|
|
623
|
-
previous_index = 0
|
|
624
|
-
while previous_index < index
|
|
625
|
-
if json_equal?(value[previous_index], value[index])
|
|
626
|
-
duplicate = true
|
|
627
|
-
break
|
|
628
|
-
end
|
|
629
|
-
previous_index += 1
|
|
630
|
-
end
|
|
631
|
-
index += 1
|
|
632
|
-
end
|
|
633
|
-
if duplicate
|
|
634
|
-
return Evaluation.invalid unless @errors
|
|
612
|
+
if rules.unique && !unique_items?(value)
|
|
613
|
+
return Evaluation.invalid unless @errors
|
|
635
614
|
|
|
636
|
-
|
|
637
|
-
end
|
|
615
|
+
add_error("uniqueItems") { Internal::ErrorMessage.unique_items }
|
|
638
616
|
end
|
|
639
617
|
|
|
640
618
|
if (prefix_items = rules.prefix_items)
|
data/sig/schemurai.rbs
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
module Schemurai
|
|
2
2
|
# A JSON-shaped Ruby value accepted as a schema value or validation instance.
|
|
3
|
-
type json_value =
|
|
4
|
-
nil
|
|
5
|
-
| bool
|
|
6
|
-
| Integer
|
|
7
|
-
| Float
|
|
8
|
-
| String
|
|
9
|
-
| Array[json_value]
|
|
10
|
-
| Hash[String, json_value]
|
|
3
|
+
type json_value = nil | bool | Integer | Float | String | Array[json_value] | Hash[String, json_value]
|
|
11
4
|
|
|
12
5
|
# A JSON Schema root.
|
|
13
6
|
type schema = bool | Hash[String, json_value]
|
|
@@ -50,12 +43,7 @@ module Schemurai
|
|
|
50
43
|
def self.[]: (String keyword, String instance_path, String schema_path, String message) -> ValidationError
|
|
51
44
|
| (keyword: String, instance_path: String, schema_path: String, message: String) -> ValidationError
|
|
52
45
|
|
|
53
|
-
def to_h: () -> {
|
|
54
|
-
keyword: String,
|
|
55
|
-
instance_path: String,
|
|
56
|
-
schema_path: String,
|
|
57
|
-
message: String
|
|
58
|
-
}
|
|
46
|
+
def to_h: () -> { keyword: String, instance_path: String, schema_path: String, message: String }
|
|
59
47
|
end
|
|
60
48
|
|
|
61
49
|
class Result
|
|
@@ -68,7 +56,9 @@ module Schemurai
|
|
|
68
56
|
|
|
69
57
|
interface _Evaluator
|
|
70
58
|
def backend: () -> backend
|
|
59
|
+
|
|
71
60
|
def validate: (json_value instance) -> Result
|
|
61
|
+
|
|
72
62
|
def valid?: (json_value instance) -> bool
|
|
73
63
|
end
|
|
74
64
|
|
|
@@ -82,76 +72,29 @@ module Schemurai
|
|
|
82
72
|
class SchemaRegistry
|
|
83
73
|
attr_reader backend: backend
|
|
84
74
|
|
|
85
|
-
def initialize: (
|
|
86
|
-
?schemas: schemas,
|
|
87
|
-
?validate_schema: bool,
|
|
88
|
-
?backend: backend_selection
|
|
89
|
-
) -> void
|
|
75
|
+
def initialize: (?schemas: schemas, ?validate_schema: bool, ?backend: backend_selection) -> void
|
|
90
76
|
|
|
91
77
|
def validate_schema?: () -> bool
|
|
92
78
|
|
|
93
|
-
def compile: (
|
|
94
|
-
schema schema,
|
|
95
|
-
?base_uri: String?,
|
|
96
|
-
?content: bool,
|
|
97
|
-
?format: bool
|
|
98
|
-
) -> Validator
|
|
79
|
+
def compile: (schema schema, ?base_uri: String?, ?content: bool, ?format: bool) -> Validator
|
|
99
80
|
|
|
100
81
|
def validate_schema: (schema schema) -> Result
|
|
101
82
|
def valid_schema?: (schema schema) -> bool
|
|
102
83
|
def make_shareable: () -> self
|
|
103
84
|
def shareable?: () -> bool
|
|
104
85
|
|
|
105
|
-
def validator_for: (
|
|
106
|
-
String uri,
|
|
107
|
-
?content: bool,
|
|
108
|
-
?format: bool
|
|
109
|
-
) -> Validator
|
|
86
|
+
def validator_for: (String uri, ?content: bool, ?format: bool) -> Validator
|
|
110
87
|
end
|
|
111
88
|
|
|
112
89
|
def self.backend: () -> backend
|
|
113
90
|
|
|
114
|
-
def self.compile: (
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
) ->
|
|
123
|
-
|
|
124
|
-
def self.validate_schema: (
|
|
125
|
-
schema schema,
|
|
126
|
-
?schemas: schemas,
|
|
127
|
-
?backend: backend_selection
|
|
128
|
-
) -> Result
|
|
129
|
-
|
|
130
|
-
def self.valid_schema?: (
|
|
131
|
-
schema schema,
|
|
132
|
-
?schemas: schemas,
|
|
133
|
-
?backend: backend_selection
|
|
134
|
-
) -> bool
|
|
135
|
-
|
|
136
|
-
def self.validate: (
|
|
137
|
-
schema schema,
|
|
138
|
-
json_value instance,
|
|
139
|
-
?schemas: schemas,
|
|
140
|
-
?base_uri: String?,
|
|
141
|
-
?content: bool,
|
|
142
|
-
?format: bool,
|
|
143
|
-
?validate_schema: bool,
|
|
144
|
-
?backend: backend_selection
|
|
145
|
-
) -> Result
|
|
146
|
-
|
|
147
|
-
def self.valid?: (
|
|
148
|
-
schema schema,
|
|
149
|
-
json_value instance,
|
|
150
|
-
?schemas: schemas,
|
|
151
|
-
?base_uri: String?,
|
|
152
|
-
?content: bool,
|
|
153
|
-
?format: bool,
|
|
154
|
-
?validate_schema: bool,
|
|
155
|
-
?backend: backend_selection
|
|
156
|
-
) -> bool
|
|
91
|
+
def self.compile: (schema schema, ?schemas: schemas, ?base_uri: String?, ?content: bool, ?format: bool, ?validate_schema: bool, ?backend: backend_selection) -> Validator
|
|
92
|
+
|
|
93
|
+
def self.validate_schema: (schema schema, ?schemas: schemas, ?backend: backend_selection) -> Result
|
|
94
|
+
|
|
95
|
+
def self.valid_schema?: (schema schema, ?schemas: schemas, ?backend: backend_selection) -> bool
|
|
96
|
+
|
|
97
|
+
def self.validate: (schema schema, json_value instance, ?schemas: schemas, ?base_uri: String?, ?content: bool, ?format: bool, ?validate_schema: bool, ?backend: backend_selection) -> Result
|
|
98
|
+
|
|
99
|
+
def self.valid?: (schema schema, json_value instance, ?schemas: schemas, ?base_uri: String?, ?content: bool, ?format: bool, ?validate_schema: bool, ?backend: backend_selection) -> bool
|
|
157
100
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: schemurai
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.2.
|
|
4
|
+
version: 2.2.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kuya KOHARA (oakcask)
|
|
@@ -53,6 +53,7 @@ files:
|
|
|
53
53
|
- lib/schemurai/schema_domain.rb
|
|
54
54
|
- lib/schemurai/schema_graph.rb
|
|
55
55
|
- lib/schemurai/schema_node.rb
|
|
56
|
+
- lib/schemurai/unique_items.rb
|
|
56
57
|
- lib/schemurai/version.rb
|
|
57
58
|
- lib/schemurai/vm/compiler.rb
|
|
58
59
|
- lib/schemurai/vm/evaluator.rb
|
|
@@ -63,7 +64,7 @@ licenses:
|
|
|
63
64
|
metadata:
|
|
64
65
|
homepage_uri: https://github.com/oakcask/schemurai
|
|
65
66
|
source_code_uri: https://github.com/oakcask/schemurai/tree/main
|
|
66
|
-
documentation_uri: https://rubydoc.info/gems/schemurai/2.2.
|
|
67
|
+
documentation_uri: https://rubydoc.info/gems/schemurai/2.2.2
|
|
67
68
|
rubygems_mfa_required: 'true'
|
|
68
69
|
rdoc_options:
|
|
69
70
|
- "--main"
|
|
@@ -87,7 +88,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
87
88
|
- !ruby/object:Gem::Version
|
|
88
89
|
version: '0'
|
|
89
90
|
requirements: []
|
|
90
|
-
rubygems_version: 4.0.
|
|
91
|
+
rubygems_version: 4.0.20
|
|
91
92
|
specification_version: 4
|
|
92
93
|
summary: A fast, Ractor-safe, pure Ruby JSON Schema Draft 7+ validator
|
|
93
94
|
test_files: []
|