xsd 2.8.1 → 2.9.1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/Gemfile +0 -1
- data/lib/xsd/base_object.rb +9 -7
- data/lib/xsd/objects/include.rb +6 -1
- data/lib/xsd/objects/schema.rb +63 -4
- data/lib/xsd/version.rb +1 -1
- data/lib/xsd/xml.rb +45 -4
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90d2e1e3950f8ada667ad7a2960940c3a4857918425c6fb54ef6c6a92702f1bc
|
|
4
|
+
data.tar.gz: 713e112c94582f982c01708d6ca735fd667006b476cb6ef0125383c55e39644b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a804d3750423fe3c04c5d99614dac7a49679941498dacb9e9f72ef319c99da2c21ab70e94df58e38e7d0bae59561c66836c81db7df651ca68030baf363c96eab
|
|
7
|
+
data.tar.gz: 7ed63b4341d8a27a6fc1b3df4d3c6aebd30a2a5e101bf91ccaa32b54997b47f475d98c329c406eb5802706d4361b9ef6dc119a6422922726234391d124a202b2
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,37 @@
|
|
|
1
|
+
## [2.9.1] - 2026-10-20
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- A reference by name (a `type`, `ref`, `base`, …) is looked up in an index of the schema's top-level declarations
|
|
6
|
+
(`Schema#declaration_node`), built on the first lookup, instead of an XPath query that scanned every child of the
|
|
7
|
+
schema again for each reference. Large schemas referenced thousands of times are read several times faster
|
|
8
|
+
there; what is found is the same.
|
|
9
|
+
- The specs no longer load `wasabi`, which only a commented-out example used and which does not load on Ruby 3.4
|
|
10
|
+
(it requires `kconv`): the suite runs on Ruby 3.4.
|
|
11
|
+
|
|
12
|
+
## [2.9.0] - 2026-10-18
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
|
|
16
|
+
- A schema with no target namespace included into one that has a namespace ("chameleon" include) is seen through
|
|
17
|
+
a view in the including namespace (`XML#chameleon`, `Schema#chameleon?`, `#chameleon_source`). Everything it
|
|
18
|
+
declares reports that namespace (`namespace`, `absolute_name`, `Schema#target_namespace`), and an unqualified
|
|
19
|
+
name inside it resolves in that namespace. Before, its definitions reported no namespace at all.
|
|
20
|
+
- A schema included into two namespaces declares two sets of definitions, one per namespace: a view keeps its own
|
|
21
|
+
object cache (`Schema#object_cache`), where one object per node used to serve both.
|
|
22
|
+
- `XML#schemas_for_namespace` returns what the schemas of the namespace include as well, chameleon views among
|
|
23
|
+
them, and so does the lookup of a referenced name: includes of includes, and what a schema reached through an
|
|
24
|
+
import includes (only the schema itself, or one level of includes, was searched). A schema only seen through a
|
|
25
|
+
chameleon view is no longer among those of the absent namespace
|
|
26
|
+
- `XML#[]` and the collectors (`elements`, `complex_types`, `simple_types`, …) go through the schemas that stand
|
|
27
|
+
on their own (`XML#standalone_schemas`): a schema only seen through a chameleon view contributes its
|
|
28
|
+
definitions through the schemas including it, not a second time in the absent namespace
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- `XML#target_namespaces`: every namespace the schemas declare definitions in, includes resolved; the absent
|
|
33
|
+
namespace (nil) only for a schema with no namespace that nothing includes
|
|
34
|
+
|
|
1
35
|
## [2.8.1] - 2026-10-18
|
|
2
36
|
|
|
3
37
|
### Fixed
|
data/Gemfile
CHANGED
data/lib/xsd/base_object.rb
CHANGED
|
@@ -85,9 +85,10 @@ module XSD
|
|
|
85
85
|
ns = prefix || ns_or_prefix
|
|
86
86
|
|
|
87
87
|
if schema.targets_namespace?(ns)
|
|
88
|
-
|
|
88
|
+
schema.with_includes
|
|
89
89
|
elsif (import = schema.import_by_namespace(ns))
|
|
90
|
-
|
|
90
|
+
# what the imported schema includes is part of its namespace too
|
|
91
|
+
import.imported_schema.with_includes
|
|
91
92
|
else
|
|
92
93
|
raise Error, "Schema not found for namespace '#{ns}' in '#{schema.id || schema.target_namespace}'"
|
|
93
94
|
end
|
|
@@ -128,9 +129,9 @@ module XSD
|
|
|
128
129
|
# determine schema for namespace
|
|
129
130
|
search_schemas = schemas_for_namespace(name_prefix)
|
|
130
131
|
|
|
131
|
-
# find
|
|
132
|
+
# find the declaration in the schemas of the namespace
|
|
132
133
|
search_schemas.each do |s|
|
|
133
|
-
node = s.
|
|
134
|
+
node = s.declaration_node(node_name, name_local)
|
|
134
135
|
return s.node_to_object(node) if node
|
|
135
136
|
end
|
|
136
137
|
|
|
@@ -141,14 +142,15 @@ module XSD
|
|
|
141
142
|
# @param [Nokogiri::XML::Node] node
|
|
142
143
|
# @return BaseObject
|
|
143
144
|
def node_to_object(node)
|
|
144
|
-
# check object in cache first
|
|
145
|
-
|
|
145
|
+
# check object in cache first; a chameleon view keeps a cache of its own (see Schema#object_cache)
|
|
146
|
+
cache = schema.object_cache
|
|
147
|
+
cached = cache[node]
|
|
146
148
|
return cached if cached
|
|
147
149
|
|
|
148
150
|
klass = XML::CLASS_MAP[node.name]
|
|
149
151
|
raise Error, "Object class not found for '#{node.name}'" unless klass
|
|
150
152
|
|
|
151
|
-
|
|
153
|
+
cache[node] = klass.new(options.merge(node: node, schema: schema))
|
|
152
154
|
end
|
|
153
155
|
|
|
154
156
|
# Get xml parent object
|
data/lib/xsd/objects/include.rb
CHANGED
|
@@ -17,7 +17,12 @@ module XSD
|
|
|
17
17
|
|
|
18
18
|
new_schema = reader.load_schema(schema_location, base: schema.location)
|
|
19
19
|
|
|
20
|
-
# an included schema without a target namespace adopts the namespace of the including schema ("chameleon"
|
|
20
|
+
# an included schema without a target namespace adopts the namespace of the including schema ("chameleon"
|
|
21
|
+
# include): what it declares belongs to that namespace, so it is seen through a view that says so
|
|
22
|
+
if new_schema.target_namespace.nil? && !schema.target_namespace.nil?
|
|
23
|
+
return reader.chameleon(new_schema, schema.target_namespace)
|
|
24
|
+
end
|
|
25
|
+
|
|
21
26
|
unless new_schema.target_namespace.nil? || schema.target_namespace == new_schema.target_namespace
|
|
22
27
|
raise ImportError, "Schema targetNamespace '#{schema.target_namespace}' does not match included schema " \
|
|
23
28
|
"targetNamespace '#{new_schema.target_namespace}'"
|
data/lib/xsd/objects/schema.rb
CHANGED
|
@@ -99,6 +99,63 @@ module XSD
|
|
|
99
99
|
self
|
|
100
100
|
end
|
|
101
101
|
|
|
102
|
+
# Target namespace of the schema. For a chameleon view (see XML#chameleon), the namespace of the schema
|
|
103
|
+
# that includes it — the one everything it declares belongs to.
|
|
104
|
+
# @return String, nil
|
|
105
|
+
def target_namespace
|
|
106
|
+
return options[:chameleon_namespace] if chameleon?
|
|
107
|
+
|
|
108
|
+
method_missing(:target_namespace)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Is this a schema with no target namespace, seen as part of the namespace of a schema including it?
|
|
112
|
+
# @return Boolean
|
|
113
|
+
def chameleon?
|
|
114
|
+
options.key?(:chameleon_namespace)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# The schema a chameleon view is a view of
|
|
118
|
+
# @return Schema, nil
|
|
119
|
+
def chameleon_source
|
|
120
|
+
options[:chameleon_source]
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Objects made of this schema's nodes. A chameleon view keeps its own: the same node is a different
|
|
124
|
+
# definition in each namespace that includes it.
|
|
125
|
+
# @return Hash
|
|
126
|
+
def object_cache
|
|
127
|
+
chameleon? ? (@object_cache ||= {}.compare_by_identity) : reader.object_cache
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# The node of a top-level declaration of this schema, by its kind and name — what a reference by name looks up
|
|
131
|
+
# (BaseObject#object_by_name). The children are indexed in one pass on the first lookup: a query per lookup
|
|
132
|
+
# scans all of them again, and a large schema is looked up in once for every reference to it.
|
|
133
|
+
# @param [Symbol, String] node_name kind of declaration: complexType, simpleType, element, group, ...
|
|
134
|
+
# @param [String] name local name
|
|
135
|
+
# @return Nokogiri::XML::Element, nil
|
|
136
|
+
def declaration_node(node_name, name)
|
|
137
|
+
@declaration_nodes ||= node.element_children.each_with_object({}) do |child, index|
|
|
138
|
+
next unless child.namespace&.href == XML_SCHEMA
|
|
139
|
+
|
|
140
|
+
# the first one, as a query would find it
|
|
141
|
+
index[[child.name, child['name']]] ||= child
|
|
142
|
+
end
|
|
143
|
+
@declaration_nodes[[node_name.to_s, name]]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# This schema and every schema it includes, those included in turn too
|
|
147
|
+
# @return Array<Schema>
|
|
148
|
+
def with_includes
|
|
149
|
+
found = [self]
|
|
150
|
+
found.each do |schema|
|
|
151
|
+
schema.includes.each do |include|
|
|
152
|
+
included = include.imported_schema
|
|
153
|
+
found << included unless found.any? { |known| known.equal?(included) }
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
found
|
|
157
|
+
end
|
|
158
|
+
|
|
102
159
|
# Get all available elements on the current stack level, for schema same as elements
|
|
103
160
|
# @return Array<Element>
|
|
104
161
|
def collect_elements(*)
|
|
@@ -133,15 +190,17 @@ module XSD
|
|
|
133
190
|
# @param [String, nil] namespace
|
|
134
191
|
# @return Boolean
|
|
135
192
|
def targets_namespace?(namespace)
|
|
136
|
-
namespace == target_namespace || namespaces[namespace.nil? ? 'xmlns' : "xmlns:#{namespace}"] == target_namespace
|
|
193
|
+
namespace == target_namespace || namespaces[namespace.nil? ? 'xmlns' : "xmlns:#{namespace}"] == target_namespace ||
|
|
194
|
+
# an unqualified name in a chameleon schema names something of the namespace it was included into
|
|
195
|
+
(chameleon? && namespace.nil? && !namespaces.key?('xmlns'))
|
|
137
196
|
end
|
|
138
197
|
|
|
139
198
|
# Override map_children on schema to get objects from all loaded schemas
|
|
140
199
|
# @param [Symbol] name
|
|
141
200
|
# @param [Hash] cache Schema nodes already visited, guards against import/include cycles
|
|
142
201
|
# @return Array<BaseObject>
|
|
143
|
-
def map_children(name, cache = {})
|
|
144
|
-
cache[
|
|
202
|
+
def map_children(name, cache = {}.compare_by_identity)
|
|
203
|
+
cache[self] = true
|
|
145
204
|
super(name) + import_map_children(name, cache)
|
|
146
205
|
end
|
|
147
206
|
|
|
@@ -154,7 +213,7 @@ module XSD
|
|
|
154
213
|
|
|
155
214
|
(includes + imports).flat_map do |import|
|
|
156
215
|
imported = import.imported_schema
|
|
157
|
-
next [] if cache.key?(imported
|
|
216
|
+
next [] if cache.key?(imported)
|
|
158
217
|
|
|
159
218
|
imported.map_children(name, cache)
|
|
160
219
|
end
|
data/lib/xsd/version.rb
CHANGED
data/lib/xsd/xml.rb
CHANGED
|
@@ -85,6 +85,7 @@ module XSD
|
|
|
85
85
|
@object_cache = {}.compare_by_identity
|
|
86
86
|
@schemas = []
|
|
87
87
|
@schemas_by_location = {}
|
|
88
|
+
@chameleons = {}
|
|
88
89
|
end
|
|
89
90
|
|
|
90
91
|
def logger
|
|
@@ -163,15 +164,55 @@ module XSD
|
|
|
163
164
|
schemas.first
|
|
164
165
|
end
|
|
165
166
|
|
|
166
|
-
# Get schemas by namespace
|
|
167
|
+
# Get schemas by namespace: those that target it, and what they include — a chameleon schema (one with no
|
|
168
|
+
# target namespace of its own) as a view in this namespace
|
|
167
169
|
# @param [String, nil] namespace
|
|
168
170
|
# @return Array<Schema>
|
|
169
171
|
def schemas_for_namespace(namespace)
|
|
170
|
-
|
|
172
|
+
found = []
|
|
173
|
+
# a schema only seen through a chameleon view is not one of the absent namespace
|
|
174
|
+
standalone_schemas.select { |schema| schema.target_namespace == namespace }.each do |schema|
|
|
175
|
+
schema.with_includes.each { |included| found << included unless found.any? { |known| known.equal?(included) } }
|
|
176
|
+
end
|
|
177
|
+
found
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# A schema with no target namespace, seen as part of the namespace of a schema that includes it ("chameleon"
|
|
181
|
+
# include): the same document, with everything it declares in that namespace. One view per schema and
|
|
182
|
+
# namespace, so a schema included into two namespaces declares two sets of definitions.
|
|
183
|
+
# @param [Schema] source
|
|
184
|
+
# @param [String] namespace
|
|
185
|
+
# @return Schema
|
|
186
|
+
def chameleon(source, namespace)
|
|
187
|
+
@chameleons[[source.object_id, namespace]] ||=
|
|
188
|
+
Schema.new(source.options.merge(chameleon_namespace: namespace, chameleon_source: source))
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# The chameleon views made so far
|
|
192
|
+
# @return Array<Schema>
|
|
193
|
+
def chameleons
|
|
194
|
+
@chameleons.values
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Every namespace the schemas declare definitions in, includes resolved. A schema with no target namespace
|
|
198
|
+
# stands for the absent namespace (nil) only when no schema includes it: otherwise what it declares is part
|
|
199
|
+
# of the namespaces including it.
|
|
200
|
+
# @return Array<String, nil>
|
|
201
|
+
def target_namespaces
|
|
202
|
+
(standalone_schemas + chameleons).map(&:target_namespace).uniq
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# The schemas that stand on their own, includes resolved: all but those only seen through a chameleon view,
|
|
206
|
+
# whose definitions belong to the namespaces including them
|
|
207
|
+
# @return Array<Schema>
|
|
208
|
+
def standalone_schemas
|
|
209
|
+
schemas.each(&:with_includes)
|
|
210
|
+
sources = chameleons.map(&:chameleon_source)
|
|
211
|
+
schemas.reject { |schema| sources.any? { |source| source.equal?(schema) } }
|
|
171
212
|
end
|
|
172
213
|
|
|
173
214
|
def [](*args)
|
|
174
|
-
|
|
215
|
+
standalone_schemas.each do |schema|
|
|
175
216
|
item = schema[*args]
|
|
176
217
|
return item if item
|
|
177
218
|
end
|
|
@@ -232,7 +273,7 @@ module XSD
|
|
|
232
273
|
end
|
|
233
274
|
|
|
234
275
|
def collect(name, *args)
|
|
235
|
-
|
|
276
|
+
standalone_schemas.flat_map { |schema| schema.send(name, *args) }.uniq
|
|
236
277
|
end
|
|
237
278
|
|
|
238
279
|
def default_logger
|
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xsd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.9.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- d.arkhipov
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: builder
|
|
@@ -119,7 +118,6 @@ metadata:
|
|
|
119
118
|
homepage_uri: https://github.com/ekzo-dev/ruby-xsd
|
|
120
119
|
source_code_uri: https://github.com/ekzo-dev/ruby-xsd
|
|
121
120
|
changelog_uri: https://github.com/ekzo-dev/ruby-xsd/blob/main/CHANGELOG.md
|
|
122
|
-
post_install_message:
|
|
123
121
|
rdoc_options: []
|
|
124
122
|
require_paths:
|
|
125
123
|
- lib
|
|
@@ -134,8 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
134
132
|
- !ruby/object:Gem::Version
|
|
135
133
|
version: '0'
|
|
136
134
|
requirements: []
|
|
137
|
-
rubygems_version: 3.
|
|
138
|
-
signing_key:
|
|
135
|
+
rubygems_version: 3.6.9
|
|
139
136
|
specification_version: 4
|
|
140
137
|
summary: The Ruby XSD library is an XML Schema implementation for Ruby.
|
|
141
138
|
test_files: []
|