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
data/lib/jsi/schema.rb CHANGED
@@ -4,24 +4,29 @@ module JSI
4
4
  # JSI::Schema is a module which extends {JSI::Base} instances which represent JSON schemas.
5
5
  #
6
6
  # This module is included on the {Schema#jsi_schema_module JSI Schema module} of any schema
7
- # which describes other schemas, i.e. is a metaschema or other {Schema::DescribesSchema}.
8
- # Therefore, any JSI instance described by a schema which is a {Schema::DescribesSchema} is
7
+ # that describes other schemas, i.e. is a meta-schema (a {Schema::MetaSchema}).
8
+ # Therefore, any JSI instance described by a schema which is a {Schema::MetaSchema} is
9
9
  # a schema and is extended by this module.
10
10
  #
11
11
  # The content of an instance which is a JSI::Schema (referred to in this context as schema_content) is
12
12
  # typically a Hash (JSON object) or a boolean.
13
13
  module Schema
14
- autoload :Application, 'jsi/schema/application'
15
- autoload :Validation, 'jsi/schema/validation'
14
+ autoload(:Element, 'jsi/schema/element')
15
+ autoload(:Vocabulary, 'jsi/schema/vocabulary')
16
+ autoload(:Dialect, 'jsi/schema/dialect')
17
+ autoload(:Cxt, 'jsi/schema/cxt')
18
+
19
+ autoload(:Elements, 'jsi/schema/elements')
16
20
 
17
21
  autoload :Issue, 'jsi/schema/issue'
18
- autoload :Ref, 'jsi/schema/ref'
22
+ autoload(:DynamicAnchorMap, 'jsi/schema/dynamic_anchor_map')
19
23
 
20
24
  autoload :SchemaAncestorNode, 'jsi/schema/schema_ancestor_node'
21
25
 
22
26
  autoload :Draft04, 'jsi/schema/draft04'
23
27
  autoload :Draft06, 'jsi/schema/draft06'
24
28
  autoload :Draft07, 'jsi/schema/draft07'
29
+ autoload(:Draft202012, 'jsi/schema/draft202012')
25
30
 
26
31
  class Error < StandardError
27
32
  end
@@ -30,219 +35,147 @@ module JSI
30
35
  class NotASchemaError < Error
31
36
  end
32
37
 
33
- # an exception raised when we are unable to resolve a schema reference
34
- class ReferenceError < StandardError
35
- end
36
-
37
- # extends any schema which uses the keyword '$id' to identify its canonical URI
38
- module BigMoneyId
39
- # the contents of a $id keyword whose value is a string, or nil
40
- # @return [#to_str, nil]
41
- def id
42
- if keyword?('$id') && schema_content['$id'].respond_to?(:to_str)
43
- schema_content['$id']
44
- else
45
- nil
46
- end
47
- end
48
- end
49
-
50
- # extends any schema which uses the keyword 'id' to identify its canonical URI
51
- module OldId
52
- # the contents of an `id` keyword whose value is a string, or nil
53
- # @return [#to_str, nil]
54
- def id
55
- if keyword?('id') && schema_content['id'].respond_to?(:to_str)
56
- schema_content['id']
57
- else
58
- nil
59
- end
60
- end
38
+ class NotAMetaSchemaError < TypeError
61
39
  end
62
40
 
63
- # extends any schema which defines an anchor as a URI fragment in the schema id
64
- module IdWithAnchor
65
- # a URI for the schema's id, unless the id defines an anchor in its
66
- # fragment. nil if the schema defines no id.
67
- # @return [Addressable::URI, nil]
68
- def id_without_fragment
69
- if id
70
- id_uri = Util.uri(id)
71
- if id_uri.merge(fragment: nil).empty?
72
- # fragment-only id is just an anchor
73
- # e.g. #foo
74
- nil
75
- elsif id_uri.fragment == nil
76
- # no fragment
77
- # e.g. http://localhost:1234/bar
78
- id_uri
79
- elsif id_uri.fragment == ''
80
- # empty fragment
81
- # e.g. http://json-schema.org/draft-07/schema#
82
- id_uri.merge(fragment: nil).freeze
83
- elsif jsi_schema_base_uri && jsi_schema_base_uri.join(id_uri).merge(fragment: nil) == jsi_schema_base_uri
84
- # the id, resolved against the base uri, consists of the base uri plus an anchor fragment.
85
- # so there's no non-fragment id.
86
- # e.g. base uri is http://localhost:1234/bar
87
- # and id is http://localhost:1234/bar#foo
88
- nil
89
- else
90
- # e.g. http://localhost:1234/bar#foo
91
- id_uri.merge(fragment: nil).freeze
92
- end
93
- else
94
- nil
95
- end
96
- end
97
-
98
- # an anchor defined by a non-empty fragment in the id uri
99
- # @return [String]
100
- def anchor
101
- if id
102
- id_uri = Util.uri(id)
103
- if id_uri.fragment == ''
104
- nil
105
- else
106
- id_uri.fragment
107
- end
108
- else
109
- nil
110
- end
41
+ # @deprecated alias after v0.8
42
+ # an exception raised when we are unable to resolve a schema reference
43
+ ReferenceError = ResolutionError
44
+
45
+ # A reference to a schema identified by a given URI.
46
+ # {#resolve} will return a Schema, and param `referrer` must be a Schema.
47
+ class Ref < Ref
48
+ # @param ref_schema [Schema] deprecated; use `referrer`
49
+ def initialize(ref, ref_schema: nil, **kw)
50
+ super(ref, referrer: ref_schema, **kw)
111
51
  end
112
- end
113
52
 
114
- # @private
115
- module IntegerAllows0Fraction
116
- # is `value` an integer?
117
- # @private
118
- # @param value
119
53
  # @return [Boolean]
120
- def internal_integer?(value)
121
- value.is_a?(Integer) || (value.is_a?(Numeric) && value % 1.0 == 0.0)
54
+ def resolve_schema?
55
+ true
122
56
  end
123
- end
124
57
 
125
- # @private
126
- module IntegerDisallows0Fraction
127
- # is `value` an integer?
128
- # @private
129
- # @param value
130
- # @return [Boolean]
131
- def internal_integer?(value)
132
- value.is_a?(Integer)
58
+ # @deprecated after v0.8
59
+ def deref_schema
60
+ resolve
133
61
  end
134
62
  end
135
63
 
136
- # This module extends any JSI Schema which describes schemas.
64
+ # This module extends any JSI Schema that is a meta-schema, i.e. it describes schemas.
137
65
  #
138
- # Examples of a schema which describes schemas include the JSON Schema metaschemas and
66
+ # Examples of a meta-schema include the JSON Schema meta-schemas and
139
67
  # the OpenAPI schema definition which describes "A deterministic version of a JSON Schema object."
140
68
  #
141
- # Schemas which describes schemas include {JSI::Schema} in their
69
+ # Meta-schemas include {JSI::Schema} in their
142
70
  # {Schema#jsi_schema_module JSI Schema module}, so for a schema which is an instance of
143
- # DescribesSchema, instances of that schema are instances of {JSI::Schema} and are schemas.
71
+ # JSI::Schema::MetaSchema, instances of that schema are instances of {JSI::Schema} and are schemas.
144
72
  #
145
73
  # A schema is indicated as describing other schemas using the {Schema#describes_schema!} method.
146
- module DescribesSchema
74
+ module MetaSchema
75
+ # @return [Schema::Dialect]
76
+ attr_reader(:described_dialect)
77
+
147
78
  # Instantiates the given schema content as a JSI Schema.
148
79
  #
149
- # By default, the schema will be registered with the {JSI.schema_registry}.
150
- # This can be controlled by params `register` and `schema_registry`.
80
+ # By default, the schema will be registered with the {JSI.registry}.
81
+ # This can be controlled by the `register` param and {Base::Conf#registry `registry`} {Base::Conf configuration}.
151
82
  #
152
83
  # By default, the `schema_content` will have any Symbol keys of Hashes replaced with Strings
153
84
  # (recursively through the document). This is controlled by the param `stringify_symbol_keys`.
154
85
  #
155
- # @param schema_content an object to be instantiated as a JSI Schema - typically a Hash
156
- # @param uri [#to_str, Addressable::URI] The retrieval URI of the schema document.
157
- # If specified, the root schema will be identified by this URI, in addition
158
- # to any absolute URI declared with an id keyword, for resolution in the `schema_registry`.
159
- #
160
- # It is rare that this needs to be specified. Most schemas, if they use absolute URIs, will
161
- # use the `$id` keyword (`id` in draft 4) to specify this. A different retrieval URI is useful
162
- # in unusual cases:
86
+ # Schemas instantiated with `new_schema` are immutable, their content transformed using
87
+ # the {Base::Conf configured} {Base::Conf#to_immutable `to_immutable`}.
163
88
  #
164
- # - A schema in the document uses relative URIs for `$id` or `$ref` without an absolute id in an
165
- # ancestor schema - these will be resolved relative to this URI
166
- # - Another schema refers with `$ref` to the schema being instantiated by this retrieval URI,
167
- # rather than an id declared in the schema - the schema is resolvable by this URI in the
168
- # `schema_registry`.
169
- # @param register [Boolean] Whether the instantiated schema and any subschemas with absolute URIs
170
- # will be registered in the schema registry indicated by param `schema_registry`.
171
- # @param schema_registry [SchemaRegistry, nil] The registry this schema will use.
89
+ # Parameters are passed to {SchemaSet#new_jsi} and are documented there, but some have
90
+ # different defaults for new_schema.
172
91
  #
173
- # - The schema and subschemas will be registered here with any declared URI,
174
- # unless the `register` param is false.
175
- # - References from within the schema (typically from `$ref` keywords) are resolved using this registry.
176
- # @param stringify_symbol_keys [Boolean] Whether the schema content will have any Symbol keys of Hashes
177
- # replaced with Strings (recursively through the document).
178
- # Replacement is done on a copy; the given schema content is not modified.
179
- # @yield If a block is given, it is evaluated in the context of the schema's JSI schema module
180
- # using [Module#module_exec](https://ruby-doc.org/core/Module.html#method-i-module_exec).
181
- # @return [JSI::Base subclass + JSI::Schema] a JSI which is a {JSI::Schema} whose content comes from
182
- # the given `schema_content` and whose schemas are this schema's inplace applicators.
92
+ # @param schema_content an object to be instantiated as a JSI Schema - typically a Hash
93
+ # @param base_uri
94
+ # @param register
95
+ # @param stringify_symbol_keys
96
+ # @param conf_kw (see SchemaSet#new_jsi)
97
+ # @return [Base + Schema] A JSI which is a {Schema} whose content comes from
98
+ # the given `schema_content` and whose schemas are this meta-schema's in-place applicators.
183
99
  def new_schema(schema_content,
184
- uri: nil,
100
+ base_uri: nil,
185
101
  register: true,
186
- schema_registry: JSI.schema_registry,
187
102
  stringify_symbol_keys: true,
188
- &block
103
+ **conf_kw
189
104
  )
190
- schema_jsi = new_jsi(schema_content,
191
- uri: uri,
105
+ raise(BlockGivenError) if block_given?
106
+ new_jsi(schema_content,
107
+ base_uri: base_uri,
192
108
  register: register,
193
- schema_registry: schema_registry,
194
109
  stringify_symbol_keys: stringify_symbol_keys,
110
+ **conf_kw,
111
+ mutable: false,
195
112
  )
196
- if block
197
- schema_jsi.jsi_schema_module_exec(&block)
198
- end
199
- schema_jsi
200
113
  end
201
114
 
202
115
  # Instantiates the given schema content as a JSI Schema, passing all params to
203
- # {Schema::DescribesSchema#new_schema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
116
+ # {Schema::MetaSchema#new_schema}, and returns its {Schema#jsi_schema_module JSI Schema Module}.
204
117
  #
205
- # @return [Module + JSI::SchemaModule] the JSI Schema Module of the instantiated schema
118
+ # @yield If a block is given, it is evaluated in the context of the schema module
119
+ # using [Module#module_exec](https://ruby-doc.org/core/Module.html#method-i-module_exec).
120
+ # @return [JSI::SchemaModule] the JSI Schema Module of the instantiated schema
206
121
  def new_schema_module(schema_content, **kw, &block)
207
- new_schema(schema_content, **kw, &block).jsi_schema_module
122
+ schema_jsi = new_schema(schema_content, **kw)
123
+ schema_jsi.jsi_schema_module_exec(&block) if block
124
+ schema_jsi.jsi_schema_module
208
125
  end
209
126
  end
210
127
 
211
- class << self
128
+ # @private
129
+ module ExtendedInitialize
212
130
  def extended(o)
213
131
  super
214
132
  o.send(:jsi_schema_initialize)
215
133
  end
134
+
135
+ def included(m)
136
+ super
137
+ return if m.is_a?(Class)
138
+
139
+ # if a module (m) includes Schema, and an object (o) is extended with m,
140
+ # then o should have #jsi_schema_initialize called, but Schema.extended is not called,
141
+ # so m needs its own .extended method to call jsi_schema_initialize.
142
+ # note: extending m with ExtendedInitialize for .extended, rather than m.define_singleton_method(:extended),
143
+ # avoids possibly clobbering an existing singleton .extended method the module has defined.
144
+ m.extend(ExtendedInitialize)
145
+ end
216
146
  end
147
+
148
+ extend(ExtendedInitialize)
217
149
  end
218
150
 
219
151
  class << self
220
- # An application-wide default metaschema set by {default_metaschema=}, used by {JSI.new_schema}
221
- # to instantiate schemas which do not specify their metaschema using a `$schema` property.
152
+ # An application-wide default meta-schema set by {default_metaschema=}, used by {JSI.new_schema}
153
+ # to instantiate schemas that do not specify their meta-schema using a `$schema` property.
222
154
  #
223
- # @return [nil, Base + Schema + Schema::DescribesSchema]
155
+ # @return [nil, Base + Schema + Schema::MetaSchema]
224
156
  def default_metaschema
225
157
  @default_metaschema
226
158
  end
227
159
 
228
160
  # Sets {default_metaschema} to a schema indicated by the given param.
229
161
  #
230
- # @param default_metaschema [Schema::DescribesSchema, SchemaModule::DescribesSchemaModule, #to_str, nil]
231
- # Indicates the default metaschema.
232
- # This may be a metaschema or a metaschema's schema module (e.g. `JSI::JSONSchemaDraft07`),
162
+ # @param default_metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str, nil]
163
+ # Indicates the default meta-schema.
164
+ # This may be a meta-schema or a meta-schema's schema module (e.g. `JSI::JSONSchemaDraft07`),
233
165
  # or a URI (as would be in a `$schema` keyword).
234
166
  #
235
167
  # `nil` to unset.
236
168
  def default_metaschema=(default_metaschema)
237
- @default_metaschema = default_metaschema.nil? ? nil : ensure_describes_schema(default_metaschema)
169
+ @default_metaschema = default_metaschema.nil? ? nil : Schema.ensure_metaschema(default_metaschema)
238
170
  end
239
171
 
240
172
  # Instantiates the given schema content as a JSI Schema.
241
173
  #
242
- # The metaschema which describes the schema must be indicated:
174
+ # The meta-schema that describes the schema must be indicated:
243
175
  #
244
- # - If the schema object has a `$schema` property, that URI is resolved using the `schema_registry`
245
- # param (by default {JSI.schema_registry}), and that metaschema is used. For example:
176
+ # - If the schema object has a `$schema` property, that URI is resolved using the
177
+ # {Base::Conf configured} {Base::Conf#registry `registry`} (by default {JSI.registry JSI.registry}),
178
+ # and that meta-schema is used. For example:
246
179
  #
247
180
  # ```ruby
248
181
  # JSI.new_schema({
@@ -267,60 +200,65 @@ module JSI
267
200
  # JSI.new_schema({"properties" => ...})
268
201
  # ```
269
202
  #
270
- # an ArgumentError is raised if none of these indicate a metaschema to use.
203
+ # An ArgumentError is raised if none of these indicates a meta-schema to use.
271
204
  #
272
205
  # Note that if you are instantiating a schema known to have no `$schema` property, an alternative to
273
206
  # specifying a `default_metaschema` is to call `new_schema` on the
274
- # {Schema::DescribesSchema#new_schema metaschema} or its
275
- # {SchemaModule::DescribesSchemaModule#new_schema schema module}, e.g.
276
- # `JSI::JSONSchemaDraft07.new_schema(my_schema_content)`
207
+ # {Schema::MetaSchema#new_schema meta-schema} or its
208
+ # {SchemaModule::MetaSchemaModule#new_schema schema module}, e.g.
209
+ # `JSI::JSONSchemaDraft07.new_schema(my_schema_content)`. This will ignore any `$schema` keyword
210
+ # that may be present.
211
+ #
212
+ # Schemas instantiated with `new_schema` are immutable, their content transformed using
213
+ # the {Base::Conf configured} {Base::Conf#to_immutable `to_immutable`}.
214
+ #
215
+ # Most parameters are passed to {SchemaSet#new_jsi} and are documented there, but some have
216
+ # different defaults for JSI.new_schema.
277
217
  #
278
- # @param schema_content (see Schema::DescribesSchema#new_schema)
279
- # @param default_metaschema [Schema::DescribesSchema, SchemaModule::DescribesSchemaModule, #to_str]
280
- # Indicates the metaschema to use if the given schema_content does not have a `$schema` property.
281
- # This may be a metaschema or a metaschema's schema module (e.g. `JSI::JSONSchemaDraft07`),
218
+ # @param schema_content (see Schema::MetaSchema#new_schema)
219
+ # @param default_metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str]
220
+ # Indicates the meta-schema to use if the given `schema_content` does not have a `$schema` property.
221
+ # This may be a meta-schema or a meta-schema's schema module (e.g. `JSI::JSONSchemaDraft07`),
282
222
  # or a URI (as would be in a `$schema` keyword).
283
- # @param uri (see Schema::DescribesSchema#new_schema)
284
- # @param register (see DescribesSchema#new_schema)
285
- # @param schema_registry (see DescribesSchema#new_schema)
286
- # @param stringify_symbol_keys (see Schema::DescribesSchema#new_schema)
287
- # @yield (see Schema::DescribesSchema#new_schema)
288
- # @return [JSI::Base subclass + JSI::Schema] a JSI which is a {JSI::Schema} whose content comes from
289
- # the given `schema_content` and whose schemas are inplace applicators of the indicated metaschema
223
+ # @param base_uri
224
+ # @param register
225
+ # @param stringify_symbol_keys
226
+ # @param conf_kw (see SchemaSet#new_jsi)
227
+ # @return [Base + Schema] A JSI which is a {Schema} whose content comes from
228
+ # the given `schema_content` and whose schemas are in-place applicators of the indicated meta-schema.
290
229
  def new_schema(schema_content,
291
230
  default_metaschema: nil,
292
- # params of DescribesSchema#new_schema have their default values repeated here. delegating in a splat
293
- # would remove repetition, but yard doesn't display delegated defaults with its (see X) directive.
294
- uri: nil,
231
+ base_uri: nil,
295
232
  register: true,
296
- schema_registry: JSI.schema_registry,
297
233
  stringify_symbol_keys: true,
298
- &block
234
+ **conf_kw
299
235
  )
236
+ raise(BlockGivenError) if block_given?
300
237
  new_schema_params = {
301
- uri: uri,
238
+ base_uri: base_uri,
302
239
  register: register,
303
- schema_registry: schema_registry,
304
240
  stringify_symbol_keys: stringify_symbol_keys,
241
+ **conf_kw,
305
242
  }
243
+ conf = Base::Conf.new(**conf_kw) # some redundancy instantiating this - not passed to MetaSchema#new_schema, just used in this method
306
244
  default_metaschema_new_schema = -> {
307
245
  default_metaschema = if default_metaschema
308
- Schema.ensure_describes_schema(default_metaschema, name: "default_metaschema")
246
+ Schema.ensure_metaschema(default_metaschema, name: "default_metaschema", registry: conf.registry)
309
247
  elsif self.default_metaschema
310
248
  self.default_metaschema
311
249
  else
312
250
  raise(ArgumentError, [
313
- "When instantiating a schema with no `$schema` property, you must specify its metaschema by one of these methods:",
251
+ "When instantiating a schema with no `$schema` property, you must specify its meta-schema by one of these methods:",
314
252
  "- pass the `default_metaschema` param to this method",
315
253
  " e.g.: JSI.new_schema(..., default_metaschema: JSI::JSONSchemaDraft07)",
316
- "- invoke `new_schema` on the appropriate metaschema or its schema module",
254
+ "- invoke `new_schema` on the appropriate meta-schema or its schema module",
317
255
  " e.g.: JSI::JSONSchemaDraft07.new_schema(...)",
318
- "- set JSI.default_metaschema to an application-wide default metaschema initially",
256
+ "- set JSI.default_metaschema to an application-wide default meta-schema initially",
319
257
  " e.g.: JSI.default_metaschema = JSI::JSONSchemaDraft07",
320
258
  "instantiating schema_content: #{schema_content.pretty_inspect.chomp}",
321
259
  ].join("\n"))
322
260
  end
323
- default_metaschema.new_schema(schema_content, **new_schema_params, &block)
261
+ default_metaschema.new_schema(schema_content, **new_schema_params)
324
262
  }
325
263
  if schema_content.is_a?(Schema)
326
264
  raise(TypeError, [
@@ -338,8 +276,8 @@ module JSI
338
276
  unless id.respond_to?(:to_str)
339
277
  raise(ArgumentError, "given schema_content keyword `$schema` is not a string")
340
278
  end
341
- metaschema = Schema.ensure_describes_schema(id, name: '$schema', schema_registry: schema_registry)
342
- metaschema.new_schema(schema_content, **new_schema_params, &block)
279
+ metaschema = Schema.ensure_metaschema(id, name: '$schema', registry: conf.registry)
280
+ metaschema.new_schema(schema_content, **new_schema_params)
343
281
  else
344
282
  default_metaschema_new_schema.call
345
283
  end
@@ -356,59 +294,67 @@ module JSI
356
294
  # ensure the given object is a JSI Schema
357
295
  #
358
296
  # @param schema [Object] the thing the caller wishes to ensure is a Schema
359
- # @param msg [#to_s, #to_ary] lines of the error message preceding the pretty-printed schema param
360
- # if the schema param is not a schema
297
+ # @yieldreturn [#to_s, #to_ary] first line(s) of the error message, overriding the default
361
298
  # @raise [NotASchemaError] if the schema param is not a schema
362
299
  # @return [Schema] the given schema
363
- def ensure_schema(schema, msg: "indicated object is not a schema:", reinstantiate_as: nil)
300
+ def ensure_schema(schema, reinstantiate_as: nil)
364
301
  if schema.is_a?(Schema)
365
302
  schema
366
303
  else
367
304
  if reinstantiate_as && schema.is_a?(JSI::Base)
368
305
  # TODO warn; behavior is undefined and I hate this implementation
369
306
 
370
- result_schema_schemas = schema.jsi_schemas + reinstantiate_as
307
+ result_schema_indicated_schemas = SchemaSet.new(schema.jsi_indicated_schemas + reinstantiate_as)
308
+ result_schema_applied_schemas = result_schema_indicated_schemas.each_yield_set do |is, y|
309
+ is.each_inplace_applicator_schema(schema.jsi_node_content, &y)
310
+ end
371
311
 
372
- result_schema_class = JSI::SchemaClasses.class_for_schemas(result_schema_schemas,
373
- includes: SchemaClasses.includes_for(schema.jsi_node_content)
312
+ result_schema_class = JSI::SchemaClasses.class_for_schemas(result_schema_applied_schemas,
313
+ includes: SchemaClasses.includes_for(schema.jsi_node_content),
314
+ mutable: schema.jsi_mutable?,
374
315
  )
375
316
 
376
- result_schema_class.new(schema.jsi_document,
317
+ result_schema_class.new(
318
+ jsi_document: schema.jsi_document,
377
319
  jsi_ptr: schema.jsi_ptr,
378
- jsi_indicated_schemas: schema.jsi_indicated_schemas,
379
- jsi_schema_base_uri: schema.jsi_schema_base_uri,
320
+ jsi_indicated_schemas: result_schema_indicated_schemas,
321
+ jsi_base_uri: schema.jsi_base_uri,
380
322
  jsi_schema_resource_ancestors: schema.jsi_schema_resource_ancestors,
381
- jsi_schema_registry: schema.jsi_schema_registry,
382
- jsi_root_node: schema.jsi_ptr.root? ? nil : schema.jsi_root_node, # bad
383
- )
323
+ jsi_schema_dynamic_anchor_map: schema.jsi_schema_dynamic_anchor_map,
324
+ jsi_conf: schema.equal?(schema.jsi_root_node) ? schema.jsi_conf : nil,
325
+ jsi_root_node: schema.equal?(schema.jsi_root_node) ? nil : schema.jsi_root_node, # bad
326
+ ).send(:jsi_initialized)
384
327
  else
385
- raise(NotASchemaError, [
386
- *msg,
387
- schema.pretty_inspect.chomp,
388
- ].join("\n"))
328
+ msg = []
329
+ msg.concat([*(block_given? ? yield : "indicated object is not a schema:")])
330
+ msg << schema.pretty_inspect.chomp
331
+ if schema.is_a?(Base)
332
+ msg << "its schemas (which should include a Meta-Schema): #{schema.jsi_schemas.pretty_inspect.chomp}"
333
+ end
334
+ raise(NotASchemaError, msg.compact.join("\n"))
389
335
  end
390
336
  end
391
337
  end
392
338
 
393
- # Ensures the given param identifies a JSI Schema which describes schemas, and returns that schema.
339
+ # Ensures the given param identifies a meta-schema and returns that meta-schema.
394
340
  #
395
341
  # @api private
396
- # @param describes_schema [Schema::DescribesSchema, SchemaModule::DescribesSchemaModule, #to_str]
397
- # @raise [TypeError] if the param does not indicate a schema which describes schemas
398
- # @return [Base + Schema + Schema::DescribesSchema]
399
- def ensure_describes_schema(describes_schema, name: nil, schema_registry: JSI.schema_registry)
400
- if describes_schema.respond_to?(:to_str)
401
- schema = Schema::Ref.new(describes_schema, schema_registry: schema_registry).deref_schema
342
+ # @param metaschema [Schema::MetaSchema, SchemaModule::MetaSchemaModule, #to_str]
343
+ # @raise [TypeError] if the param does not indicate a meta-schema
344
+ # @return [Base + Schema + Schema::MetaSchema]
345
+ def ensure_metaschema(metaschema, name: nil, registry: JSI.registry)
346
+ if metaschema.respond_to?(:to_str)
347
+ schema = Schema::Ref.new(metaschema, registry: registry).resolve
402
348
  if !schema.describes_schema?
403
- raise(TypeError, [name, "URI indicates a schema which does not describe schemas: #{describes_schema.pretty_inspect.chomp}"].compact.join(" "))
349
+ raise(NotAMetaSchemaError, [name, "URI indicates a schema that is not a meta-schema: #{metaschema.pretty_inspect.chomp}"].compact.join(" "))
404
350
  end
405
351
  schema
406
- elsif describes_schema.is_a?(SchemaModule::DescribesSchemaModule)
407
- describes_schema.schema
408
- elsif describes_schema.is_a?(DescribesSchema)
409
- describes_schema
352
+ elsif metaschema.is_a?(SchemaModule::MetaSchemaModule)
353
+ metaschema.schema
354
+ elsif metaschema.is_a?(Schema::MetaSchema)
355
+ metaschema
410
356
  else
411
- raise(TypeError, "#{name || "param"} does not indicate a schema which describes schemas: #{describes_schema.pretty_inspect.chomp}")
357
+ raise(NotAMetaSchemaError, "#{name || "param"} does not indicate a meta-schema: #{metaschema.pretty_inspect.chomp}")
412
358
  end
413
359
  end
414
360
  end
@@ -425,6 +371,12 @@ module JSI
425
371
  end
426
372
  end
427
373
 
374
+ # @!method dialect
375
+ # The dialect of this schema
376
+ # @return [Schema::Dialect]
377
+ # note: defined on a meta-schema's schema module by Schema#describes_schema!
378
+
379
+
428
380
  # the underlying JSON data used to instantiate this JSI::Schema.
429
381
  # this is an alias for {Base#jsi_node_content}, named for clarity in the context of working with
430
382
  # a schema.
@@ -439,83 +391,93 @@ module JSI
439
391
  schema_content.respond_to?(:to_hash) && schema_content.key?(keyword)
440
392
  end
441
393
 
442
- # the URI of this schema, calculated from our `#id`, resolved against our `#jsi_schema_base_uri`
443
- # @return [Addressable::URI, nil]
394
+ # Does this schema contain the given keyword with the given value?
395
+ # @return [Boolean]
396
+ def keyword_value?(keyword, value)
397
+ keyword?(keyword) && schema_content[keyword] == value
398
+ end
399
+
400
+ # the string contents of an `$id`/`id` keyword, or nil
401
+ # @return [#to_str, nil]
402
+ def id
403
+ dialect_invoke_each(:id).first
404
+ end
405
+
406
+ # @return [Enumerable<String>]
407
+ def anchors
408
+ anchors = Set[]
409
+ anchors.merge(dialect_invoke_each(:anchor))
410
+ anchors.merge(dialect_invoke_each(:dynamicAnchor))
411
+ anchors.freeze
412
+ end
413
+
414
+ # the URI of this schema, from an `$id` keyword, resolved against our `#jsi_base_uri`
415
+ # @deprecated after v0.8 - use `#jsi_resource_uri`
416
+ # @return [URI, nil]
444
417
  def schema_absolute_uri
445
- if respond_to?(:id_without_fragment) && id_without_fragment
446
- if jsi_schema_base_uri
447
- jsi_schema_base_uri.join(id_without_fragment).freeze
418
+ jsi_resource_uri
419
+ end
420
+
421
+ # @deprecated after v0.8 - use `#jsi_resource_uris`
422
+ # @return [Enumerable<URI>]
423
+ def schema_absolute_uris
424
+ jsi_resource_uris
425
+ end
426
+
427
+ # @yield [URI]
428
+ private def jsi_each_resource_uri_compute
429
+ dialect_invoke_each(:id_without_fragment) do |id_without_fragment|
430
+ if jsi_base_uri
431
+ yield(jsi_base_uri.join(id_without_fragment))
448
432
  elsif id_without_fragment.absolute?
449
- id_without_fragment
450
- else
451
- # TODO warn / schema_error
452
- nil
433
+ yield(id_without_fragment)
453
434
  end
454
435
  end
436
+ super
455
437
  end
456
438
 
457
439
  # a nonrelative URI which refers to this schema.
458
- # nil if no parent of this schema defines an id.
440
+ # `nil` if no ancestor of this schema defines an id.
459
441
  # see {#schema_uris} for all URIs known to refer to this schema.
460
- # @return [Addressable::URI, nil]
442
+ # @return [URI, nil]
461
443
  def schema_uri
462
444
  schema_uris.first
463
445
  end
464
446
 
465
447
  # nonrelative URIs (that is, absolute, but possibly with a fragment) which refer to this schema
466
- # @return [Array<Addressable::URI>]
448
+ # @return [Array<URI>]
467
449
  def schema_uris
468
- @schema_uris_map[]
450
+ @schema_uris_map[schema_content: schema_content]
469
451
  end
470
452
 
471
- private def schema_uris_compute(**_) # TODO remove **_ eventually (keyword argument compatibility)
472
- each_schema_uri.to_a
473
- end
453
+ # @yield [URI]
454
+ private def schema_uris_compute(&block)
455
+ jsi_resource_uris.each(&block)
474
456
 
475
- # see {#schema_uris}
476
- # @yield [Addressable::URI]
477
- # @return [Enumerator, nil]
478
- def each_schema_uri
479
- return to_enum(__method__) unless block_given?
480
-
481
- yield schema_absolute_uri if schema_absolute_uri
482
-
483
- parent_schemas = jsi_subschema_resource_ancestors.reverse_each.select do |resource|
484
- resource.schema_absolute_uri
485
- end
486
-
487
- anchored = respond_to?(:anchor) ? anchor : nil
488
- parent_schemas.each do |parent_schema|
489
- if anchored
490
- if parent_schema.jsi_anchor_subschema(anchor) == self
491
- yield parent_schema.schema_absolute_uri.merge(fragment: anchor).freeze
492
- else
493
- anchored = false
457
+ if jsi_resource_root
458
+ anchors.each do |anchor|
459
+ jsi_resource_root.jsi_resource_uris.each do |uri|
460
+ yield(uri.merge(fragment: anchor))
494
461
  end
495
462
  end
463
+ end
496
464
 
497
- relative_ptr = jsi_ptr.relative_to(parent_schema.jsi_ptr)
498
- yield parent_schema.schema_absolute_uri.merge(fragment: relative_ptr.fragment).freeze
465
+ jsi_subschema_resource_ancestors.reverse_each do |ancestor_schema|
466
+ relative_ptr = jsi_ptr.relative_to(ancestor_schema.jsi_ptr)
467
+ ancestor_schema.jsi_resource_uris.each do |uri|
468
+ yield(uri.merge(fragment: relative_ptr.fragment))
469
+ end
499
470
  end
500
471
 
501
472
  nil
502
473
  end
503
474
 
504
- # a module which extends all instances of this schema. this may be opened by the application to add
505
- # methods to schema instances.
506
- #
507
- # some functionality is also defined on the module itself (its singleton class, not for its instances):
475
+ # The {SchemaModule JSI Schema Module} for this schema.
476
+ # JSI instances described by this schema are instances of this module.
508
477
  #
509
- # - the module is extended with {JSI::SchemaModule}, which defines .new_jsi to instantiate instances
510
- # of this schema (see {#new_jsi}).
511
- # - properties described by this schema's metaschema are defined as methods to get subschemas' schema
512
- # modules, so for example `schema.jsi_schema_module.items` returns the same module
513
- # as `schema.items.jsi_schema_module`.
514
- # - method .schema which returns this schema.
515
- #
516
- # @return [Module + SchemaModule]
478
+ # @return [SchemaModule]
517
479
  def jsi_schema_module
518
- JSI::SchemaClasses.module_for_schema(self)
480
+ jsi_schema_module_connection
519
481
  end
520
482
 
521
483
  # Evaluates the given block in the context of this schema's JSI schema module.
@@ -528,79 +490,109 @@ module JSI
528
490
  jsi_schema_module.module_exec(*a, **kw, &block)
529
491
  end
530
492
 
493
+ # @return [String, nil]
494
+ def jsi_schema_module_name
495
+ # don't hit #jsi_schema_module - avoid creating module, avoid erroring for MSN::BootstrapSchema
496
+ @memos[:schema_module_connection] && @memos[:schema_module_connection].name
497
+ end
498
+
499
+ # @return [String, nil]
500
+ def jsi_schema_module_name_from_ancestor
501
+ is_a?(Base) ? jsi_schema_module.name_from_ancestor : nil
502
+ end
503
+
531
504
  # Instantiates a new JSI whose content comes from the given `instance` param.
532
- # This schema indicates the schemas of the JSI - its schemas are inplace
505
+ # This schema indicates the schemas of the JSI - its schemas are in-place
533
506
  # applicators of this schema which apply to the given instance.
534
507
  #
535
- # @param (see SchemaSet#new_jsi)
536
- # @return [JSI::Base subclass] a JSI whose content comes from the given instance and whose schemas are
537
- # inplace applicators of this schema.
508
+ # All parameters are passed to {SchemaSet#new_jsi}.
509
+ #
510
+ # @return [Base] a JSI whose content comes from the given instance and whose schemas are
511
+ # in-place applicators of this schema.
538
512
  def new_jsi(instance, **kw)
513
+ raise(BlockGivenError) if block_given?
539
514
  SchemaSet[self].new_jsi(instance, **kw)
540
515
  end
541
516
 
542
- # @param keyword schema keyword e.g. "$ref", "$schema"
517
+ # @param ref [#to_str] ref URI
543
518
  # @return [Schema::Ref]
544
- def schema_ref(keyword: "$ref")
545
- raise(ArgumentError, "keyword not present: #{keyword}") unless keyword?(keyword)
546
- @schema_ref_map[keyword: keyword, value: schema_content[keyword]]
519
+ def schema_ref(ref = schema_content["$ref"])
520
+ @schema_ref_map[ref]
547
521
  end
548
522
 
549
- # does this schema itself describe a schema?
523
+ # Does this schema itself describe a schema? I.e. is this schema a meta-schema?
550
524
  # @return [Boolean]
551
525
  def describes_schema?
552
- jsi_schema_module <= JSI::Schema || false
526
+ is_a?(Schema::MetaSchema)
527
+ end
528
+
529
+ # Is this a JSI Schema?
530
+ # @return [Boolean]
531
+ def jsi_is_schema?
532
+ true
553
533
  end
554
534
 
555
- # indicates that this schema describes a schema.
556
- # this schema is extended with {DescribesSchema} and its {#jsi_schema_module} is extended
557
- # with {SchemaModule::DescribesSchemaModule}, and the JSI Schema Module will include
558
- # JSI::Schema and the given modules.
535
+ # Indicates that this schema describes schemas, i.e. it is a meta-schema.
536
+ # this schema is extended with {Schema::MetaSchema} and its {#jsi_schema_module} is extended
537
+ # with {SchemaModule::MetaSchemaModule}, and the JSI Schema Module will include
538
+ # JSI::Schema.
559
539
  #
560
- # @param schema_implementation_modules [Enumerable<Module>] modules which implement the functionality of
561
- # the schema to extend schemas described by this schema.
540
+ # @param dialect [Schema::Dialect, nil] dialect may be passed, or inferred from `$vocabulary`
562
541
  # @return [void]
563
- def describes_schema!(schema_implementation_modules)
564
- schema_implementation_modules = Util.ensure_module_set(schema_implementation_modules)
542
+ def describes_schema!(dialect = nil)
543
+ # TODO rm bridge code hax
544
+ dialect = dialect.first::DIALECT if dialect.is_a?(Array) && dialect.size == 1
545
+
546
+ if !dialect
547
+ raise(ArgumentError, "no dialect given and no $vocabulary hash/object") if !schema_content['$vocabulary'].respond_to?(:to_hash)
548
+ dialect = Schema::Dialect.from_xvocabulary(schema_content['$vocabulary'], registry: jsi_registry)
549
+ end
565
550
 
566
- if describes_schema?
567
- # this schema, or one equal to it, has already had describes_schema! called on it.
551
+ raise(TypeError) if !dialect.is_a?(Schema::Dialect)
552
+
553
+ if jsi_schema_module <= Schema
554
+ # this schema has already had describes_schema! called on it.
568
555
  # this is to be avoided, but is not particularly a problem.
569
- # it is a bug if it was called different times with different schema_implementation_modules, though.
570
- unless jsi_schema_module.schema_implementation_modules == schema_implementation_modules
571
- raise(ArgumentError, "this schema already describes a schema with different schema_implementation_modules")
556
+ # it is a bug if it was called different times with different dialect, though.
557
+ if @described_dialect != dialect
558
+ raise(ArgumentError, "this schema already describes a schema with different dialect")
572
559
  end
573
560
  else
574
561
  jsi_schema_module.include(Schema)
575
- schema_implementation_modules.each do |mod|
576
- jsi_schema_module.include(mod)
577
- end
578
- jsi_schema_module.extend(SchemaModule::DescribesSchemaModule)
579
- jsi_schema_module.instance_variable_set(:@schema_implementation_modules, schema_implementation_modules)
562
+ jsi_schema_module.send(:define_method, :dialect) { dialect }
563
+ proc { |metaschema| jsi_schema_module.send(:define_method, :metaschema) { metaschema } }[self]
564
+ jsi_schema_module.extend(SchemaModule::MetaSchemaModule)
580
565
  end
581
566
 
582
- extend(DescribesSchema)
567
+ @described_dialect = dialect
568
+ extend(Schema::MetaSchema)
583
569
 
584
570
  nil
585
571
  end
586
572
 
587
573
  # a resource containing this schema.
588
574
  #
589
- # if any parent, or this schema itself, is a schema with an absolute uri (see {#schema_absolute_uri}),
575
+ # If any ancestor, or this schema itself, is a schema with an absolute uri (see {#schema_absolute_uri}),
590
576
  # the resource root is the closest schema with an absolute uri.
591
577
  #
592
- # If no parent schema has an absolute uri, the schema_resource_root is the {Base#jsi_root_node document's root node}.
578
+ # If no ancestor schema has an absolute uri, the schema_resource_root is the {Base#jsi_root_node document's root node}.
593
579
  # In this case, the resource root may or may not be a schema itself.
594
580
  #
581
+ # @deprecated after v0.8
595
582
  # @return [JSI::Base] resource containing this schema
596
583
  def schema_resource_root
597
- jsi_subschema_resource_ancestors.last || jsi_root_node
584
+ jsi_resource_root
598
585
  end
599
586
 
600
587
  # is this schema the root of a schema resource?
601
588
  # @return [Boolean]
589
+ def jsi_is_resource_root?
590
+ super || jsi_resource_uris.any?
591
+ end
592
+
593
+ # @deprecated after v0.8
602
594
  def schema_resource_root?
603
- jsi_ptr.root? || !!schema_absolute_uri
595
+ jsi_is_resource_root?
604
596
  end
605
597
 
606
598
  # a subschema of this Schema
@@ -608,83 +600,202 @@ module JSI
608
600
  # @param subptr [JSI::Ptr, #to_ary] a relative pointer, or array of tokens, pointing to the subschema
609
601
  # @return [JSI::Schema] the subschema at the location indicated by subptr. self if subptr is empty.
610
602
  def subschema(subptr)
611
- @subschema_map[subptr: Ptr.ary_ptr(subptr)]
612
- end
613
-
614
- private def subschema_compute(subptr: )
615
- Schema.ensure_schema(jsi_descendent_node(subptr), msg: [
616
- "subschema is not a schema at pointer: #{subptr.pointer}"
617
- ])
603
+ Schema.ensure_schema(jsi_descendent_node(subptr)) { "subschema is not a schema at pointer: #{Ptr.ary_ptr(subptr).pointer}" }
618
604
  end
619
605
 
620
- # a schema in the same schema resource as this one (see {#schema_resource_root}) at the given
606
+ # A schema in the same schema resource as this one (see {Schema::SchemaAncestorNode#jsi_resource_root}) at the given
621
607
  # pointer relative to the root of the schema resource.
622
608
  #
623
609
  # @param ptr [JSI::Ptr, #to_ary] a pointer to a schema from our schema resource root
624
610
  # @return [JSI::Schema] the schema pointed to by ptr
625
611
  def resource_root_subschema(ptr)
626
- @resource_root_subschema_map[ptr: Ptr.ary_ptr(ptr)]
627
- end
628
-
629
- private def resource_root_subschema_compute(ptr: )
630
- Schema.ensure_schema(schema_resource_root.jsi_descendent_node(ptr),
631
- msg: [
632
- "subschema is not a schema at pointer: #{ptr.pointer}"
633
- ],
634
- reinstantiate_as: jsi_schemas.select(&:describes_schema?)
612
+ Schema.ensure_schema(jsi_resource_root.jsi_descendent_node(ptr),
613
+ reinstantiate_as: jsi_conf.reinstantiate_nonschemas && jsi_schemas.select(&:describes_schema?),
635
614
  )
636
615
  end
637
616
 
638
- # a set of inplace applicator schemas of this schema (from $ref, allOf, etc.) which apply to the
639
- # given instance.
640
- #
641
- # the returned set will contain this schema itself, unless this schema contains a $ref keyword.
642
- #
643
- # @param instance [Object] the instance to check any applicators against
644
- # @return [JSI::SchemaSet] matched applicator schemas
645
- def inplace_applicator_schemas(instance)
646
- SchemaSet.new(each_inplace_applicator_schema(instance))
617
+ # @yield [Schema]
618
+ def jsi_each_descendent_schema(&block)
619
+ return(to_enum(__method__)) unless block_given?
620
+
621
+ yield(self)
622
+ dialect_invoke_each(:subschema) { |ptr| subschema(ptr).jsi_each_descendent_schema(&block) }
647
623
  end
648
624
 
649
- # yields each inplace applicator schema which applies to the given instance.
650
- #
651
- # @param instance (see #inplace_applicator_schemas)
652
- # @param visited_refs [Enumerable<JSI::Schema::Ref>]
653
- # @yield [JSI::Schema]
654
- # @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
655
- def each_inplace_applicator_schema(instance, visited_refs: [], &block)
656
- return to_enum(__method__, instance, visited_refs: visited_refs) unless block
625
+ # yields each descendent of this node (including itself) within the same resource that is a Schema
626
+ # @yield [Schema]
627
+ def jsi_each_descendent_schema_same_resource(&block)
628
+ return(to_enum(__method__)) unless block_given?
657
629
 
658
- catch(:jsi_application_done) do
659
- internal_inplace_applicate_keywords(instance, visited_refs, &block)
630
+ yield(self)
631
+ dialect_invoke_each(:subschema) do |ptr|
632
+ desc = subschema(ptr)
633
+ if !desc.jsi_is_resource_root?
634
+ desc.jsi_each_descendent_schema_same_resource(&block)
635
+ end
660
636
  end
637
+ end
661
638
 
662
- nil
639
+ # @yield [Ptr]
640
+ def each_immediate_subschema_ptr
641
+ return(to_enum(__method__)) unless block_given?
642
+
643
+ dialect_invoke_each(:subschema) { |ptr| yield(Ptr.ary_ptr(ptr)) }
644
+ end
645
+
646
+ # @private
647
+ # @yield each in-place applicator schema and params yielded from action :inplace_applicate
648
+ def each_immediate_inplace_applicator_schema(
649
+ instance: ,
650
+ visited_refs: ,
651
+ collect_evaluated: ,
652
+ &block
653
+ )
654
+ # if collect_evaluated, applicators must validate the instance to set `evaluated`
655
+ if collect_evaluated || @inplace_application_requires_instance
656
+ dialect_invoke_each(:inplace_applicate, Cxt::InplaceApplication::WithInstance,
657
+ instance: instance,
658
+ visited_refs: visited_refs,
659
+ collect_evaluated: collect_evaluated,
660
+ &block
661
+ )
662
+ else
663
+ # memoize: if the instance is not used by any in-place applicator present in this schema,
664
+ # the schema can do in-place application once instead of for every instance,
665
+ # for a very substantial performance gain.
666
+ #
667
+ # :inplace_applicate yields (schema, **keywords)
668
+ # so @memos[:immediate_inplace_applicators] is a 2D Array of tuples (schema, keywords)
669
+ @memos[:immediate_inplace_applicators] ||= begin
670
+ immediate_inplace_applicators = []
671
+ dialect_invoke_each(:inplace_applicate, Cxt::InplaceApplication,
672
+ visited_refs: visited_refs,
673
+ ) do |s, **kw|
674
+ immediate_inplace_applicators.push([s, kw])
675
+ end
676
+ immediate_inplace_applicators.freeze
677
+ end
678
+
679
+ @memos[:immediate_inplace_applicators].each do |(s, kw)|
680
+ yield(s, **kw)
681
+ end
682
+ nil
683
+ end
663
684
  end
664
685
 
665
- # a set of child applicator subschemas of this schema which apply to the child of the given instance
666
- # on the given token.
686
+ # Yields each in-place applicator schema which applies to the given instance.
667
687
  #
668
- # @param token [Object] the array index or object property name for the child instance
669
- # @param instance [Object] the instance to check any child applicators against
670
- # @return [JSI::SchemaSet] child applicator subschemas of this schema for the given token
671
- # of the instance
672
- def child_applicator_schemas(token, instance)
673
- SchemaSet.new(each_child_applicator_schema(token, instance))
688
+ # @param instance [Object] the instance to check any applicators against
689
+ # @param visited_refs [Enumerable<JSI::Schema::Ref>]
690
+ # @yield [JSI::Schema]
691
+ # @return [nil]
692
+ def each_inplace_applicator_schema(
693
+ instance,
694
+ visited_refs: Util::EMPTY_ARY,
695
+ &block
696
+ )
697
+ each_immediate_inplace_applicator_schema(
698
+ instance: instance,
699
+ visited_refs: visited_refs,
700
+ collect_evaluated: false, # child application is not invoked so no evaluated children to collect
701
+ ) do |schema, ref: nil, applicate: true|
702
+ if schema.equal?(self) && !ref
703
+ yield(self)
704
+ elsif applicate
705
+ schema.each_inplace_applicator_schema(
706
+ instance,
707
+ visited_refs: Util.add_visited_ref(visited_refs, ref),
708
+ &block
709
+ )
710
+ end
711
+ end
674
712
  end
675
713
 
676
714
  # yields each child applicator subschema (from properties, items, etc.) which applies to the child of
677
715
  # the given instance on the given token.
678
716
  #
679
- # @param (see #child_applicator_schemas)
717
+ # @param token [Object] the array index or object property name for the child instance
718
+ # @param instance [Object] the instance to check any child applicators against
680
719
  # @yield [JSI::Schema]
681
720
  # @return [nil, Enumerator] an Enumerator if invoked without a block; otherwise nil
682
721
  def each_child_applicator_schema(token, instance, &block)
683
- return to_enum(__method__, token, instance) unless block
722
+ dialect_invoke_each(:child_applicate,
723
+ Cxt::ChildApplication,
724
+ instance: instance,
725
+ token: token,
726
+ collect_evaluated: false,
727
+ collect_evaluated_validate: false,
728
+ evaluated: false,
729
+ &block
730
+ )
731
+ end
684
732
 
685
- internal_child_applicate_keywords(token, instance, &block)
733
+ # For each in-place applicator schema that applies to the given instance, yields each child applicator
734
+ # of that schema that applies to the child of the instance on the given token.
735
+ #
736
+ # This method handles collection of whether the child was evaluated by any applicator
737
+ # when that evaluation is needed by either this schema or the caller (per param `collect_evaluated`).
738
+ # This is relevant to schemas containing `unevaluatedProperties` or `unevaluatedItems`.
739
+ #
740
+ # @param token [Object] array index or hash/object property name
741
+ # @param instance [Object]
742
+ # @param collect_evaluated [Boolean] Does the caller need this method to collect successful child evaluation?
743
+ # Note: this method will still collect child evaluation if this schema needs it; this only needs to be
744
+ # passed true when called by an in-place applicator schema that needs it (i.e. contains `unevaluated*`).
745
+ # @param collect_evaluated_validate [Boolean] See {Base::Conf#application_collect_evaluated_validate}
746
+ # @yield [Schema]
747
+ # @return [Boolean] if `collect_evaluated` is true, whether the child was successfully evaluated
748
+ # by a child applicator schema. if `collect_evaluated` is false, undefined/void.
749
+ def each_inplace_child_applicator_schema(
750
+ token,
751
+ instance,
752
+ visited_refs: Util::EMPTY_ARY,
753
+ collect_evaluated: false,
754
+ collect_evaluated_validate: false,
755
+ &block
756
+ )
757
+ collect_evaluated ||= application_requires_evaluated
758
+ inplace_child_evaluated = false
759
+ applicate_self = false
760
+
761
+ each_immediate_inplace_applicator_schema(
762
+ instance: instance,
763
+ visited_refs: visited_refs,
764
+ collect_evaluated: collect_evaluated,
765
+ ) do |schema, ref: nil, applicate: true|
766
+ if schema.equal?(self) && !ref
767
+ applicate_self = true
768
+ elsif applicate || (collect_evaluated && !inplace_child_evaluated)
769
+ schema_evaluated = schema.each_inplace_child_applicator_schema(
770
+ token,
771
+ instance,
772
+ visited_refs: Util.add_visited_ref(visited_refs, ref),
773
+ collect_evaluated: collect_evaluated && !inplace_child_evaluated,
774
+ collect_evaluated_validate: collect_evaluated_validate,
775
+ # the `if` keyword needs to yield to here because it does affect `evaluated`,
776
+ # but it does not applicate itself/its applicators, so does not yield to the given block.
777
+ &(applicate ? block : proc { })
778
+ )
779
+ inplace_child_evaluated ||= collect_evaluated && schema_evaluated && (!collect_evaluated_validate || schema.instance_valid?(instance))
780
+ end
781
+ end
686
782
 
687
- nil
783
+ if applicate_self
784
+ child_application = dialect.invoke(:child_applicate, Cxt::ChildApplication.new(
785
+ schema: self,
786
+ abort: false,
787
+ token: token,
788
+ instance: instance,
789
+ collect_evaluated: collect_evaluated,
790
+ collect_evaluated_validate: collect_evaluated_validate,
791
+ evaluated: inplace_child_evaluated,
792
+ block: block,
793
+ ))
794
+
795
+ child_application.evaluated
796
+ else
797
+ inplace_child_evaluated
798
+ end
688
799
  end
689
800
 
690
801
  # any object property names this schema indicates may be present on its instances.
@@ -692,24 +803,13 @@ module JSI
692
803
  # array of "required" property keys.
693
804
  # @return [Set]
694
805
  def described_object_property_names
695
- @described_object_property_names_map[]
806
+ @described_object_property_names_map[schema_content: schema_content]
696
807
  end
697
808
 
698
- private def described_object_property_names_compute(**_) # TODO remove **_ eventually (keyword argument compatibility)
699
- Set.new.tap do |property_names|
700
- if schema_content.respond_to?(:to_hash) && schema_content['properties'].respond_to?(:to_hash)
701
- property_names.merge(schema_content['properties'].keys)
702
- end
703
- if schema_content.respond_to?(:to_hash) && schema_content['required'].respond_to?(:to_ary)
704
- property_names.merge(schema_content['required'].to_ary)
705
- end
706
- end.freeze
707
- end
708
-
709
- # validates the given instance against this schema
809
+ # Validates the given instance against this schema, returning a result with each validation error.
710
810
  #
711
811
  # @param instance [Object] the instance to validate against this schema
712
- # @return [JSI::Validation::Result]
812
+ # @return [JSI::Validation::Result::Full]
713
813
  def instance_validate(instance)
714
814
  if instance.is_a?(SchemaAncestorNode)
715
815
  instance_ptr = instance.jsi_ptr
@@ -731,6 +831,15 @@ module JSI
731
831
  internal_validate_instance(Ptr[], instance, validate_only: true).valid?
732
832
  end
733
833
 
834
+ # Asserts that the given instance is valid against this schema.
835
+ # {JSI::Invalid} is raised if it is not.
836
+ #
837
+ # @raise [Invalid]
838
+ # @return [nil]
839
+ def instance_valid!(instance)
840
+ instance_validate(instance).valid!
841
+ end
842
+
734
843
  # validates the given instance against this schema
735
844
  #
736
845
  # @private
@@ -739,54 +848,160 @@ module JSI
739
848
  # @param validate_only [Boolean] whether to return a full schema validation result or a simple, validation-only result
740
849
  # @param visited_refs [Enumerable<JSI::Schema::Ref>]
741
850
  # @return [JSI::Validation::Result]
742
- def internal_validate_instance(instance_ptr, instance_document, validate_only: false, visited_refs: [])
851
+ def internal_validate_instance(
852
+ instance_ptr,
853
+ instance_document,
854
+ visited_refs: Util::EMPTY_ARY,
855
+ validate_only: false
856
+ )
743
857
  if validate_only
744
- result = JSI::Validation::VALID
858
+ result = JSI::Validation::Result::Valid.new
745
859
  else
746
- result = JSI::Validation::FullResult.new
860
+ result = JSI::Validation::Result::Full.new
747
861
  end
748
- result_builder = result.builder(self, instance_ptr, instance_document, validate_only, visited_refs)
862
+ result_builder = result.class::Builder.new(
863
+ result: result,
864
+ schema: self,
865
+ abort: false,
866
+ instance_ptr: instance_ptr,
867
+ instance_document: instance_document,
868
+ validate_only: validate_only,
869
+ visited_refs: visited_refs,
870
+ )
749
871
 
750
872
  catch(:jsi_validation_result) do
751
- # note: true/false are not valid as schemas in draft 4; they are only values of
752
- # additionalProperties / additionalItems. since their behavior is undefined, though,
753
- # it's fine for them to behave the same as boolean schemas in later drafts.
754
- # I don't care about draft 4 to implement a different structuring for that.
755
- if schema_content == true
756
- # noop
757
- elsif schema_content == false
758
- result_builder.validate(false, 'instance is not valid against `false` schema')
759
- elsif schema_content.respond_to?(:to_hash)
760
- internal_validate_keywords(result_builder)
761
- else
762
- result_builder.schema_error('schema is not a boolean or a JSON object')
763
- end
873
+ dialect.invoke(:validate, result_builder)
874
+
764
875
  result
765
876
  end.freeze
766
877
  end
767
878
 
879
+ # See {Base#jsi_as_child_default_as_jsi}. true for Schema, including boolean schemas.
880
+ def jsi_as_child_default_as_jsi
881
+ true
882
+ end
883
+
884
+ # @param action_name [Symbol]
885
+ # @param cxt_class [Class]
886
+ # @yield
887
+ def dialect_invoke_each(
888
+ action_name,
889
+ cxt_class = Cxt::Block,
890
+ **cxt_param,
891
+ &block
892
+ )
893
+ return(to_enum(__method__, action_name, cxt_class, **cxt_param)) unless block_given?
894
+
895
+ cxt = cxt_class.new(
896
+ schema: self,
897
+ abort: false,
898
+ block: block,
899
+ **cxt_param,
900
+ )
901
+ dialect.invoke(action_name, cxt)
902
+
903
+ nil
904
+ end
905
+
768
906
  # schema resources which are ancestors of any subschemas below this schema.
769
907
  # this may include this schema if this is a schema resource root.
770
908
  # @api private
771
909
  # @return [Array<JSI::Schema>]
772
910
  def jsi_subschema_resource_ancestors
773
- if schema_resource_root?
911
+ if jsi_is_resource_root?
774
912
  jsi_schema_resource_ancestors.dup.push(self).freeze
775
913
  else
776
914
  jsi_schema_resource_ancestors
777
915
  end
778
916
  end
779
917
 
918
+ # @private
919
+ def jsi_next_schema_dynamic_anchor_map
920
+ return @memos[:next_schema_dynamic_anchor_map] if @memos.key?(:next_schema_dynamic_anchor_map)
921
+
922
+ if !dialect.elements.any? { |e| e.invokes?(:dynamicAnchor) }
923
+ return @memos[:next_schema_dynamic_anchor_map] = jsi_schema_dynamic_anchor_map
924
+ end
925
+ if !jsi_resource_root.is_a?(Schema)
926
+ # if the resource root is not a schema, then this schema does not add to dynamic_anchor_map.
927
+ # we could treat self as the anchor root, but that complicates DynamicAnchorMap#without_node.
928
+ return @memos[:next_schema_dynamic_anchor_map] = jsi_schema_dynamic_anchor_map
929
+ end
930
+
931
+ map = jsi_schema_dynamic_anchor_map
932
+
933
+ anchor_root = jsi_resource_root
934
+ descendent_schemas = [[anchor_root, Util::EMPTY_ARY]]
935
+
936
+ while !descendent_schemas.empty?
937
+ descendent_schema, ptrs = *descendent_schemas.shift
938
+
939
+ descendent_schema.dialect_invoke_each(:dynamicAnchor) do |anchor|
940
+ next if map.key?(anchor)
941
+ map = map.merge({
942
+ anchor => [anchor_root.jsi_with_schema_dynamic_anchor_map(Schema::DynamicAnchorMap::EMPTY), ptrs].freeze,
943
+ }).freeze
944
+ end
945
+
946
+ descendent_schema.each_immediate_subschema_ptr do |subptr|
947
+ # we want a schema at subptr to
948
+ # - check if it is a schema resource root
949
+ # - check for $dynamicAnchor
950
+ # can't use #subschema here (it would need to pass this method's result to instantiate the subschema);
951
+ # a minimal bootstrap schema is used instead.
952
+ # note: not using dialect.bootstrap_schema. this bootstrap is only used once, skip memoization.
953
+ descendent_subschema = MetaSchemaNode::BootstrapSchema.new(
954
+ dialect: dialect,
955
+ jsi_document: jsi_document,
956
+ jsi_ptr: descendent_schema.jsi_ptr + subptr,
957
+ # note: same as anchor_root.jsi_next_base_uri since we don't cross resource boundaries.
958
+ jsi_base_uri: descendent_schema.jsi_next_base_uri,
959
+ )
960
+ if !descendent_subschema.jsi_is_resource_root?
961
+ descendent_schemas.push([descendent_subschema, ptrs.dup.push(subptr).freeze])
962
+ end
963
+ end
964
+ end
965
+
966
+ @memos[:next_schema_dynamic_anchor_map] = map
967
+ end
968
+
969
+ # @private pending stronger stability of dynamic scope
970
+ def with_dynamic_scope_from(node)
971
+ node = node.jsi_node if node.is_a?(SchemaModule::Connection)
972
+ jsi_with_schema_dynamic_anchor_map(node.jsi_next_schema_dynamic_anchor_map)
973
+ end
974
+
975
+ # Does application require collection of evaluated children?
976
+ # (i.e. does the schema contain `unevaluatedItems` / `unevaluatedProperties`?)
977
+ # @private
978
+ # @return [Boolean]
979
+ attr_reader(:application_requires_evaluated)
980
+
981
+ # @private
982
+ # @return [#to_s, nil]
983
+ def jsi_schema_identifier(required: false)
984
+ name = jsi_schema_module_name_from_ancestor
985
+ return name if name
986
+ return schema_uri || (required ? jsi_ptr.uri : nil) if jsi_schema_dynamic_anchor_map.empty?
987
+ -"#{schema_uri || jsi_ptr.uri}#{jsi_schema_dynamic_anchor_map.anchor_schemas_identifier}"
988
+ end
989
+
780
990
  private
781
991
 
992
+ KEY_BY_NONE = proc { nil }
993
+
782
994
  def jsi_schema_initialize
783
- @schema_ref_map = jsi_memomap(key_by: proc { |i| i[:keyword] }) do |keyword: , value: |
784
- Schema::Ref.new(value, ref_schema: self)
995
+ # guard against being called twice on MetaSchemaNode, first from extend(Schema) then extend(jsi_schema_module) that includes Schema.
996
+ # both extends need to initialize for edge case of draft4's boolean schema that is not described by meta-schema.
997
+ instance_variable_defined?(:@jsi_schema_initialized) ? return : (@jsi_schema_initialized = true)
998
+ @schema_ref_map = Hash.new { |h, ref| h[ref] = Schema::Ref.new(ref, referrer: self) }
999
+ @schema_uris_map = jsi_memomap(key_by: KEY_BY_NONE) { to_enum(:schema_uris_compute).to_a.freeze }
1000
+ @described_object_property_names_map = jsi_memomap(key_by: KEY_BY_NONE) do
1001
+ Set.new(dialect_invoke_each(:described_object_property_names)).freeze
785
1002
  end
786
- @schema_uris_map = jsi_memomap(&method(:schema_uris_compute))
787
- @subschema_map = jsi_memomap(&method(:subschema_compute))
788
- @resource_root_subschema_map = jsi_memomap(&method(:resource_root_subschema_compute))
789
- @described_object_property_names_map = jsi_memomap(&method(:described_object_property_names_compute))
1003
+ @application_requires_evaluated = dialect_invoke_each(:application_requires_evaluated).any?
1004
+ @inplace_application_requires_instance = dialect_invoke_each(:inplace_application_requires_instance).any?
790
1005
  end
791
1006
  end
792
1007
  end