jsi 0.2.0 → 0.6.0

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