jsi 0.4.0 → 0.7.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.
- checksums.yaml +4 -4
- data/.yardopts +1 -1
- data/CHANGELOG.md +33 -0
- data/LICENSE.md +1 -1
- data/README.md +114 -42
- data/jsi.gemspec +14 -12
- data/lib/jsi/base/node.rb +183 -0
- data/lib/jsi/base.rb +388 -220
- data/lib/jsi/jsi_coder.rb +8 -7
- data/lib/jsi/metaschema.rb +0 -1
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +101 -0
- data/lib/jsi/metaschema_node.rb +159 -135
- data/lib/jsi/ptr.rb +303 -0
- data/lib/jsi/schema/application/child_application/contains.rb +25 -0
- data/lib/jsi/schema/application/child_application/draft04.rb +22 -0
- data/lib/jsi/schema/application/child_application/draft06.rb +29 -0
- data/lib/jsi/schema/application/child_application/draft07.rb +29 -0
- data/lib/jsi/schema/application/child_application/items.rb +18 -0
- data/lib/jsi/schema/application/child_application/properties.rb +25 -0
- data/lib/jsi/schema/application/child_application.rb +38 -0
- data/lib/jsi/schema/application/draft04.rb +8 -0
- data/lib/jsi/schema/application/draft06.rb +8 -0
- data/lib/jsi/schema/application/draft07.rb +8 -0
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +28 -0
- data/lib/jsi/schema/application/inplace_application/draft04.rb +26 -0
- data/lib/jsi/schema/application/inplace_application/draft06.rb +27 -0
- data/lib/jsi/schema/application/inplace_application/draft07.rb +33 -0
- data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +20 -0
- data/lib/jsi/schema/application/inplace_application/ref.rb +18 -0
- data/lib/jsi/schema/application/inplace_application/someof.rb +44 -0
- data/lib/jsi/schema/application/inplace_application.rb +41 -0
- data/lib/jsi/schema/application.rb +12 -0
- data/lib/jsi/schema/draft04.rb +14 -0
- data/lib/jsi/schema/draft06.rb +14 -0
- data/lib/jsi/schema/draft07.rb +14 -0
- data/lib/jsi/schema/issue.rb +36 -0
- data/lib/jsi/schema/ref.rb +160 -0
- data/lib/jsi/schema/schema_ancestor_node.rb +113 -0
- data/lib/jsi/schema/validation/array.rb +69 -0
- data/lib/jsi/schema/validation/const.rb +20 -0
- data/lib/jsi/schema/validation/contains.rb +25 -0
- data/lib/jsi/schema/validation/core.rb +39 -0
- data/lib/jsi/schema/validation/dependencies.rb +49 -0
- data/lib/jsi/schema/validation/draft04/minmax.rb +91 -0
- data/lib/jsi/schema/validation/draft04.rb +112 -0
- data/lib/jsi/schema/validation/draft06.rb +122 -0
- data/lib/jsi/schema/validation/draft07.rb +159 -0
- data/lib/jsi/schema/validation/enum.rb +25 -0
- data/lib/jsi/schema/validation/ifthenelse.rb +46 -0
- data/lib/jsi/schema/validation/items.rb +54 -0
- data/lib/jsi/schema/validation/not.rb +20 -0
- data/lib/jsi/schema/validation/numeric.rb +121 -0
- data/lib/jsi/schema/validation/object.rb +45 -0
- data/lib/jsi/schema/validation/pattern.rb +34 -0
- data/lib/jsi/schema/validation/properties.rb +101 -0
- data/lib/jsi/schema/validation/property_names.rb +32 -0
- data/lib/jsi/schema/validation/ref.rb +40 -0
- data/lib/jsi/schema/validation/required.rb +27 -0
- data/lib/jsi/schema/validation/someof.rb +90 -0
- data/lib/jsi/schema/validation/string.rb +47 -0
- data/lib/jsi/schema/validation/type.rb +49 -0
- data/lib/jsi/schema/validation.rb +51 -0
- data/lib/jsi/schema.rb +508 -149
- data/lib/jsi/schema_classes.rb +199 -59
- data/lib/jsi/schema_registry.rb +151 -0
- data/lib/jsi/schema_set.rb +181 -0
- data/lib/jsi/simple_wrap.rb +23 -4
- data/lib/jsi/util/private/attr_struct.rb +127 -0
- data/lib/jsi/util/private.rb +204 -0
- data/lib/jsi/util/typelike.rb +229 -0
- data/lib/jsi/util.rb +89 -53
- data/lib/jsi/validation/error.rb +34 -0
- data/lib/jsi/validation/result.rb +210 -0
- data/lib/jsi/validation.rb +15 -0
- data/lib/jsi/version.rb +3 -1
- data/lib/jsi.rb +44 -14
- data/lib/schemas/json-schema.org/draft-04/schema.rb +10 -3
- data/lib/schemas/json-schema.org/draft-06/schema.rb +10 -3
- data/lib/schemas/json-schema.org/draft-07/schema.rb +14 -0
- data/readme.rb +138 -0
- data/{resources}/schemas/json-schema.org/draft-04/schema.json +149 -0
- data/{resources}/schemas/json-schema.org/draft-06/schema.json +154 -0
- data/{resources}/schemas/json-schema.org/draft-07/schema.json +168 -0
- metadata +75 -122
- data/.simplecov +0 -3
- data/Rakefile.rb +0 -9
- data/lib/jsi/base/to_rb.rb +0 -128
- data/lib/jsi/json/node.rb +0 -203
- data/lib/jsi/json/pointer.rb +0 -419
- data/lib/jsi/json-schema-fragments.rb +0 -61
- data/lib/jsi/json.rb +0 -10
- data/lib/jsi/pathed_node.rb +0 -118
- data/lib/jsi/typelike_modules.rb +0 -240
- data/resources/icons/AGPL-3.0.png +0 -0
- data/test/base_array_test.rb +0 -323
- data/test/base_hash_test.rb +0 -337
- data/test/base_test.rb +0 -486
- data/test/jsi_coder_test.rb +0 -85
- data/test/jsi_json_arraynode_test.rb +0 -150
- data/test/jsi_json_hashnode_test.rb +0 -132
- data/test/jsi_json_node_test.rb +0 -257
- data/test/jsi_json_pointer_test.rb +0 -102
- data/test/jsi_test.rb +0 -11
- data/test/jsi_typelike_as_json_test.rb +0 -53
- data/test/metaschema_node_test.rb +0 -19
- data/test/schema_module_test.rb +0 -21
- data/test/schema_test.rb +0 -208
- data/test/spreedly_openapi_test.rb +0 -8
- data/test/test_helper.rb +0 -97
- data/test/util_test.rb +0 -62
data/lib/jsi/util.rb
CHANGED
|
@@ -1,13 +1,74 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module JSI
|
|
4
|
-
# JSI::Util
|
|
5
|
-
# do not rely on them.
|
|
4
|
+
# JSI::Util contains public utilities
|
|
6
5
|
module Util
|
|
7
|
-
|
|
8
|
-
NOOP = -> (*_) { }
|
|
6
|
+
autoload :Private, 'jsi/util/private'
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
include Private
|
|
9
|
+
|
|
10
|
+
autoload :Arraylike, 'jsi/util/typelike'
|
|
11
|
+
autoload :Hashlike, 'jsi/util/typelike'
|
|
12
|
+
|
|
13
|
+
# yields the content of the given param `object`. for objects which have a #jsi_modified_copy
|
|
14
|
+
# method of their own (JSI::Base, JSI::MetaschemaNode) that method is invoked with the given
|
|
15
|
+
# block. otherwise the given object itself is yielded.
|
|
16
|
+
#
|
|
17
|
+
# the given block must result in a modified copy of its block parameter
|
|
18
|
+
# (not destructively modifying the yielded content).
|
|
19
|
+
#
|
|
20
|
+
# @yield [Object] the content of the given object. the block should result
|
|
21
|
+
# in a (nondestructively) modified copy of this.
|
|
22
|
+
# @return [object.class] modified copy of the given object
|
|
23
|
+
def modified_copy(object, &block)
|
|
24
|
+
if object.respond_to?(:jsi_modified_copy)
|
|
25
|
+
object.jsi_modified_copy(&block)
|
|
26
|
+
else
|
|
27
|
+
return yield(object)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# recursive method to express the given argument object in json-compatible
|
|
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.
|
|
35
|
+
#
|
|
36
|
+
# similar effect could be achieved by requiring 'json/add/core' and using #as_json,
|
|
37
|
+
# but I don't much care for how it represents classes that are
|
|
38
|
+
# not naturally expressable in JSON, and prefer not to load its
|
|
39
|
+
# monkey-patching.
|
|
40
|
+
#
|
|
41
|
+
# @param object [Object] the object to be converted to jsonifiability
|
|
42
|
+
# @return [Array, Hash, String, Boolean, NilClass, Numeric] jsonifiable
|
|
43
|
+
# expression of param object
|
|
44
|
+
# @raise [TypeError] when the object (or an object nested with a hash or
|
|
45
|
+
# array of object) cannot be expressed as json
|
|
46
|
+
def as_json(object, *opt)
|
|
47
|
+
if object.is_a?(JSI::Base)
|
|
48
|
+
as_json(object.jsi_node_content, *opt)
|
|
49
|
+
elsif object.respond_to?(:to_hash)
|
|
50
|
+
(object.respond_to?(:map) ? object : object.to_hash).map do |k, v|
|
|
51
|
+
unless k.is_a?(Symbol) || k.respond_to?(:to_str)
|
|
52
|
+
raise(TypeError, "json object (hash) cannot be keyed with: #{k.pretty_inspect.chomp}")
|
|
53
|
+
end
|
|
54
|
+
{k.to_s => as_json(v, *opt)}
|
|
55
|
+
end.inject({}, &:update)
|
|
56
|
+
elsif object.respond_to?(:to_ary)
|
|
57
|
+
(object.respond_to?(:map) ? object : object.to_ary).map { |e| as_json(e, *opt) }
|
|
58
|
+
elsif [String, TrueClass, FalseClass, NilClass, Numeric].any? { |c| object.is_a?(c) }
|
|
59
|
+
object
|
|
60
|
+
elsif object.is_a?(Symbol)
|
|
61
|
+
object.to_s
|
|
62
|
+
elsif object.is_a?(Set)
|
|
63
|
+
as_json(object.to_a, *opt)
|
|
64
|
+
elsif object.respond_to?(:as_json)
|
|
65
|
+
as_json(object.as_json(*opt), *opt)
|
|
66
|
+
else
|
|
67
|
+
raise(TypeError, "cannot express object as json: #{object.pretty_inspect.chomp}")
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# a hash copied from the given hashlike, in which any symbol keys are
|
|
11
72
|
# converted to strings. behavior on collisions is undefined (but in the
|
|
12
73
|
# future could take a block like
|
|
13
74
|
# ActiveSupport::HashWithIndifferentAccess#update)
|
|
@@ -25,7 +86,7 @@ module JSI
|
|
|
25
86
|
unless hashlike.respond_to?(:to_hash)
|
|
26
87
|
raise(ArgumentError, "expected argument to be a hash; got #{hashlike.class.inspect}: #{hashlike.pretty_inspect.chomp}")
|
|
27
88
|
end
|
|
28
|
-
JSI::
|
|
89
|
+
JSI::Util.modified_copy(hashlike) do |hash|
|
|
29
90
|
out = {}
|
|
30
91
|
hash.each do |k, v|
|
|
31
92
|
out[k.is_a?(Symbol) ? k.to_s : k] = v
|
|
@@ -36,7 +97,7 @@ module JSI
|
|
|
36
97
|
|
|
37
98
|
def deep_stringify_symbol_keys(object)
|
|
38
99
|
if object.respond_to?(:to_hash)
|
|
39
|
-
JSI::
|
|
100
|
+
JSI::Util.modified_copy(object) do |hash|
|
|
40
101
|
out = {}
|
|
41
102
|
(hash.respond_to?(:each) ? hash : hash.to_hash).each do |k, v|
|
|
42
103
|
out[k.is_a?(Symbol) ? k.to_s : deep_stringify_symbol_keys(k)] = deep_stringify_symbol_keys(v)
|
|
@@ -44,7 +105,7 @@ module JSI
|
|
|
44
105
|
out
|
|
45
106
|
end
|
|
46
107
|
elsif object.respond_to?(:to_ary)
|
|
47
|
-
JSI::
|
|
108
|
+
JSI::Util.modified_copy(object) do |ary|
|
|
48
109
|
(ary.respond_to?(:each) ? ary : ary.to_ary).map do |e|
|
|
49
110
|
deep_stringify_symbol_keys(e)
|
|
50
111
|
end
|
|
@@ -54,55 +115,30 @@ module JSI
|
|
|
54
115
|
end
|
|
55
116
|
end
|
|
56
117
|
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
# length = ycomb do |len|
|
|
61
|
-
# proc { |list| list == [] ? 0 : 1 + len.call(list[1..-1]) }
|
|
62
|
-
# end
|
|
118
|
+
# ensures the given param becomes a frozen Set of Modules.
|
|
119
|
+
# returns the param if it is already that, otherwise initializes and freezes such a Set.
|
|
63
120
|
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def ==(other)
|
|
74
|
-
object_id == other.object_id || (other.respond_to?(:jsi_fingerprint) && other.jsi_fingerprint == self.jsi_fingerprint)
|
|
121
|
+
# @param modules [Set, Enumerable] the object to ensure becomes a frozen Set of Modules
|
|
122
|
+
# @return [Set] frozen Set containing the given modules
|
|
123
|
+
# @raise [ArgumentError] when the modules param is not an Enumerable
|
|
124
|
+
# @raise [Schema::NotASchemaError] when the modules param contains objects which are not Schemas
|
|
125
|
+
def ensure_module_set(modules)
|
|
126
|
+
if modules.is_a?(Set) && modules.frozen?
|
|
127
|
+
set = modules
|
|
128
|
+
else
|
|
129
|
+
set = Set.new(modules).freeze
|
|
75
130
|
end
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
131
|
+
not_modules = set.reject { |s| s.is_a?(Module) }
|
|
132
|
+
if !not_modules.empty?
|
|
133
|
+
raise(TypeError, [
|
|
134
|
+
"ensure_module_set given non-Module objects:",
|
|
135
|
+
*not_modules.map { |ns| ns.pretty_inspect.chomp },
|
|
136
|
+
].join("\n"))
|
|
82
137
|
end
|
|
83
|
-
end
|
|
84
138
|
|
|
85
|
-
|
|
86
|
-
def jsi_memoize(key, *args_)
|
|
87
|
-
@jsi_memos ||= {}
|
|
88
|
-
@jsi_memos[key] ||= Hash.new do |h, args|
|
|
89
|
-
h[args] = yield(*args)
|
|
90
|
-
end
|
|
91
|
-
@jsi_memos[key][args_]
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def jsi_clear_memo(key, *args)
|
|
95
|
-
@jsi_memos ||= {}
|
|
96
|
-
if @jsi_memos[key]
|
|
97
|
-
if args.empty?
|
|
98
|
-
@jsi_memos[key].clear
|
|
99
|
-
else
|
|
100
|
-
@jsi_memos[key].delete(args)
|
|
101
|
-
end
|
|
102
|
-
end
|
|
103
|
-
end
|
|
139
|
+
set
|
|
104
140
|
end
|
|
141
|
+
|
|
142
|
+
extend self
|
|
105
143
|
end
|
|
106
|
-
public
|
|
107
|
-
extend Util
|
|
108
144
|
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Validation
|
|
5
|
+
Error = Util::AttrStruct[*%w(
|
|
6
|
+
message
|
|
7
|
+
keyword
|
|
8
|
+
schema
|
|
9
|
+
instance_ptr
|
|
10
|
+
instance_document
|
|
11
|
+
)]
|
|
12
|
+
|
|
13
|
+
# a validation error of a schema instance against a schema
|
|
14
|
+
#
|
|
15
|
+
# @!attribute message
|
|
16
|
+
# a message describing the error
|
|
17
|
+
# @return [String]
|
|
18
|
+
# @!attribute keyword
|
|
19
|
+
# the keyword of the schema which failed to validate.
|
|
20
|
+
# this may be absent if the error is not from a schema keyword (i.e, `false` schema).
|
|
21
|
+
# @return [String]
|
|
22
|
+
# @!attribute schema
|
|
23
|
+
# the schema against which the instance failed to validate
|
|
24
|
+
# @return [JSI::Schema]
|
|
25
|
+
# @!attribute instance_ptr
|
|
26
|
+
# pointer to the instance in instance_document
|
|
27
|
+
# @return [JSI::Ptr]
|
|
28
|
+
# @!attribute instance_document
|
|
29
|
+
# document containing the instance at instance_ptr
|
|
30
|
+
# @return [Object]
|
|
31
|
+
class Error
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Validation
|
|
5
|
+
# a result of validating an instance against schemas which describe it.
|
|
6
|
+
# virtual base class.
|
|
7
|
+
class Result
|
|
8
|
+
include Util::Virtual
|
|
9
|
+
|
|
10
|
+
Builder = Util::AttrStruct[*%w(
|
|
11
|
+
result
|
|
12
|
+
schema
|
|
13
|
+
instance_ptr
|
|
14
|
+
instance_document
|
|
15
|
+
validate_only
|
|
16
|
+
visited_refs
|
|
17
|
+
)]
|
|
18
|
+
|
|
19
|
+
# @private
|
|
20
|
+
# a structure used to build a Result. virtual base class.
|
|
21
|
+
class Builder
|
|
22
|
+
def instance
|
|
23
|
+
instance_ptr.evaluate(instance_document)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def schema_issue(*_)
|
|
27
|
+
virtual_method
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def schema_error(message, keyword = nil)
|
|
31
|
+
schema_issue(:error, message, keyword)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def schema_warning(message, keyword = nil)
|
|
35
|
+
schema_issue(:warning, message, keyword)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# @param subschema_ptr [JSI::Ptr, #to_ary]
|
|
39
|
+
# @return [JSI::Validation::Result]
|
|
40
|
+
def inplace_subschema_validate(subschema_ptr)
|
|
41
|
+
subresult = schema.subschema(subschema_ptr).internal_validate_instance(
|
|
42
|
+
instance_ptr,
|
|
43
|
+
instance_document,
|
|
44
|
+
validate_only: validate_only,
|
|
45
|
+
visited_refs: visited_refs,
|
|
46
|
+
)
|
|
47
|
+
merge_schema_issues(subresult)
|
|
48
|
+
subresult
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @param subschema_ptr [JSI::Ptr, #to_ary]
|
|
52
|
+
# @param subinstance_ptr [JSI::Ptr, #to_ary]
|
|
53
|
+
# @return [JSI::Validation::Result]
|
|
54
|
+
def child_subschema_validate(subschema_ptr, subinstance_ptr)
|
|
55
|
+
subresult = schema.subschema(subschema_ptr).internal_validate_instance(
|
|
56
|
+
instance_ptr + subinstance_ptr,
|
|
57
|
+
instance_document,
|
|
58
|
+
validate_only: validate_only,
|
|
59
|
+
)
|
|
60
|
+
merge_schema_issues(subresult)
|
|
61
|
+
subresult
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param other_result [JSI::Validation::Result]
|
|
65
|
+
# @return [void]
|
|
66
|
+
def merge_schema_issues(other_result)
|
|
67
|
+
unless validate_only
|
|
68
|
+
# schema_issues are always merged from subschema results (not depending on validation results)
|
|
69
|
+
result.schema_issues.merge(other_result.schema_issues)
|
|
70
|
+
end
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def builder(schema, instance_ptr, instance_document, validate_only, visited_refs)
|
|
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
|
+
|
|
86
|
+
# is the instance valid against its schemas?
|
|
87
|
+
# @return [Boolean]
|
|
88
|
+
def valid?
|
|
89
|
+
# :nocov:
|
|
90
|
+
virtual_method
|
|
91
|
+
# :nocov:
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
include Util::FingerprintHash
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# a full result of validating an instance against its schemas, with each validation error
|
|
98
|
+
class FullResult < Result
|
|
99
|
+
# @private
|
|
100
|
+
class Builder < Result::Builder
|
|
101
|
+
def validate(
|
|
102
|
+
valid,
|
|
103
|
+
message,
|
|
104
|
+
keyword: nil,
|
|
105
|
+
results: []
|
|
106
|
+
)
|
|
107
|
+
results.each { |res| result.schema_issues.merge(res.schema_issues) }
|
|
108
|
+
if !valid
|
|
109
|
+
results.each { |res| result.validation_errors.merge(res.validation_errors) }
|
|
110
|
+
result.validation_errors << Validation::Error.new({
|
|
111
|
+
message: message,
|
|
112
|
+
keyword: keyword,
|
|
113
|
+
schema: schema,
|
|
114
|
+
instance_ptr: instance_ptr,
|
|
115
|
+
instance_document: instance_document,
|
|
116
|
+
})
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def schema_issue(level, message, keyword = nil)
|
|
121
|
+
result.schema_issues << Schema::Issue.new({
|
|
122
|
+
level: level,
|
|
123
|
+
message: message,
|
|
124
|
+
keyword: keyword,
|
|
125
|
+
schema: schema,
|
|
126
|
+
})
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def initialize
|
|
131
|
+
@validation_errors = Set.new
|
|
132
|
+
@schema_issues = Set.new
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
attr_reader :validation_errors
|
|
136
|
+
attr_reader :schema_issues
|
|
137
|
+
|
|
138
|
+
def valid?
|
|
139
|
+
validation_errors.empty?
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def freeze
|
|
143
|
+
@validation_errors.each(&:freeze)
|
|
144
|
+
@schema_issues.each(&:freeze)
|
|
145
|
+
@validation_errors.freeze
|
|
146
|
+
@schema_issues.freeze
|
|
147
|
+
super
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def merge(result)
|
|
151
|
+
unless result.is_a?(FullResult)
|
|
152
|
+
raise(TypeError, "not a #{FullResult.name}: #{result.pretty_inspect.chomp}")
|
|
153
|
+
end
|
|
154
|
+
validation_errors.merge(result.validation_errors)
|
|
155
|
+
schema_issues.merge(result.schema_issues)
|
|
156
|
+
self
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def +(result)
|
|
160
|
+
FullResult.new.merge(self).merge(result)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
# @private
|
|
164
|
+
def jsi_fingerprint
|
|
165
|
+
{
|
|
166
|
+
class: self.class,
|
|
167
|
+
validation_errors: validation_errors,
|
|
168
|
+
schema_issues: schema_issues,
|
|
169
|
+
}
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# a result indicating only whether an instance is valid against its schemas
|
|
174
|
+
class ValidityResult < Result
|
|
175
|
+
# @private
|
|
176
|
+
class Builder < Result::Builder
|
|
177
|
+
def validate(
|
|
178
|
+
valid,
|
|
179
|
+
message,
|
|
180
|
+
keyword: nil,
|
|
181
|
+
results: []
|
|
182
|
+
)
|
|
183
|
+
if !valid
|
|
184
|
+
throw(:jsi_validation_result, INVALID)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def schema_issue(*_)
|
|
189
|
+
# noop
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def initialize(valid)
|
|
194
|
+
@valid = valid
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def valid?
|
|
198
|
+
@valid
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# @private
|
|
202
|
+
def jsi_fingerprint
|
|
203
|
+
{
|
|
204
|
+
class: self.class,
|
|
205
|
+
valid: valid?,
|
|
206
|
+
}
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
module Validation
|
|
5
|
+
autoload :Error, 'jsi/validation/error'
|
|
6
|
+
|
|
7
|
+
autoload :Result, 'jsi/validation/result'
|
|
8
|
+
autoload :FullResult, 'jsi/validation/result'
|
|
9
|
+
autoload :ValidityResult, 'jsi/validation/result'
|
|
10
|
+
|
|
11
|
+
VALID = ValidityResult.new(true).freeze
|
|
12
|
+
|
|
13
|
+
INVALID = ValidityResult.new(false).freeze
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/jsi/version.rb
CHANGED
data/lib/jsi.rb
CHANGED
|
@@ -5,12 +5,10 @@ require "pp"
|
|
|
5
5
|
require "set"
|
|
6
6
|
require "json"
|
|
7
7
|
require "pathname"
|
|
8
|
+
require "bigdecimal"
|
|
8
9
|
require "addressable/uri"
|
|
9
10
|
|
|
10
|
-
require "jsi/json-schema-fragments"
|
|
11
|
-
|
|
12
11
|
require "jsi/util"
|
|
13
|
-
require "jsi/typelike_modules"
|
|
14
12
|
|
|
15
13
|
module JSI
|
|
16
14
|
# generally put in code paths that are not expected to be valid control flow paths.
|
|
@@ -21,30 +19,62 @@ module JSI
|
|
|
21
19
|
class Bug < NotImplementedError
|
|
22
20
|
end
|
|
23
21
|
|
|
22
|
+
# @private
|
|
24
23
|
ROOT_PATH = Pathname.new(__FILE__).dirname.parent.expand_path
|
|
25
|
-
RESOURCES_PATH = ROOT_PATH.join('resources')
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
25
|
+
# @private
|
|
26
|
+
RESOURCES_PATH = ROOT_PATH.join('{resources}')
|
|
27
|
+
|
|
28
|
+
# @private
|
|
29
|
+
SCHEMAS_PATH = RESOURCES_PATH.join('schemas')
|
|
30
|
+
|
|
31
|
+
autoload :Ptr, 'jsi/ptr'
|
|
32
|
+
autoload :Typelike, 'jsi/util/typelike'
|
|
32
33
|
autoload :Schema, 'jsi/schema'
|
|
34
|
+
autoload :SchemaSet, 'jsi/schema_set'
|
|
33
35
|
autoload :Base, 'jsi/base'
|
|
34
36
|
autoload :Metaschema, 'jsi/metaschema'
|
|
35
37
|
autoload :MetaschemaNode, 'jsi/metaschema_node'
|
|
36
38
|
autoload :SchemaClasses, 'jsi/schema_classes'
|
|
39
|
+
autoload :SchemaRegistry, 'jsi/schema_registry'
|
|
40
|
+
autoload :Validation, 'jsi/validation'
|
|
37
41
|
autoload :JSICoder, 'jsi/jsi_coder'
|
|
38
42
|
|
|
39
43
|
autoload :JSONSchemaOrgDraft04, 'schemas/json-schema.org/draft-04/schema'
|
|
40
44
|
autoload :JSONSchemaOrgDraft06, 'schemas/json-schema.org/draft-06/schema'
|
|
45
|
+
autoload :JSONSchemaOrgDraft07, 'schemas/json-schema.org/draft-07/schema'
|
|
41
46
|
|
|
42
47
|
autoload :SimpleWrap, 'jsi/simple_wrap'
|
|
43
48
|
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
# instantiates a given schema object as a JSI Schema.
|
|
50
|
+
#
|
|
51
|
+
# see {JSI::Schema.new_schema}
|
|
52
|
+
#
|
|
53
|
+
# @param (see JSI::Schema.new_schema)
|
|
54
|
+
# @return (see JSI::Schema.new_schema)
|
|
55
|
+
def self.new_schema(schema_object, **kw)
|
|
56
|
+
JSI::Schema.new_schema(schema_object, **kw)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# instantiates a given schema object as a JSI Schema and returns its JSI Schema Module.
|
|
60
|
+
#
|
|
61
|
+
# shortcut to chain {JSI::Schema.new_schema} + {Schema#jsi_schema_module}.
|
|
62
|
+
#
|
|
63
|
+
# @param (see JSI::Schema.new_schema)
|
|
64
|
+
# @return [Module, JSI::SchemaModule] the JSI Schema Module of the schema
|
|
65
|
+
def self.new_schema_module(schema_object, **kw)
|
|
66
|
+
JSI::Schema.new_schema(schema_object, **kw).jsi_schema_module
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# `JSI.schema_registry` is the {JSI::SchemaRegistry} in which schemas are registered.
|
|
70
|
+
#
|
|
71
|
+
# @return [JSI::SchemaRegistry]
|
|
72
|
+
def self.schema_registry
|
|
73
|
+
return @schema_registry if instance_variable_defined?(:@schema_registry)
|
|
74
|
+
@schema_registry = SchemaRegistry.new
|
|
49
75
|
end
|
|
50
76
|
end
|
|
77
|
+
|
|
78
|
+
JSI.schema_registry.autoload_uri("http://json-schema.org/draft-04/schema") { JSI::JSONSchemaOrgDraft04.schema }
|
|
79
|
+
JSI.schema_registry.autoload_uri("http://json-schema.org/draft-06/schema") { JSI::JSONSchemaOrgDraft06.schema }
|
|
80
|
+
JSI.schema_registry.autoload_uri("http://json-schema.org/draft-07/schema") { JSI::JSONSchemaOrgDraft07.schema }
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module JSI
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
metaschema_document = ::JSON.parse(SCHEMAS_PATH.join('json-schema.org/draft-04/schema.json').read)
|
|
5
|
+
JSONSchemaOrgDraft04 = MetaschemaNode.new(metaschema_document,
|
|
6
|
+
schema_implementation_modules: [JSI::Schema::Draft04],
|
|
7
|
+
).jsi_schema_module
|
|
8
|
+
|
|
9
|
+
# the JSI schema module for `http://json-schema.org/draft-04/schema`
|
|
10
|
+
module JSONSchemaOrgDraft04
|
|
11
|
+
# @!parse extend JSI::DescribesSchemaModule
|
|
12
|
+
# @!parse include JSI::Schema::Draft04
|
|
13
|
+
end
|
|
7
14
|
end
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module JSI
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
metaschema_document = ::JSON.parse(SCHEMAS_PATH.join('json-schema.org/draft-06/schema.json').read)
|
|
5
|
+
JSONSchemaOrgDraft06 = MetaschemaNode.new(metaschema_document,
|
|
6
|
+
schema_implementation_modules: [JSI::Schema::Draft06],
|
|
7
|
+
).jsi_schema_module
|
|
8
|
+
|
|
9
|
+
# the JSI schema module for `http://json-schema.org/draft-06/schema`
|
|
10
|
+
module JSONSchemaOrgDraft06
|
|
11
|
+
# @!parse extend JSI::DescribesSchemaModule
|
|
12
|
+
# @!parse include JSI::Schema::Draft06
|
|
13
|
+
end
|
|
7
14
|
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module JSI
|
|
4
|
+
metaschema_document = ::JSON.parse(SCHEMAS_PATH.join('json-schema.org/draft-07/schema.json').read)
|
|
5
|
+
JSONSchemaOrgDraft07 = MetaschemaNode.new(metaschema_document,
|
|
6
|
+
schema_implementation_modules: [JSI::Schema::Draft07],
|
|
7
|
+
).jsi_schema_module
|
|
8
|
+
|
|
9
|
+
# the JSI schema module for `http://json-schema.org/draft-07/schema`
|
|
10
|
+
module JSONSchemaOrgDraft07
|
|
11
|
+
# @!parse extend JSI::DescribesSchemaModule
|
|
12
|
+
# @!parse include JSI::Schema::Draft07
|
|
13
|
+
end
|
|
14
|
+
end
|