lutaml-xsd 1.1.0 → 1.1.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/lib/lutaml/xsd/spa/schema_serializer.rb +190 -110
- data/lib/lutaml/xsd/version.rb +1 -1
- data/lutaml-xsd.gemspec +1 -1
- metadata +12 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 29e9ba5ed3edf116b76cdf22ed853a37399d00ffda6b11f78a0cccdb5502b4e4
|
|
4
|
+
data.tar.gz: 6213f84485cad0ffed1eef19d60ae29fea7d5e274522ba8dfcc996048694e677
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 07a2c5d774d76218e043372749950daa031c3b1ff3177a4eb65c5a05de6e6747479bbbd4acd7a5c7a808553bb75e62808d66bb68157324b4341d25045e11aa0b
|
|
7
|
+
data.tar.gz: cba0111db7b0b7bf12fb9e0c55edbdfcbc4e3f4f11a653957ad663e6d8c067a1980bcbb7a54898f50b6e408869ca56d0179a822a7fcae8ba201111c68fceeb15
|
|
@@ -35,6 +35,12 @@ module Lutaml
|
|
|
35
35
|
class SchemaSerializer
|
|
36
36
|
include ::Lutaml::Xsd::Spa::Utils::ExtractEnumeration
|
|
37
37
|
|
|
38
|
+
# Fields to merge when combining schemas with the same targetNamespace
|
|
39
|
+
MERGEABLE_CONTENT_FIELDS = %i[
|
|
40
|
+
elements complex_types simple_types
|
|
41
|
+
attributes groups attribute_groups
|
|
42
|
+
].freeze
|
|
43
|
+
|
|
38
44
|
attr_reader :repository, :config, :package
|
|
39
45
|
|
|
40
46
|
# Initialize schema serializer
|
|
@@ -217,12 +223,104 @@ module Lutaml
|
|
|
217
223
|
serialize_schema(schema, index, file_path)
|
|
218
224
|
end.compact
|
|
219
225
|
|
|
226
|
+
# Merge schemas that share the same target namespace (from <include>)
|
|
227
|
+
# XSD <include> merges included schemas into the same namespace.
|
|
228
|
+
# Group by namespace, keep entry point as primary, merge content from others.
|
|
229
|
+
schemas_data = merge_included_schemas(schemas_data)
|
|
230
|
+
|
|
220
231
|
# Post-process: add used_by reverse references
|
|
221
232
|
attach_used_by_references(schemas_data)
|
|
222
233
|
|
|
223
234
|
schemas_data
|
|
224
235
|
end
|
|
225
236
|
|
|
237
|
+
# Merge schemas sharing the same targetNamespace (from <include> directives)
|
|
238
|
+
#
|
|
239
|
+
# In XSD, <include> means the included schema targets the same namespace.
|
|
240
|
+
# These should appear as a single merged schema in the SPA, not as
|
|
241
|
+
# separate empty + populated entries.
|
|
242
|
+
#
|
|
243
|
+
# Schemas with nil namespace (chameleon schemas) are NOT merged since
|
|
244
|
+
# they adopt the namespace of their including schema at parse time.
|
|
245
|
+
#
|
|
246
|
+
# @param schemas_data [Array<Hash>] Serialized schema data
|
|
247
|
+
# @return [Array<Hash>] Merged schema data
|
|
248
|
+
def merge_included_schemas(schemas_data)
|
|
249
|
+
return schemas_data if schemas_data.length <= 1
|
|
250
|
+
|
|
251
|
+
ns_groups = {}
|
|
252
|
+
schemas_data.each do |schema|
|
|
253
|
+
ns = schema[:namespace]
|
|
254
|
+
# Skip nil-namespace schemas — chameleon schemas should not be merged
|
|
255
|
+
next unless ns
|
|
256
|
+
|
|
257
|
+
ns_groups[ns] ||= []
|
|
258
|
+
ns_groups[ns] << schema
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# Collect schemas that were NOT grouped (nil namespace)
|
|
262
|
+
merged_schemas = schemas_data.reject { |s| s[:namespace] }
|
|
263
|
+
|
|
264
|
+
ns_groups.each_value do |group|
|
|
265
|
+
if group.length == 1
|
|
266
|
+
merged_schemas << group.first
|
|
267
|
+
next
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
primary = group.find { |s| s[:is_entrypoint] }
|
|
271
|
+
primary ||= group.max_by { |s| content_weight(s) }
|
|
272
|
+
secondaries = group.reject { |s| s[:id] == primary[:id] }
|
|
273
|
+
|
|
274
|
+
merge_content_into!(primary, secondaries)
|
|
275
|
+
merged_schemas << primary
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
merged_schemas
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Measure content richness of a serialized schema for primary selection
|
|
282
|
+
#
|
|
283
|
+
# @param schema [Hash] Serialized schema data
|
|
284
|
+
# @return [Integer] Total item count across content fields
|
|
285
|
+
def content_weight(schema)
|
|
286
|
+
MERGEABLE_CONTENT_FIELDS.sum { |f| (schema[f] || []).length }
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
# Merge content arrays from secondary schemas into the primary schema
|
|
290
|
+
#
|
|
291
|
+
# Uses Set for O(1) deduplication by hash identity.
|
|
292
|
+
#
|
|
293
|
+
# @param primary [Hash] Primary schema to merge into (mutated)
|
|
294
|
+
# @param secondaries [Array<Hash>] Secondary schemas to absorb
|
|
295
|
+
# @return [void]
|
|
296
|
+
def merge_content_into!(primary, secondaries)
|
|
297
|
+
MERGEABLE_CONTENT_FIELDS.each do |field|
|
|
298
|
+
primary[field] ||= []
|
|
299
|
+
seen = primary[field].to_set
|
|
300
|
+
secondaries.each do |sec|
|
|
301
|
+
(sec[field] || []).each do |item|
|
|
302
|
+
primary[field] << item unless seen.include?(item)
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
end
|
|
306
|
+
|
|
307
|
+
# Merge includes and imports (deduplicated by hash equality)
|
|
308
|
+
%i[includes imports].each do |field|
|
|
309
|
+
primary[field] ||= []
|
|
310
|
+
seen = primary[field].to_set
|
|
311
|
+
secondaries.each do |sec|
|
|
312
|
+
(sec[field] || []).each do |item|
|
|
313
|
+
primary[field] << item unless seen.include?(item)
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# Collect all file paths
|
|
319
|
+
all_paths = [primary[:file_path]]
|
|
320
|
+
secondaries.each { |s| all_paths << s[:file_path] if s[:file_path] }
|
|
321
|
+
primary[:file_paths] = all_paths.compact
|
|
322
|
+
end
|
|
323
|
+
|
|
226
324
|
# Serialize single schema (template method hook)
|
|
227
325
|
#
|
|
228
326
|
# Subclasses should override to customize schema serialization
|
|
@@ -746,24 +844,29 @@ schema_source = nil)
|
|
|
746
844
|
end.sort_by { |ag| ag[:name] || "" }
|
|
747
845
|
end
|
|
748
846
|
|
|
749
|
-
# Extract source
|
|
750
|
-
#
|
|
847
|
+
# Extract source XML for a schema component identified by type, key, value
|
|
848
|
+
#
|
|
849
|
+
# @param type [String] XSD element type name (e.g., "attributeGroup")
|
|
850
|
+
# @param key [String] Attribute name to match on (e.g., "name")
|
|
851
|
+
# @param value [String] Attribute value to match
|
|
852
|
+
# @param prefix [String, nil] Optional namespace prefix
|
|
853
|
+
# @param source [String, nil] Raw XSD source XML
|
|
854
|
+
# @return [String, nil] Extracted source XML or nil
|
|
751
855
|
def extract_source_by_type_key_value(type, key, value, prefix = nil,
|
|
752
856
|
source = nil)
|
|
753
857
|
return nil unless source && value
|
|
754
858
|
|
|
755
|
-
# parse the schema source and find the attribute group by name
|
|
756
859
|
begin
|
|
757
860
|
doc = Moxml::Context.new.parse(source)
|
|
861
|
+
escaped_value = value.gsub("'", "''")
|
|
758
862
|
xpath = if prefix
|
|
759
|
-
"//#{prefix}:#{type}[@#{key}='#{
|
|
863
|
+
"//#{prefix}:#{type}[@#{key}='#{escaped_value}']"
|
|
760
864
|
else
|
|
761
|
-
"//#{type}[@#{key}='#{
|
|
865
|
+
"//#{type}[@#{key}='#{escaped_value}']"
|
|
762
866
|
end
|
|
763
|
-
|
|
764
|
-
|
|
867
|
+
node = doc.at_xpath(xpath)
|
|
868
|
+
node&.to_xml(indent: 2)
|
|
765
869
|
rescue StandardError
|
|
766
|
-
# If parsing fails, return nil
|
|
767
870
|
nil
|
|
768
871
|
end
|
|
769
872
|
end
|
|
@@ -889,45 +992,52 @@ source = nil)
|
|
|
889
992
|
end
|
|
890
993
|
end
|
|
891
994
|
|
|
995
|
+
# Collect attribute group references from a model (handles extension nesting)
|
|
996
|
+
#
|
|
997
|
+
# Used for both direct attribute groups and those inside content model extensions.
|
|
998
|
+
#
|
|
999
|
+
# @param model [Object] Any object that may have attribute_group or extension
|
|
1000
|
+
# @return [Array<Object>] Collected attribute group reference objects
|
|
1001
|
+
def collect_attribute_group_refs(model)
|
|
1002
|
+
refs = []
|
|
1003
|
+
|
|
1004
|
+
if model.respond_to?(:attribute_group) && model.attribute_group
|
|
1005
|
+
groups = model.attribute_group.is_a?(Array) ? model.attribute_group : [model.attribute_group]
|
|
1006
|
+
refs.concat(groups)
|
|
1007
|
+
end
|
|
1008
|
+
|
|
1009
|
+
if model.respond_to?(:extension) && model.extension
|
|
1010
|
+
refs.concat(collect_attribute_group_refs(model.extension))
|
|
1011
|
+
end
|
|
1012
|
+
|
|
1013
|
+
refs
|
|
1014
|
+
end
|
|
1015
|
+
|
|
892
1016
|
# Serialize attribute group references from a complex type
|
|
893
1017
|
#
|
|
1018
|
+
# Collects attribute group refs from three possible locations:
|
|
1019
|
+
# 1. Direct attribute groups on the type
|
|
1020
|
+
# 2. Inside simpleContent.extension
|
|
1021
|
+
# 3. Inside complexContent.extension
|
|
1022
|
+
#
|
|
894
1023
|
# @param type [ComplexType] Complex type
|
|
895
1024
|
# @return [Array<Hash>] Serialized attribute group references with attributes
|
|
896
1025
|
def serialize_type_attr_groups(type)
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
# 2. Inside simpleContent.extension
|
|
900
|
-
# 3. Inside complexContent.extension
|
|
1026
|
+
return [] unless type
|
|
1027
|
+
|
|
901
1028
|
all_ag_refs = []
|
|
902
1029
|
|
|
903
|
-
# 1. Direct attribute groups
|
|
904
1030
|
if type.respond_to?(:attribute_group) && type.attribute_group
|
|
905
1031
|
direct_groups = type.attribute_group.is_a?(Array) ? type.attribute_group : [type.attribute_group]
|
|
906
1032
|
all_ag_refs.concat(direct_groups)
|
|
907
1033
|
end
|
|
908
1034
|
|
|
909
|
-
# 2. Attribute groups inside simpleContent.extension
|
|
910
1035
|
if type.respond_to?(:simple_content) && type.simple_content
|
|
911
|
-
|
|
912
|
-
if sc.respond_to?(:extension) && sc.extension
|
|
913
|
-
extension = sc.extension
|
|
914
|
-
if extension.respond_to?(:attribute_group) && extension.attribute_group
|
|
915
|
-
ext_groups = extension.attribute_group.is_a?(Array) ? extension.attribute_group : [extension.attribute_group]
|
|
916
|
-
all_ag_refs.concat(ext_groups)
|
|
917
|
-
end
|
|
918
|
-
end
|
|
1036
|
+
all_ag_refs.concat(collect_attribute_group_refs(type.simple_content))
|
|
919
1037
|
end
|
|
920
1038
|
|
|
921
|
-
# 3. Attribute groups inside complexContent.extension
|
|
922
1039
|
if type.respond_to?(:complex_content) && type.complex_content
|
|
923
|
-
|
|
924
|
-
if cc.respond_to?(:extension) && cc.extension
|
|
925
|
-
extension = cc.extension
|
|
926
|
-
if extension.respond_to?(:attribute_group) && extension.attribute_group
|
|
927
|
-
ext_groups = extension.attribute_group.is_a?(Array) ? extension.attribute_group : [extension.attribute_group]
|
|
928
|
-
all_ag_refs.concat(ext_groups)
|
|
929
|
-
end
|
|
930
|
-
end
|
|
1040
|
+
all_ag_refs.concat(collect_attribute_group_refs(type.complex_content))
|
|
931
1041
|
end
|
|
932
1042
|
|
|
933
1043
|
return [] if all_ag_refs.empty?
|
|
@@ -936,12 +1046,8 @@ source = nil)
|
|
|
936
1046
|
ag_name = ag.respond_to?(:ref) ? ag.ref : ag.name
|
|
937
1047
|
next unless ag_name
|
|
938
1048
|
|
|
939
|
-
# Look up the actual attribute group definition to get its attributes
|
|
940
1049
|
attrs = lookup_attribute_group_attributes(ag_name)
|
|
941
|
-
{
|
|
942
|
-
ref: ag_name,
|
|
943
|
-
attributes: attrs,
|
|
944
|
-
}
|
|
1050
|
+
{ ref: ag_name, attributes: attrs }
|
|
945
1051
|
end
|
|
946
1052
|
end
|
|
947
1053
|
|
|
@@ -1180,74 +1286,43 @@ source = nil)
|
|
|
1180
1286
|
}
|
|
1181
1287
|
end
|
|
1182
1288
|
|
|
1289
|
+
# Facet serializers: maps facet name to how to extract and format it
|
|
1290
|
+
#
|
|
1291
|
+
# Each entry: [method_name, facet_type_label]
|
|
1292
|
+
# method_name — what restriction.respond_to?(:method_name) && restriction.method_name to call
|
|
1293
|
+
# facet_type — the type string emitted in serialized facet
|
|
1294
|
+
FACET_METHODS = [
|
|
1295
|
+
[:enumerations, "enumeration"],
|
|
1296
|
+
[:pattern, "pattern"],
|
|
1297
|
+
[:min_length, "min_length"],
|
|
1298
|
+
[:max_length, "max_length"],
|
|
1299
|
+
[:length, "length"],
|
|
1300
|
+
[:min_inclusive, "min_inclusive"],
|
|
1301
|
+
[:max_inclusive, "max_inclusive"],
|
|
1302
|
+
[:min_exclusive, "min_exclusive"],
|
|
1303
|
+
[:max_exclusive, "max_exclusive"],
|
|
1304
|
+
[:total_digits, "total_digits"],
|
|
1305
|
+
[:fraction_digits, "fraction_digits"],
|
|
1306
|
+
[:white_space, "white_space"],
|
|
1307
|
+
].freeze
|
|
1308
|
+
|
|
1183
1309
|
# Serialize facets
|
|
1184
1310
|
#
|
|
1185
1311
|
# @param restriction [Restriction] Restriction object
|
|
1186
1312
|
# @return [Array<Hash>] Serialized facets
|
|
1187
1313
|
def serialize_facets(restriction)
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
if restriction.respond_to?(:enumerations) && restriction.enumerations
|
|
1191
|
-
facets << { type: "enumeration",
|
|
1192
|
-
values: restriction.enumerations }
|
|
1193
|
-
end
|
|
1194
|
-
|
|
1195
|
-
if restriction.respond_to?(:pattern) && restriction.pattern
|
|
1196
|
-
facets << { type: "pattern",
|
|
1197
|
-
value: restriction.pattern }
|
|
1198
|
-
end
|
|
1199
|
-
|
|
1200
|
-
if restriction.respond_to?(:min_length) && restriction.min_length
|
|
1201
|
-
facets << { type: "min_length",
|
|
1202
|
-
value: restriction.min_length }
|
|
1203
|
-
end
|
|
1204
|
-
|
|
1205
|
-
if restriction.respond_to?(:max_length) && restriction.max_length
|
|
1206
|
-
facets << { type: "max_length",
|
|
1207
|
-
value: restriction.max_length }
|
|
1208
|
-
end
|
|
1209
|
-
|
|
1210
|
-
if restriction.respond_to?(:length) && restriction.length
|
|
1211
|
-
facets << { type: "length",
|
|
1212
|
-
value: restriction.length }
|
|
1213
|
-
end
|
|
1314
|
+
return [] unless restriction
|
|
1214
1315
|
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
end
|
|
1219
|
-
|
|
1220
|
-
if restriction.respond_to?(:max_inclusive) && restriction.max_inclusive
|
|
1221
|
-
facets << { type: "max_inclusive",
|
|
1222
|
-
value: restriction.max_inclusive }
|
|
1223
|
-
end
|
|
1316
|
+
FACET_METHODS.filter_map do |method_name, facet_type|
|
|
1317
|
+
value = restriction.respond_to?(method_name) && restriction.send(method_name)
|
|
1318
|
+
next unless value
|
|
1224
1319
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
if restriction.respond_to?(:max_exclusive) && restriction.max_exclusive
|
|
1231
|
-
facets << { type: "max_exclusive",
|
|
1232
|
-
value: restriction.max_exclusive }
|
|
1233
|
-
end
|
|
1234
|
-
|
|
1235
|
-
if restriction.respond_to?(:total_digits) && restriction.total_digits
|
|
1236
|
-
facets << { type: "total_digits",
|
|
1237
|
-
value: restriction.total_digits }
|
|
1238
|
-
end
|
|
1239
|
-
|
|
1240
|
-
if restriction.respond_to?(:fraction_digits) && restriction.fraction_digits
|
|
1241
|
-
facets << { type: "fraction_digits",
|
|
1242
|
-
value: restriction.fraction_digits }
|
|
1243
|
-
end
|
|
1244
|
-
|
|
1245
|
-
if restriction.respond_to?(:white_space) && restriction.white_space
|
|
1246
|
-
facets << { type: "white_space",
|
|
1247
|
-
value: restriction.white_space }
|
|
1320
|
+
if method_name == :enumerations
|
|
1321
|
+
{ type: facet_type, values: value }
|
|
1322
|
+
else
|
|
1323
|
+
{ type: facet_type, value: value }
|
|
1324
|
+
end
|
|
1248
1325
|
end
|
|
1249
|
-
|
|
1250
|
-
facets
|
|
1251
1326
|
end
|
|
1252
1327
|
|
|
1253
1328
|
# Extract documentation from object
|
|
@@ -1279,29 +1354,34 @@ source = nil)
|
|
|
1279
1354
|
"empty"
|
|
1280
1355
|
end
|
|
1281
1356
|
|
|
1357
|
+
# Extract base type from a content model's extension or restriction
|
|
1358
|
+
#
|
|
1359
|
+
# @param content_model [Object] simpleContent or complexContent
|
|
1360
|
+
# @return [String, nil] Base type name
|
|
1361
|
+
def base_from_content_model(content_model)
|
|
1362
|
+
if content_model.respond_to?(:extension) && content_model.extension
|
|
1363
|
+
ext = content_model.extension
|
|
1364
|
+
return ext.base if ext.respond_to?(:base)
|
|
1365
|
+
elsif content_model.respond_to?(:restriction) && content_model.restriction
|
|
1366
|
+
rst = content_model.restriction
|
|
1367
|
+
return rst.base if rst.respond_to?(:base)
|
|
1368
|
+
end
|
|
1369
|
+
nil
|
|
1370
|
+
end
|
|
1371
|
+
|
|
1282
1372
|
# Extract base type from complex type
|
|
1283
1373
|
#
|
|
1284
1374
|
# @param type [ComplexType] Complex type
|
|
1285
1375
|
# @return [String, nil] Base type name
|
|
1286
1376
|
def extract_base_type(type)
|
|
1287
|
-
# Check complex_content for extension or restriction
|
|
1288
1377
|
if type.respond_to?(:complex_content) && type.complex_content
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
return cc.extension.base if cc.extension.respond_to?(:base)
|
|
1292
|
-
elsif cc.respond_to?(:restriction) && cc.restriction
|
|
1293
|
-
return cc.restriction.base if cc.restriction.respond_to?(:base)
|
|
1294
|
-
end
|
|
1378
|
+
base = base_from_content_model(type.complex_content)
|
|
1379
|
+
return base if base
|
|
1295
1380
|
end
|
|
1296
1381
|
|
|
1297
|
-
# Check simple_content for extension or restriction
|
|
1298
1382
|
if type.respond_to?(:simple_content) && type.simple_content
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
return sc.extension.base if sc.extension.respond_to?(:base)
|
|
1302
|
-
elsif sc.respond_to?(:restriction) && sc.restriction
|
|
1303
|
-
return sc.restriction.base if sc.restriction.respond_to?(:base)
|
|
1304
|
-
end
|
|
1383
|
+
base = base_from_content_model(type.simple_content)
|
|
1384
|
+
return base if base
|
|
1305
1385
|
end
|
|
1306
1386
|
|
|
1307
1387
|
nil
|
data/lib/lutaml/xsd/version.rb
CHANGED
data/lutaml-xsd.gemspec
CHANGED
|
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|
|
31
31
|
end + Dir.glob("frontend/dist/*")
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
spec.add_dependency "liquid", "
|
|
34
|
+
spec.add_dependency "liquid", ">= 4.0", "< 6.0"
|
|
35
35
|
spec.add_dependency "lutaml-model", "~> 0.8.0"
|
|
36
36
|
spec.add_dependency "moxml"
|
|
37
37
|
spec.add_dependency "paint", "~> 2.3"
|
metadata
CHANGED
|
@@ -1,29 +1,35 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lutaml-xsd
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.
|
|
4
|
+
version: 1.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose Inc.
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-06 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: liquid
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '4.0'
|
|
20
|
+
- - "<"
|
|
18
21
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '
|
|
22
|
+
version: '6.0'
|
|
20
23
|
type: :runtime
|
|
21
24
|
prerelease: false
|
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
26
|
requirements:
|
|
24
|
-
- - "
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '4.0'
|
|
30
|
+
- - "<"
|
|
25
31
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '
|
|
32
|
+
version: '6.0'
|
|
27
33
|
- !ruby/object:Gem::Dependency
|
|
28
34
|
name: lutaml-model
|
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|