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.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +6 -1
  3. data/CHANGELOG.md +33 -0
  4. data/LICENSE.md +1 -1
  5. data/README.md +29 -23
  6. data/jsi.gemspec +29 -0
  7. data/lib/jsi/base/mutability.rb +44 -0
  8. data/lib/jsi/base/node.rb +348 -0
  9. data/lib/jsi/base.rb +497 -339
  10. data/lib/jsi/jsi_coder.rb +19 -17
  11. data/lib/jsi/metaschema_node/bootstrap_schema.rb +61 -26
  12. data/lib/jsi/metaschema_node.rb +161 -133
  13. data/lib/jsi/ptr.rb +80 -47
  14. data/lib/jsi/schema/application/child_application/contains.rb +11 -2
  15. data/lib/jsi/schema/application/child_application/draft04.rb +0 -1
  16. data/lib/jsi/schema/application/child_application/draft06.rb +0 -1
  17. data/lib/jsi/schema/application/child_application/draft07.rb +0 -1
  18. data/lib/jsi/schema/application/child_application/items.rb +3 -3
  19. data/lib/jsi/schema/application/child_application/properties.rb +3 -3
  20. data/lib/jsi/schema/application/child_application.rb +0 -27
  21. data/lib/jsi/schema/application/inplace_application/dependencies.rb +1 -1
  22. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -1
  23. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -1
  24. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -1
  25. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +3 -3
  26. data/lib/jsi/schema/application/inplace_application/ref.rb +2 -2
  27. data/lib/jsi/schema/application/inplace_application/someof.rb +26 -11
  28. data/lib/jsi/schema/application/inplace_application.rb +0 -32
  29. data/lib/jsi/schema/draft04.rb +0 -1
  30. data/lib/jsi/schema/draft06.rb +0 -1
  31. data/lib/jsi/schema/draft07.rb +0 -1
  32. data/lib/jsi/schema/ref.rb +46 -19
  33. data/lib/jsi/schema/schema_ancestor_node.rb +69 -66
  34. data/lib/jsi/schema/validation/array.rb +3 -3
  35. data/lib/jsi/schema/validation/const.rb +1 -1
  36. data/lib/jsi/schema/validation/contains.rb +2 -2
  37. data/lib/jsi/schema/validation/dependencies.rb +1 -1
  38. data/lib/jsi/schema/validation/draft04/minmax.rb +8 -6
  39. data/lib/jsi/schema/validation/draft04.rb +0 -2
  40. data/lib/jsi/schema/validation/draft06.rb +0 -2
  41. data/lib/jsi/schema/validation/draft07.rb +0 -2
  42. data/lib/jsi/schema/validation/enum.rb +1 -1
  43. data/lib/jsi/schema/validation/ifthenelse.rb +5 -5
  44. data/lib/jsi/schema/validation/items.rb +7 -7
  45. data/lib/jsi/schema/validation/not.rb +1 -1
  46. data/lib/jsi/schema/validation/numeric.rb +5 -5
  47. data/lib/jsi/schema/validation/object.rb +2 -2
  48. data/lib/jsi/schema/validation/pattern.rb +2 -2
  49. data/lib/jsi/schema/validation/properties.rb +7 -7
  50. data/lib/jsi/schema/validation/property_names.rb +1 -1
  51. data/lib/jsi/schema/validation/ref.rb +2 -2
  52. data/lib/jsi/schema/validation/required.rb +1 -1
  53. data/lib/jsi/schema/validation/someof.rb +3 -3
  54. data/lib/jsi/schema/validation/string.rb +2 -2
  55. data/lib/jsi/schema/validation/type.rb +1 -1
  56. data/lib/jsi/schema/validation.rb +1 -3
  57. data/lib/jsi/schema.rb +443 -226
  58. data/lib/jsi/schema_classes.rb +241 -147
  59. data/lib/jsi/schema_registry.rb +78 -19
  60. data/lib/jsi/schema_set.rb +114 -28
  61. data/lib/jsi/simple_wrap.rb +18 -4
  62. data/lib/jsi/util/private/attr_struct.rb +141 -0
  63. data/lib/jsi/util/private/memo_map.rb +75 -0
  64. data/lib/jsi/util/private.rb +185 -0
  65. data/lib/jsi/{typelike_modules.rb → util/typelike.rb} +79 -105
  66. data/lib/jsi/util.rb +157 -153
  67. data/lib/jsi/validation/error.rb +4 -0
  68. data/lib/jsi/validation/result.rb +18 -32
  69. data/lib/jsi/version.rb +1 -1
  70. data/lib/jsi.rb +65 -39
  71. data/lib/schemas/json-schema.org/draft-04/schema.rb +160 -3
  72. data/lib/schemas/json-schema.org/draft-06/schema.rb +162 -3
  73. data/lib/schemas/json-schema.org/draft-07/schema.rb +189 -3
  74. metadata +27 -11
  75. data/lib/jsi/metaschema.rb +0 -7
  76. data/lib/jsi/pathed_node.rb +0 -116
  77. data/lib/jsi/schema/validation/core.rb +0 -39
  78. data/lib/jsi/util/attr_struct.rb +0 -106
@@ -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
@@ -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