xsd 2.7.9 → 2.8.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/.rubocop.yml +0 -3
- data/CHANGELOG.md +35 -0
- data/lib/xsd/base_object.rb +97 -85
- data/lib/xsd/objects/complex_type.rb +21 -18
- data/lib/xsd/objects/element.rb +11 -12
- data/lib/xsd/objects/import.rb +8 -6
- data/lib/xsd/objects/include.rb +8 -8
- data/lib/xsd/objects/restriction.rb +13 -15
- data/lib/xsd/objects/schema.rb +58 -58
- data/lib/xsd/version.rb +1 -1
- data/lib/xsd/xml.rb +77 -14
- data/lib/xsd.rb +1 -0
- metadata +4 -5
- data/xsd.gemspec +0 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 40e6cbfb1356c6d21529ad7339f3ada85dbf85be27d7d0fcd3ecd59e55250192
|
|
4
|
+
data.tar.gz: 544ea96a3cc35ac6a37f8ab8971ae7d30c1b4a89c11c80ae6475ad1fcc61a6ae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6fbc961bab3eac175a05dd99a8cbb34dce5384f682def039f708407f281cbbbe75f5ec6a57a751fff2c8fbad927fd83edf2b1e8e488f255655777a0ce7cf36d0
|
|
7
|
+
data.tar.gz: 4f8c2fa9687ed1f4647e91884abd60d63be92fcbda2475cc3b0aa3b340f9ca6ae7fbe12d91343389de1cd0616a676c45e018e88e8dcbe557d7014ce24c02d227
|
data/.rubocop.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
|
+
## [2.8.0] - 2026-09-21
|
|
2
|
+
|
|
3
|
+
### Fixed
|
|
4
|
+
|
|
5
|
+
- `XSD::XML.open` resolves relative import/include locations against the schema they were read from, so the default
|
|
6
|
+
resource resolver no longer requires a custom one for ordinary multi-file schemas
|
|
7
|
+
- `Schema#validate` writes included schemas into the temporary directory too, so schemas wired together with
|
|
8
|
+
`<xs:include>` can be validated
|
|
9
|
+
- `Schema#validate` serializes the temporary schemas as UTF-8, so schemas in another encoding no longer fail to load
|
|
10
|
+
- `XML#logger` no longer raises `NameError` when no logger is configured (`logger` was never required)
|
|
11
|
+
- `BaseObject#parent` returns `nil` instead of raising when the parent node has no XSD object (document, `wsdl:types`)
|
|
12
|
+
- `object_by_name` passes the searched name as an xpath variable instead of interpolating it into the expression
|
|
13
|
+
- `ComplexType#mixed_content?` and `#data_type` no longer raise on a `complexContent`/`simpleContent` without a
|
|
14
|
+
derivation
|
|
15
|
+
- An `<xs:include>` of a schema without a target namespace ("chameleon" include) is accepted
|
|
16
|
+
- Import/include cycles no longer yield duplicated definitions
|
|
17
|
+
- The compiled validator no longer leaks an open file descriptor
|
|
18
|
+
- `Element#attributes?` and `ComplexType#complex_content?` return booleans instead of the underlying objects
|
|
19
|
+
- Import/include error messages name the namespaces that did not match
|
|
20
|
+
- `require 'xsd'` defines `XSD::VERSION`, which previously needed `require 'xsd/version'` on top of it
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Imported/included schemas are loaded once per location and shared, instead of being re-read and re-parsed for every
|
|
25
|
+
reference to them
|
|
26
|
+
- Property, link and child lookups cache `nil` and `false` results, which cuts repeated xpath searches (roughly 2.5x
|
|
27
|
+
faster traversal of the bundled example schemas)
|
|
28
|
+
- `XSD::XML#add_schema_xml`/`#add_schema_node` accept a `location:` keyword, and `Schema#location` exposes it
|
|
29
|
+
- `XML#object_cache` is keyed by node identity (`Hash#compare_by_identity`) rather than by `node.object_id`, which
|
|
30
|
+
lets the `Lint/HashCompareByIdentity` rule be enabled again
|
|
31
|
+
|
|
32
|
+
## [2.7.10] - 2024-04-26
|
|
33
|
+
|
|
34
|
+
- Fix data_type detection in complex_type simple_content extension
|
|
35
|
+
|
|
1
36
|
## [2.7.9] - 2024-04-25
|
|
2
37
|
|
|
3
38
|
- Fix resolving schemas with unprefixed namespace
|
data/lib/xsd/base_object.rb
CHANGED
|
@@ -16,6 +16,9 @@ module XSD
|
|
|
16
16
|
# Base XMLSchema namespace
|
|
17
17
|
XML_SCHEMA = 'http://www.w3.org/2001/XMLSchema'
|
|
18
18
|
|
|
19
|
+
# Namespace map used for all internal xpath queries
|
|
20
|
+
XML_SCHEMA_NS = { 'xs' => XML_SCHEMA }.freeze
|
|
21
|
+
|
|
19
22
|
class << self
|
|
20
23
|
attr_reader :properties, :children, :links
|
|
21
24
|
|
|
@@ -33,10 +36,10 @@ module XSD
|
|
|
33
36
|
end
|
|
34
37
|
|
|
35
38
|
def initialize(options = {})
|
|
36
|
-
|
|
37
|
-
@cache = {}
|
|
39
|
+
raise Error, "#{self.class}.new expects a hash parameter" unless options.is_a?(Hash)
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
@options = options
|
|
42
|
+
@cache = {}
|
|
40
43
|
end
|
|
41
44
|
|
|
42
45
|
# Get current XML node
|
|
@@ -67,9 +70,10 @@ module XSD
|
|
|
67
70
|
|
|
68
71
|
# Get child nodes
|
|
69
72
|
# @param [Symbol] name
|
|
73
|
+
# @param [Boolean] deep
|
|
70
74
|
# @return Nokogiri::XML::NodeSet
|
|
71
75
|
def nodes(name = :*, deep = false)
|
|
72
|
-
node.xpath("./#{deep ? '/' : ''}xs:#{name}",
|
|
76
|
+
node.xpath("./#{deep ? '/' : ''}xs:#{name}", XML_SCHEMA_NS)
|
|
73
77
|
end
|
|
74
78
|
|
|
75
79
|
# Get schemas by namespace or prefix
|
|
@@ -77,7 +81,7 @@ module XSD
|
|
|
77
81
|
# @return Array<Schema>
|
|
78
82
|
def schemas_for_namespace(ns_or_prefix)
|
|
79
83
|
# resolve namespace for current node if prefix was provided
|
|
80
|
-
prefix =
|
|
84
|
+
prefix = namespaces[['xmlns', (ns_or_prefix == '' ? nil : ns_or_prefix)].compact.join(':')]
|
|
81
85
|
ns = prefix || ns_or_prefix
|
|
82
86
|
|
|
83
87
|
if schema.targets_namespace?(ns)
|
|
@@ -116,7 +120,7 @@ module XSD
|
|
|
116
120
|
def object_by_name(node_name, name)
|
|
117
121
|
# get prefix and local name
|
|
118
122
|
name_prefix = get_prefix(name)
|
|
119
|
-
name_local
|
|
123
|
+
name_local = strip_prefix(name)
|
|
120
124
|
|
|
121
125
|
# do not search for built-in types
|
|
122
126
|
return nil if schema.namespace_prefix == name_prefix
|
|
@@ -124,9 +128,9 @@ module XSD
|
|
|
124
128
|
# determine schema for namespace
|
|
125
129
|
search_schemas = schemas_for_namespace(name_prefix)
|
|
126
130
|
|
|
127
|
-
# find element in target schema
|
|
131
|
+
# find element in target schema, name is passed as an xpath variable to stay safe with any name contents
|
|
128
132
|
search_schemas.each do |s|
|
|
129
|
-
node = s.node.at_xpath("./xs:#{node_name}[@name
|
|
133
|
+
node = s.node.at_xpath("./xs:#{node_name}[@name=$name]", XML_SCHEMA_NS, name: name_local)
|
|
130
134
|
return s.node_to_object(node) if node
|
|
131
135
|
end
|
|
132
136
|
|
|
@@ -134,22 +138,30 @@ module XSD
|
|
|
134
138
|
end
|
|
135
139
|
|
|
136
140
|
# Get reader object for node
|
|
137
|
-
# @param [Nokogiri::XML::Node]
|
|
141
|
+
# @param [Nokogiri::XML::Node] node
|
|
138
142
|
# @return BaseObject
|
|
139
143
|
def node_to_object(node)
|
|
140
144
|
# check object in cache first
|
|
141
|
-
|
|
145
|
+
cached = reader.object_cache[node]
|
|
146
|
+
return cached if cached
|
|
142
147
|
|
|
143
148
|
klass = XML::CLASS_MAP[node.name]
|
|
144
149
|
raise Error, "Object class not found for '#{node.name}'" unless klass
|
|
145
150
|
|
|
146
|
-
reader.object_cache[node
|
|
151
|
+
reader.object_cache[node] = klass.new(options.merge(node: node, schema: schema))
|
|
147
152
|
end
|
|
148
153
|
|
|
149
154
|
# Get xml parent object
|
|
150
155
|
# @return BaseObject, nil
|
|
151
156
|
def parent
|
|
152
|
-
|
|
157
|
+
return @parent if defined?(@parent)
|
|
158
|
+
|
|
159
|
+
parent_node = node.respond_to?(:parent) ? node.parent : nil
|
|
160
|
+
|
|
161
|
+
# the parent may be a document or a node of a foreign schema (e.g. wsdl:types), there is no object for those
|
|
162
|
+
@parent = if parent_node.is_a?(Nokogiri::XML::Element) && XML::CLASS_MAP.key?(parent_node.name)
|
|
163
|
+
node_to_object(parent_node)
|
|
164
|
+
end
|
|
153
165
|
end
|
|
154
166
|
|
|
155
167
|
# Get current schema object
|
|
@@ -205,55 +217,47 @@ module XSD
|
|
|
205
217
|
# @param [Nokogiri::XML::Node] node
|
|
206
218
|
# @return Array<String>
|
|
207
219
|
def documentation_for(node)
|
|
208
|
-
node.xpath('./xs:annotation/xs:documentation/text()',
|
|
220
|
+
node.xpath('./xs:annotation/xs:documentation/text()', XML_SCHEMA_NS).map { |x| x.text.strip }
|
|
209
221
|
end
|
|
210
222
|
|
|
211
223
|
# Get all available elements on the current stack level
|
|
212
224
|
# @return Array<Element>
|
|
213
225
|
def collect_elements(*)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
end.flatten
|
|
230
|
-
end
|
|
231
|
-
|
|
232
|
-
@collect_elements = r
|
|
226
|
+
@collect_elements ||= if NO_ELEMENTS_CONTAINER.include?(self.class.mapped_name)
|
|
227
|
+
[]
|
|
228
|
+
elsif is_a?(Referenced) && ref
|
|
229
|
+
reference.collect_elements
|
|
230
|
+
else
|
|
231
|
+
# map children recursive
|
|
232
|
+
map_children(:*).flat_map do |obj|
|
|
233
|
+
if obj.is_a?(Element) || obj.is_a?(Any)
|
|
234
|
+
obj
|
|
235
|
+
else
|
|
236
|
+
# get elements considering references
|
|
237
|
+
(obj.is_a?(Referenced) && obj.ref ? obj.reference : obj).collect_elements
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
233
241
|
end
|
|
234
242
|
|
|
235
243
|
# Get all available attributes on the current stack level
|
|
236
244
|
# @return Array<Attribute>
|
|
237
245
|
def collect_attributes(*)
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
end.flatten
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
@collect_attributes = r
|
|
246
|
+
@collect_attributes ||= if NO_ATTRIBUTES_CONTAINER.include?(self.class.mapped_name)
|
|
247
|
+
[]
|
|
248
|
+
elsif is_a?(Referenced) && ref
|
|
249
|
+
reference.collect_attributes
|
|
250
|
+
else
|
|
251
|
+
# map children recursive
|
|
252
|
+
map_children(:*).flat_map do |obj|
|
|
253
|
+
if obj.is_a?(Attribute) || obj.is_a?(AnyAttribute)
|
|
254
|
+
obj
|
|
255
|
+
else
|
|
256
|
+
# get attributes considering references
|
|
257
|
+
(obj.is_a?(Referenced) && obj.ref ? obj.reference : obj).collect_attributes
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
end
|
|
257
261
|
end
|
|
258
262
|
|
|
259
263
|
# Get reader instance
|
|
@@ -307,35 +311,17 @@ module XSD
|
|
|
307
311
|
# @param [Symbol] method
|
|
308
312
|
# @param [Array] args
|
|
309
313
|
def method_missing(method, *args)
|
|
310
|
-
# check cache first
|
|
311
|
-
return @cache[method] if @cache
|
|
314
|
+
# check cache first, nil and false results are cached as well
|
|
315
|
+
return @cache[method] if @cache.key?(method)
|
|
312
316
|
|
|
313
317
|
# check for property first
|
|
314
318
|
if (property = self.class.properties[method])
|
|
315
|
-
|
|
316
|
-
r = if value.nil?
|
|
317
|
-
if node['ref']
|
|
318
|
-
# if object has reference - search property in referenced object
|
|
319
|
-
reference.send(method, *args)
|
|
320
|
-
else
|
|
321
|
-
property[:default].is_a?(Proc) ? instance_eval(&property[:default]) : property[:default]
|
|
322
|
-
end
|
|
323
|
-
else
|
|
324
|
-
case property[:type]
|
|
325
|
-
when :integer
|
|
326
|
-
property[:name] == :maxOccurs && value == 'unbounded' ? :unbounded : value.to_i
|
|
327
|
-
when :boolean
|
|
328
|
-
value == 'true'
|
|
329
|
-
else
|
|
330
|
-
value
|
|
331
|
-
end
|
|
332
|
-
end
|
|
333
|
-
return @cache[method] = r
|
|
319
|
+
return @cache[method] = property_value(property, method, args)
|
|
334
320
|
end
|
|
335
321
|
|
|
336
322
|
# if object has ref it cannot contain any type and children, so proxy call to target object
|
|
337
323
|
if node['ref'] && method != :ref && method != :reference
|
|
338
|
-
return reference.send(method, *args)
|
|
324
|
+
return @cache[method] = reference.send(method, *args)
|
|
339
325
|
end
|
|
340
326
|
|
|
341
327
|
# then check for linked types
|
|
@@ -345,7 +331,7 @@ module XSD
|
|
|
345
331
|
return @cache[method] = object_by_name(link[:type], name)
|
|
346
332
|
elsif is_a?(Restriction) && %i[base_simple_type base_complex_type].include?(method)
|
|
347
333
|
# handle restriction without base
|
|
348
|
-
return nil
|
|
334
|
+
return @cache[method] = nil
|
|
349
335
|
end
|
|
350
336
|
end
|
|
351
337
|
|
|
@@ -356,26 +342,24 @@ module XSD
|
|
|
356
342
|
end
|
|
357
343
|
|
|
358
344
|
super
|
|
359
|
-
# api = self.class.properties.keys + self.class.links.keys + self.class.children.keys
|
|
360
|
-
# raise Error, "Tried to access unknown object '#{method}' on '#{self.class.name}'. Available options are: #{api}"
|
|
361
345
|
end
|
|
362
346
|
|
|
363
347
|
# Does object has property/link/child?
|
|
364
348
|
# @param [Symbol] method
|
|
365
|
-
# @param [
|
|
366
|
-
def respond_to_missing?(method,
|
|
367
|
-
self.class.properties[method] || self.class.links[method]
|
|
349
|
+
# @param [Boolean] include_private
|
|
350
|
+
def respond_to_missing?(method, include_private = false)
|
|
351
|
+
!self.class.properties[method].nil? || !self.class.links[method].nil? ||
|
|
352
|
+
!self.class.children[method].nil? || super
|
|
368
353
|
end
|
|
369
354
|
|
|
370
355
|
# Get mapped element name
|
|
371
356
|
# @return Symbol
|
|
372
357
|
def self.mapped_name
|
|
373
|
-
# @mapped_name ||= XML::CLASS_MAP.each { |k, v| return k.to_sym if v == self }
|
|
374
358
|
@mapped_name ||= begin
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
359
|
+
name = self.name.split('::').last
|
|
360
|
+
name[0] = name[0].downcase
|
|
361
|
+
name.to_sym
|
|
362
|
+
end
|
|
379
363
|
end
|
|
380
364
|
|
|
381
365
|
# Return string if it is not empty, or nil otherwise
|
|
@@ -387,6 +371,34 @@ module XSD
|
|
|
387
371
|
|
|
388
372
|
private
|
|
389
373
|
|
|
374
|
+
# Resolve property value from the node attribute, resolver block, reference or default
|
|
375
|
+
# @param [Hash] property
|
|
376
|
+
# @param [Symbol] method
|
|
377
|
+
# @param [Array] args
|
|
378
|
+
def property_value(property, method, args)
|
|
379
|
+
value = property[:resolve] ? property[:resolve].call(self) : node[property[:name].to_s]
|
|
380
|
+
return cast_property_value(property, value) unless value.nil?
|
|
381
|
+
|
|
382
|
+
# if object has reference - search property in referenced object
|
|
383
|
+
return reference.send(method, *args) if node['ref']
|
|
384
|
+
|
|
385
|
+
property[:default].is_a?(Proc) ? instance_eval(&property[:default]) : property[:default]
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
# Cast raw attribute value to the declared property type
|
|
389
|
+
# @param [Hash] property
|
|
390
|
+
# @param [String] value
|
|
391
|
+
def cast_property_value(property, value)
|
|
392
|
+
case property[:type]
|
|
393
|
+
when :integer
|
|
394
|
+
property[:name] == :maxOccurs && value == 'unbounded' ? :unbounded : value.to_i
|
|
395
|
+
when :boolean
|
|
396
|
+
value == 'true'
|
|
397
|
+
else
|
|
398
|
+
value
|
|
399
|
+
end
|
|
400
|
+
end
|
|
401
|
+
|
|
390
402
|
def definition_match?(definition, query)
|
|
391
403
|
# namespace included in query
|
|
392
404
|
if query.start_with?('{')
|
|
@@ -395,7 +407,7 @@ module XSD
|
|
|
395
407
|
|
|
396
408
|
namespace, name = parts
|
|
397
409
|
|
|
398
|
-
return false if namespace != definition.namespace
|
|
410
|
+
return false if nil_if_empty(namespace) != definition.namespace
|
|
399
411
|
else
|
|
400
412
|
name = query
|
|
401
413
|
end
|
|
@@ -88,7 +88,9 @@ module XSD
|
|
|
88
88
|
if complex_content
|
|
89
89
|
return true if complex_content.mixed
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
derivation = complex_content.extension || complex_content.restriction
|
|
92
|
+
|
|
93
|
+
return derivation&.base_complex_type&.mixed_content? || false
|
|
92
94
|
end
|
|
93
95
|
|
|
94
96
|
false
|
|
@@ -102,7 +104,7 @@ module XSD
|
|
|
102
104
|
elsif complex_content
|
|
103
105
|
true
|
|
104
106
|
else
|
|
105
|
-
group || all || choice || sequence
|
|
107
|
+
!!(group || all || choice || sequence)
|
|
106
108
|
end
|
|
107
109
|
end
|
|
108
110
|
|
|
@@ -118,22 +120,23 @@ module XSD
|
|
|
118
120
|
return nil unless simple_content
|
|
119
121
|
|
|
120
122
|
restriction = simple_content.restriction
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
123
|
+
return base_data_type(simple_content.extension) unless restriction
|
|
124
|
+
return restriction.simple_type&.data_type unless restriction.base
|
|
125
|
+
|
|
126
|
+
base_data_type(restriction)
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
private
|
|
130
|
+
|
|
131
|
+
# Get data type of an extension/restriction base type
|
|
132
|
+
# @param [Extension, Restriction, nil] derivation
|
|
133
|
+
# @return String, nil
|
|
134
|
+
def base_data_type(derivation)
|
|
135
|
+
return nil if derivation.nil?
|
|
136
|
+
|
|
137
|
+
derivation.base_simple_type&.data_type ||
|
|
138
|
+
derivation.base_complex_type&.data_type ||
|
|
139
|
+
strip_prefix(derivation.base)
|
|
137
140
|
end
|
|
138
141
|
end
|
|
139
142
|
end
|
data/lib/xsd/objects/element.rb
CHANGED
|
@@ -121,7 +121,7 @@ module XSD
|
|
|
121
121
|
# Determine if element has attributes
|
|
122
122
|
# @return Boolean
|
|
123
123
|
def attributes?
|
|
124
|
-
complex_type && collect_attributes.any?
|
|
124
|
+
!!complex_type && collect_attributes.any?
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
# Determine if element has mixed content
|
|
@@ -133,17 +133,16 @@ module XSD
|
|
|
133
133
|
# Get substitutable elements
|
|
134
134
|
# @return Array<Element>
|
|
135
135
|
def substitutable_elements
|
|
136
|
-
@substitutable_elements ||=
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
end
|
|
136
|
+
@substitutable_elements ||= if ref
|
|
137
|
+
reference.substitutable_elements
|
|
138
|
+
elsif !global? || %w[#all substitution].include?(block)
|
|
139
|
+
[]
|
|
140
|
+
else
|
|
141
|
+
# TODO: we search substituted element only in the same schema with head element,
|
|
142
|
+
# is this enough?
|
|
143
|
+
# TODO: maybe don't collect all elements but search with xpath first?
|
|
144
|
+
schema.collect_elements.select do |element|
|
|
145
|
+
strip_prefix(element.substitution_group) == name
|
|
147
146
|
end
|
|
148
147
|
end
|
|
149
148
|
end
|
data/lib/xsd/objects/import.rb
CHANGED
|
@@ -18,18 +18,20 @@ module XSD
|
|
|
18
18
|
# Get imported schema
|
|
19
19
|
# @return Schema
|
|
20
20
|
def imported_schema
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
# a schema for the namespace may already be known, either added manually or by another import
|
|
22
|
+
known_schema = reader.schemas_for_namespace(namespace).find { |s| !s.equal?(schema) }
|
|
23
|
+
return known_schema if known_schema
|
|
23
24
|
|
|
24
25
|
unless schema_location
|
|
25
|
-
raise ImportError,
|
|
26
|
+
raise ImportError,
|
|
27
|
+
"Schema location not provided for namespace '#{namespace}', use add_schema_xml()/add_schema_node()"
|
|
26
28
|
end
|
|
27
29
|
|
|
28
|
-
|
|
29
|
-
new_schema = reader.add_schema_xml(xml)
|
|
30
|
+
new_schema = reader.load_schema(schema_location, namespace: namespace, base: schema.location)
|
|
30
31
|
|
|
31
32
|
unless namespace == new_schema.target_namespace
|
|
32
|
-
raise ImportError,
|
|
33
|
+
raise ImportError, "Import namespace '#{namespace}' does not match imported schema targetNamespace " \
|
|
34
|
+
"'#{new_schema.target_namespace}'"
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
new_schema
|
data/lib/xsd/objects/include.rb
CHANGED
|
@@ -10,20 +10,20 @@ module XSD
|
|
|
10
10
|
# @return String
|
|
11
11
|
property :schemaLocation, :string
|
|
12
12
|
|
|
13
|
-
# Get
|
|
13
|
+
# Get included schema
|
|
14
14
|
# @return Schema
|
|
15
15
|
def imported_schema
|
|
16
|
-
|
|
17
|
-
return @imported_schema if @imported_schema
|
|
16
|
+
raise ImportError, 'Schema location not provided for include' unless schema_location
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
new_schema = reader.add_schema_xml(xml)
|
|
18
|
+
new_schema = reader.load_schema(schema_location, base: schema.location)
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
# an included schema without a target namespace adopts the namespace of the including schema ("chameleon" include)
|
|
21
|
+
unless new_schema.target_namespace.nil? || schema.target_namespace == new_schema.target_namespace
|
|
22
|
+
raise ImportError, "Schema targetNamespace '#{schema.target_namespace}' does not match included schema " \
|
|
23
|
+
"targetNamespace '#{new_schema.target_namespace}'"
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
new_schema
|
|
27
27
|
end
|
|
28
28
|
end
|
|
29
29
|
end
|
|
@@ -39,22 +39,20 @@ module XSD
|
|
|
39
39
|
# Get restriction facets
|
|
40
40
|
# @return Hash
|
|
41
41
|
def facets
|
|
42
|
-
nodes.
|
|
43
|
-
|
|
44
|
-
key = node.name
|
|
45
|
-
value = node['value']
|
|
42
|
+
@facets ||= nodes.each_with_object({}) do |node, hash|
|
|
43
|
+
next unless FACET_ELEMENTS.include?(node.name)
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
45
|
+
key = node.name
|
|
46
|
+
value = node['value']
|
|
47
|
+
|
|
48
|
+
case key
|
|
49
|
+
when 'enumeration'
|
|
50
|
+
(hash[key] ||= {})[value] = documentation_for(node)
|
|
51
|
+
when 'pattern'
|
|
52
|
+
(hash[key] ||= []).push(value)
|
|
53
|
+
else
|
|
54
|
+
hash[key] = value
|
|
56
55
|
end
|
|
57
|
-
hash
|
|
58
56
|
end
|
|
59
57
|
end
|
|
60
58
|
|
|
@@ -73,7 +71,7 @@ module XSD
|
|
|
73
71
|
def collect_attributes(include_base = true)
|
|
74
72
|
result = super(include_base)
|
|
75
73
|
# Filter restricted attributes to avoid duplicates from restricting and restricted type
|
|
76
|
-
include_base ? result.
|
|
74
|
+
include_base ? result.to_h { |item| [item.name, item] }.values : result
|
|
77
75
|
end
|
|
78
76
|
end
|
|
79
77
|
end
|
data/lib/xsd/objects/schema.rb
CHANGED
|
@@ -120,7 +120,13 @@ module XSD
|
|
|
120
120
|
# Get schema namespace prefix
|
|
121
121
|
# @return String, nil
|
|
122
122
|
def namespace_prefix
|
|
123
|
-
nil_if_empty(@namespace_prefix ||= namespaces.key(XML_SCHEMA)
|
|
123
|
+
nil_if_empty(@namespace_prefix ||= namespaces.key(XML_SCHEMA)&.sub(/^xmlns:?/, '') || '')
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Location the schema was read from, used to resolve relative import/include locations
|
|
127
|
+
# @return String, nil
|
|
128
|
+
def location
|
|
129
|
+
options[:location]
|
|
124
130
|
end
|
|
125
131
|
|
|
126
132
|
# Check if namespace is a target namespace
|
|
@@ -132,26 +138,26 @@ module XSD
|
|
|
132
138
|
|
|
133
139
|
# Override map_children on schema to get objects from all loaded schemas
|
|
134
140
|
# @param [Symbol] name
|
|
141
|
+
# @param [Hash] cache Schema nodes already visited, guards against import/include cycles
|
|
135
142
|
# @return Array<BaseObject>
|
|
136
143
|
def map_children(name, cache = {})
|
|
144
|
+
cache[node] = true
|
|
137
145
|
super(name) + import_map_children(name, cache)
|
|
138
146
|
end
|
|
139
147
|
|
|
140
148
|
# Get children from all loaded schemas
|
|
141
149
|
# @param [Symbol] name
|
|
150
|
+
# @param [Hash] cache
|
|
142
151
|
# @return Array<BaseObject>
|
|
143
152
|
def import_map_children(name, cache)
|
|
144
153
|
return [] if %i[import include].include?(name.to_sym)
|
|
145
154
|
|
|
146
|
-
(includes + imports).
|
|
147
|
-
|
|
148
|
-
if cache.key?(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
import.imported_schema.map_children(name, cache)
|
|
153
|
-
end
|
|
154
|
-
end.compact.flatten
|
|
155
|
+
(includes + imports).flat_map do |import|
|
|
156
|
+
imported = import.imported_schema
|
|
157
|
+
next [] if cache.key?(imported.node)
|
|
158
|
+
|
|
159
|
+
imported.map_children(name, cache)
|
|
160
|
+
end
|
|
155
161
|
end
|
|
156
162
|
|
|
157
163
|
# Get import by namespace or prefix
|
|
@@ -170,7 +176,9 @@ module XSD
|
|
|
170
176
|
# @param [String, Pathname, Nokogiri::XML::Document] xml
|
|
171
177
|
def validate_xml(xml)
|
|
172
178
|
# validate input
|
|
173
|
-
|
|
179
|
+
unless xml.is_a?(Nokogiri::XML::Document) || xml.is_a?(Pathname) || xml.is_a?(String)
|
|
180
|
+
raise ValidationError, "XML must be a String, Pathname or Nokogiri::XML::Document, got #{xml.class}"
|
|
181
|
+
end
|
|
174
182
|
|
|
175
183
|
begin
|
|
176
184
|
document = xml.is_a?(Nokogiri::XML::Document) ? xml : Nokogiri::XML(xml)
|
|
@@ -200,69 +208,61 @@ module XSD
|
|
|
200
208
|
def schema_validator
|
|
201
209
|
return @schema_validator if @schema_validator
|
|
202
210
|
|
|
203
|
-
#
|
|
204
|
-
# imports are explicitly provided - put all files in one tmpdir and update import paths appropriately
|
|
205
|
-
# TODO: save file/path map to display in errors
|
|
211
|
+
# imports/includes are resolved by the reader, so put all schemas in one tmpdir with rewritten locations
|
|
206
212
|
Dir.mktmpdir('XSD', reader.tmp_dir) do |dir|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
# create imported xsd files
|
|
211
|
-
recursive_import_xsd(self, file, Set.new) do |f, data|
|
|
212
|
-
File.write("#{dir}/#{f}", data)
|
|
213
|
+
file = recursive_import_xsd(self, {}) do |name, data|
|
|
214
|
+
File.write(File.join(dir, name), data, encoding: Encoding::UTF_8)
|
|
213
215
|
end
|
|
214
216
|
|
|
215
217
|
# read schema from tmp file descriptor
|
|
216
|
-
|
|
217
|
-
@schema_validator = create_xml_schema(io)
|
|
218
|
+
File.open(File.join(dir, file)) { |io| @schema_validator = create_xml_schema(io) }
|
|
218
219
|
end
|
|
219
|
-
# else
|
|
220
|
-
# @schema_validator = create_xml_schema(schema.node.to_xml)
|
|
221
|
-
# end
|
|
222
220
|
|
|
223
221
|
@schema_validator
|
|
224
222
|
end
|
|
225
223
|
|
|
226
|
-
#
|
|
224
|
+
# Write the schema and all of its imports/includes as a flat set of files with rewritten locations
|
|
227
225
|
# @param [Schema] schema
|
|
228
|
-
# @param [
|
|
229
|
-
# @
|
|
230
|
-
def recursive_import_xsd(schema,
|
|
231
|
-
# handle recursion
|
|
232
|
-
return if processed.
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
node
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
node[attr] = ns unless prefixes.include?(prefix)
|
|
247
|
-
end
|
|
226
|
+
# @param [Hash] processed Maps already written schema nodes to generated file names
|
|
227
|
+
# @return String Name of the file the schema was written to
|
|
228
|
+
def recursive_import_xsd(schema, processed, &block)
|
|
229
|
+
# handle recursion, schemas are identified by node because includes share the target namespace
|
|
230
|
+
return processed[schema.node] if processed.key?(schema.node)
|
|
231
|
+
|
|
232
|
+
file = "#{::SecureRandom.urlsafe_base64}.xsd"
|
|
233
|
+
processed[schema.node] = file
|
|
234
|
+
|
|
235
|
+
node = schema_node_with_namespaces(schema)
|
|
236
|
+
references = schema.imports + schema.includes
|
|
237
|
+
nodes = node.xpath('./xs:import', XML_SCHEMA_NS).to_a + node.xpath('./xs:include', XML_SCHEMA_NS).to_a
|
|
238
|
+
|
|
239
|
+
# point every import/include to the generated file of the referenced schema
|
|
240
|
+
references.zip(nodes).each do |reference, reference_node|
|
|
241
|
+
next if reference_node.nil?
|
|
242
|
+
|
|
243
|
+
reference_node['schemaLocation'] = recursive_import_xsd(reference.imported_schema, processed, &block)
|
|
248
244
|
end
|
|
249
245
|
|
|
250
|
-
|
|
246
|
+
# always serialize as UTF-8 with an explicit declaration, the source document may use another encoding
|
|
247
|
+
block.call(file, %(<?xml version="1.0" encoding="UTF-8"?>\n#{node.to_xml(encoding: 'UTF-8')}))
|
|
251
248
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
location = import.schema_location
|
|
255
|
-
namespace = import.namespace
|
|
249
|
+
file
|
|
250
|
+
end
|
|
256
251
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
252
|
+
# Get a detached copy of the schema node with all inherited namespaces declared on it
|
|
253
|
+
# @param [Schema] schema
|
|
254
|
+
# @return Nokogiri::XML::Node
|
|
255
|
+
def schema_node_with_namespaces(schema)
|
|
256
|
+
node = schema.node.dup
|
|
257
|
+
prefixes = schema.node.namespace_definitions.map(&:prefix)
|
|
258
|
+
|
|
259
|
+
schema.node.namespaces.each do |attr, ns|
|
|
260
|
+
prefix = attr == 'xmlns' ? nil : attr.sub('xmlns:', '')
|
|
261
|
+
# add_namespace_definition does not work on a detached node, declare the namespace as an attribute instead
|
|
262
|
+
node[attr] = ns unless prefixes.include?(prefix)
|
|
263
263
|
end
|
|
264
264
|
|
|
265
|
-
|
|
265
|
+
node
|
|
266
266
|
end
|
|
267
267
|
|
|
268
268
|
# Create Nokogiri XML Schema instance with preconfigured options
|
data/lib/xsd/version.rb
CHANGED
data/lib/xsd/xml.rb
CHANGED
|
@@ -2,13 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
require 'nokogiri'
|
|
4
4
|
require 'net/http'
|
|
5
|
+
require 'pathname'
|
|
6
|
+
require 'logger'
|
|
7
|
+
require 'uri'
|
|
5
8
|
|
|
6
9
|
module XSD
|
|
7
10
|
class XML
|
|
8
11
|
attr_reader :options, :object_cache, :schemas
|
|
9
12
|
|
|
13
|
+
# Matches locations that have to be fetched over the network
|
|
14
|
+
REMOTE_LOCATION = %r{\Ahttps?://}i
|
|
15
|
+
|
|
16
|
+
# Legacy resource resolver, kept for backwards compatibility. Resolves absolute paths and URLs only,
|
|
17
|
+
# relative locations are resolved by the reader itself, see #read_resource
|
|
10
18
|
DEFAULT_RESOURCE_RESOLVER = proc do |location, namespace|
|
|
11
|
-
if location
|
|
19
|
+
if location.match?(REMOTE_LOCATION)
|
|
12
20
|
Net::HTTP.get(URI(location))
|
|
13
21
|
elsif Pathname.new(location).absolute?
|
|
14
22
|
File.read(location)
|
|
@@ -62,31 +70,35 @@ module XSD
|
|
|
62
70
|
}.freeze
|
|
63
71
|
|
|
64
72
|
# Create reader from a file path
|
|
65
|
-
# @param [String] path
|
|
73
|
+
# @param [String, Pathname] path
|
|
66
74
|
# @param [Hash] options
|
|
67
75
|
# @return XML
|
|
68
76
|
def self.open(path, **options)
|
|
69
77
|
reader = new(**options)
|
|
70
|
-
reader.add_schema_xml(File.read(path))
|
|
78
|
+
reader.add_schema_xml(File.read(path.to_s), location: path.to_s)
|
|
71
79
|
reader
|
|
72
80
|
end
|
|
73
81
|
|
|
74
82
|
def initialize(**options)
|
|
75
|
-
@options
|
|
76
|
-
|
|
77
|
-
@
|
|
83
|
+
@options = options
|
|
84
|
+
# keyed by node identity, the parsed documents are treated as immutable
|
|
85
|
+
@object_cache = {}.compare_by_identity
|
|
86
|
+
@schemas = []
|
|
87
|
+
@schemas_by_location = {}
|
|
78
88
|
end
|
|
79
89
|
|
|
80
90
|
def logger
|
|
81
91
|
options[:logger] || default_logger
|
|
82
92
|
end
|
|
83
93
|
|
|
94
|
+
# Resolver used to fetch imported/included schemas
|
|
95
|
+
# @return Proc
|
|
84
96
|
def resource_resolver
|
|
85
97
|
@options[:resource_resolver] || DEFAULT_RESOURCE_RESOLVER
|
|
86
98
|
end
|
|
87
99
|
|
|
88
100
|
def tmp_dir
|
|
89
|
-
|
|
101
|
+
options[:tmp_dir]
|
|
90
102
|
end
|
|
91
103
|
|
|
92
104
|
def read_document(xml)
|
|
@@ -94,24 +106,51 @@ module XSD
|
|
|
94
106
|
end
|
|
95
107
|
|
|
96
108
|
# Add schema xml to reader instance
|
|
109
|
+
# @param [String] xml
|
|
110
|
+
# @param [String, nil] location Location the xml was read from, used to resolve relative imports
|
|
97
111
|
# @return Schema
|
|
98
|
-
def add_schema_xml(xml)
|
|
112
|
+
def add_schema_xml(xml, location: nil)
|
|
99
113
|
doc = read_document(xml)
|
|
100
114
|
raise Error, 'Schema node not found, xml does not seem to be a valid XSD' unless doc.root&.name == 'schema'
|
|
101
115
|
|
|
102
|
-
add_schema_node(doc.root)
|
|
116
|
+
add_schema_node(doc.root, location: location)
|
|
103
117
|
end
|
|
104
118
|
|
|
105
119
|
# Add schema node to reader instance
|
|
120
|
+
# @param [Nokogiri::XML::Node] node
|
|
121
|
+
# @param [String, nil] location Location the node was read from, used to resolve relative imports
|
|
106
122
|
# @return Schema
|
|
107
|
-
def add_schema_node(node)
|
|
123
|
+
def add_schema_node(node, location: nil)
|
|
108
124
|
raise Error, 'Added schema must be of type Nokogiri::XML::Node' unless node.is_a?(Nokogiri::XML::Node)
|
|
109
125
|
|
|
110
|
-
new_schema = Schema.new(options.merge(node: node, reader: self))
|
|
126
|
+
new_schema = Schema.new(options.merge(node: node, reader: self, location: location))
|
|
111
127
|
schemas.push(new_schema)
|
|
128
|
+
# register the schema so that an import/include pointing back at this location reuses it
|
|
129
|
+
@schemas_by_location[location] ||= new_schema if location
|
|
112
130
|
new_schema
|
|
113
131
|
end
|
|
114
132
|
|
|
133
|
+
# Load schema referenced by an import/include, reusing an already loaded one when possible
|
|
134
|
+
# @param [String] location
|
|
135
|
+
# @param [String, nil] namespace
|
|
136
|
+
# @param [String, nil] base Location of the referencing schema
|
|
137
|
+
# @return Schema
|
|
138
|
+
def load_schema(location, namespace: nil, base: nil)
|
|
139
|
+
raise ImportError, 'Schema location must be provided' if location.nil? || location.empty?
|
|
140
|
+
|
|
141
|
+
expanded = expand_location(location, base)
|
|
142
|
+
return @schemas_by_location[expanded] if @schemas_by_location.key?(expanded)
|
|
143
|
+
|
|
144
|
+
# a custom resolver is responsible for resolving relative locations on its own
|
|
145
|
+
xml = if options[:resource_resolver]
|
|
146
|
+
options[:resource_resolver].call(location, namespace)
|
|
147
|
+
else
|
|
148
|
+
read_resource(expanded, namespace)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
@schemas_by_location[expanded] = add_schema_xml(xml, location: expanded)
|
|
152
|
+
end
|
|
153
|
+
|
|
115
154
|
# Get first added (considered primary) schema
|
|
116
155
|
# @return Schema, nil
|
|
117
156
|
def schema
|
|
@@ -160,10 +199,34 @@ module XSD
|
|
|
160
199
|
|
|
161
200
|
private
|
|
162
201
|
|
|
202
|
+
# Read schema contents from an absolute path or URL
|
|
203
|
+
# @param [String] location
|
|
204
|
+
# @param [String, nil] namespace
|
|
205
|
+
# @return String
|
|
206
|
+
def read_resource(location, namespace = nil)
|
|
207
|
+
if location.match?(REMOTE_LOCATION)
|
|
208
|
+
Net::HTTP.get(URI(location))
|
|
209
|
+
elsif File.file?(location)
|
|
210
|
+
File.read(location)
|
|
211
|
+
else
|
|
212
|
+
raise ImportError, "Failed to locate import '#{location}'#{namespace ? " for namespace '#{namespace}'" : ''}"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Resolve a possibly relative location against the location of the referencing schema
|
|
217
|
+
# @param [String] location
|
|
218
|
+
# @param [String, nil] base
|
|
219
|
+
# @return String
|
|
220
|
+
def expand_location(location, base)
|
|
221
|
+
return location if location.match?(REMOTE_LOCATION) || Pathname.new(location).absolute?
|
|
222
|
+
return location if base.nil?
|
|
223
|
+
return URI.join(base, location).to_s if base.match?(REMOTE_LOCATION)
|
|
224
|
+
|
|
225
|
+
Pathname.new(base).dirname.join(location).cleanpath.to_s
|
|
226
|
+
end
|
|
227
|
+
|
|
163
228
|
def collect(name, *args)
|
|
164
|
-
|
|
165
|
-
schemas.each { |schema| result.merge(schema.send(name, *args)) }
|
|
166
|
-
result.to_a
|
|
229
|
+
schemas.flat_map { |schema| schema.send(name, *args) }.uniq
|
|
167
230
|
end
|
|
168
231
|
|
|
169
232
|
def default_logger
|
data/lib/xsd.rb
CHANGED
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.8.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:
|
|
11
|
+
date: 2026-09-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: builder
|
|
@@ -55,7 +55,7 @@ dependencies:
|
|
|
55
55
|
description: The Ruby XSD library is an XML Schema implementation for Ruby.Provides
|
|
56
56
|
easy and flexible access to XSD information
|
|
57
57
|
email:
|
|
58
|
-
- d.arkhipov@
|
|
58
|
+
- d.arkhipov@ekzo.dev
|
|
59
59
|
executables: []
|
|
60
60
|
extensions: []
|
|
61
61
|
extra_rdoc_files: []
|
|
@@ -111,14 +111,13 @@ files:
|
|
|
111
111
|
- lib/xsd/shared/simple_typed.rb
|
|
112
112
|
- lib/xsd/version.rb
|
|
113
113
|
- lib/xsd/xml.rb
|
|
114
|
-
- xsd.gemspec
|
|
115
114
|
homepage: https://github.com/ekzo-dev/ruby-xsd
|
|
116
115
|
licenses:
|
|
117
116
|
- MIT
|
|
118
117
|
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
|
-
changelog_uri: https://github.com/ekzo-dev/ruby-xsd/CHANGELOG.md
|
|
120
|
+
changelog_uri: https://github.com/ekzo-dev/ruby-xsd/blob/main/CHANGELOG.md
|
|
122
121
|
post_install_message:
|
|
123
122
|
rdoc_options: []
|
|
124
123
|
require_paths:
|
data/xsd.gemspec
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
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@netcitylife.ru']
|
|
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/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
|