jsi 0.7.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +6 -1
- data/CHANGELOG.md +15 -0
- data/README.md +19 -18
- data/jsi.gemspec +2 -3
- data/lib/jsi/base/mutability.rb +44 -0
- data/lib/jsi/base/node.rb +199 -34
- data/lib/jsi/base.rb +412 -228
- data/lib/jsi/jsi_coder.rb +18 -16
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +57 -23
- data/lib/jsi/metaschema_node.rb +138 -107
- data/lib/jsi/ptr.rb +59 -37
- data/lib/jsi/schema/application/child_application/draft04.rb +0 -1
- data/lib/jsi/schema/application/child_application/draft06.rb +0 -1
- data/lib/jsi/schema/application/child_application/draft07.rb +0 -1
- data/lib/jsi/schema/application/child_application.rb +0 -25
- data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -1
- data/lib/jsi/schema/application/inplace_application/ref.rb +1 -1
- data/lib/jsi/schema/application/inplace_application/someof.rb +1 -1
- data/lib/jsi/schema/application/inplace_application.rb +0 -27
- data/lib/jsi/schema/draft04.rb +0 -1
- data/lib/jsi/schema/draft06.rb +0 -1
- data/lib/jsi/schema/draft07.rb +0 -1
- data/lib/jsi/schema/ref.rb +44 -18
- data/lib/jsi/schema/schema_ancestor_node.rb +65 -56
- data/lib/jsi/schema/validation/contains.rb +1 -1
- data/lib/jsi/schema/validation/draft04/minmax.rb +2 -0
- data/lib/jsi/schema/validation/draft04.rb +0 -2
- data/lib/jsi/schema/validation/draft06.rb +0 -2
- data/lib/jsi/schema/validation/draft07.rb +0 -2
- data/lib/jsi/schema/validation/items.rb +3 -3
- data/lib/jsi/schema/validation/pattern.rb +1 -1
- data/lib/jsi/schema/validation/properties.rb +4 -4
- data/lib/jsi/schema/validation/ref.rb +1 -1
- data/lib/jsi/schema/validation.rb +0 -2
- data/lib/jsi/schema.rb +405 -194
- data/lib/jsi/schema_classes.rb +196 -127
- data/lib/jsi/schema_registry.rb +66 -17
- data/lib/jsi/schema_set.rb +76 -30
- data/lib/jsi/simple_wrap.rb +2 -7
- data/lib/jsi/util/private/attr_struct.rb +28 -14
- data/lib/jsi/util/private/memo_map.rb +75 -0
- data/lib/jsi/util/private.rb +73 -92
- data/lib/jsi/util/typelike.rb +28 -28
- data/lib/jsi/util.rb +120 -36
- data/lib/jsi/validation/error.rb +4 -0
- data/lib/jsi/validation/result.rb +18 -32
- data/lib/jsi/version.rb +1 -1
- data/lib/jsi.rb +67 -25
- data/lib/schemas/json-schema.org/draft-04/schema.rb +159 -4
- data/lib/schemas/json-schema.org/draft-06/schema.rb +161 -4
- data/lib/schemas/json-schema.org/draft-07/schema.rb +188 -4
- metadata +19 -5
- data/lib/jsi/metaschema.rb +0 -6
- data/lib/jsi/schema/validation/core.rb +0 -39
data/lib/jsi/util/typelike.rb
CHANGED
@@ -1,23 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module JSI
|
4
|
-
# @deprecated after v0.6
|
5
|
-
module Typelike
|
6
|
-
extend Util
|
7
|
-
end
|
8
|
-
|
9
4
|
# a module of methods for objects which behave like Hash but are not Hash.
|
10
5
|
#
|
11
6
|
# this module is intended to be internal to JSI. no guarantees or API promises
|
12
7
|
# are made for non-JSI classes including this module.
|
13
8
|
module Util::Hashlike
|
9
|
+
include(Enumerable)
|
10
|
+
|
14
11
|
# safe methods which can be delegated to #to_hash (which the includer is assumed to have defined).
|
15
12
|
# 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
|
16
13
|
|
17
14
|
# methods which do not need to access the value.
|
18
|
-
SAFE_KEY_ONLY_METHODS = %w(each_key empty? has_key? include? key? keys length member? size)
|
19
|
-
SAFE_KEY_VALUE_METHODS = %w(< <= > >= any? assoc compact dig each_pair each_value fetch fetch_values has_value? invert key merge rassoc reject select to_h to_proc transform_values value? values values_at)
|
20
|
-
DESTRUCTIVE_METHODS = %w(clear delete delete_if keep_if reject! replace select! shift)
|
15
|
+
SAFE_KEY_ONLY_METHODS = %w(each_key empty? has_key? include? key? keys length member? size).map(&:freeze).freeze
|
16
|
+
SAFE_KEY_VALUE_METHODS = %w(< <= > >= any? assoc compact dig each_pair each_value fetch fetch_values has_value? invert key merge rassoc reject select to_h to_proc transform_values value? values values_at).map(&:freeze).freeze
|
17
|
+
DESTRUCTIVE_METHODS = %w(clear delete delete_if keep_if reject! replace select! shift).map(&:freeze).freeze
|
21
18
|
# these return a modified copy
|
22
19
|
safe_modified_copy_methods = %w(compact)
|
23
20
|
# select and reject will return a modified copy but need the yielded block variable value from #[]
|
@@ -73,9 +70,8 @@ module JSI
|
|
73
70
|
unless other.respond_to?(:to_hash)
|
74
71
|
raise(TypeError, "cannot update with argument that does not respond to #to_hash: #{other.pretty_inspect.chomp}")
|
75
72
|
end
|
76
|
-
self_respondingto_key = respond_to?(:key?) ? self : to_hash
|
77
73
|
other.to_hash.each_pair do |key, value|
|
78
|
-
if block &&
|
74
|
+
if block && key?(key)
|
79
75
|
value = yield(key, self[key], value)
|
80
76
|
end
|
81
77
|
self[key] = value
|
@@ -92,7 +88,9 @@ module JSI
|
|
92
88
|
# @return duplicate of this hash with the other hash merged in
|
93
89
|
# @raise [TypeError] when `other` does not respond to #to_hash
|
94
90
|
def merge(other, &block)
|
95
|
-
|
91
|
+
jsi_modified_copy do |instance|
|
92
|
+
instance.merge(other.is_a?(Base) ? other.jsi_node_content : other, &block)
|
93
|
+
end
|
96
94
|
end
|
97
95
|
|
98
96
|
# basically the same #inspect as Hash, but has the class name and, if responsive,
|
@@ -100,19 +98,20 @@ module JSI
|
|
100
98
|
# @return [String]
|
101
99
|
def inspect
|
102
100
|
object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
|
103
|
-
"\#{<#{object_group_str}>#{map { |k, v| " #{k.inspect} => #{v.inspect}" }.join(',')}}"
|
101
|
+
-"\#{<#{object_group_str}>#{map { |k, v| " #{k.inspect} => #{v.inspect}" }.join(',')}}"
|
104
102
|
end
|
105
103
|
|
106
|
-
|
104
|
+
def to_s
|
105
|
+
inspect
|
106
|
+
end
|
107
107
|
|
108
108
|
# pretty-prints a representation of this hashlike to the given printer
|
109
109
|
# @return [void]
|
110
110
|
def pretty_print(q)
|
111
111
|
object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
|
112
112
|
q.text "\#{<#{object_group_str}>"
|
113
|
-
q.
|
114
|
-
|
115
|
-
q.breakable(empty? ? '' : ' ')
|
113
|
+
q.group(2) {
|
114
|
+
q.breakable ' ' if !empty?
|
116
115
|
q.seplist(self, nil, :each_pair) { |k, v|
|
117
116
|
q.group {
|
118
117
|
q.pp k
|
@@ -120,9 +119,8 @@ module JSI
|
|
120
119
|
q.pp v
|
121
120
|
}
|
122
121
|
}
|
123
|
-
}
|
124
122
|
}
|
125
|
-
q.breakable ''
|
123
|
+
q.breakable '' if !empty?
|
126
124
|
q.text '}'
|
127
125
|
end
|
128
126
|
end
|
@@ -132,14 +130,16 @@ module JSI
|
|
132
130
|
# this module is intended to be internal to JSI. no guarantees or API promises
|
133
131
|
# are made for non-JSI classes including this module.
|
134
132
|
module Util::Arraylike
|
133
|
+
include(Enumerable)
|
134
|
+
|
135
135
|
# safe methods which can be delegated to #to_ary (which the includer is assumed to have defined).
|
136
136
|
# 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
|
137
137
|
|
138
138
|
# methods which do not need to access the element.
|
139
|
-
SAFE_INDEX_ONLY_METHODS = %w(each_index empty? length size)
|
139
|
+
SAFE_INDEX_ONLY_METHODS = %w(each_index empty? length size).map(&:freeze).freeze
|
140
140
|
# there are some ambiguous ones that are omitted, like #sort, #map / #collect.
|
141
|
-
SAFE_INDEX_ELEMENT_METHODS = %w(| & * + - <=> abbrev at bsearch bsearch_index combination compact count cycle dig drop drop_while fetch find_index first include? index join last pack permutation product reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shelljoin shuffle slice sort take take_while transpose uniq values_at zip)
|
142
|
-
DESTRUCTIVE_METHODS = %w(<< clear collect! compact! concat delete delete_at delete_if fill flatten! insert keep_if map! pop push reject! replace reverse! rotate! select! shift shuffle! slice! sort! sort_by! uniq! unshift)
|
141
|
+
SAFE_INDEX_ELEMENT_METHODS = %w(| & * + - <=> abbrev at bsearch bsearch_index combination compact count cycle dig drop drop_while fetch find_index first include? index join last pack permutation product reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shelljoin shuffle slice sort take take_while transpose uniq values_at zip).map(&:freeze).freeze
|
142
|
+
DESTRUCTIVE_METHODS = %w(<< clear collect! compact! concat delete delete_at delete_if fill flatten! insert keep_if map! pop push reject! replace reverse! rotate! select! shift shuffle! slice! sort! sort_by! uniq! unshift).map(&:freeze).freeze
|
143
143
|
|
144
144
|
# methods (well, method) that returns a modified copy and doesn't need any handling of block variable(s)
|
145
145
|
safe_modified_copy_methods = %w(compact)
|
@@ -204,25 +204,25 @@ module JSI
|
|
204
204
|
# @return [String]
|
205
205
|
def inspect
|
206
206
|
object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
|
207
|
-
"\#[<#{object_group_str}>#{map { |e| ' ' + e.inspect }.join(',')}]"
|
207
|
+
-"\#[<#{object_group_str}>#{map { |e| ' ' + e.inspect }.join(',')}]"
|
208
208
|
end
|
209
209
|
|
210
|
-
|
210
|
+
def to_s
|
211
|
+
inspect
|
212
|
+
end
|
211
213
|
|
212
214
|
# pretty-prints a representation of this arraylike to the given printer
|
213
215
|
# @return [void]
|
214
216
|
def pretty_print(q)
|
215
217
|
object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
|
216
218
|
q.text "\#[<#{object_group_str}>"
|
217
|
-
q.
|
218
|
-
|
219
|
-
q.breakable(empty? ? '' : ' ')
|
219
|
+
q.group(2) {
|
220
|
+
q.breakable ' ' if !empty?
|
220
221
|
q.seplist(self, nil, :each) { |e|
|
221
222
|
q.pp e
|
222
223
|
}
|
223
|
-
}
|
224
224
|
}
|
225
|
-
q.breakable ''
|
225
|
+
q.breakable '' if !empty?
|
226
226
|
q.text ']'
|
227
227
|
end
|
228
228
|
end
|
data/lib/jsi/util.rb
CHANGED
@@ -7,11 +7,13 @@ module JSI
|
|
7
7
|
|
8
8
|
include Private
|
9
9
|
|
10
|
+
extend self
|
11
|
+
|
10
12
|
autoload :Arraylike, 'jsi/util/typelike'
|
11
13
|
autoload :Hashlike, 'jsi/util/typelike'
|
12
14
|
|
13
15
|
# yields the content of the given param `object`. for objects which have a #jsi_modified_copy
|
14
|
-
# method of their own (JSI::Base, JSI::
|
16
|
+
# method of their own (JSI::Base, JSI::MetaSchemaNode) that method is invoked with the given
|
15
17
|
# block. otherwise the given object itself is yielded.
|
16
18
|
#
|
17
19
|
# the given block must result in a modified copy of its block parameter
|
@@ -24,47 +26,69 @@ module JSI
|
|
24
26
|
if object.respond_to?(:jsi_modified_copy)
|
25
27
|
object.jsi_modified_copy(&block)
|
26
28
|
else
|
27
|
-
|
29
|
+
yield(object)
|
28
30
|
end
|
29
31
|
end
|
30
32
|
|
31
|
-
#
|
32
|
-
# types of Hash, Array, and basic types of String/boolean/numeric/nil. this
|
33
|
-
# will raise TypeError if an object is given that is not a type that seems
|
34
|
-
# to be expressable as json.
|
33
|
+
# A structure like the given `object`, recursively coerced to JSON-compatible types.
|
35
34
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
35
|
+
# - Structures of Hash, Array, and basic types of String/number/boolean/nil are returned as-is.
|
36
|
+
# - If the object responds to `#as_json`, that method is used, passing any given options.
|
37
|
+
# - If the object supports [implicit conversion](https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html)
|
38
|
+
# with `#to_hash`, `#to_ary`, `#to_str`, or `#to_int`, that is used.
|
39
|
+
# - Set becomes Array; Symbol becomes String.
|
40
|
+
# - Types with no known coersion to JSON-compatible raise TypeError.
|
40
41
|
#
|
41
|
-
# @param object [Object]
|
42
|
-
# @return [Array, Hash, String, Boolean, NilClass
|
43
|
-
#
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
42
|
+
# @param object [Object]
|
43
|
+
# @return [Array, Hash, String, Integer, Float, Boolean, NilClass] a JSON-compatible structure like the given `object`
|
44
|
+
# @raise [TypeError] If the object cannot be coerced to a JSON-compatible structure
|
45
|
+
def as_json(object, options = {})
|
46
|
+
type_err = proc { raise(TypeError, "cannot express object as json: #{object.pretty_inspect.chomp}") }
|
47
|
+
if object.respond_to?(:as_json)
|
48
|
+
options.empty? ? object.as_json : object.as_json(**options) # TODO remove eventually (keyword argument compatibility)
|
49
|
+
elsif object.is_a?(Addressable::URI)
|
50
|
+
object.to_s
|
51
|
+
elsif object.respond_to?(:to_hash) && (object_to_hash = object.to_hash).is_a?(Hash)
|
52
|
+
result = {}
|
53
|
+
object_to_hash.each_pair do |k, v|
|
54
|
+
ks = k.is_a?(String) ? k :
|
55
|
+
k.is_a?(Symbol) ? k.to_s :
|
56
|
+
k.respond_to?(:to_str) && (kstr = k.to_str).is_a?(String) ? kstr :
|
52
57
|
raise(TypeError, "json object (hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
elsif object.respond_to?(:to_ary)
|
57
|
-
|
58
|
-
elsif [String, TrueClass, FalseClass, NilClass
|
58
|
+
result[ks] = as_json(v, **options)
|
59
|
+
end
|
60
|
+
result
|
61
|
+
elsif object.respond_to?(:to_ary) && (object_to_ary = object.to_ary).is_a?(Array)
|
62
|
+
object_to_ary.map { |e| as_json(e, **options) }
|
63
|
+
elsif [String, Integer, TrueClass, FalseClass, NilClass].any? { |c| object.is_a?(c) }
|
64
|
+
object
|
65
|
+
elsif object.is_a?(Float)
|
66
|
+
type_err.call unless object.finite?
|
59
67
|
object
|
60
68
|
elsif object.is_a?(Symbol)
|
61
69
|
object.to_s
|
62
70
|
elsif object.is_a?(Set)
|
63
|
-
as_json(object.to_a,
|
64
|
-
elsif object.respond_to?(:
|
65
|
-
|
71
|
+
as_json(object.to_a, **options)
|
72
|
+
elsif object.respond_to?(:to_str) && (object_to_str = object.to_str).is_a?(String)
|
73
|
+
object_to_str
|
74
|
+
elsif object.respond_to?(:to_int) && (object_to_int = object.to_int).is_a?(Integer)
|
75
|
+
object_to_int
|
66
76
|
else
|
67
|
-
|
77
|
+
type_err.call
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# A JSON encoded string of the given object.
|
82
|
+
#
|
83
|
+
# - If the object has a `#to_json` method that isn't defined by the stdlib `json` gem,
|
84
|
+
# that method is used, passing any given options.
|
85
|
+
# - Otherwise, JSON is generated using {as_json} to coerce to compatible types.
|
86
|
+
# @return [String]
|
87
|
+
def to_json(object, options = {})
|
88
|
+
if USE_TO_JSON_METHOD[object.class]
|
89
|
+
options.empty? ? object.to_json : object.to_json(**options) # TODO remove eventually (keyword argument compatibility)
|
90
|
+
else
|
91
|
+
JSON.generate(as_json(object, **options))
|
68
92
|
end
|
69
93
|
end
|
70
94
|
|
@@ -96,11 +120,11 @@ module JSI
|
|
96
120
|
end
|
97
121
|
|
98
122
|
def deep_stringify_symbol_keys(object)
|
99
|
-
if object.respond_to?(:to_hash)
|
123
|
+
if object.respond_to?(:to_hash) && !object.is_a?(Addressable::URI)
|
100
124
|
JSI::Util.modified_copy(object) do |hash|
|
101
125
|
out = {}
|
102
126
|
(hash.respond_to?(:each) ? hash : hash.to_hash).each do |k, v|
|
103
|
-
out[k.is_a?(Symbol) ? k.to_s : deep_stringify_symbol_keys(k)] = deep_stringify_symbol_keys(v)
|
127
|
+
out[k.is_a?(Symbol) ? k.to_s.freeze : deep_stringify_symbol_keys(k)] = deep_stringify_symbol_keys(v)
|
104
128
|
end
|
105
129
|
out
|
106
130
|
end
|
@@ -115,9 +139,69 @@ module JSI
|
|
115
139
|
end
|
116
140
|
end
|
117
141
|
|
142
|
+
# returns an object which is equal to the param object, and is recursively frozen.
|
143
|
+
# the given object is not modified.
|
144
|
+
def deep_to_frozen(object, not_implemented: nil)
|
145
|
+
dtf = proc { |o| deep_to_frozen(o, not_implemented: not_implemented) }
|
146
|
+
if object.instance_of?(Hash)
|
147
|
+
out = {}
|
148
|
+
identical = object.frozen?
|
149
|
+
object.each do |k, v|
|
150
|
+
fk = dtf[k]
|
151
|
+
fv = dtf[v]
|
152
|
+
identical &&= fk.__id__ == k.__id__
|
153
|
+
identical &&= fv.__id__ == v.__id__
|
154
|
+
out[fk] = fv
|
155
|
+
end
|
156
|
+
if !object.default.nil?
|
157
|
+
out.default = dtf[object.default]
|
158
|
+
identical &&= out.default.__id__ == object.default.__id__
|
159
|
+
end
|
160
|
+
if object.default_proc
|
161
|
+
raise(ArgumentError, "cannot make immutable copy of a Hash with default_proc")
|
162
|
+
end
|
163
|
+
if identical
|
164
|
+
object
|
165
|
+
else
|
166
|
+
out.freeze
|
167
|
+
end
|
168
|
+
elsif object.instance_of?(Array)
|
169
|
+
identical = object.frozen?
|
170
|
+
out = Array.new(object.size)
|
171
|
+
object.each_with_index do |e, i|
|
172
|
+
fe = dtf[e]
|
173
|
+
identical &&= fe.__id__ == e.__id__
|
174
|
+
out[i] = fe
|
175
|
+
end
|
176
|
+
if identical
|
177
|
+
object
|
178
|
+
else
|
179
|
+
out.freeze
|
180
|
+
end
|
181
|
+
elsif object.instance_of?(String)
|
182
|
+
if object.frozen?
|
183
|
+
object
|
184
|
+
else
|
185
|
+
object.dup.freeze
|
186
|
+
end
|
187
|
+
elsif CLASSES_ALWAYS_FROZEN.any? { |c| object.is_a?(c) } # note: `is_a?`, not `instance_of?`, here because instance_of?(Integer) is false until Fixnum/Bignum is gone. this is fine here; there is no concern of subclasses of CLASSES_ALWAYS_FROZEN duping/freezing differently (as with e.g. ActiveSupport::HashWithIndifferentAccess)
|
188
|
+
object
|
189
|
+
else
|
190
|
+
if not_implemented
|
191
|
+
not_implemented.call(object)
|
192
|
+
else
|
193
|
+
raise(NotImplementedError, [
|
194
|
+
"deep_to_frozen not implemented for class: #{object.class}",
|
195
|
+
"object: #{object.pretty_inspect.chomp}",
|
196
|
+
].join("\n"))
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
118
201
|
# ensures the given param becomes a frozen Set of Modules.
|
119
202
|
# returns the param if it is already that, otherwise initializes and freezes such a Set.
|
120
203
|
#
|
204
|
+
# @api private
|
121
205
|
# @param modules [Set, Enumerable] the object to ensure becomes a frozen Set of Modules
|
122
206
|
# @return [Set] frozen Set containing the given modules
|
123
207
|
# @raise [ArgumentError] when the modules param is not an Enumerable
|
@@ -125,8 +209,10 @@ module JSI
|
|
125
209
|
def ensure_module_set(modules)
|
126
210
|
if modules.is_a?(Set) && modules.frozen?
|
127
211
|
set = modules
|
128
|
-
|
212
|
+
elsif modules.is_a?(Enumerable)
|
129
213
|
set = Set.new(modules).freeze
|
214
|
+
else
|
215
|
+
raise(TypeError, "not given an Enumerable of Modules")
|
130
216
|
end
|
131
217
|
not_modules = set.reject { |s| s.is_a?(Module) }
|
132
218
|
if !not_modules.empty?
|
@@ -138,7 +224,5 @@ module JSI
|
|
138
224
|
|
139
225
|
set
|
140
226
|
end
|
141
|
-
|
142
|
-
extend self
|
143
227
|
end
|
144
228
|
end
|
data/lib/jsi/validation/error.rb
CHANGED
@@ -3,10 +3,7 @@
|
|
3
3
|
module JSI
|
4
4
|
module Validation
|
5
5
|
# a result of validating an instance against schemas which describe it.
|
6
|
-
# virtual base class.
|
7
6
|
class Result
|
8
|
-
include Util::Virtual
|
9
|
-
|
10
7
|
Builder = Util::AttrStruct[*%w(
|
11
8
|
result
|
12
9
|
schema
|
@@ -24,7 +21,6 @@ module JSI
|
|
24
21
|
end
|
25
22
|
|
26
23
|
def schema_issue(*_)
|
27
|
-
virtual_method
|
28
24
|
end
|
29
25
|
|
30
26
|
def schema_error(message, keyword = nil)
|
@@ -48,12 +44,12 @@ module JSI
|
|
48
44
|
subresult
|
49
45
|
end
|
50
46
|
|
47
|
+
# @param instance_child_token [String, Integer]
|
51
48
|
# @param subschema_ptr [JSI::Ptr, #to_ary]
|
52
|
-
# @param subinstance_ptr [JSI::Ptr, #to_ary]
|
53
49
|
# @return [JSI::Validation::Result]
|
54
|
-
def child_subschema_validate(
|
50
|
+
def child_subschema_validate(instance_child_token, subschema_ptr)
|
55
51
|
subresult = schema.subschema(subschema_ptr).internal_validate_instance(
|
56
|
-
instance_ptr
|
52
|
+
instance_ptr[instance_child_token],
|
57
53
|
instance_document,
|
58
54
|
validate_only: validate_only,
|
59
55
|
)
|
@@ -71,24 +67,13 @@ module JSI
|
|
71
67
|
nil
|
72
68
|
end
|
73
69
|
end
|
70
|
+
end
|
74
71
|
|
75
|
-
|
76
|
-
self.class::Builder.new(
|
77
|
-
result: self,
|
78
|
-
schema: schema,
|
79
|
-
instance_ptr: instance_ptr,
|
80
|
-
instance_document: instance_document,
|
81
|
-
validate_only: validate_only,
|
82
|
-
visited_refs: visited_refs,
|
83
|
-
)
|
84
|
-
end
|
85
|
-
|
72
|
+
class Result
|
86
73
|
# is the instance valid against its schemas?
|
87
74
|
# @return [Boolean]
|
88
75
|
def valid?
|
89
|
-
#
|
90
|
-
virtual_method
|
91
|
-
# :nocov:
|
76
|
+
#chkbug raise(NotImplementedError)
|
92
77
|
end
|
93
78
|
|
94
79
|
include Util::FingerprintHash
|
@@ -102,7 +87,7 @@ module JSI
|
|
102
87
|
valid,
|
103
88
|
message,
|
104
89
|
keyword: nil,
|
105
|
-
results:
|
90
|
+
results: Util::EMPTY_ARY
|
106
91
|
)
|
107
92
|
results.each { |res| result.schema_issues.merge(res.schema_issues) }
|
108
93
|
if !valid
|
@@ -126,7 +111,9 @@ module JSI
|
|
126
111
|
})
|
127
112
|
end
|
128
113
|
end
|
114
|
+
end
|
129
115
|
|
116
|
+
class FullResult
|
130
117
|
def initialize
|
131
118
|
@validation_errors = Set.new
|
132
119
|
@schema_issues = Set.new
|
@@ -140,7 +127,6 @@ module JSI
|
|
140
127
|
end
|
141
128
|
|
142
129
|
def freeze
|
143
|
-
@validation_errors.each(&:freeze)
|
144
130
|
@schema_issues.each(&:freeze)
|
145
131
|
@validation_errors.freeze
|
146
132
|
@schema_issues.freeze
|
@@ -156,17 +142,14 @@ module JSI
|
|
156
142
|
self
|
157
143
|
end
|
158
144
|
|
159
|
-
|
160
|
-
|
161
|
-
end
|
162
|
-
|
163
|
-
# @private
|
145
|
+
# see {Util::Private::FingerprintHash}
|
146
|
+
# @api private
|
164
147
|
def jsi_fingerprint
|
165
148
|
{
|
166
149
|
class: self.class,
|
167
150
|
validation_errors: validation_errors,
|
168
151
|
schema_issues: schema_issues,
|
169
|
-
}
|
152
|
+
}.freeze
|
170
153
|
end
|
171
154
|
end
|
172
155
|
|
@@ -178,7 +161,7 @@ module JSI
|
|
178
161
|
valid,
|
179
162
|
message,
|
180
163
|
keyword: nil,
|
181
|
-
results:
|
164
|
+
results: Util::EMPTY_ARY
|
182
165
|
)
|
183
166
|
if !valid
|
184
167
|
throw(:jsi_validation_result, INVALID)
|
@@ -189,7 +172,9 @@ module JSI
|
|
189
172
|
# noop
|
190
173
|
end
|
191
174
|
end
|
175
|
+
end
|
192
176
|
|
177
|
+
class ValidityResult
|
193
178
|
def initialize(valid)
|
194
179
|
@valid = valid
|
195
180
|
end
|
@@ -198,12 +183,13 @@ module JSI
|
|
198
183
|
@valid
|
199
184
|
end
|
200
185
|
|
201
|
-
#
|
186
|
+
# see {Util::Private::FingerprintHash}
|
187
|
+
# @api private
|
202
188
|
def jsi_fingerprint
|
203
189
|
{
|
204
190
|
class: self.class,
|
205
191
|
valid: valid?,
|
206
|
-
}
|
192
|
+
}.freeze
|
207
193
|
end
|
208
194
|
end
|
209
195
|
end
|
data/lib/jsi/version.rb
CHANGED
data/lib/jsi.rb
CHANGED
@@ -8,8 +8,6 @@ require "pathname"
|
|
8
8
|
require "bigdecimal"
|
9
9
|
require "addressable/uri"
|
10
10
|
|
11
|
-
require "jsi/util"
|
12
|
-
|
13
11
|
module JSI
|
14
12
|
# generally put in code paths that are not expected to be valid control flow paths.
|
15
13
|
# rather a NotImplementedCorrectlyError. but that's too long.
|
@@ -19,6 +17,9 @@ module JSI
|
|
19
17
|
class Bug < NotImplementedError
|
20
18
|
end
|
21
19
|
|
20
|
+
# @private TODO remove, any ruby without this is already long EOL
|
21
|
+
FrozenError = Object.const_defined?(:FrozenError) ? ::FrozenError : Class.new(StandardError)
|
22
|
+
|
22
23
|
# @private
|
23
24
|
ROOT_PATH = Pathname.new(__FILE__).dirname.parent.expand_path
|
24
25
|
|
@@ -28,53 +29,94 @@ module JSI
|
|
28
29
|
# @private
|
29
30
|
SCHEMAS_PATH = RESOURCES_PATH.join('schemas')
|
30
31
|
|
32
|
+
DEFAULT_CONTENT_TO_IMMUTABLE = proc do |content|
|
33
|
+
Util.deep_to_frozen(content, not_implemented: proc do |instance|
|
34
|
+
raise(ArgumentError, [
|
35
|
+
"JSI does not know how to make the given instance immutable.",
|
36
|
+
"See new_jsi / new_schema params `mutable` and `to_immutable` documentation for options.",
|
37
|
+
"https://www.rubydoc.info/gems/jsi/#{VERSION}/JSI/SchemaSet#new_jsi-instance_method",
|
38
|
+
"Given instance: #{instance.pretty_inspect.chomp}",
|
39
|
+
].join("\n"))
|
40
|
+
end)
|
41
|
+
end
|
42
|
+
|
43
|
+
autoload :Util, 'jsi/util'
|
31
44
|
autoload :Ptr, 'jsi/ptr'
|
32
|
-
autoload :Typelike, 'jsi/util/typelike'
|
33
45
|
autoload :Schema, 'jsi/schema'
|
34
46
|
autoload :SchemaSet, 'jsi/schema_set'
|
35
47
|
autoload :Base, 'jsi/base'
|
36
|
-
autoload
|
37
|
-
autoload :
|
48
|
+
autoload(:MetaSchemaNode, 'jsi/metaschema_node')
|
49
|
+
autoload :SchemaModule, 'jsi/schema_classes'
|
38
50
|
autoload :SchemaClasses, 'jsi/schema_classes'
|
39
51
|
autoload :SchemaRegistry, 'jsi/schema_registry'
|
40
52
|
autoload :Validation, 'jsi/validation'
|
41
53
|
autoload :JSICoder, 'jsi/jsi_coder'
|
42
54
|
|
55
|
+
autoload :JSONSchemaDraft04, 'schemas/json-schema.org/draft-04/schema'
|
56
|
+
autoload :JSONSchemaDraft06, 'schemas/json-schema.org/draft-06/schema'
|
57
|
+
autoload :JSONSchemaDraft07, 'schemas/json-schema.org/draft-07/schema'
|
43
58
|
autoload :JSONSchemaOrgDraft04, 'schemas/json-schema.org/draft-04/schema'
|
44
59
|
autoload :JSONSchemaOrgDraft06, 'schemas/json-schema.org/draft-06/schema'
|
45
60
|
autoload :JSONSchemaOrgDraft07, 'schemas/json-schema.org/draft-07/schema'
|
46
61
|
|
47
62
|
autoload :SimpleWrap, 'jsi/simple_wrap'
|
48
63
|
|
49
|
-
#
|
64
|
+
# Instantiates the given schema content as a JSI Schema, passing all params to
|
65
|
+
# {JSI.new_schema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
|
50
66
|
#
|
51
|
-
# see
|
52
|
-
|
53
|
-
|
54
|
-
# @return (see JSI::Schema.new_schema)
|
55
|
-
def self.new_schema(schema_object, **kw)
|
56
|
-
JSI::Schema.new_schema(schema_object, **kw)
|
67
|
+
# @return (see JSI::Schema::MetaSchema#new_schema_module)
|
68
|
+
def self.new_schema_module(schema_content, **kw, &block)
|
69
|
+
new_schema(schema_content, **kw, &block).jsi_schema_module
|
57
70
|
end
|
58
71
|
|
59
|
-
#
|
72
|
+
# @private pending dialect/vocabularies
|
73
|
+
# Instantiates the given document as a JSI Meta-Schema.
|
60
74
|
#
|
61
|
-
#
|
75
|
+
# @param metaschema_document an object to be instantiated as a JSI Meta-Schema
|
76
|
+
# @param schema_implementation_modules (see MetaSchemaNode#initialize)
|
77
|
+
# @param to_immutable (see SchemaSet#new_jsi)
|
78
|
+
# @return [JSI::MetaSchemaNode + JSI::Schema::MetaSchema + JSI::Schema]
|
79
|
+
def self.new_metaschema(metaschema_document,
|
80
|
+
schema_implementation_modules: ,
|
81
|
+
to_immutable: DEFAULT_CONTENT_TO_IMMUTABLE
|
82
|
+
)
|
83
|
+
metaschema_document = to_immutable.call(metaschema_document) if to_immutable
|
84
|
+
|
85
|
+
MetaSchemaNode.new(metaschema_document,
|
86
|
+
schema_implementation_modules: schema_implementation_modules,
|
87
|
+
jsi_content_to_immutable: to_immutable,
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
# @private pending dialect/vocabularies
|
92
|
+
# Instantiates the given document as a JSI Meta-Schema, passing all params to
|
93
|
+
# {new_metaschema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
|
62
94
|
#
|
63
|
-
# @
|
64
|
-
|
65
|
-
|
66
|
-
JSI::Schema.new_schema(schema_object, **kw).jsi_schema_module
|
95
|
+
# @return [JSI::SchemaModule + JSI::SchemaModule::MetaSchemaModule]
|
96
|
+
def self.new_metaschema_module(metaschema_document, **kw)
|
97
|
+
new_metaschema(metaschema_document, **kw).jsi_schema_module
|
67
98
|
end
|
68
99
|
|
69
|
-
# `JSI.schema_registry` is the {JSI::SchemaRegistry} in which schemas are registered
|
100
|
+
# `JSI.schema_registry` is the default {JSI::SchemaRegistry} in which schemas are registered and from
|
101
|
+
# which they resolve references.
|
70
102
|
#
|
71
103
|
# @return [JSI::SchemaRegistry]
|
72
104
|
def self.schema_registry
|
73
|
-
|
74
|
-
|
105
|
+
@schema_registry
|
106
|
+
end
|
107
|
+
|
108
|
+
# @param schema_registry [JSI::SchemaRegistry]
|
109
|
+
def self.schema_registry=(schema_registry)
|
110
|
+
@schema_registry = schema_registry
|
75
111
|
end
|
76
|
-
end
|
77
112
|
|
78
|
-
|
79
|
-
|
80
|
-
|
113
|
+
DEFAULT_SCHEMA_REGISTRY = SchemaRegistry.new.tap do |schema_registry|
|
114
|
+
schema_registry.autoload_uri("http://json-schema.org/draft-04/schema") { JSI::JSONSchemaDraft04.schema }
|
115
|
+
schema_registry.autoload_uri("http://json-schema.org/draft-06/schema") { JSI::JSONSchemaDraft06.schema }
|
116
|
+
schema_registry.autoload_uri("http://json-schema.org/draft-07/schema") { JSI::JSONSchemaDraft07.schema }
|
117
|
+
end.freeze
|
118
|
+
|
119
|
+
self.schema_registry = DEFAULT_SCHEMA_REGISTRY.dup
|
120
|
+
|
121
|
+
Schema # trigger autoload, ensure JSI methods (new_schema etc) defined in schema.rb load
|
122
|
+
end
|