xsd 2.8.0 → 2.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/lib/xsd/base_object.rb +7 -5
- data/lib/xsd/objects/include.rb +6 -1
- data/lib/xsd/objects/schema.rb +47 -4
- data/lib/xsd/version.rb +1 -1
- data/lib/xsd/xml.rb +52 -5
- data/xsd.gemspec +37 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6dafeffb7f25c0134087002cfa7a33a2ae26bfb5d7aefc83dd998f73ce6551ce
|
|
4
|
+
data.tar.gz: 309a398a467318eb24d8912556010c11cba92f72419088b709540775dc270e00
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 292b2948f27d88c4363149e41aabca8858cd3c9105b804c5e85231135f1deb9d1a2bab10a6868839721dfd866b75a6579aca43d9243ca115dd752597cbaa7d81
|
|
7
|
+
data.tar.gz: cdc5245afa7c4fd48064b8c6ddac6966d8e0010d8a98e23fc59b43eb3df98e2159f4912db2e4ca3c5610e7713f0af1d525b6137e1c5c74edd38b8d87896c0717
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,34 @@
|
|
|
1
|
+
## [2.9.0] - 2026-10-18
|
|
2
|
+
|
|
3
|
+
### Changed
|
|
4
|
+
|
|
5
|
+
- A schema with no target namespace included into one that has a namespace ("chameleon" include) is seen through
|
|
6
|
+
a view in the including namespace (`XML#chameleon`, `Schema#chameleon?`, `#chameleon_source`). Everything it
|
|
7
|
+
declares reports that namespace (`namespace`, `absolute_name`, `Schema#target_namespace`), and an unqualified
|
|
8
|
+
name inside it resolves in that namespace. Before, its definitions reported no namespace at all.
|
|
9
|
+
- A schema included into two namespaces declares two sets of definitions, one per namespace: a view keeps its own
|
|
10
|
+
object cache (`Schema#object_cache`), where one object per node used to serve both.
|
|
11
|
+
- `XML#schemas_for_namespace` returns what the schemas of the namespace include as well, chameleon views among
|
|
12
|
+
them, and so does the lookup of a referenced name: includes of includes, and what a schema reached through an
|
|
13
|
+
import includes (only the schema itself, or one level of includes, was searched). A schema only seen through a
|
|
14
|
+
chameleon view is no longer among those of the absent namespace
|
|
15
|
+
- `XML#[]` and the collectors (`elements`, `complex_types`, `simple_types`, …) go through the schemas that stand
|
|
16
|
+
on their own (`XML#standalone_schemas`): a schema only seen through a chameleon view contributes its
|
|
17
|
+
definitions through the schemas including it, not a second time in the absent namespace
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `XML#target_namespaces`: every namespace the schemas declare definitions in, includes resolved; the absent
|
|
22
|
+
namespace (nil) only for a schema with no namespace that nothing includes
|
|
23
|
+
|
|
24
|
+
## [2.8.1] - 2026-10-18
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- An import/include location written with backslashes (`import\types.xsd`) is resolved as one written with
|
|
29
|
+
slashes. A `schemaLocation` is a URI reference, but schemas authored on Windows carry such locations; a custom
|
|
30
|
+
resource resolver is handed the location so normalized
|
|
31
|
+
|
|
1
32
|
## [2.8.0] - 2026-09-21
|
|
2
33
|
|
|
3
34
|
### Fixed
|
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
|
|
@@ -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,47 @@ 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
|
+
# This schema and every schema it includes, those included in turn too
|
|
131
|
+
# @return Array<Schema>
|
|
132
|
+
def with_includes
|
|
133
|
+
found = [self]
|
|
134
|
+
found.each do |schema|
|
|
135
|
+
schema.includes.each do |include|
|
|
136
|
+
included = include.imported_schema
|
|
137
|
+
found << included unless found.any? { |known| known.equal?(included) }
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
found
|
|
141
|
+
end
|
|
142
|
+
|
|
102
143
|
# Get all available elements on the current stack level, for schema same as elements
|
|
103
144
|
# @return Array<Element>
|
|
104
145
|
def collect_elements(*)
|
|
@@ -133,15 +174,17 @@ module XSD
|
|
|
133
174
|
# @param [String, nil] namespace
|
|
134
175
|
# @return Boolean
|
|
135
176
|
def targets_namespace?(namespace)
|
|
136
|
-
namespace == target_namespace || namespaces[namespace.nil? ? 'xmlns' : "xmlns:#{namespace}"] == target_namespace
|
|
177
|
+
namespace == target_namespace || namespaces[namespace.nil? ? 'xmlns' : "xmlns:#{namespace}"] == target_namespace ||
|
|
178
|
+
# an unqualified name in a chameleon schema names something of the namespace it was included into
|
|
179
|
+
(chameleon? && namespace.nil? && !namespaces.key?('xmlns'))
|
|
137
180
|
end
|
|
138
181
|
|
|
139
182
|
# Override map_children on schema to get objects from all loaded schemas
|
|
140
183
|
# @param [Symbol] name
|
|
141
184
|
# @param [Hash] cache Schema nodes already visited, guards against import/include cycles
|
|
142
185
|
# @return Array<BaseObject>
|
|
143
|
-
def map_children(name, cache = {})
|
|
144
|
-
cache[
|
|
186
|
+
def map_children(name, cache = {}.compare_by_identity)
|
|
187
|
+
cache[self] = true
|
|
145
188
|
super(name) + import_map_children(name, cache)
|
|
146
189
|
end
|
|
147
190
|
|
|
@@ -154,7 +197,7 @@ module XSD
|
|
|
154
197
|
|
|
155
198
|
(includes + imports).flat_map do |import|
|
|
156
199
|
imported = import.imported_schema
|
|
157
|
-
next [] if cache.key?(imported
|
|
200
|
+
next [] if cache.key?(imported)
|
|
158
201
|
|
|
159
202
|
imported.map_children(name, cache)
|
|
160
203
|
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
|
|
@@ -130,7 +131,12 @@ module XSD
|
|
|
130
131
|
new_schema
|
|
131
132
|
end
|
|
132
133
|
|
|
133
|
-
# Load schema referenced by an import/include, reusing an already loaded one when possible
|
|
134
|
+
# Load schema referenced by an import/include, reusing an already loaded one when possible.
|
|
135
|
+
#
|
|
136
|
+
# A location written with backslashes (`import\types.xsd`) is read as one written with slashes: a
|
|
137
|
+
# `schemaLocation` is a URI reference, where the separator is `/` alone, but schemas authored on Windows
|
|
138
|
+
# carry such locations, and the tools they were made with accept them. A custom resolver is handed the
|
|
139
|
+
# location so normalized.
|
|
134
140
|
# @param [String] location
|
|
135
141
|
# @param [String, nil] namespace
|
|
136
142
|
# @param [String, nil] base Location of the referencing schema
|
|
@@ -138,6 +144,7 @@ module XSD
|
|
|
138
144
|
def load_schema(location, namespace: nil, base: nil)
|
|
139
145
|
raise ImportError, 'Schema location must be provided' if location.nil? || location.empty?
|
|
140
146
|
|
|
147
|
+
location = location.tr('\\', '/')
|
|
141
148
|
expanded = expand_location(location, base)
|
|
142
149
|
return @schemas_by_location[expanded] if @schemas_by_location.key?(expanded)
|
|
143
150
|
|
|
@@ -157,15 +164,55 @@ module XSD
|
|
|
157
164
|
schemas.first
|
|
158
165
|
end
|
|
159
166
|
|
|
160
|
-
# 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
|
|
161
169
|
# @param [String, nil] namespace
|
|
162
170
|
# @return Array<Schema>
|
|
163
171
|
def schemas_for_namespace(namespace)
|
|
164
|
-
|
|
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) } }
|
|
165
212
|
end
|
|
166
213
|
|
|
167
214
|
def [](*args)
|
|
168
|
-
|
|
215
|
+
standalone_schemas.each do |schema|
|
|
169
216
|
item = schema[*args]
|
|
170
217
|
return item if item
|
|
171
218
|
end
|
|
@@ -226,7 +273,7 @@ module XSD
|
|
|
226
273
|
end
|
|
227
274
|
|
|
228
275
|
def collect(name, *args)
|
|
229
|
-
|
|
276
|
+
standalone_schemas.flat_map { |schema| schema.send(name, *args) }.uniq
|
|
230
277
|
end
|
|
231
278
|
|
|
232
279
|
def default_logger
|
data/xsd.gemspec
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/xsd/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'xsd'
|
|
7
|
+
spec.version = XSD::VERSION
|
|
8
|
+
spec.authors = ['d.arkhipov']
|
|
9
|
+
spec.email = ['d.arkhipov@ekzo.dev']
|
|
10
|
+
|
|
11
|
+
spec.summary = 'The Ruby XSD library is an XML Schema implementation for Ruby.'
|
|
12
|
+
spec.description =
|
|
13
|
+
'The Ruby XSD library is an XML Schema implementation for Ruby.Provides easy and flexible access to XSD information'
|
|
14
|
+
|
|
15
|
+
spec.homepage = 'https://github.com/ekzo-dev/ruby-xsd'
|
|
16
|
+
spec.license = 'MIT'
|
|
17
|
+
spec.required_ruby_version = '>= 2.7.0'
|
|
18
|
+
|
|
19
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
|
20
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/ekzo-dev/ruby-xsd/blob/main/CHANGELOG.md'
|
|
22
|
+
|
|
23
|
+
# Specify which files should be added to the gem when it is released.
|
|
24
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
25
|
+
spec.files = Dir.chdir(__dir__) do
|
|
26
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
27
|
+
(File.expand_path(f) == __FILE__) || f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
spec.bindir = 'exe'
|
|
31
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
|
32
|
+
spec.require_paths = ['lib']
|
|
33
|
+
|
|
34
|
+
spec.add_dependency 'builder', '~> 3.2'
|
|
35
|
+
spec.add_dependency 'nokogiri', '~> 1.11'
|
|
36
|
+
spec.add_dependency 'rake', '~> 13.0'
|
|
37
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xsd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- d.arkhipov
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-10-19 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: builder
|
|
@@ -111,6 +111,7 @@ files:
|
|
|
111
111
|
- lib/xsd/shared/simple_typed.rb
|
|
112
112
|
- lib/xsd/version.rb
|
|
113
113
|
- lib/xsd/xml.rb
|
|
114
|
+
- xsd.gemspec
|
|
114
115
|
homepage: https://github.com/ekzo-dev/ruby-xsd
|
|
115
116
|
licenses:
|
|
116
117
|
- MIT
|