jsi-dev 0.0.8 → 0.0.9

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 (148) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +3 -4
  3. data/CHANGELOG.md +19 -0
  4. data/LICENSE.md +2 -3
  5. data/README.md +87 -43
  6. data/docs/{glossary.md → Glossary.md} +84 -52
  7. data/jsi.gemspec +1 -1
  8. data/lib/jsi/base/mutability.rb +48 -0
  9. data/lib/jsi/base/node.rb +66 -52
  10. data/lib/jsi/base.rb +592 -176
  11. data/lib/jsi/jsi_coder.rb +4 -2
  12. data/lib/jsi/metaschema_node/bootstrap_schema.rb +118 -59
  13. data/lib/jsi/metaschema_node.rb +244 -154
  14. data/lib/jsi/ptr.rb +45 -17
  15. data/lib/jsi/ref.rb +197 -0
  16. data/lib/jsi/registry.rb +311 -0
  17. data/lib/jsi/schema/cxt/child_application.rb +35 -0
  18. data/lib/jsi/schema/cxt/inplace_application.rb +37 -0
  19. data/lib/jsi/schema/cxt.rb +80 -0
  20. data/lib/jsi/schema/dialect.rb +137 -0
  21. data/lib/jsi/schema/draft04.rb +113 -5
  22. data/lib/jsi/schema/draft06.rb +123 -5
  23. data/lib/jsi/schema/draft07.rb +157 -5
  24. data/lib/jsi/schema/draft202012.rb +303 -0
  25. data/lib/jsi/schema/dynamic_anchor_map.rb +63 -0
  26. data/lib/jsi/schema/element.rb +69 -0
  27. data/lib/jsi/schema/elements/anchor.rb +13 -0
  28. data/lib/jsi/schema/elements/array_validation.rb +82 -0
  29. data/lib/jsi/schema/elements/comment.rb +10 -0
  30. data/lib/jsi/schema/{validation → elements}/const.rb +11 -7
  31. data/lib/jsi/schema/elements/contains.rb +59 -0
  32. data/lib/jsi/schema/elements/contains_minmax.rb +91 -0
  33. data/lib/jsi/schema/elements/content_encoding.rb +10 -0
  34. data/lib/jsi/schema/elements/content_media_type.rb +10 -0
  35. data/lib/jsi/schema/elements/content_schema.rb +16 -0
  36. data/lib/jsi/schema/elements/default.rb +11 -0
  37. data/lib/jsi/schema/elements/definitions.rb +19 -0
  38. data/lib/jsi/schema/elements/dependencies.rb +99 -0
  39. data/lib/jsi/schema/elements/dependent_required.rb +49 -0
  40. data/lib/jsi/schema/elements/dependent_schemas.rb +69 -0
  41. data/lib/jsi/schema/elements/dynamic_ref.rb +69 -0
  42. data/lib/jsi/schema/elements/enum.rb +26 -0
  43. data/lib/jsi/schema/elements/examples.rb +10 -0
  44. data/lib/jsi/schema/elements/format.rb +10 -0
  45. data/lib/jsi/schema/elements/id.rb +30 -0
  46. data/lib/jsi/schema/elements/if_then_else.rb +82 -0
  47. data/lib/jsi/schema/elements/info_bool.rb +10 -0
  48. data/lib/jsi/schema/elements/info_string.rb +10 -0
  49. data/lib/jsi/schema/elements/items.rb +93 -0
  50. data/lib/jsi/schema/elements/items_prefixed.rb +96 -0
  51. data/lib/jsi/schema/elements/not.rb +31 -0
  52. data/lib/jsi/schema/elements/numeric.rb +137 -0
  53. data/lib/jsi/schema/elements/numeric_draft04.rb +77 -0
  54. data/lib/jsi/schema/elements/object_validation.rb +55 -0
  55. data/lib/jsi/schema/elements/pattern.rb +35 -0
  56. data/lib/jsi/schema/elements/properties.rb +145 -0
  57. data/lib/jsi/schema/elements/property_names.rb +48 -0
  58. data/lib/jsi/schema/elements/ref.rb +62 -0
  59. data/lib/jsi/schema/elements/required.rb +34 -0
  60. data/lib/jsi/schema/elements/self.rb +24 -0
  61. data/lib/jsi/schema/elements/some_of.rb +180 -0
  62. data/lib/jsi/schema/elements/string_validation.rb +57 -0
  63. data/lib/jsi/schema/elements/type.rb +43 -0
  64. data/lib/jsi/schema/elements/unevaluated_items.rb +54 -0
  65. data/lib/jsi/schema/elements/unevaluated_properties.rb +54 -0
  66. data/lib/jsi/schema/elements/xschema.rb +10 -0
  67. data/lib/jsi/schema/elements/xvocabulary.rb +10 -0
  68. data/lib/jsi/schema/elements.rb +101 -0
  69. data/lib/jsi/schema/issue.rb +3 -4
  70. data/lib/jsi/schema/schema_ancestor_node.rb +105 -52
  71. data/lib/jsi/schema/vocabulary.rb +36 -0
  72. data/lib/jsi/schema.rb +598 -383
  73. data/lib/jsi/schema_classes.rb +195 -141
  74. data/lib/jsi/schema_set.rb +85 -128
  75. data/lib/jsi/set.rb +23 -0
  76. data/lib/jsi/simple_wrap.rb +14 -17
  77. data/lib/jsi/struct.rb +57 -0
  78. data/lib/jsi/uri.rb +40 -0
  79. data/lib/jsi/util/private/memo_map.rb +9 -13
  80. data/lib/jsi/util/private.rb +59 -31
  81. data/lib/jsi/util/typelike.rb +19 -60
  82. data/lib/jsi/util.rb +53 -34
  83. data/lib/jsi/validation/error.rb +45 -2
  84. data/lib/jsi/validation/result.rb +121 -90
  85. data/lib/jsi/validation.rb +1 -6
  86. data/lib/jsi/version.rb +1 -1
  87. data/lib/jsi.rb +170 -36
  88. data/lib/schemas/json-schema.org/draft/2020-12/schema.rb +62 -0
  89. data/lib/schemas/json-schema.org/draft-04/schema.rb +60 -109
  90. data/lib/schemas/json-schema.org/draft-06/schema.rb +53 -108
  91. data/lib/schemas/json-schema.org/draft-07/schema.rb +63 -127
  92. data/readme.rb +4 -4
  93. data/{resources}/schemas/2020-12_strict.json +19 -0
  94. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/applicator.json +48 -0
  95. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/content.json +17 -0
  96. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/core.json +51 -0
  97. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-annotation.json +14 -0
  98. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/format-assertion.json +14 -0
  99. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/meta-data.json +37 -0
  100. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/unevaluated.json +15 -0
  101. data/{resources}/schemas/json-schema.org/draft/2020-12/meta/validation.json +98 -0
  102. data/{resources}/schemas/json-schema.org/draft/2020-12/schema.json +58 -0
  103. metadata +73 -52
  104. data/lib/jsi/metaschema.rb +0 -6
  105. data/lib/jsi/schema/application/child_application/contains.rb +0 -25
  106. data/lib/jsi/schema/application/child_application/draft04.rb +0 -21
  107. data/lib/jsi/schema/application/child_application/draft06.rb +0 -28
  108. data/lib/jsi/schema/application/child_application/draft07.rb +0 -28
  109. data/lib/jsi/schema/application/child_application/items.rb +0 -18
  110. data/lib/jsi/schema/application/child_application/properties.rb +0 -25
  111. data/lib/jsi/schema/application/child_application.rb +0 -13
  112. data/lib/jsi/schema/application/draft04.rb +0 -8
  113. data/lib/jsi/schema/application/draft06.rb +0 -8
  114. data/lib/jsi/schema/application/draft07.rb +0 -8
  115. data/lib/jsi/schema/application/inplace_application/dependencies.rb +0 -28
  116. data/lib/jsi/schema/application/inplace_application/draft04.rb +0 -25
  117. data/lib/jsi/schema/application/inplace_application/draft06.rb +0 -26
  118. data/lib/jsi/schema/application/inplace_application/draft07.rb +0 -32
  119. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +0 -20
  120. data/lib/jsi/schema/application/inplace_application/ref.rb +0 -18
  121. data/lib/jsi/schema/application/inplace_application/someof.rb +0 -44
  122. data/lib/jsi/schema/application/inplace_application.rb +0 -14
  123. data/lib/jsi/schema/application.rb +0 -12
  124. data/lib/jsi/schema/ref.rb +0 -183
  125. data/lib/jsi/schema/validation/array.rb +0 -69
  126. data/lib/jsi/schema/validation/contains.rb +0 -25
  127. data/lib/jsi/schema/validation/dependencies.rb +0 -49
  128. data/lib/jsi/schema/validation/draft04/minmax.rb +0 -91
  129. data/lib/jsi/schema/validation/draft04.rb +0 -110
  130. data/lib/jsi/schema/validation/draft06.rb +0 -120
  131. data/lib/jsi/schema/validation/draft07.rb +0 -157
  132. data/lib/jsi/schema/validation/enum.rb +0 -25
  133. data/lib/jsi/schema/validation/ifthenelse.rb +0 -46
  134. data/lib/jsi/schema/validation/items.rb +0 -54
  135. data/lib/jsi/schema/validation/not.rb +0 -20
  136. data/lib/jsi/schema/validation/numeric.rb +0 -121
  137. data/lib/jsi/schema/validation/object.rb +0 -45
  138. data/lib/jsi/schema/validation/pattern.rb +0 -34
  139. data/lib/jsi/schema/validation/properties.rb +0 -101
  140. data/lib/jsi/schema/validation/property_names.rb +0 -32
  141. data/lib/jsi/schema/validation/ref.rb +0 -40
  142. data/lib/jsi/schema/validation/required.rb +0 -27
  143. data/lib/jsi/schema/validation/someof.rb +0 -90
  144. data/lib/jsi/schema/validation/string.rb +0 -47
  145. data/lib/jsi/schema/validation/type.rb +0 -49
  146. data/lib/jsi/schema/validation.rb +0 -49
  147. data/lib/jsi/schema_registry.rb +0 -190
  148. data/lib/jsi/util/private/attr_struct.rb +0 -130
@@ -5,23 +5,24 @@ module JSI
5
5
  #
6
6
  # this module is intended to be internal to JSI. no guarantees or API promises
7
7
  # are made for non-JSI classes including this module.
8
+ #
9
+ # @api private
8
10
  module Util::Hashlike
11
+ include(Enumerable)
12
+
9
13
  # safe methods which can be delegated to #to_hash (which the includer is assumed to have defined).
10
14
  # 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
11
15
 
12
16
  # methods which do not need to access the value.
13
17
  SAFE_KEY_ONLY_METHODS = %w(each_key empty? has_key? include? key? keys length member? size).map(&:freeze).freeze
14
- SAFE_KEY_VALUE_METHODS = %w(< <= > >= any? assoc compact dig each_pair each_value fetch fetch_values has_value? invert key merge rassoc reject select to_h to_proc transform_values value? values values_at).map(&:freeze).freeze
15
- DESTRUCTIVE_METHODS = %w(clear delete delete_if keep_if reject! replace select! shift).map(&:freeze).freeze
18
+ SAFE_KEY_VALUE_METHODS = %w(< <= > >= any? assoc compact dig each_pair each_value fetch fetch_values flatten has_value? invert key merge rassoc reject select filter to_h to_proc transform_values value? values values_at).map(&:freeze).freeze
19
+ DESTRUCTIVE_METHODS = %w(clear delete delete_if filter! flatten! keep_if reject! replace select! shift).map(&:freeze).freeze
16
20
  # these return a modified copy
17
- safe_modified_copy_methods = %w(compact)
18
- # select and reject will return a modified copy but need the yielded block variable value from #[]
19
- safe_kv_block_modified_copy_methods = %w(select reject)
21
+ safe_modified_copy_methods = %w(compact slice except)
20
22
  SAFE_METHODS = SAFE_KEY_ONLY_METHODS | SAFE_KEY_VALUE_METHODS
21
23
  custom_methods = %w(merge) # defined below
22
24
  safe_to_hash_methods = SAFE_METHODS -
23
25
  safe_modified_copy_methods -
24
- safe_kv_block_modified_copy_methods -
25
26
  custom_methods
26
27
  safe_to_hash_methods.each do |method_name|
27
28
  if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
@@ -47,16 +48,6 @@ module JSI
47
48
  end
48
49
  end
49
50
  end
50
- safe_kv_block_modified_copy_methods.each do |method_name|
51
- define_method(method_name) do |**kw, &b|
52
- jsi_modified_copy do |object_to_modify|
53
- responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_hash
54
- responsive_object.public_send(method_name) do |k, _v|
55
- b.call(k, self[k, **kw])
56
- end
57
- end
58
- end
59
- end
60
51
 
61
52
  # like [Hash#update](https://ruby-doc.org/core/Hash.html#method-i-update)
62
53
  # @param other [#to_hash] the other hash to update this hash from
@@ -91,34 +82,22 @@ module JSI
91
82
  end
92
83
  end
93
84
 
94
- # basically the same #inspect as Hash, but has the class name and, if responsive,
95
- # self's #jsi_object_group_text
96
- # @return [String]
97
- def inspect
98
- object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
99
- -"\#{<#{object_group_str}>#{map { |k, v| " #{k.inspect} => #{v.inspect}" }.join(',')}}"
100
- end
101
-
102
- alias_method :to_s, :inspect
103
-
104
85
  # pretty-prints a representation of this hashlike to the given printer
105
86
  # @return [void]
106
87
  def pretty_print(q)
107
88
  object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
108
89
  q.text "\#{<#{object_group_str}>"
109
- q.group_sub {
90
+ q.group {
110
91
  q.nest(2) {
111
92
  q.breakable ' ' if !empty?
112
- q.seplist(self, nil, :each_pair) { |k, v|
113
- q.group {
93
+ q.seplist(self) { |k, v|
114
94
  q.pp k
115
95
  q.text ' => '
116
96
  q.pp v
117
- }
118
97
  }
119
98
  }
99
+ q.breakable('') if !empty?
120
100
  }
121
- q.breakable '' if !empty?
122
101
  q.text '}'
123
102
  end
124
103
  end
@@ -127,24 +106,25 @@ module JSI
127
106
  #
128
107
  # this module is intended to be internal to JSI. no guarantees or API promises
129
108
  # are made for non-JSI classes including this module.
109
+ #
110
+ # @api private
130
111
  module Util::Arraylike
112
+ include(Enumerable)
113
+
131
114
  # safe methods which can be delegated to #to_ary (which the includer is assumed to have defined).
132
115
  # 'safe' means, in this context, nondestructive - methods which do not modify the receiver.
133
116
 
134
117
  # methods which do not need to access the element.
135
118
  SAFE_INDEX_ONLY_METHODS = %w(each_index empty? length size).map(&:freeze).freeze
136
119
  # there are some ambiguous ones that are omitted, like #sort, #map / #collect.
137
- SAFE_INDEX_ELEMENT_METHODS = %w(| & * + - <=> abbrev at bsearch bsearch_index combination compact count cycle dig drop drop_while fetch find_index first include? index join last pack permutation product reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shelljoin shuffle slice sort take take_while transpose uniq values_at zip).map(&:freeze).freeze
120
+ SAFE_INDEX_ELEMENT_METHODS = %w(| & * + - <=> abbrev at bsearch bsearch_index combination compact count cycle difference dig drop drop_while fetch find_index first include? index intersection intersect? join last pack permutation product reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shelljoin shuffle slice sort take take_while transpose union uniq values_at zip).map(&:freeze).freeze
138
121
  DESTRUCTIVE_METHODS = %w(<< clear collect! compact! concat delete delete_at delete_if fill flatten! insert keep_if map! pop push reject! replace reverse! rotate! select! shift shuffle! slice! sort! sort_by! uniq! unshift).map(&:freeze).freeze
139
122
 
140
123
  # methods (well, method) that returns a modified copy and doesn't need any handling of block variable(s)
141
124
  safe_modified_copy_methods = %w(compact)
142
125
 
143
- # methods that return a modified copy and do need handling of block variables
144
- safe_el_block_methods = %w(reject select)
145
-
146
126
  SAFE_METHODS = SAFE_INDEX_ONLY_METHODS | SAFE_INDEX_ELEMENT_METHODS
147
- safe_to_ary_methods = SAFE_METHODS - safe_modified_copy_methods - safe_el_block_methods
127
+ safe_to_ary_methods = SAFE_METHODS - safe_modified_copy_methods
148
128
  safe_to_ary_methods.each do |method_name|
149
129
  if Util::LAST_ARGUMENT_AS_KEYWORD_PARAMETERS
150
130
  define_method(method_name) { |*a, &b| to_ary.public_send(method_name, *a, &b) }
@@ -169,17 +149,6 @@ module JSI
169
149
  end
170
150
  end
171
151
  end
172
- safe_el_block_methods.each do |method_name|
173
- define_method(method_name) do |**kw, &b|
174
- jsi_modified_copy do |object_to_modify|
175
- i = 0
176
- responsive_object = object_to_modify.respond_to?(method_name) ? object_to_modify : object_to_modify.to_ary
177
- responsive_object.public_send(method_name) do |_e|
178
- b.call(self[i, **kw]).tap { i += 1 }
179
- end
180
- end
181
- end
182
- end
183
152
 
184
153
  # see [Array#assoc](https://ruby-doc.org/core/Array.html#method-i-assoc)
185
154
  def assoc(obj)
@@ -195,30 +164,20 @@ module JSI
195
164
  detect { |e| e.respond_to?(:to_ary) and e[1] == obj }
196
165
  end
197
166
 
198
- # basically the same #inspect as Array, but has the class name and, if responsive,
199
- # self's #jsi_object_group_text
200
- # @return [String]
201
- def inspect
202
- object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
203
- -"\#[<#{object_group_str}>#{map { |e| ' ' + e.inspect }.join(',')}]"
204
- end
205
-
206
- alias_method :to_s, :inspect
207
-
208
167
  # pretty-prints a representation of this arraylike to the given printer
209
168
  # @return [void]
210
169
  def pretty_print(q)
211
170
  object_group_str = (respond_to?(:jsi_object_group_text, true) ? jsi_object_group_text : [self.class]).join(' ')
212
171
  q.text "\#[<#{object_group_str}>"
213
- q.group_sub {
172
+ q.group {
214
173
  q.nest(2) {
215
174
  q.breakable ' ' if !empty?
216
- q.seplist(self, nil, :each) { |e|
175
+ q.seplist(self) { |e|
217
176
  q.pp e
218
177
  }
219
178
  }
179
+ q.breakable('') if !empty?
220
180
  }
221
- q.breakable '' if !empty?
222
181
  q.text ']'
223
182
  end
224
183
  end
data/lib/jsi/util.rb CHANGED
@@ -1,10 +1,48 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require("delegate")
4
+
3
5
  module JSI
4
6
  # JSI::Util contains public utilities
5
7
  module Util
6
8
  autoload :Private, 'jsi/util/private'
7
9
 
10
+ # common methods of inspecting / pretty-printing
11
+ # @private (not in Util::Private due to dependency order)
12
+ module Pretty
13
+ # @return [String]
14
+ def inspect
15
+ out = String.new
16
+ PP.singleline_pp(self, out)
17
+ out.freeze
18
+ end
19
+
20
+ # @return [String]
21
+ def to_s
22
+ inspect
23
+ end
24
+
25
+ private
26
+
27
+ def jsi_pp_object_group(q, pres = [self.class.name].freeze, empty: false)
28
+ q.text('#<')
29
+ pres.each_with_index do |pre, i|
30
+ q.text(' ') if i != 0
31
+ q.text(pre.to_s)
32
+ end
33
+ if block_given? && !empty
34
+ q.group do
35
+ q.nest(2) do
36
+ q.breakable(' ')
37
+ yield
38
+ end
39
+ q.breakable('')
40
+ end
41
+ end
42
+ q.text('>')
43
+ end
44
+ end
45
+
8
46
  include Private
9
47
 
10
48
  extend self
@@ -13,7 +51,7 @@ module JSI
13
51
  autoload :Hashlike, 'jsi/util/typelike'
14
52
 
15
53
  # yields the content of the given param `object`. for objects which have a #jsi_modified_copy
16
- # method of their own (JSI::Base, JSI::MetaschemaNode) that method is invoked with the given
54
+ # method of their own (JSI::Base, JSI::MetaSchemaNode) that method is invoked with the given
17
55
  # block. otherwise the given object itself is yielded.
18
56
  #
19
57
  # the given block must result in a modified copy of its block parameter
@@ -32,7 +70,7 @@ module JSI
32
70
 
33
71
  # A structure like the given `object`, recursively coerced to JSON-compatible types.
34
72
  #
35
- # - Structures of Hash, Array, and basic types of String/number/boolean/nil are returned as-is.
73
+ # - Structures of Hash, Array, and simple types of String/number/boolean/nil are returned as-is.
36
74
  # - If the object responds to `#as_json`, that method is used, passing any given options.
37
75
  # - If the object supports [implicit conversion](https://docs.ruby-lang.org/en/master/implicit_conversion_rdoc.html)
38
76
  # with `#to_hash`, `#to_ary`, `#to_str`, or `#to_int`, that is used.
@@ -46,7 +84,7 @@ module JSI
46
84
  type_err = proc { raise(TypeError, "cannot express object as json: #{object.pretty_inspect.chomp}") }
47
85
  if object.respond_to?(:as_json)
48
86
  options.empty? ? object.as_json : object.as_json(**options) # TODO remove eventually (keyword argument compatibility)
49
- elsif object.is_a?(Addressable::URI)
87
+ elsif object.is_a?(URI)
50
88
  object.to_s
51
89
  elsif object.respond_to?(:to_hash) && (object_to_hash = object.to_hash).is_a?(Hash)
52
90
  result = {}
@@ -67,7 +105,7 @@ module JSI
67
105
  object
68
106
  elsif object.is_a?(Symbol)
69
107
  object.to_s
70
- elsif object.is_a?(Set)
108
+ elsif object.is_a?(::Set)
71
109
  as_json(object.to_a, **options)
72
110
  elsif object.respond_to?(:to_str) && (object_to_str = object.to_str).is_a?(String)
73
111
  object_to_str
@@ -85,10 +123,15 @@ module JSI
85
123
  # - Otherwise, JSON is generated using {as_json} to coerce to compatible types.
86
124
  # @return [String]
87
125
  def to_json(object, options = {})
126
+ options_state = options.class.name =~ /\AJSON:.*:Generator::State\z/
88
127
  if USE_TO_JSON_METHOD[object.class]
89
- options.empty? ? object.to_json : object.to_json(**options) # TODO remove eventually (keyword argument compatibility)
128
+ (options_state || !options.empty?) ? object.to_json(options) : object.to_json # TODO remove eventually (keyword argument compatibility)
90
129
  else
91
- JSON.generate(as_json(object, **options))
130
+ if options_state
131
+ JSON.generate(as_json(object), options)
132
+ else
133
+ JSON.generate(as_json(object, **options))
134
+ end
92
135
  end
93
136
  end
94
137
 
@@ -120,7 +163,7 @@ module JSI
120
163
  end
121
164
 
122
165
  def deep_stringify_symbol_keys(object)
123
- if object.respond_to?(:to_hash) && !object.is_a?(Addressable::URI)
166
+ if object.respond_to?(:to_hash) && !object.is_a?(URI)
124
167
  JSI::Util.modified_copy(object) do |hash|
125
168
  out = {}
126
169
  (hash.respond_to?(:each) ? hash : hash.to_hash).each do |k, v|
@@ -143,7 +186,9 @@ module JSI
143
186
  # the given object is not modified.
144
187
  def deep_to_frozen(object, not_implemented: nil)
145
188
  dtf = proc { |o| deep_to_frozen(o, not_implemented: not_implemented) }
146
- if object.instance_of?(Hash)
189
+ if object.is_a?(Delegator)
190
+ object.class.new(dtf[object.__getobj__]).freeze
191
+ elsif object.instance_of?(Hash)
147
192
  out = {}
148
193
  identical = object.frozen?
149
194
  object.each do |k, v|
@@ -197,31 +242,5 @@ module JSI
197
242
  end
198
243
  end
199
244
  end
200
-
201
- # ensures the given param becomes a frozen Set of Modules.
202
- # returns the param if it is already that, otherwise initializes and freezes such a Set.
203
- #
204
- # @param modules [Set, Enumerable] the object to ensure becomes a frozen Set of Modules
205
- # @return [Set] frozen Set containing the given modules
206
- # @raise [ArgumentError] when the modules param is not an Enumerable
207
- # @raise [Schema::NotASchemaError] when the modules param contains objects which are not Schemas
208
- def ensure_module_set(modules)
209
- if modules.is_a?(Set) && modules.frozen?
210
- set = modules
211
- elsif modules.is_a?(Enumerable)
212
- set = Set.new(modules).freeze
213
- else
214
- raise(TypeError, "not given an Enumerable of Modules")
215
- end
216
- not_modules = set.reject { |s| s.is_a?(Module) }
217
- if !not_modules.empty?
218
- raise(TypeError, [
219
- "ensure_module_set given non-Module objects:",
220
- *not_modules.map { |ns| ns.pretty_inspect.chomp },
221
- ].join("\n"))
222
- end
223
-
224
- set
225
- end
226
245
  end
227
246
  end
@@ -2,13 +2,15 @@
2
2
 
3
3
  module JSI
4
4
  module Validation
5
- Error = Util::AttrStruct[*%w(
5
+ Error = Struct.subclass(*%i(
6
6
  message
7
7
  keyword
8
+ additional
8
9
  schema
9
10
  instance_ptr
10
11
  instance_document
11
- )]
12
+ nested_errors
13
+ ))
12
14
 
13
15
  # a validation error of a schema instance against a schema
14
16
  #
@@ -19,6 +21,9 @@ module JSI
19
21
  # the keyword of the schema which failed to validate.
20
22
  # this may be absent if the error is not from a schema keyword (i.e, `false` schema).
21
23
  # @return [String]
24
+ # @!attribute additional
25
+ # additional contextual information about the error
26
+ # @return [Hash]
22
27
  # @!attribute schema
23
28
  # the schema against which the instance failed to validate
24
29
  # @return [JSI::Schema]
@@ -28,7 +33,45 @@ module JSI
28
33
  # @!attribute instance_document
29
34
  # document containing the instance at instance_ptr
30
35
  # @return [Object]
36
+ # @!attribute nested_errors
37
+ # @return [Set<Validation::Error>]
31
38
  class Error
39
+ def initialize(attributes = {})
40
+ super
41
+ freeze
42
+ end
43
+
44
+ # @yield [Validation::Error]
45
+ def each_validation_error(&block)
46
+ return(to_enum(__method__)) if !block_given?
47
+ nested_errors.each { |nested_error| nested_error.each_validation_error(&block) }
48
+ yield(self)
49
+ nil
50
+ end
51
+
52
+ # @return [Object]
53
+ def instance
54
+ instance_ptr.evaluate(instance_document)
55
+ end
56
+
57
+ def pretty_print(q)
58
+ info = {
59
+ message: message,
60
+ instance: instance,
61
+ instance_ptr: instance_ptr,
62
+ keyword: keyword,
63
+ additional: additional,
64
+ 'schema uri': schema.schema_uri || schema.jsi_ptr.uri,
65
+ nested_errors: nested_errors,
66
+ }
67
+ jsi_pp_object_group(q) do
68
+ q.seplist(info) do |k, v|
69
+ q.text(k.to_s)
70
+ q.text(': ')
71
+ q.pp(v)
72
+ end
73
+ end
74
+ end
32
75
  end
33
76
  end
34
77
  end