jsi 0.6.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 +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
|