jsi 0.6.0 → 0.8.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 +6 -1
- data/CHANGELOG.md +33 -0
- data/LICENSE.md +1 -1
- data/README.md +29 -23
- data/jsi.gemspec +29 -0
- data/lib/jsi/base/mutability.rb +44 -0
- data/lib/jsi/base/node.rb +348 -0
- data/lib/jsi/base.rb +497 -339
- data/lib/jsi/jsi_coder.rb +19 -17
- data/lib/jsi/metaschema_node/bootstrap_schema.rb +61 -26
- data/lib/jsi/metaschema_node.rb +161 -133
- data/lib/jsi/ptr.rb +80 -47
- data/lib/jsi/schema/application/child_application/contains.rb +11 -2
- 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/items.rb +3 -3
- data/lib/jsi/schema/application/child_application/properties.rb +3 -3
- data/lib/jsi/schema/application/child_application.rb +0 -27
- data/lib/jsi/schema/application/inplace_application/dependencies.rb +1 -1
- 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/ifthenelse.rb +3 -3
- data/lib/jsi/schema/application/inplace_application/ref.rb +2 -2
- data/lib/jsi/schema/application/inplace_application/someof.rb +26 -11
- data/lib/jsi/schema/application/inplace_application.rb +0 -32
- 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 +46 -19
- data/lib/jsi/schema/schema_ancestor_node.rb +69 -66
- data/lib/jsi/schema/validation/array.rb +3 -3
- data/lib/jsi/schema/validation/const.rb +1 -1
- data/lib/jsi/schema/validation/contains.rb +2 -2
- data/lib/jsi/schema/validation/dependencies.rb +1 -1
- data/lib/jsi/schema/validation/draft04/minmax.rb +8 -6
- 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/enum.rb +1 -1
- data/lib/jsi/schema/validation/ifthenelse.rb +5 -5
- data/lib/jsi/schema/validation/items.rb +7 -7
- data/lib/jsi/schema/validation/not.rb +1 -1
- data/lib/jsi/schema/validation/numeric.rb +5 -5
- data/lib/jsi/schema/validation/object.rb +2 -2
- data/lib/jsi/schema/validation/pattern.rb +2 -2
- data/lib/jsi/schema/validation/properties.rb +7 -7
- data/lib/jsi/schema/validation/property_names.rb +1 -1
- data/lib/jsi/schema/validation/ref.rb +2 -2
- data/lib/jsi/schema/validation/required.rb +1 -1
- data/lib/jsi/schema/validation/someof.rb +3 -3
- data/lib/jsi/schema/validation/string.rb +2 -2
- data/lib/jsi/schema/validation/type.rb +1 -1
- data/lib/jsi/schema/validation.rb +1 -3
- data/lib/jsi/schema.rb +443 -226
- data/lib/jsi/schema_classes.rb +241 -147
- data/lib/jsi/schema_registry.rb +78 -19
- data/lib/jsi/schema_set.rb +114 -28
- data/lib/jsi/simple_wrap.rb +18 -4
- data/lib/jsi/util/private/attr_struct.rb +141 -0
- data/lib/jsi/util/private/memo_map.rb +75 -0
- data/lib/jsi/util/private.rb +185 -0
- data/lib/jsi/{typelike_modules.rb → util/typelike.rb} +79 -105
- data/lib/jsi/util.rb +157 -153
- 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 +65 -39
- data/lib/schemas/json-schema.org/draft-04/schema.rb +160 -3
- data/lib/schemas/json-schema.org/draft-06/schema.rb +162 -3
- data/lib/schemas/json-schema.org/draft-07/schema.rb +189 -3
- metadata +27 -11
- data/lib/jsi/metaschema.rb +0 -7
- data/lib/jsi/pathed_node.rb +0 -116
- data/lib/jsi/schema/validation/core.rb +0 -39
- data/lib/jsi/util/attr_struct.rb +0 -106
data/lib/jsi/pathed_node.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JSI
|
4
|
-
# this module represents a node in a document.
|
5
|
-
#
|
6
|
-
# including class MUST define
|
7
|
-
#
|
8
|
-
# - `#jsi_document` [Object] the document
|
9
|
-
# - `#jsi_ptr` [JSI::Ptr] a pointer to the node in the document
|
10
|
-
module PathedNode
|
11
|
-
# the content of this node
|
12
|
-
def jsi_node_content
|
13
|
-
content = jsi_ptr.evaluate(jsi_document)
|
14
|
-
content
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
# module extending a {JSI::PathedNode} object when its jsi_node_content is Hash-like (responds to #to_hash)
|
19
|
-
module PathedHashNode
|
20
|
-
# yields each hash key and value of this node.
|
21
|
-
#
|
22
|
-
# each yielded key is the same as a key of the node content hash,
|
23
|
-
# and each yielded value is the result of self[key] (see #[]).
|
24
|
-
#
|
25
|
-
# returns an Enumerator if no block is given.
|
26
|
-
#
|
27
|
-
# @param a arguments are passed to `#[]`
|
28
|
-
# @yield [Object, Object] each key and value of this hash node
|
29
|
-
# @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
|
30
|
-
def each(*a, &block)
|
31
|
-
return to_enum(__method__) { jsi_node_content_hash_pubsend(:size) } unless block
|
32
|
-
if block.arity > 1
|
33
|
-
jsi_node_content_hash_pubsend(:each_key) { |k| yield k, self[k, *a] }
|
34
|
-
else
|
35
|
-
jsi_node_content_hash_pubsend(:each_key) { |k| yield [k, self[k, *a]] }
|
36
|
-
end
|
37
|
-
self
|
38
|
-
end
|
39
|
-
|
40
|
-
# a hash in which each key is a key of the jsi_node_content hash and each value is the
|
41
|
-
# result of `self[key]`
|
42
|
-
# @param a arguments are passed to `#[]`
|
43
|
-
# @return [Hash]
|
44
|
-
def to_hash(*a)
|
45
|
-
{}.tap { |h| jsi_node_content_hash_pubsend(:each_key) { |k| h[k] = self[k, *a] } }
|
46
|
-
end
|
47
|
-
|
48
|
-
include Hashlike
|
49
|
-
|
50
|
-
# invokes the method with the given name on the jsi_node_content (if defined) or its #to_hash
|
51
|
-
# @param method_name [String, Symbol]
|
52
|
-
# @param a arguments and block are passed to the invocation of method_name
|
53
|
-
# @return [Object] the result of calling method method_name on the jsi_node_content or its #to_hash
|
54
|
-
def jsi_node_content_hash_pubsend(method_name, *a, &b)
|
55
|
-
if jsi_node_content.respond_to?(method_name)
|
56
|
-
jsi_node_content.public_send(method_name, *a, &b)
|
57
|
-
else
|
58
|
-
jsi_node_content.to_hash.public_send(method_name, *a, &b)
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# methods that don't look at the value; can skip the overhead of #[] (invoked by #to_hash)
|
63
|
-
SAFE_KEY_ONLY_METHODS.each do |method_name|
|
64
|
-
define_method(method_name) do |*a, &b|
|
65
|
-
jsi_node_content_hash_pubsend(method_name, *a, &b)
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
module PathedArrayNode
|
71
|
-
# yields each array element of this node.
|
72
|
-
#
|
73
|
-
# each yielded element is the result of self[index] for each index of our array (see #[]).
|
74
|
-
#
|
75
|
-
# returns an Enumerator if no block is given.
|
76
|
-
#
|
77
|
-
# @param a arguments are passed to `#[]`
|
78
|
-
# @yield [Object] each element of this array node
|
79
|
-
# @return [self, Enumerator] an Enumerator if invoked without a block; otherwise self
|
80
|
-
def each(*a, &block)
|
81
|
-
return to_enum(__method__) { jsi_node_content_ary_pubsend(:size) } unless block
|
82
|
-
jsi_node_content_ary_pubsend(:each_index) { |i| yield(self[i, *a]) }
|
83
|
-
self
|
84
|
-
end
|
85
|
-
|
86
|
-
# an array, the same size as the jsi_node_content, in which the element at each index is the
|
87
|
-
# result of `self[index]`
|
88
|
-
# @param a arguments are passed to `#[]`
|
89
|
-
# @return [Array]
|
90
|
-
def to_ary(*a)
|
91
|
-
to_a(*a)
|
92
|
-
end
|
93
|
-
|
94
|
-
include Arraylike
|
95
|
-
|
96
|
-
# invokes the method with the given name on the jsi_node_content (if defined) or its #to_ary
|
97
|
-
# @param method_name [String, Symbol]
|
98
|
-
# @param a arguments and block are passed to the invocation of method_name
|
99
|
-
# @return [Object] the result of calling method method_name on the jsi_node_content or its #to_ary
|
100
|
-
def jsi_node_content_ary_pubsend(method_name, *a, &b)
|
101
|
-
if jsi_node_content.respond_to?(method_name)
|
102
|
-
jsi_node_content.public_send(method_name, *a, &b)
|
103
|
-
else
|
104
|
-
jsi_node_content.to_ary.public_send(method_name, *a, &b)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# methods that don't look at the value; can skip the overhead of #[] (invoked by #to_a).
|
109
|
-
# we override these methods from Arraylike
|
110
|
-
SAFE_INDEX_ONLY_METHODS.each do |method_name|
|
111
|
-
define_method(method_name) do |*a, &b|
|
112
|
-
jsi_node_content_ary_pubsend(method_name, *a, &b)
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JSI
|
4
|
-
module Schema::Validation::Core
|
5
|
-
# validates the given instance against this schema
|
6
|
-
#
|
7
|
-
# @private
|
8
|
-
# @param instance_ptr [JSI::Ptr] a pointer to the instance to validate against the schema, in the instance_document
|
9
|
-
# @param instance_document [#to_hash, #to_ary, Object] document containing the instance instance_ptr pointer points to
|
10
|
-
# @param validate_only [Boolean] whether to return a full schema validation result or a simple, validation-only result
|
11
|
-
# @param visited_refs [Enumerable<JSI::Schema::Ref>]
|
12
|
-
# @return [JSI::Validation::Result]
|
13
|
-
def internal_validate_instance(instance_ptr, instance_document, validate_only: false, visited_refs: [])
|
14
|
-
if validate_only
|
15
|
-
result = JSI::Validation::VALID
|
16
|
-
else
|
17
|
-
result = JSI::Validation::FullResult.new
|
18
|
-
end
|
19
|
-
result_builder = result.builder(self, instance_ptr, instance_document, validate_only, visited_refs)
|
20
|
-
|
21
|
-
catch(:jsi_validation_result) do
|
22
|
-
# note: true/false are not valid as schemas in draft 4; they are only values of
|
23
|
-
# additionalProperties / additionalItems. since their behavior is undefined, though,
|
24
|
-
# it's fine for them to behave the same as boolean schemas in later drafts.
|
25
|
-
# I don't care about draft 4 to implement a different structuring for that.
|
26
|
-
if schema_content == true
|
27
|
-
# noop
|
28
|
-
elsif schema_content == false
|
29
|
-
result_builder.validate(false, 'instance is not valid against `false` schema')
|
30
|
-
elsif schema_content.respond_to?(:to_hash)
|
31
|
-
internal_validate_keywords(result_builder)
|
32
|
-
else
|
33
|
-
result_builder.schema_error('schema is not a boolean or a JSON object')
|
34
|
-
end
|
35
|
-
result
|
36
|
-
end.freeze
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
data/lib/jsi/util/attr_struct.rb
DELETED
@@ -1,106 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module JSI
|
4
|
-
module Util
|
5
|
-
# like a Struct, but stores all the attributes in one @attributes Hash, instead of individual instance
|
6
|
-
# variables for each attribute.
|
7
|
-
# this tends to be easier to work with and more flexible. keys which are symbols are converted to strings.
|
8
|
-
class AttrStruct
|
9
|
-
class AttrStructError < StandardError
|
10
|
-
end
|
11
|
-
|
12
|
-
class UndefinedAttributeKey < AttrStructError
|
13
|
-
end
|
14
|
-
|
15
|
-
class << self
|
16
|
-
# creates a AttrStruct subclass with the given attribute keys.
|
17
|
-
# @param attribute_keys [Enumerable<String, Symbol>]
|
18
|
-
def [](*attribute_keys)
|
19
|
-
unless self == AttrStruct
|
20
|
-
# :nocov:
|
21
|
-
raise(NotImplementedError, "AttrStruct multiple inheritance not supported")
|
22
|
-
# :nocov:
|
23
|
-
end
|
24
|
-
|
25
|
-
bad = attribute_keys.reject { |key| key.respond_to?(:to_str) || key.is_a?(Symbol) }
|
26
|
-
unless bad.empty?
|
27
|
-
raise ArgumentError, "attribute keys must be String or Symbol; got keys: #{bad.map(&:inspect).join(', ')}"
|
28
|
-
end
|
29
|
-
attribute_keys = attribute_keys.map { |key| key.is_a?(Symbol) ? key.to_s : key }
|
30
|
-
|
31
|
-
Class.new(AttrStruct).tap do |klass|
|
32
|
-
klass.define_singleton_method(:attribute_keys) { attribute_keys }
|
33
|
-
klass.send(:define_method, :attribute_keys) { attribute_keys }
|
34
|
-
attribute_keys.each do |attribute_key|
|
35
|
-
# reader
|
36
|
-
klass.send(:define_method, attribute_key) do
|
37
|
-
@attributes[attribute_key]
|
38
|
-
end
|
39
|
-
|
40
|
-
# writer
|
41
|
-
klass.send(:define_method, "#{attribute_key}=") do |value|
|
42
|
-
@attributes[attribute_key] = value
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def initialize(attributes = {})
|
50
|
-
unless attributes.respond_to?(:to_hash)
|
51
|
-
raise(TypeError, "expected attributes to be a Hash; got: #{attributes.inspect}")
|
52
|
-
end
|
53
|
-
attributes = attributes.map { |k, v| {k.is_a?(Symbol) ? k.to_s : k => v} }.inject({}, &:update)
|
54
|
-
bad = attributes.keys.reject { |k| self.attribute_keys.include?(k) }
|
55
|
-
unless bad.empty?
|
56
|
-
raise UndefinedAttributeKey, "undefined attribute keys: #{bad.map(&:inspect).join(', ')}"
|
57
|
-
end
|
58
|
-
@attributes = attributes
|
59
|
-
end
|
60
|
-
|
61
|
-
def [](key)
|
62
|
-
key = key.to_s if key.is_a?(Symbol)
|
63
|
-
@attributes[key]
|
64
|
-
end
|
65
|
-
|
66
|
-
def []=(key, value)
|
67
|
-
key = key.to_s if key.is_a?(Symbol)
|
68
|
-
unless self.attribute_keys.include?(key)
|
69
|
-
raise UndefinedAttributeKey, "undefined attribute key: #{key.inspect}"
|
70
|
-
end
|
71
|
-
@attributes[key] = value
|
72
|
-
end
|
73
|
-
|
74
|
-
# @return [String]
|
75
|
-
def inspect
|
76
|
-
"\#<#{self.class.name}#{@attributes.empty? ? '' : ' '}#{@attributes.map { |k, v| "#{k}: #{v.inspect}" }.join(', ')}>"
|
77
|
-
end
|
78
|
-
|
79
|
-
# pretty-prints a representation of self to the given printer
|
80
|
-
# @return [void]
|
81
|
-
def pretty_print(q)
|
82
|
-
q.text '#<'
|
83
|
-
q.text self.class.name
|
84
|
-
q.group_sub {
|
85
|
-
q.nest(2) {
|
86
|
-
q.breakable(@attributes.empty? ? '' : ' ')
|
87
|
-
q.seplist(@attributes, nil, :each_pair) { |k, v|
|
88
|
-
q.group {
|
89
|
-
q.text k
|
90
|
-
q.text ': '
|
91
|
-
q.pp v
|
92
|
-
}
|
93
|
-
}
|
94
|
-
}
|
95
|
-
}
|
96
|
-
q.breakable ''
|
97
|
-
q.text '>'
|
98
|
-
end
|
99
|
-
|
100
|
-
include FingerprintHash
|
101
|
-
def jsi_fingerprint
|
102
|
-
{class: self.class, attributes: @attributes}
|
103
|
-
end
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|