jsi 0.6.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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