lutaml-model 0.5.2 → 0.5.4
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/.github/workflows/dependent-tests.yml +2 -0
- data/.rubocop_todo.yml +39 -13
- data/Gemfile +1 -0
- data/README.adoc +430 -52
- data/lib/lutaml/model/constants.rb +7 -0
- data/lib/lutaml/model/error/type/invalid_value_error.rb +19 -0
- data/lib/lutaml/model/error.rb +1 -0
- data/lib/lutaml/model/key_value_mapping.rb +31 -2
- data/lib/lutaml/model/mapping_hash.rb +8 -0
- data/lib/lutaml/model/mapping_rule.rb +8 -0
- data/lib/lutaml/model/schema/templates/simple_type.rb +247 -0
- data/lib/lutaml/model/schema/xml_compiler.rb +720 -0
- data/lib/lutaml/model/schema.rb +5 -0
- data/lib/lutaml/model/serialize.rb +33 -13
- data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +1 -2
- data/lib/lutaml/model/type/hash.rb +11 -11
- data/lib/lutaml/model/utils.rb +7 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +5 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -15
- data/lib/lutaml/model/xml_mapping.rb +4 -2
- data/lib/lutaml/model/xml_mapping_rule.rb +1 -4
- data/lib/lutaml/model.rb +1 -0
- data/spec/fixtures/xml/invalid_math_document.xml +4 -0
- data/spec/fixtures/xml/math_document_schema.xsd +56 -0
- data/spec/fixtures/xml/test_schema.xsd +53 -0
- data/spec/fixtures/xml/valid_math_document.xml +4 -0
- data/spec/lutaml/model/cdata_spec.rb +2 -2
- data/spec/lutaml/model/custom_model_spec.rb +7 -20
- data/spec/lutaml/model/key_value_mapping_spec.rb +27 -0
- data/spec/lutaml/model/map_all_spec.rb +188 -0
- data/spec/lutaml/model/mixed_content_spec.rb +15 -15
- data/spec/lutaml/model/render_nil_spec.rb +29 -0
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +1431 -0
- data/spec/lutaml/model/with_child_mapping_spec.rb +2 -2
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +52 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +108 -1
- metadata +12 -2
@@ -0,0 +1,720 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "erb"
|
4
|
+
require "lutaml/xsd"
|
5
|
+
require_relative "templates/simple_type"
|
6
|
+
|
7
|
+
module Lutaml
|
8
|
+
module Model
|
9
|
+
module Schema
|
10
|
+
module XmlCompiler
|
11
|
+
extend self
|
12
|
+
|
13
|
+
DEFAULT_CLASSES = %w[string integer int boolean].freeze
|
14
|
+
ELEMENT_ORDER_IGNORABLE = %w[import include].freeze
|
15
|
+
|
16
|
+
MODEL_TEMPLATE = ERB.new(<<~TEMPLATE, trim_mode: "-")
|
17
|
+
# frozen_string_literal: true
|
18
|
+
require "lutaml/model"
|
19
|
+
<%=
|
20
|
+
requiring_files = resolve_required_files(content)
|
21
|
+
if requiring_files.any?
|
22
|
+
requiring_files.map { |file| "require_relative \\\"\#{file}\\\"" }.join("\n") + "\n"
|
23
|
+
end
|
24
|
+
-%>
|
25
|
+
|
26
|
+
class <%= Utils.camel_case(name) %> < <%= resolve_parent_class(content) %>
|
27
|
+
<%=
|
28
|
+
if content&.key_exist?(:attributes)
|
29
|
+
output = content.attributes.map do |attribute|
|
30
|
+
attribute = @attributes[attribute.ref_class.split(":").last] if attribute.key?(:ref_class)
|
31
|
+
" attribute :\#{Utils.snake_case(attribute.name)}, \#{resolve_attribute_class(attribute)}\#{resolve_attribute_default(attribute) if attribute.key_exist?(:default)}"
|
32
|
+
end.join("\n")
|
33
|
+
output + "\n" if output && !output&.empty?
|
34
|
+
end
|
35
|
+
-%>
|
36
|
+
<%=
|
37
|
+
if content&.key_exist?(:sequence) || content&.key_exist?(:choice) || content&.key_exist?(:group)
|
38
|
+
output = resolve_content(content).map do |element_name, element|
|
39
|
+
element = @elements[element.ref_class.split(":")&.last] if element&.key_exist?(:ref_class)
|
40
|
+
" attribute :\#{Utils.snake_case(element_name)}, \#{Utils.camel_case(element.type_name.split(":").last)}\#{resolve_occurs(element.arguments) if element.key_exist?(:arguments)}"
|
41
|
+
end.join("\n")
|
42
|
+
output + "\n" if output && !output&.empty?
|
43
|
+
end
|
44
|
+
-%>
|
45
|
+
<%=
|
46
|
+
if content&.key_exist?(:complex_content)
|
47
|
+
resolve_complex_content(content.complex_content).map do |element_name, element|
|
48
|
+
if element_name == :attributes
|
49
|
+
element.map { |attribute| " attribute :\#{Utils.snake_case(attribute.name)}, \#{resolve_attribute_class(attribute)}\#{resolve_attribute_default(attribute.default) if attribute.key_exist?(:default)}" }.join("\n")
|
50
|
+
else
|
51
|
+
element = @elements[element.ref_class.split(":")&.last] if element&.key_exist?(:ref_class)
|
52
|
+
" attribute :\#{Utils.snake_case(element_name)}, \#{Utils.camel_case(element.type_name.split(":").last)}\#{resolve_occurs(element.arguments) if element.key_exist?(:arguments)}"
|
53
|
+
end
|
54
|
+
end.join("\n")
|
55
|
+
output + "\n" if output && !output&.empty?
|
56
|
+
end
|
57
|
+
-%>
|
58
|
+
<%= " attribute :content, \#{content.simple_content.extension_base}" if content_exist = content.key_exist?(:simple_content) && content.simple_content.key_exist?(:extension_base) -%>
|
59
|
+
|
60
|
+
xml do
|
61
|
+
root "<%= name %>", mixed: true
|
62
|
+
<%= resolve_namespace(options) %>
|
63
|
+
<%= " map_content to: :content\n" if content_exist -%>
|
64
|
+
<%=
|
65
|
+
if content&.key_exist?(:attributes)
|
66
|
+
output = content.attributes.map do |attribute|
|
67
|
+
attribute = @attributes[attribute.ref_class.split(":").last] if attribute.key?(:ref_class)
|
68
|
+
" map_attribute :\#{Utils.snake_case(attribute.name)}, to: :\#{Utils.snake_case(attribute.name)}"
|
69
|
+
end.join("\n")
|
70
|
+
output + "\n" if output && !output&.empty?
|
71
|
+
end
|
72
|
+
-%>
|
73
|
+
<%=
|
74
|
+
if content&.key_exist?(:sequence) || content&.key_exist?(:choice) || content&.key_exist?(:group)
|
75
|
+
output = resolve_content(content).map do |element_name, element|
|
76
|
+
element = @elements[element.ref_class.split(":")&.last] if element&.key_exist?(:ref_class)
|
77
|
+
" map_element :\#{element_name}, to: :\#{Utils.snake_case(element_name)}"
|
78
|
+
end.join("\n")
|
79
|
+
output + "\n" if output && !output&.empty?
|
80
|
+
end
|
81
|
+
-%>
|
82
|
+
<%=
|
83
|
+
if content&.key_exist?(:complex_content)
|
84
|
+
output = resolve_complex_content(content.complex_content).map do |element_name, element|
|
85
|
+
if element_name == :attributes
|
86
|
+
element.map { |attribute| " map_attribute :\#{Utils.snake_case(attribute.name)}, to: :\#{Utils.snake_case(attribute.name)}" }.join("\n")
|
87
|
+
else
|
88
|
+
element = @elements[element.ref_class.split(":")&.last] if element&.key_exist?(:ref_class)
|
89
|
+
" map_element :\#{element_name}, to: :\#{Utils.snake_case(element_name)}"
|
90
|
+
end
|
91
|
+
end.join("\n")
|
92
|
+
output + "\n" if output && !output&.empty?
|
93
|
+
end
|
94
|
+
-%>
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
TEMPLATE
|
99
|
+
|
100
|
+
XML_ADAPTER_NOT_SET_MESSAGE = <<~MSG
|
101
|
+
Nokogiri is not set as XML Adapter.
|
102
|
+
Make sure Nokogiri is installed and set as XML Adapter eg.
|
103
|
+
execute: gem install nokogiri
|
104
|
+
require 'lutaml/model/adapter/nokogiri'
|
105
|
+
Lutaml::Model.xml_adapter = Lutaml::Model::Adapter::Nokogiri
|
106
|
+
MSG
|
107
|
+
|
108
|
+
def to_models(schema, options = {})
|
109
|
+
as_models(schema, options: options)
|
110
|
+
dir = options.fetch(:output_dir, "lutaml_models_#{Time.now.to_i}")
|
111
|
+
FileUtils.mkdir_p(dir)
|
112
|
+
|
113
|
+
@data_types_classes = Templates::SimpleType.create_simple_types(@simple_types)
|
114
|
+
@data_types_classes.each { |name, content| create_file(name, content, dir) }
|
115
|
+
@complex_types.each { |name, content| create_file(name, MODEL_TEMPLATE.result(binding), dir) }
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def create_file(name, content, dir)
|
122
|
+
File.write("#{dir}/#{Utils.snake_case(name)}.rb", content)
|
123
|
+
end
|
124
|
+
|
125
|
+
# START: STRUCTURE SETUP METHODS
|
126
|
+
|
127
|
+
def as_models(schema, options: {})
|
128
|
+
raise Error, XML_ADAPTER_NOT_SET_MESSAGE unless Config.xml_adapter.name.end_with?("NokogiriAdapter")
|
129
|
+
|
130
|
+
parsed_schema = Xsd.parse(schema, location: options[:location])
|
131
|
+
|
132
|
+
@elements = MappingHash.new
|
133
|
+
@attributes = MappingHash.new
|
134
|
+
@group_types = MappingHash.new
|
135
|
+
@simple_types = MappingHash.new
|
136
|
+
@complex_types = MappingHash.new
|
137
|
+
@attribute_groups = MappingHash.new
|
138
|
+
|
139
|
+
schema_to_models([parsed_schema])
|
140
|
+
end
|
141
|
+
|
142
|
+
def schema_to_models(schemas)
|
143
|
+
return if schemas.empty?
|
144
|
+
|
145
|
+
schemas.each do |schema|
|
146
|
+
schema_to_models(schema.include) if schema.include.any?
|
147
|
+
schema_to_models(schema.import) if schema.import.any?
|
148
|
+
resolved_element_order(schema).each do |order_item|
|
149
|
+
item_name = order_item&.name
|
150
|
+
case order_item
|
151
|
+
when Xsd::SimpleType
|
152
|
+
@simple_types[item_name] = setup_simple_type(order_item)
|
153
|
+
when Xsd::Group
|
154
|
+
@group_types[item_name] = setup_group_type(order_item)
|
155
|
+
when Xsd::ComplexType
|
156
|
+
@complex_types[item_name] = setup_complex_type(order_item)
|
157
|
+
when Xsd::Element
|
158
|
+
@elements[item_name] = setup_element(order_item)
|
159
|
+
when Xsd::Attribute
|
160
|
+
@attributes[item_name] = setup_attribute(order_item)
|
161
|
+
when Xsd::AttributeGroup
|
162
|
+
@attribute_groups[item_name] = setup_attribute_groups(order_item)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
nil
|
167
|
+
end
|
168
|
+
|
169
|
+
def setup_simple_type(simple_type)
|
170
|
+
MappingHash.new.tap do |hash|
|
171
|
+
setup_restriction(simple_type.restriction, hash) if simple_type&.restriction
|
172
|
+
hash[:union] = setup_union(simple_type.union) if simple_type.union
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
def restriction_content(hash, restriction)
|
177
|
+
return hash unless restriction.respond_to?(:max_length)
|
178
|
+
|
179
|
+
hash[:max_length] = restriction.max_length.map(&:value).min if restriction.max_length&.any?
|
180
|
+
hash[:min_length] = restriction.min_length.map(&:value).max if restriction.min_length&.any?
|
181
|
+
hash[:min_inclusive] = restriction.min_inclusive.map(&:value).max if restriction.min_inclusive&.any?
|
182
|
+
hash[:max_inclusive] = restriction.max_inclusive.map(&:value).min if restriction.max_inclusive&.any?
|
183
|
+
hash[:length] = restriction_length(restriction.length) if restriction.length.any?
|
184
|
+
end
|
185
|
+
|
186
|
+
def restriction_length(lengths)
|
187
|
+
lengths.map do |length|
|
188
|
+
MappingHash.new.tap do |hash|
|
189
|
+
hash[:value] = length.value
|
190
|
+
hash[:fixed] = length.fixed if length.fixed
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def setup_complex_type(complex_type)
|
196
|
+
MappingHash.new.tap do |hash|
|
197
|
+
hash[:attributes] = [] if complex_type.attribute.any?
|
198
|
+
hash[:attribute_groups] = [] if complex_type.attribute_group.any?
|
199
|
+
resolved_element_order(complex_type).each do |element|
|
200
|
+
case element
|
201
|
+
when Xsd::Attribute
|
202
|
+
hash[:attributes] << setup_attribute(element)
|
203
|
+
when Xsd::Sequence
|
204
|
+
hash[:sequence] = setup_sequence(element)
|
205
|
+
when Xsd::Choice
|
206
|
+
hash[:choice] = setup_choice(element)
|
207
|
+
when Xsd::ComplexContent
|
208
|
+
hash[:complex_content] = setup_complex_content(element)
|
209
|
+
when Xsd::AttributeGroup
|
210
|
+
hash[:attribute_groups] << setup_attribute_groups(element)
|
211
|
+
when Xsd::Group
|
212
|
+
hash[:group] = setup_group_type(element)
|
213
|
+
when Xsd::SimpleContent
|
214
|
+
hash[:simple_content] = setup_simple_content(element)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def setup_simple_content(simple_content)
|
221
|
+
if simple_content.extension
|
222
|
+
setup_extension(simple_content.extension)
|
223
|
+
elsif simple_content.restriction
|
224
|
+
setup_restriction(simple_content.restriction, {})
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
def setup_sequence(sequence)
|
229
|
+
MappingHash.new.tap do |hash|
|
230
|
+
hash[:sequences] = [] if sequence.sequence.any?
|
231
|
+
hash[:elements] = [] if sequence.element.any?
|
232
|
+
hash[:choice] = [] if sequence.choice.any?
|
233
|
+
hash[:groups] = [] if sequence.group.any?
|
234
|
+
resolved_element_order(sequence).each do |instance|
|
235
|
+
case instance
|
236
|
+
when Xsd::Sequence
|
237
|
+
hash[:sequences] << setup_sequence(instance)
|
238
|
+
when Xsd::Element
|
239
|
+
hash[:elements] << if instance.name
|
240
|
+
setup_element(instance)
|
241
|
+
else
|
242
|
+
create_mapping_hash(instance.ref, hash_key: :ref_class)
|
243
|
+
end
|
244
|
+
when Xsd::Choice
|
245
|
+
hash[:choice] << setup_choice(instance)
|
246
|
+
when Xsd::Group
|
247
|
+
hash[:groups] << if instance.name
|
248
|
+
setup_group_type(instance)
|
249
|
+
else
|
250
|
+
create_mapping_hash(instance.ref, hash_key: :ref_class)
|
251
|
+
end
|
252
|
+
when Xsd::Any
|
253
|
+
# No implementation yet!
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
def setup_group_type(group)
|
260
|
+
MappingHash.new.tap do |hash|
|
261
|
+
if group.ref
|
262
|
+
hash[:ref_class] = group.ref
|
263
|
+
else
|
264
|
+
resolved_element_order(group).map do |instance|
|
265
|
+
case instance
|
266
|
+
when Xsd::Sequence
|
267
|
+
hash[:sequence] = setup_sequence(instance)
|
268
|
+
when Xsd::Choice
|
269
|
+
hash[:choice] = setup_choice(instance)
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def setup_choice(choice)
|
277
|
+
MappingHash.new.tap do |hash|
|
278
|
+
resolved_element_order(choice).each do |element|
|
279
|
+
case element
|
280
|
+
when Xsd::Element
|
281
|
+
element_name = element.name || @elements[element.ref.split(":").last]&.element_name
|
282
|
+
hash[element_name] = setup_element(element)
|
283
|
+
when Xsd::Sequence
|
284
|
+
hash[:sequence] = setup_sequence(element)
|
285
|
+
when Xsd::Group
|
286
|
+
hash[:group] = setup_group_type(element)
|
287
|
+
when Xsd::Choice
|
288
|
+
hash[:choice] = setup_choice(element)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def setup_union(union)
|
295
|
+
union.member_types.split.map do |member_type|
|
296
|
+
@simple_types[member_type]
|
297
|
+
end.flatten
|
298
|
+
end
|
299
|
+
|
300
|
+
def setup_attribute(attribute)
|
301
|
+
MappingHash.new.tap do |attr_hash|
|
302
|
+
if attribute.ref
|
303
|
+
attr_hash[:ref_class] = attribute.ref
|
304
|
+
else
|
305
|
+
attr_hash[:name] = attribute.name
|
306
|
+
attr_hash[:base_class] = attribute.type
|
307
|
+
attr_hash[:default] = attribute.default if attribute.default
|
308
|
+
end
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
def setup_attribute_groups(attribute_group)
|
313
|
+
MappingHash.new.tap do |hash|
|
314
|
+
if attribute_group.ref
|
315
|
+
hash[:ref_class] = attribute_group.ref
|
316
|
+
else
|
317
|
+
hash[:attributes] = [] if attribute_group.attribute.any?
|
318
|
+
hash[:attribute_groups] = [] if attribute_group.attribute_group.any?
|
319
|
+
resolved_element_order(attribute_group).each do |instance|
|
320
|
+
case instance
|
321
|
+
when Xsd::Attribute
|
322
|
+
hash[:attributes] << setup_attribute(instance)
|
323
|
+
when Xsd::AttributeGroup
|
324
|
+
hash[:attribute_groups] << setup_attribute_groups(instance)
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def create_mapping_hash(value, hash_key: :class_name)
|
332
|
+
MappingHash.new.tap do |hash|
|
333
|
+
hash[hash_key] = value
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
def setup_element(element)
|
338
|
+
MappingHash.new.tap do |hash|
|
339
|
+
if element.ref
|
340
|
+
hash[:ref_class] = element.ref
|
341
|
+
else
|
342
|
+
hash[:type_name] = element.type
|
343
|
+
hash[:element_name] = element.name
|
344
|
+
element_arguments(element, hash)
|
345
|
+
return hash unless complex_type = element.complex_type
|
346
|
+
|
347
|
+
hash[:complex_type] = setup_complex_type(complex_type)
|
348
|
+
@complex_types[complex_type.name] = hash[:complex_type]
|
349
|
+
end
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def setup_restriction(restriction, hash)
|
354
|
+
hash[:base_class] = restriction.base
|
355
|
+
restriction_patterns(restriction.pattern, hash) if restriction.respond_to?(:pattern)
|
356
|
+
restriction_content(hash, restriction)
|
357
|
+
return hash unless restriction.respond_to?(:enumeration) && restriction.enumeration.any?
|
358
|
+
|
359
|
+
hash[:values] = restriction.enumeration.map(&:value)
|
360
|
+
hash
|
361
|
+
end
|
362
|
+
|
363
|
+
def restriction_patterns(patterns, hash)
|
364
|
+
return if patterns.empty?
|
365
|
+
|
366
|
+
hash[:pattern] = patterns.map { |pattern| "(#{pattern.value})" }.join("|")
|
367
|
+
hash
|
368
|
+
end
|
369
|
+
|
370
|
+
def setup_complex_content(complex_content)
|
371
|
+
MappingHash.new.tap do |hash|
|
372
|
+
hash[:mixed] = true if complex_content.mixed
|
373
|
+
if complex_content.extension
|
374
|
+
hash[:extension] = setup_extension(complex_content.extension)
|
375
|
+
elsif restriction = complex_content.restriction
|
376
|
+
setup_restriction(restriction, hash)
|
377
|
+
end
|
378
|
+
end
|
379
|
+
end
|
380
|
+
|
381
|
+
def setup_extension(extension)
|
382
|
+
MappingHash.new.tap do |hash|
|
383
|
+
hash[:extension_base] = extension.base
|
384
|
+
hash[:attribute_groups] = [] if extension&.attribute_group&.any?
|
385
|
+
hash[:attributes] = [] if extension&.attribute&.any?
|
386
|
+
resolved_element_order(extension).each do |element|
|
387
|
+
case element
|
388
|
+
when Xsd::Attribute
|
389
|
+
hash[:attributes] << setup_attribute(element)
|
390
|
+
when Xsd::Sequence
|
391
|
+
hash[:sequence] = setup_sequence(element)
|
392
|
+
when Xsd::Choice
|
393
|
+
hash[:choice] = setup_choice(element)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def element_arguments(element, element_hash)
|
400
|
+
MappingHash.new.tap do |hash|
|
401
|
+
hash[:min_occurs] = element.min_occurs if element.min_occurs
|
402
|
+
hash[:max_occurs] = element.max_occurs if element.max_occurs
|
403
|
+
element_hash[:arguments] = hash if hash.any?
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
def resolved_element_order(object, ignore_text: true)
|
408
|
+
return [] if object.element_order.nil?
|
409
|
+
|
410
|
+
object.element_order.each_with_object(object.element_order.dup) do |name, array|
|
411
|
+
next array.delete(name) if name == "text" && (ignore_text || !object.respond_to?(:text))
|
412
|
+
next array.delete(name) if ELEMENT_ORDER_IGNORABLE.include?(name)
|
413
|
+
|
414
|
+
index = 0
|
415
|
+
array.each_with_index do |element, i|
|
416
|
+
next unless element == name
|
417
|
+
|
418
|
+
array[i] = Array(object.send(Utils.snake_case(name)))[index]
|
419
|
+
index += 1
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
# END: STRUCTURE SETUP METHODS
|
424
|
+
|
425
|
+
# START: TEMPLATE RESOLVER METHODS
|
426
|
+
def resolve_parent_class(content)
|
427
|
+
return "Lutaml::Model::Serializable" unless content.dig(:complex_content, :extension)
|
428
|
+
|
429
|
+
Utils.camel_case(content.dig(:complex_content, :extension, :extension_base))
|
430
|
+
end
|
431
|
+
|
432
|
+
def resolve_attribute_class(attribute)
|
433
|
+
attr_class = attribute.base_class.split(":")&.last
|
434
|
+
case attr_class
|
435
|
+
when *DEFAULT_CLASSES
|
436
|
+
":#{attr_class}"
|
437
|
+
else
|
438
|
+
Utils.camel_case(attr_class)
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
def resolve_occurs(arguments)
|
443
|
+
min_occurs = arguments[:min_occurs]
|
444
|
+
max_occurs = arguments[:max_occurs]
|
445
|
+
max_occurs = max_occurs.to_s&.match?(/[A-Za-z]+/) ? nil : max_occurs.to_i if max_occurs
|
446
|
+
", collection: #{max_occurs ? min_occurs.to_i..max_occurs : true}"
|
447
|
+
end
|
448
|
+
|
449
|
+
def resolve_content(content, hash = MappingHash.new)
|
450
|
+
content.each do |key, value|
|
451
|
+
case key
|
452
|
+
when :sequence
|
453
|
+
resolve_sequence(value, hash)
|
454
|
+
when :choice
|
455
|
+
resolve_choice(value, hash)
|
456
|
+
when :group
|
457
|
+
resolve_group(value, hash)
|
458
|
+
end
|
459
|
+
end
|
460
|
+
hash
|
461
|
+
end
|
462
|
+
|
463
|
+
def resolve_elements(elements, hash = MappingHash.new)
|
464
|
+
elements.each do |element|
|
465
|
+
if element.key?(:ref_class)
|
466
|
+
new_element = @elements[element.ref_class.split(":").last]
|
467
|
+
hash[new_element.element_name] = new_element
|
468
|
+
else
|
469
|
+
hash[element.element_name] = element
|
470
|
+
end
|
471
|
+
end
|
472
|
+
hash
|
473
|
+
end
|
474
|
+
|
475
|
+
def resolve_sequence(sequence, hash = MappingHash.new)
|
476
|
+
sequence.each do |key, value|
|
477
|
+
case key
|
478
|
+
when :sequence
|
479
|
+
resolve_sequence(value, hash)
|
480
|
+
when :elements
|
481
|
+
resolve_elements(value, hash)
|
482
|
+
when :groups
|
483
|
+
value.each { |group| resolve_group(group, hash) }
|
484
|
+
when :choice
|
485
|
+
value.each { |choice| resolve_choice(choice, hash) }
|
486
|
+
end
|
487
|
+
end
|
488
|
+
hash
|
489
|
+
end
|
490
|
+
|
491
|
+
def resolve_choice(choice, hash = MappingHash.new)
|
492
|
+
choice.each do |key, value|
|
493
|
+
case key
|
494
|
+
when :element
|
495
|
+
[resolve_elements(value, hash)]
|
496
|
+
when :group
|
497
|
+
resolve_group(value, hash)
|
498
|
+
when String
|
499
|
+
hash[key] = value
|
500
|
+
when :sequence
|
501
|
+
resolve_sequence(value, hash)
|
502
|
+
end
|
503
|
+
end
|
504
|
+
hash
|
505
|
+
end
|
506
|
+
|
507
|
+
def resolve_group(group, hash = MappingHash.new)
|
508
|
+
group.each do |key, value|
|
509
|
+
case key
|
510
|
+
when :ref_class
|
511
|
+
resolve_group(@group_types[value.split(":").last], hash)
|
512
|
+
when :choice
|
513
|
+
resolve_choice(value, hash)
|
514
|
+
when :group
|
515
|
+
resolve_group(value, hash)
|
516
|
+
when :sequence
|
517
|
+
resolve_sequence(value, hash)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
hash
|
521
|
+
end
|
522
|
+
|
523
|
+
def resolve_complex_content(complex_content, hash = MappingHash.new)
|
524
|
+
complex_content.each do |key, value|
|
525
|
+
case key
|
526
|
+
when :extension
|
527
|
+
resolve_extension(value, hash)
|
528
|
+
when :restriction
|
529
|
+
# TODO: No implementation yet!
|
530
|
+
hash
|
531
|
+
end
|
532
|
+
end
|
533
|
+
hash
|
534
|
+
end
|
535
|
+
|
536
|
+
def resolve_extension(extension, hash = MappingHash.new)
|
537
|
+
hash[:attributes] = extension.attributes if extension.key?(:attributes)
|
538
|
+
resolve_sequence(extension.sequence, hash) if extension.key?(:sequence)
|
539
|
+
resolve_choice(extension.choice, hash) if extension.key?(:choice)
|
540
|
+
hash
|
541
|
+
end
|
542
|
+
|
543
|
+
def resolve_attribute_default(attribute)
|
544
|
+
klass = attribute.base_class.split(":").last
|
545
|
+
default = attribute[:default]
|
546
|
+
", default: #{resolve_attribute_default_value(klass, default)}"
|
547
|
+
end
|
548
|
+
|
549
|
+
def resolve_attribute_default_value(klass, default)
|
550
|
+
return default.inspect unless DEFAULT_CLASSES.include?(klass)
|
551
|
+
|
552
|
+
klass = "integer" if klass == "int"
|
553
|
+
type_klass = Lutaml::Model::Type.const_get(klass.capitalize)
|
554
|
+
type_klass.cast(default)
|
555
|
+
end
|
556
|
+
|
557
|
+
def resolve_namespace(options)
|
558
|
+
namespace_str = "namespace \"#{options[:namespace]}\"" if options.key?(:namespace)
|
559
|
+
namespace_str += ", \"#{options[:prefix]}\"" if options.key?(:prefix) && options.key?(:namespace)
|
560
|
+
namespace_str += "\n" if namespace_str
|
561
|
+
namespace_str
|
562
|
+
end
|
563
|
+
|
564
|
+
def resolve_required_files(content)
|
565
|
+
@required_files = []
|
566
|
+
content.each do |key, value|
|
567
|
+
case key
|
568
|
+
when :sequence
|
569
|
+
required_files_sequence(value)
|
570
|
+
when :choice
|
571
|
+
required_files_choice(value)
|
572
|
+
when :group
|
573
|
+
required_files_group(value)
|
574
|
+
when :attributes
|
575
|
+
required_files_attribute(value)
|
576
|
+
when :attribute_groups
|
577
|
+
value.each { |attribute_group| required_files_attribute_groups(attribute_group) }
|
578
|
+
when :complex_content
|
579
|
+
required_files_complex_content(value)
|
580
|
+
when :simple_content
|
581
|
+
required_files_simple_content(value)
|
582
|
+
end
|
583
|
+
end
|
584
|
+
@required_files.uniq.sort_by(&:length)
|
585
|
+
end
|
586
|
+
# END: TEMPLATE RESOLVER METHODS
|
587
|
+
|
588
|
+
# START: REQUIRED FILES LIST COMPILER METHODS
|
589
|
+
def required_files_simple_content(simple_content)
|
590
|
+
simple_content.each do |key, value|
|
591
|
+
case key
|
592
|
+
when :extension_base
|
593
|
+
# Do nothing.
|
594
|
+
when :attributes
|
595
|
+
required_files_attribute(value)
|
596
|
+
when :extension
|
597
|
+
required_files_extension(value)
|
598
|
+
when :restriction
|
599
|
+
required_files_restriction(value)
|
600
|
+
end
|
601
|
+
end
|
602
|
+
end
|
603
|
+
|
604
|
+
def required_files_complex_content(complex_content)
|
605
|
+
complex_content.each do |key, value|
|
606
|
+
case key
|
607
|
+
when :extension
|
608
|
+
required_files_extension(value)
|
609
|
+
when :restriction
|
610
|
+
required_files_restriction(value)
|
611
|
+
end
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
def required_files_extension(extension)
|
616
|
+
extension.each do |key, value|
|
617
|
+
case key
|
618
|
+
when :attribute_group
|
619
|
+
required_files_attribute_groups(value)
|
620
|
+
when :attribute, :attributes
|
621
|
+
required_files_attribute(value)
|
622
|
+
when :extension_base
|
623
|
+
# Do nothing.
|
624
|
+
when :sequence
|
625
|
+
required_files_sequence(value)
|
626
|
+
when :choice
|
627
|
+
required_files_choice(value)
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
def required_files_restriction(restriction)
|
633
|
+
restriction.each do |key, value|
|
634
|
+
case key
|
635
|
+
when :base
|
636
|
+
@required_files << Utils.snake_case(value.split(":").last)
|
637
|
+
end
|
638
|
+
end
|
639
|
+
end
|
640
|
+
|
641
|
+
def required_files_attribute_groups(attr_groups)
|
642
|
+
attr_groups.each do |key, value|
|
643
|
+
case key
|
644
|
+
when :ref_class
|
645
|
+
required_files_attribute_groups(@attribute_groups[value.split(":").last])
|
646
|
+
when :attribute, :attributes
|
647
|
+
required_files_attribute(value)
|
648
|
+
end
|
649
|
+
end
|
650
|
+
end
|
651
|
+
|
652
|
+
def required_files_attribute(attributes)
|
653
|
+
attributes.each do |attribute|
|
654
|
+
next if attribute[:ref_class]&.start_with?("xml") || attribute[:base_class]&.start_with?("xml")
|
655
|
+
|
656
|
+
attribute = @attributes[attribute.ref_class.split(":").last] if attribute.key_exist?(:ref_class)
|
657
|
+
attr_class = attribute.base_class.split(":")&.last
|
658
|
+
next if DEFAULT_CLASSES.include?(attr_class)
|
659
|
+
|
660
|
+
@required_files << Utils.snake_case(attr_class)
|
661
|
+
end
|
662
|
+
end
|
663
|
+
|
664
|
+
def required_files_choice(choice)
|
665
|
+
choice.each do |key, value|
|
666
|
+
case key
|
667
|
+
when String
|
668
|
+
value = @elements[value.ref_class.split(":").last] if value.key?(:ref_class)
|
669
|
+
@required_files << Utils.snake_case(value.type_name.split(":").last)
|
670
|
+
when :element
|
671
|
+
required_files_elements(value)
|
672
|
+
when :group
|
673
|
+
required_files_group(value)
|
674
|
+
when :choice
|
675
|
+
required_files_choice(value)
|
676
|
+
when :sequence
|
677
|
+
required_files_sequence(value)
|
678
|
+
end
|
679
|
+
end
|
680
|
+
end
|
681
|
+
|
682
|
+
def required_files_group(group)
|
683
|
+
group.each do |key, value|
|
684
|
+
case key
|
685
|
+
when :ref_class
|
686
|
+
required_files_group(@group_types[value.split(":").last])
|
687
|
+
when :choice
|
688
|
+
required_files_choice(value)
|
689
|
+
when :sequence
|
690
|
+
required_files_sequence(value)
|
691
|
+
end
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
def required_files_sequence(sequence)
|
696
|
+
sequence.each do |key, value|
|
697
|
+
case key
|
698
|
+
when :elements
|
699
|
+
required_files_elements(value)
|
700
|
+
when :sequence
|
701
|
+
required_files_sequence(value)
|
702
|
+
when :groups
|
703
|
+
value.each { |group| required_files_group(group) }
|
704
|
+
when :choice
|
705
|
+
value.each { |choice| required_files_choice(choice) }
|
706
|
+
end
|
707
|
+
end
|
708
|
+
end
|
709
|
+
|
710
|
+
def required_files_elements(elements)
|
711
|
+
elements.each do |element|
|
712
|
+
element = @elements[element.ref_class.split(":").last] if element.key_exist?(:ref_class)
|
713
|
+
@required_files << Utils.snake_case(element.type_name.split(":").last)
|
714
|
+
end
|
715
|
+
end
|
716
|
+
# END: REQUIRED FILES LIST COMPILER METHODS
|
717
|
+
end
|
718
|
+
end
|
719
|
+
end
|
720
|
+
end
|