jsi 0.2.0 → 0.6.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 (105) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +1 -1
  3. data/CHANGELOG.md +36 -0
  4. data/LICENSE.md +613 -0
  5. data/README.md +153 -52
  6. data/lib/jsi/base.rb +485 -338
  7. data/lib/jsi/jsi_coder.rb +24 -18
  8. data/lib/jsi/metaschema.rb +7 -0
  9. data/lib/jsi/metaschema_node/bootstrap_schema.rb +100 -0
  10. data/lib/jsi/metaschema_node.rb +245 -0
  11. data/lib/jsi/pathed_node.rb +49 -46
  12. data/lib/jsi/ptr.rb +292 -0
  13. data/lib/jsi/schema/application/child_application/contains.rb +16 -0
  14. data/lib/jsi/schema/application/child_application/draft04.rb +22 -0
  15. data/lib/jsi/schema/application/child_application/draft06.rb +29 -0
  16. data/lib/jsi/schema/application/child_application/draft07.rb +29 -0
  17. data/lib/jsi/schema/application/child_application/items.rb +18 -0
  18. data/lib/jsi/schema/application/child_application/properties.rb +25 -0
  19. data/lib/jsi/schema/application/child_application.rb +40 -0
  20. data/lib/jsi/schema/application/draft04.rb +8 -0
  21. data/lib/jsi/schema/application/draft06.rb +8 -0
  22. data/lib/jsi/schema/application/draft07.rb +8 -0
  23. data/lib/jsi/schema/application/inplace_application/dependencies.rb +28 -0
  24. data/lib/jsi/schema/application/inplace_application/draft04.rb +26 -0
  25. data/lib/jsi/schema/application/inplace_application/draft06.rb +27 -0
  26. data/lib/jsi/schema/application/inplace_application/draft07.rb +33 -0
  27. data/lib/jsi/schema/application/inplace_application/ifthenelse.rb +20 -0
  28. data/lib/jsi/schema/application/inplace_application/ref.rb +18 -0
  29. data/lib/jsi/schema/application/inplace_application/someof.rb +29 -0
  30. data/lib/jsi/schema/application/inplace_application.rb +46 -0
  31. data/lib/jsi/schema/application.rb +12 -0
  32. data/lib/jsi/schema/draft04.rb +14 -0
  33. data/lib/jsi/schema/draft06.rb +14 -0
  34. data/lib/jsi/schema/draft07.rb +14 -0
  35. data/lib/jsi/schema/issue.rb +36 -0
  36. data/lib/jsi/schema/ref.rb +159 -0
  37. data/lib/jsi/schema/schema_ancestor_node.rb +119 -0
  38. data/lib/jsi/schema/validation/array.rb +69 -0
  39. data/lib/jsi/schema/validation/const.rb +20 -0
  40. data/lib/jsi/schema/validation/contains.rb +25 -0
  41. data/lib/jsi/schema/validation/core.rb +39 -0
  42. data/lib/jsi/schema/validation/dependencies.rb +49 -0
  43. data/lib/jsi/schema/validation/draft04/minmax.rb +91 -0
  44. data/lib/jsi/schema/validation/draft04.rb +112 -0
  45. data/lib/jsi/schema/validation/draft06.rb +122 -0
  46. data/lib/jsi/schema/validation/draft07.rb +159 -0
  47. data/lib/jsi/schema/validation/enum.rb +25 -0
  48. data/lib/jsi/schema/validation/ifthenelse.rb +46 -0
  49. data/lib/jsi/schema/validation/items.rb +54 -0
  50. data/lib/jsi/schema/validation/not.rb +20 -0
  51. data/lib/jsi/schema/validation/numeric.rb +121 -0
  52. data/lib/jsi/schema/validation/object.rb +45 -0
  53. data/lib/jsi/schema/validation/pattern.rb +34 -0
  54. data/lib/jsi/schema/validation/properties.rb +101 -0
  55. data/lib/jsi/schema/validation/property_names.rb +32 -0
  56. data/lib/jsi/schema/validation/ref.rb +40 -0
  57. data/lib/jsi/schema/validation/required.rb +27 -0
  58. data/lib/jsi/schema/validation/someof.rb +90 -0
  59. data/lib/jsi/schema/validation/string.rb +47 -0
  60. data/lib/jsi/schema/validation/type.rb +49 -0
  61. data/lib/jsi/schema/validation.rb +51 -0
  62. data/lib/jsi/schema.rb +528 -233
  63. data/lib/jsi/schema_classes.rb +238 -51
  64. data/lib/jsi/schema_registry.rb +141 -0
  65. data/lib/jsi/schema_set.rb +141 -0
  66. data/lib/jsi/simple_wrap.rb +8 -3
  67. data/lib/jsi/typelike_modules.rb +75 -68
  68. data/lib/jsi/util/attr_struct.rb +106 -0
  69. data/lib/jsi/util.rb +167 -64
  70. data/lib/jsi/validation/error.rb +34 -0
  71. data/lib/jsi/validation/result.rb +210 -0
  72. data/lib/jsi/validation.rb +15 -0
  73. data/lib/jsi/version.rb +3 -1
  74. data/lib/jsi.rb +72 -9
  75. data/lib/schemas/json-schema.org/draft-04/schema.rb +12 -0
  76. data/lib/schemas/json-schema.org/draft-06/schema.rb +12 -0
  77. data/lib/schemas/json-schema.org/draft-07/schema.rb +12 -0
  78. data/readme.rb +138 -0
  79. data/{resources}/schemas/json-schema.org/draft-04/schema.json +149 -0
  80. data/{resources}/schemas/json-schema.org/draft-06/schema.json +154 -0
  81. data/{resources}/schemas/json-schema.org/draft-07/schema.json +168 -0
  82. metadata +80 -107
  83. data/.simplecov +0 -1
  84. data/LICENSE.txt +0 -21
  85. data/Rakefile.rb +0 -9
  86. data/jsi.gemspec +0 -31
  87. data/lib/jsi/base/to_rb.rb +0 -126
  88. data/lib/jsi/json/node.rb +0 -243
  89. data/lib/jsi/json/pointer.rb +0 -330
  90. data/lib/jsi/json-schema-fragments.rb +0 -59
  91. data/lib/jsi/json.rb +0 -8
  92. data/test/base_array_test.rb +0 -209
  93. data/test/base_hash_test.rb +0 -204
  94. data/test/base_test.rb +0 -422
  95. data/test/jsi_coder_test.rb +0 -85
  96. data/test/jsi_json_arraynode_test.rb +0 -150
  97. data/test/jsi_json_hashnode_test.rb +0 -132
  98. data/test/jsi_json_node_test.rb +0 -310
  99. data/test/jsi_json_pointer_test.rb +0 -106
  100. data/test/jsi_test.rb +0 -11
  101. data/test/jsi_typelike_as_json_test.rb +0 -53
  102. data/test/schema_test.rb +0 -196
  103. data/test/spreedly_openapi_test.rb +0 -8
  104. data/test/test_helper.rb +0 -63
  105. data/test/util_test.rb +0 -62
data/lib/jsi/jsi_coder.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSI
2
4
  # this is an ActiveRecord serialization coder intended to serialize between
3
5
  # JSON-compatible objects on the database side, and a JSI instance loaded on
@@ -9,34 +11,37 @@ module JSI
9
11
  # `preferences_json` which is an actual json column, and `preferences_txt`
10
12
  # which is a string:
11
13
  #
12
- # Preferences = JSI.class_for_schema(preferences_json_schema)
14
+ # Preferences = JSI.new_schema_module(preferences_json_schema)
13
15
  # class Foo < ActiveRecord::Base
14
16
  # # as a single serializer, loads a Preferences instance from a json column
15
- # serialize 'preferences', JSI::JSICoder.new(Preferences)
17
+ # serialize 'preferences_json', JSI::JSICoder.new(Preferences)
16
18
  #
17
19
  # # for a text column, arms_serialize will go from JSI to JSON-compatible
18
20
  # # objects to a string. the symbol `:jsi` is a shortcut for JSI::JSICoder.
19
- # arms_serialize 'preferences', [:jsi, Preferences], :json
21
+ # arms_serialize 'preferences_txt', [:jsi, Preferences], :json
20
22
  # end
21
23
  #
22
- # the column data may be either a single instance of the loaded class
24
+ # the column data may be either a single instance of the schema class
23
25
  # (represented as one json object) or an array of them (represented as a json
24
26
  # array of json objects), indicated by the keyword argument `array`.
25
27
  class JSICoder
26
- # @param loaded_class [Class] the JSI::Base subclass which #load will instantiate
27
- # @param array [Boolean] whether the dumped data represent one instance of loaded_class,
28
- # or an array of them. note that it may be preferable to have loaded_class simply be
29
- # an array schema class.
30
- def initialize(loaded_class, array: false)
31
- @loaded_class = loaded_class
28
+ # @param schema [#new_jsi] a Schema, SchemaSet, or JSI schema module. #load
29
+ # will instantiate column data using the JSI schemas represented.
30
+ # @param array [Boolean] whether the dumped data represent one instance of the schema,
31
+ # or an array of them. note that it may be preferable to simply use an array schema.
32
+ def initialize(schema, array: false)
33
+ unless schema.respond_to?(:new_jsi)
34
+ raise(ArgumentError, "schema param does not respond to #new_jsi: #{schema.inspect}")
35
+ end
36
+ @schema = schema
32
37
  @array = array
33
38
  end
34
39
 
35
- # loads the database column to instances of #loaded_class
40
+ # loads the database column to JSI instances of our schema
36
41
  #
37
42
  # @param data [Object, Array, nil] the dumped schema instance(s) of the JSI(s)
38
- # @return [loaded_class instance, Array<loaded_class instance>, nil] the JSI or JSIs
39
- # containing the schema instance(s), or nil if data is nil
43
+ # @return [JSI::Base, Array<JSI::Base>, nil] the JSI or JSIs containing the schema
44
+ # instance(s), or nil if data is nil
40
45
  def load(data)
41
46
  return nil if data.nil?
42
47
  object = if @array
@@ -50,8 +55,9 @@ module JSI
50
55
  object
51
56
  end
52
57
 
53
- # @param object [loaded_class instance, Array<loaded_class instance>, nil] the JSI or array
54
- # of JSIs containing the schema instance(s)
58
+ # dumps the object for the database
59
+ # @param object [JSI::Base, Array<JSI::Base>, nil] the JSI or array of JSIs containing
60
+ # the schema instance(s)
55
61
  # @return [Object, Array, nil] the schema instance(s) of the JSI(s), or nil if object is nil
56
62
  def dump(object)
57
63
  return nil if object.nil?
@@ -72,12 +78,12 @@ module JSI
72
78
 
73
79
  private
74
80
  # @param data [Object]
75
- # @return [loaded_class]
81
+ # @return [JSI::Base]
76
82
  def load_object(data)
77
- @loaded_class.new(data)
83
+ @schema.new_jsi(data)
78
84
  end
79
85
 
80
- # @param object [loaded_class]
86
+ # @param object [JSI::Base, Object]
81
87
  # @return [Object]
82
88
  def dump_object(object)
83
89
  JSI::Typelike.as_json(object)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ module Metaschema
5
+ include JSI::Schema::DescribesSchema
6
+ end
7
+ end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ # @private
5
+ # internal class to bootstrap a metaschema. represents a schema without the complexity of JSI::Base. the
6
+ # schema is represented but schemas describing the schema are not.
7
+ #
8
+ # this class is to only be instantiated on nodes in the document that are known to be schemas.
9
+ # Schema#subschema and Schema#resource_root_subschema are the intended mechanisms to instantiate subschemas
10
+ # and resolve references. #[] and #jsi_root_node are not implemented.
11
+ #
12
+ # metaschema instance modules are attached to generated subclasses of BootstrapSchema by
13
+ # {SchemaClasses.bootstrap_schema_class}. that subclass is instantiated with a document and
14
+ # pointer, representing a schema.
15
+ class MetaschemaNode::BootstrapSchema
16
+ include Util::Memoize
17
+ include Util::FingerprintHash
18
+ include Schema::SchemaAncestorNode
19
+
20
+ class << self
21
+ def inspect
22
+ if self == MetaschemaNode::BootstrapSchema
23
+ name
24
+ else
25
+ "#{name || MetaschemaNode::BootstrapSchema.name} (#{metaschema_instance_modules.map(&:inspect).join(', ')})"
26
+ end
27
+ end
28
+
29
+ alias_method :to_s, :inspect
30
+ end
31
+
32
+ # @param jsi_ptr [JSI::Ptr] pointer to the schema in the document
33
+ # @param jsi_document [#to_hash, #to_ary, Boolean, Object] document containing the schema
34
+ def initialize(
35
+ jsi_document,
36
+ jsi_ptr: Ptr[],
37
+ jsi_schema_base_uri: nil
38
+ )
39
+ unless respond_to?(:metaschema_instance_modules)
40
+ raise(TypeError, "cannot instantiate #{self.class.inspect} which has no method #metaschema_instance_modules")
41
+ end
42
+
43
+ jsi_initialize_memos
44
+
45
+ self.jsi_ptr = jsi_ptr
46
+ self.jsi_document = jsi_document
47
+ self.jsi_schema_base_uri = jsi_schema_base_uri
48
+ end
49
+
50
+ # document containing the schema content
51
+ attr_reader :jsi_document
52
+
53
+ # JSI::Ptr pointing to this schema within the document
54
+ attr_reader :jsi_ptr
55
+
56
+ def jsi_node_content
57
+ jsi_ptr.evaluate(jsi_document)
58
+ end
59
+
60
+ # @return [String]
61
+ def inspect
62
+ "\#<#{jsi_object_group_text.join(' ')} #{schema_content.inspect}>"
63
+ end
64
+
65
+ # pretty-prints a representation of self to the given printer
66
+ # @return [void]
67
+ def pretty_print(q)
68
+ q.text '#<'
69
+ q.text jsi_object_group_text.join(' ')
70
+ q.group_sub {
71
+ q.nest(2) {
72
+ q.breakable ' '
73
+ q.pp schema_content
74
+ }
75
+ }
76
+ q.breakable ''
77
+ q.text '>'
78
+ end
79
+
80
+ # @private
81
+ # @return [Array<String>]
82
+ def jsi_object_group_text
83
+ [
84
+ self.class.name || MetaschemaNode::BootstrapSchema.name,
85
+ "(#{metaschema_instance_modules.map(&:inspect).join(', ')})",
86
+ jsi_ptr.uri,
87
+ ]
88
+ end
89
+
90
+ # @private
91
+ def jsi_fingerprint
92
+ {
93
+ class: self.class,
94
+ jsi_ptr: @jsi_ptr,
95
+ jsi_document: @jsi_document,
96
+ metaschema_instance_modules: metaschema_instance_modules,
97
+ }
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,245 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JSI
4
+ # a MetaschemaNode is a JSI instance representing a node in a document which contains a metaschema.
5
+ # the root of the metaschema is pointed to by metaschema_root_ptr.
6
+ # the schema describing the root of the document is pointed to by root_schema_ptr.
7
+ #
8
+ # like JSI::Base's normal subclasses, this class represents an instance of a schema set, an instance
9
+ # which may itself be a schema. unlike JSI::Base, the document containing the instance and its schemas
10
+ # is the same, and a schema (the metaschema) may be an instance of itself.
11
+ #
12
+ # unlike JSI::Base's normal subclasses, the schemas describing the instance are not part of the class.
13
+ # since the metaschema describes itself, attempting to construct a class from the JSI Schema Module of a
14
+ # schema which is itself an instance of that class results in a causality loop.
15
+ # instead, a MetaschemaNode calculates its {#jsi_schemas} and extends itself with their JSI Schema
16
+ # modules during initialization.
17
+ # the MetaschemaNode of the metaschema is extended with its own JSI Schema Module.
18
+ #
19
+ # if the MetaschemaNode's schemas include its self, it is extended with JSI::Metaschema.
20
+ #
21
+ # a MetaschemaNode is extended with JSI::Schema when it represents a schema - this is the case when
22
+ # the metaschema is one of its schemas.
23
+ class MetaschemaNode < Base
24
+ autoload :BootstrapSchema, 'jsi/metaschema_node/bootstrap_schema'
25
+
26
+ # @param jsi_document the document containing the metaschema
27
+ # @param jsi_ptr [JSI::Ptr] ptr to this MetaschemaNode in jsi_document
28
+ # @param metaschema_instance_modules [Enumerable<Module>] modules which implement the functionality of the
29
+ # schema, to be applied to every schema which is an instance of the metaschema. this must include
30
+ # JSI::Schema directly or indirectly. these are the {Schema#jsi_schema_instance_modules} of the
31
+ # metaschema.
32
+ # @param metaschema_root_ptr [JSI::Ptr] ptr to the root of the metaschema in the jsi_document
33
+ # @param root_schema_ptr [JSI::Ptr] ptr to the schema describing the root of the jsi_document
34
+ def initialize(
35
+ jsi_document,
36
+ jsi_ptr: Ptr[],
37
+ metaschema_instance_modules: ,
38
+ metaschema_root_ptr: Ptr[],
39
+ root_schema_ptr: Ptr[],
40
+ jsi_schema_base_uri: nil
41
+ )
42
+ jsi_initialize_memos
43
+
44
+ self.jsi_document = jsi_document
45
+ self.jsi_ptr = jsi_ptr
46
+ @metaschema_instance_modules = Util.ensure_module_set(metaschema_instance_modules)
47
+ @metaschema_root_ptr = metaschema_root_ptr
48
+ @root_schema_ptr = root_schema_ptr
49
+
50
+ if jsi_ptr.root? && jsi_schema_base_uri
51
+ raise(NotImplementedError, "unsupported jsi_schema_base_uri on metaschema document root")
52
+ end
53
+ self.jsi_schema_base_uri = jsi_schema_base_uri
54
+
55
+ jsi_node_content = self.jsi_node_content
56
+
57
+ if jsi_node_content.respond_to?(:to_hash)
58
+ extend PathedHashNode
59
+ end
60
+ if jsi_node_content.respond_to?(:to_ary)
61
+ extend PathedArrayNode
62
+ end
63
+
64
+ instance_for_schemas = jsi_document
65
+ bootstrap_schema_class = JSI::SchemaClasses.bootstrap_schema_class(metaschema_instance_modules)
66
+ root_bootstrap_schema = bootstrap_schema_class.new(
67
+ jsi_document,
68
+ jsi_ptr: root_schema_ptr,
69
+ jsi_schema_base_uri: nil, # supplying jsi_schema_base_uri on root bootstrap schema is not supported
70
+ )
71
+ our_bootstrap_schemas = jsi_ptr.tokens.inject(SchemaSet[root_bootstrap_schema]) do |bootstrap_schemas, tok|
72
+ child_instance_for_schemas = instance_for_schemas[tok]
73
+ bootstrap_schemas_for_instance = SchemaSet.build do |schemas|
74
+ bootstrap_schemas.each do |bootstrap_schema|
75
+ bootstrap_schema.each_child_applicator_schema(tok, instance_for_schemas) do |child_app_schema|
76
+ child_app_schema.each_inplace_applicator_schema(child_instance_for_schemas) do |child_inpl_app_schema|
77
+ schemas << child_inpl_app_schema
78
+ end
79
+ end
80
+ end
81
+ end
82
+ instance_for_schemas = child_instance_for_schemas
83
+ bootstrap_schemas_for_instance
84
+ end
85
+
86
+ our_bootstrap_schemas.each do |bootstrap_schema|
87
+ if bootstrap_schema.jsi_ptr == metaschema_root_ptr
88
+ metaschema_instance_modules.each do |metaschema_instance_module|
89
+ extend metaschema_instance_module
90
+ end
91
+ end
92
+ if bootstrap_schema.jsi_ptr == jsi_ptr
93
+ extend Metaschema
94
+ self.jsi_schema_instance_modules = metaschema_instance_modules
95
+ end
96
+ end
97
+
98
+ @jsi_schemas = SchemaSet.new(our_bootstrap_schemas) do |bootstrap_schema|
99
+ if bootstrap_schema.jsi_ptr == jsi_ptr
100
+ self
101
+ else
102
+ new_node(
103
+ jsi_ptr: bootstrap_schema.jsi_ptr,
104
+ jsi_schema_base_uri: bootstrap_schema.jsi_schema_base_uri,
105
+ )
106
+ end
107
+ end
108
+
109
+ @jsi_schemas.each do |schema|
110
+ extend schema.jsi_schema_module
111
+ end
112
+
113
+ # workarounds
114
+ begin # draft 4 boolean schema workaround
115
+ # in draft 4, boolean schemas are not described in the root, but on anyOf schemas on
116
+ # properties/additionalProperties and properties/additionalItems.
117
+ # since these describe schemas, their jsi_schema_instance_modules are the metaschema_instance_modules.
118
+ addtlPropsanyOf = metaschema_root_ptr["properties"]["additionalProperties"]["anyOf"]
119
+ addtlItemsanyOf = metaschema_root_ptr["properties"]["additionalItems"]["anyOf"]
120
+
121
+ if !jsi_ptr.root? && [addtlPropsanyOf, addtlItemsanyOf].include?(jsi_ptr.parent)
122
+ self.jsi_schema_instance_modules = metaschema_instance_modules
123
+ end
124
+ end
125
+ end
126
+
127
+ # Set of modules to apply to schemas which are instances of (described by) the metaschema
128
+ # @return [Set<Module>]
129
+ attr_reader :metaschema_instance_modules
130
+
131
+ # ptr to the root of the metaschema in the jsi_document
132
+ # @return [JSI::Ptr]
133
+ attr_reader :metaschema_root_ptr
134
+
135
+ # ptr to the schema of the root of the jsi_document
136
+ # @return [JSI::Ptr]
137
+ attr_reader :root_schema_ptr
138
+
139
+ # JSI Schemas describing this MetaschemaNode
140
+ # @return [JSI::SchemaSet]
141
+ attr_reader :jsi_schemas
142
+
143
+ # document root MetaschemaNode
144
+ # @return [MetaschemaNode]
145
+ def jsi_root_node
146
+ if jsi_ptr.root?
147
+ self
148
+ else
149
+ new_node(
150
+ jsi_ptr: Ptr[],
151
+ jsi_schema_base_uri: nil,
152
+ )
153
+ end
154
+ end
155
+
156
+ # parent MetaschemaNode
157
+ # @return [MetaschemaNode]
158
+ def jsi_parent_node
159
+ jsi_ptr.parent.evaluate(jsi_root_node)
160
+ end
161
+
162
+ # subscripts to return a child value identified by the given token.
163
+ #
164
+ # @param token (see JSI::Base#[])
165
+ # @param as_jsi (see JSI::Base#[])
166
+ # @return (see JSI::Base#[])
167
+ def [](token, as_jsi: :auto)
168
+ if respond_to?(:to_hash)
169
+ token_in_range = jsi_node_content_hash_pubsend(:key?, token)
170
+ value = jsi_node_content_hash_pubsend(:[], token)
171
+ elsif respond_to?(:to_ary)
172
+ token_in_range = jsi_node_content_ary_pubsend(:each_index).include?(token)
173
+ value = jsi_node_content_ary_pubsend(:[], token)
174
+ else
175
+ raise(NoMethodError, "cannot subscript (using token: #{token.inspect}) from content: #{jsi_node_content.pretty_inspect.chomp}")
176
+ end
177
+
178
+ begin
179
+ if token_in_range
180
+ value_node = jsi_subinstance_memos[token]
181
+
182
+ jsi_subinstance_as_jsi(value, value_node.jsi_schemas, as_jsi) do
183
+ value_node
184
+ end
185
+ else
186
+ # I think I will not support Hash#default/#default_proc in this case.
187
+ nil
188
+ end
189
+ end
190
+ end
191
+
192
+ # instantiates a new MetaschemaNode whose instance is a modified copy of this MetaschemaNode's instance
193
+ # @yield [Object] the node content of the instance. the block should result
194
+ # in a (nondestructively) modified copy of this.
195
+ # @return [MetaschemaNode] modified copy of self
196
+ def jsi_modified_copy(&block)
197
+ MetaschemaNode.new(jsi_ptr.modified_document_copy(jsi_document, &block), **our_initialize_params)
198
+ end
199
+
200
+ # @private
201
+ # @return [Array<String>]
202
+ def jsi_object_group_text
203
+ if jsi_schemas && jsi_schemas.any?
204
+ class_n_schemas = "#{self.class} (#{jsi_schemas.map { |s| s.jsi_ptr.uri }.join(' ')})"
205
+ else
206
+ class_n_schemas = self.class.to_s
207
+ end
208
+ [
209
+ class_n_schemas,
210
+ is_a?(Metaschema) ? "Metaschema" : is_a?(Schema) ? "Schema" : nil,
211
+ *(jsi_node_content.respond_to?(:jsi_object_group_text) ? jsi_node_content.jsi_object_group_text : []),
212
+ ].compact
213
+ end
214
+
215
+ # an opaque fingerprint of this MetaschemaNode for FingerprintHash
216
+ def jsi_fingerprint
217
+ {class: self.class, jsi_document: jsi_document}.merge(our_initialize_params)
218
+ end
219
+
220
+ private
221
+
222
+ def our_initialize_params
223
+ {
224
+ jsi_ptr: jsi_ptr,
225
+ metaschema_instance_modules: metaschema_instance_modules,
226
+ metaschema_root_ptr: metaschema_root_ptr,
227
+ root_schema_ptr: root_schema_ptr,
228
+ jsi_schema_base_uri: jsi_schema_base_uri,
229
+ }
230
+ end
231
+
232
+ def new_node(params)
233
+ MetaschemaNode.new(jsi_document, **our_initialize_params.merge(params))
234
+ end
235
+
236
+ def jsi_subinstance_memos
237
+ jsi_memomap(:subinstance) do |token|
238
+ new_node(
239
+ jsi_ptr: jsi_ptr[token],
240
+ jsi_schema_base_uri: jsi_resource_ancestor_uri,
241
+ )
242
+ end
243
+ end
244
+ end
245
+ end
@@ -1,26 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module JSI
2
- # including class MUST define
3
- # - #node_document [Object] returning the document
4
- # - #node_ptr [JSI::JSON::Pointer] returning a pointer for the node path in the document
5
- # - #document_root_node [JSI::PathedNode] returning a PathedNode pointing at the document root
6
- # - #parent_node [JSI::PathedNode] returning the parent node of this PathedNode
7
- # - #deref [JSI::PathedNode] following a $ref
4
+ # this module represents a node in a document.
8
5
  #
9
- # given these, this module represents the node in the document at the path.
6
+ # including class MUST define
10
7
  #
11
- # the node content (#node_content) is the result of evaluating the node document at the path.
8
+ # - `#jsi_document` [Object] the document
9
+ # - `#jsi_ptr` [JSI::Ptr] a pointer to the node in the document
12
10
  module PathedNode
13
- def node_content
14
- content = node_ptr.evaluate(node_document)
11
+ # the content of this node
12
+ def jsi_node_content
13
+ content = jsi_ptr.evaluate(jsi_document)
15
14
  content
16
15
  end
17
-
18
- def node_ptr_deref(&block)
19
- node_ptr.deref(node_document, &block)
20
- end
21
16
  end
22
17
 
23
- # module extending a {JSI::PathedNode} object when its node_content is Hash-like (responds to #to_hash)
18
+ # module extending a {JSI::PathedNode} object when its jsi_node_content is Hash-like (responds to #to_hash)
24
19
  module PathedHashNode
25
20
  # yields each hash key and value of this node.
26
21
  #
@@ -29,41 +24,45 @@ module JSI
29
24
  #
30
25
  # returns an Enumerator if no block is given.
31
26
  #
27
+ # @param a arguments are passed to `#[]`
32
28
  # @yield [Object, Object] each key and value of this hash node
33
- # @return [self, Enumerator]
34
- def each(&block)
35
- return to_enum(__method__) { node_content_hash_pubsend(:size) } unless block_given?
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
36
32
  if block.arity > 1
37
- node_content_hash_pubsend(:each_key) { |k| yield k, self[k] }
33
+ jsi_node_content_hash_pubsend(:each_key) { |k| yield k, self[k, *a] }
38
34
  else
39
- node_content_hash_pubsend(:each_key) { |k| yield [k, self[k]] }
35
+ jsi_node_content_hash_pubsend(:each_key) { |k| yield [k, self[k, *a]] }
40
36
  end
41
37
  self
42
38
  end
43
39
 
44
- # @return [Hash] a hash in which each key is a key of the node_content hash and
45
- # each value is the result of self[key] (see #[]).
46
- def to_hash
47
- {}.tap { |h| each_key { |k| h[k] = self[k] } }
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] } }
48
46
  end
49
47
 
50
48
  include Hashlike
51
49
 
50
+ # invokes the method with the given name on the jsi_node_content (if defined) or its #to_hash
52
51
  # @param method_name [String, Symbol]
53
- # @param *a, &b are passed to the invocation of method_name
54
- # @return [Object] the result of calling method method_name on the node_content or its #to_hash
55
- def node_content_hash_pubsend(method_name, *a, &b)
56
- if node_content.respond_to?(method_name)
57
- node_content.public_send(method_name, *a, &b)
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)
58
57
  else
59
- node_content.to_hash.public_send(method_name, *a, &b)
58
+ jsi_node_content.to_hash.public_send(method_name, *a, &b)
60
59
  end
61
60
  end
62
61
 
63
62
  # methods that don't look at the value; can skip the overhead of #[] (invoked by #to_hash)
64
63
  SAFE_KEY_ONLY_METHODS.each do |method_name|
65
64
  define_method(method_name) do |*a, &b|
66
- node_content_hash_pubsend(method_name, *a, &b)
65
+ jsi_node_content_hash_pubsend(method_name, *a, &b)
67
66
  end
68
67
  end
69
68
  end
@@ -75,30 +74,34 @@ module JSI
75
74
  #
76
75
  # returns an Enumerator if no block is given.
77
76
  #
77
+ # @param a arguments are passed to `#[]`
78
78
  # @yield [Object] each element of this array node
79
- # @return [self, Enumerator]
80
- def each
81
- return to_enum(__method__) { node_content_ary_pubsend(:size) } unless block_given?
82
- node_content_ary_pubsend(:each_index) { |i| yield(self[i]) }
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
83
  self
84
84
  end
85
85
 
86
- # @return [Array] an array, the same size as the node_content, in which the
87
- # element at each index is the result of self[index] (see #[])
88
- def to_ary
89
- to_a
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)
90
92
  end
91
93
 
92
94
  include Arraylike
93
95
 
96
+ # invokes the method with the given name on the jsi_node_content (if defined) or its #to_ary
94
97
  # @param method_name [String, Symbol]
95
- # @param *a, &b are passed to the invocation of method_name
96
- # @return [Object] the result of calling method method_name on the node_content or its #to_ary
97
- def node_content_ary_pubsend(method_name, *a, &b)
98
- if node_content.respond_to?(method_name)
99
- node_content.public_send(method_name, *a, &b)
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)
100
103
  else
101
- node_content.to_ary.public_send(method_name, *a, &b)
104
+ jsi_node_content.to_ary.public_send(method_name, *a, &b)
102
105
  end
103
106
  end
104
107
 
@@ -106,7 +109,7 @@ module JSI
106
109
  # we override these methods from Arraylike
107
110
  SAFE_INDEX_ONLY_METHODS.each do |method_name|
108
111
  define_method(method_name) do |*a, &b|
109
- node_content_ary_pubsend(method_name, *a, &b)
112
+ jsi_node_content_ary_pubsend(method_name, *a, &b)
110
113
  end
111
114
  end
112
115
  end