lutaml-model 0.8.19 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of lutaml-model might be problematic. Click here for more details.

Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +1 -1
  3. data/.github/workflows/rake.yml +1 -0
  4. data/.rubocop_todo.yml +133 -72
  5. data/bench/bench_common.rb +6 -3
  6. data/bench/bench_niso.rb +1 -2
  7. data/lib/compat/opal/generate_boot.rb +3 -1
  8. data/lib/lutaml/key_value/transform.rb +4 -1
  9. data/lib/lutaml/key_value/transformation/value_serializer.rb +0 -2
  10. data/lib/lutaml/model/attribute.rb +23 -2
  11. data/lib/lutaml/model/global_context.rb +4 -2
  12. data/lib/lutaml/model/schema/class_loader.rb +2 -1
  13. data/lib/lutaml/model/schema/definitions/namespace.rb +2 -1
  14. data/lib/lutaml/model/schema/file_writer.rb +5 -2
  15. data/lib/lutaml/model/schema/renderers/base.rb +2 -1
  16. data/lib/lutaml/model/schema/renderers/mappings.rb +9 -3
  17. data/lib/lutaml/model/schema/renderers/member_decls.rb +9 -3
  18. data/lib/lutaml/model/schema/renderers/model.rb +16 -4
  19. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +2 -1
  20. data/lib/lutaml/model/schema/renderers.rb +2 -1
  21. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +6 -2
  22. data/lib/lutaml/model/schema/rnc_compiler.rb +3 -1
  23. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +35 -15
  24. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +4 -2
  25. data/lib/lutaml/model/schema/rng_compiler.rb +10 -5
  26. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +2 -1
  27. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +4 -2
  28. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +39 -13
  29. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +29 -10
  30. data/lib/lutaml/model/schema/xml_compiler.rb +10 -5
  31. data/lib/lutaml/model/serialize/attribute_definition.rb +1 -0
  32. data/lib/lutaml/model/serialize/initialization.rb +9 -1
  33. data/lib/lutaml/model/serialize/model_import.rb +1 -0
  34. data/lib/lutaml/model/serialize.rb +5 -4
  35. data/lib/lutaml/model/union.rb +5 -2
  36. data/lib/lutaml/model/version.rb +1 -1
  37. data/lib/lutaml/xml/mapping_rule.rb +55 -1
  38. data/lib/lutaml/xml/model_transform.rb +53 -5
  39. data/lib/lutaml/xml/schema/xsd.rb +0 -2
  40. data/lib/lutaml/xml/schema/xsd_schema.rb +2 -1
  41. data/lib/lutaml/xml/transformation/element_builder.rb +2 -1
  42. data/lib/lutaml/xml/transformation/ordered_applier.rb +2 -1
  43. data/lutaml-model.gemspec +4 -1
  44. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +26 -13
  45. data/spec/lutaml/model/attribute_spec.rb +8 -3
  46. data/spec/lutaml/model/custom_from_empty_values_spec.rb +74 -0
  47. data/spec/lutaml/model/defaults_spec.rb +45 -0
  48. data/spec/lutaml/model/json_spec.rb +1 -2
  49. data/spec/lutaml/model/serialize/builder_spec.rb +6 -3
  50. data/spec/lutaml/model/transform_apply_value_map_spec.rb +6 -3
  51. data/spec/lutaml/model/union_attribute_spec.rb +4 -2
  52. data/spec/lutaml/xml/attribute_namespace_disjointness_spec.rb +125 -0
  53. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +17 -6
  54. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +15 -6
  55. data/spec/lutaml/xml/w3c_types_spec.rb +3 -6
  56. metadata +5 -3
@@ -14,7 +14,8 @@ module Lutaml
14
14
  new(spec, **).render
15
15
  end
16
16
 
17
- def initialize(spec, indent: 2, module_namespace: nil, register_id: :default)
17
+ def initialize(spec, indent: 2, module_namespace: nil,
18
+ register_id: :default)
18
19
  @spec = spec
19
20
  @indent = indent.is_a?(Integer) ? " " * indent : indent
20
21
  @extended_indent = @indent * 2
@@ -39,8 +40,15 @@ module Lutaml
39
40
 
40
41
  def rendered_class_name = @spec.class_name
41
42
  def serializable_class_parent = @spec.parent_class
42
- def serializable_class_required_files = format_files(@spec.required_files)
43
- def serializable_class_documentation = format_doc_block(@spec.documentation)
43
+
44
+ def serializable_class_required_files
45
+ format_files(@spec.required_files)
46
+ end
47
+
48
+ def serializable_class_documentation
49
+ format_doc_block(@spec.documentation)
50
+ end
51
+
44
52
  def serializable_class_imports = render_imports
45
53
 
46
54
  def serializable_class_attributes
@@ -76,7 +84,11 @@ module Lutaml
76
84
  end
77
85
 
78
86
  def xml_mixed_content? = @spec.mixed
79
- def xml_text_content? = @spec.text_content || !!@spec.simple_content || @spec.mixed
87
+
88
+ def xml_text_content?
89
+ @spec.text_content || !!@spec.simple_content || @spec.mixed
90
+ end
91
+
80
92
  def xml_extra_mappings = ""
81
93
 
82
94
  def module_opening = ModuleNesting.opening(modules)
@@ -34,7 +34,8 @@ module Lutaml
34
34
  Definitions::MemberWalk.each_attribute(model.members) do |attr|
35
35
  deps.concat(xml_attribute_requires(attr, skippable_type))
36
36
  end
37
- deps.concat(simple_content_requires(model.simple_content, skippable_type))
37
+ deps.concat(simple_content_requires(model.simple_content,
38
+ skippable_type))
38
39
  deps << namespace_require(model.namespace_class_name) if model.namespace_class_name
39
40
  deps.uniq
40
41
  end
@@ -6,7 +6,8 @@ module Lutaml
6
6
  module Renderers
7
7
  autoload :Base, "#{__dir__}/renderers/base"
8
8
  autoload :Registration, "#{__dir__}/renderers/registration"
9
- autoload :RequiredFilesCalculator, "#{__dir__}/renderers/required_files_calculator"
9
+ autoload :RequiredFilesCalculator,
10
+ "#{__dir__}/renderers/required_files_calculator"
10
11
  autoload :MemberDecls, "#{__dir__}/renderers/member_decls"
11
12
  autoload :Mappings, "#{__dir__}/renderers/mappings"
12
13
  autoload :Model, "#{__dir__}/renderers/model"
@@ -20,9 +20,13 @@ module Lutaml
20
20
  if File.file?(expanded)
21
21
  return file_source(expanded) if input.nil? || input.to_s.empty?
22
22
 
23
- return string_source(input, base_dir: File.dirname(expanded), path: expanded)
23
+ return string_source(input, base_dir: File.dirname(expanded),
24
+ path: expanded)
25
+ end
26
+ if File.directory?(expanded)
27
+ return string_source(input,
28
+ base_dir: expanded)
24
29
  end
25
- return string_source(input, base_dir: expanded) if File.directory?(expanded)
26
30
 
27
31
  string_source(input)
28
32
  end
@@ -47,7 +47,9 @@ module Lutaml
47
47
  # the 0.3.7 release; the released parse_rnc takes only `(rnc)`.
48
48
  def rnc_parser_available?
49
49
  defined?(::Rng::Grammar) && ::Rng.respond_to?(:parse_rnc) &&
50
- ::Rng.method(:parse_rnc).parameters.any? { |_type, name| name == :location }
50
+ ::Rng.method(:parse_rnc).parameters.any? do |_type, name|
51
+ name == :location
52
+ end
51
53
  end
52
54
 
53
55
  def default_output_dir
@@ -23,8 +23,10 @@ module Lutaml
23
23
  ].freeze
24
24
 
25
25
  REPETITION_CTX = {
26
- zeroOrMore: { collection: (0..Float::INFINITY), initialize_empty: true },
27
- oneOrMore: { collection: (1..Float::INFINITY), initialize_empty: true },
26
+ zeroOrMore: { collection: (0..Float::INFINITY),
27
+ initialize_empty: true },
28
+ oneOrMore: { collection: (1..Float::INFINITY),
29
+ initialize_empty: true },
28
30
  list: { collection: (0..Float::INFINITY), initialize_empty: true },
29
31
  }.freeze
30
32
 
@@ -60,7 +62,8 @@ module Lutaml
60
62
  class_name = Utils.camel_case(element.attr_name)
61
63
  model = @classes[class_name] ||= Definitions::Model.new(
62
64
  class_name: class_name,
63
- xml_root: Definitions::XmlRoot.new(kind: :element, name: element.attr_name),
65
+ xml_root: Definitions::XmlRoot.new(kind: :element,
66
+ name: element.attr_name),
64
67
  documentation: documentation_text(element),
65
68
  namespace_class_name: namespace_class_for(element),
66
69
  )
@@ -90,7 +93,8 @@ module Lutaml
90
93
  wrapping = define.element.size == 1 ? define.element.first : nil
91
94
 
92
95
  xml_root = if wrapping
93
- Definitions::XmlRoot.new(kind: :element, name: wrapping.attr_name)
96
+ Definitions::XmlRoot.new(kind: :element,
97
+ name: wrapping.attr_name)
94
98
  else
95
99
  Definitions::XmlRoot.new(kind: :fragment)
96
100
  end
@@ -198,22 +202,30 @@ module Lutaml
198
202
  doc = documentation_text(child)
199
203
  value_type = @value_type_resolver.resolve(child)
200
204
  type_ref = type_ref_for_element(child, value_type)
201
- push_attribute(parent, build_attribute(child, type_ref, :element, ctx, doc))
205
+ push_attribute(parent,
206
+ build_attribute(child, type_ref, :element, ctx, doc))
202
207
  end
203
208
 
204
209
  def type_ref_for_element(child, value_type)
205
- return Definitions::TypeRef.new(kind: :symbol, value: value_type.to_s) if value_type
210
+ if value_type
211
+ return Definitions::TypeRef.new(kind: :symbol,
212
+ value: value_type.to_s)
213
+ end
206
214
 
207
215
  compiled = compile_element(child)
208
- Definitions::TypeRef.new(kind: :class_ref, value: compiled.class_name)
216
+ Definitions::TypeRef.new(kind: :class_ref,
217
+ value: compiled.class_name)
209
218
  end
210
219
 
211
220
  def handle_attribute(_kind, child, parent, ctx)
212
221
  doc = documentation_text(child)
213
222
  symbol = attribute_type_symbol(child)
214
- type_ref = Definitions::TypeRef.new(kind: :symbol, value: symbol.to_s)
223
+ type_ref = Definitions::TypeRef.new(kind: :symbol,
224
+ value: symbol.to_s)
215
225
  fixed = fixed_value_default(child)
216
- push_attribute(parent, build_attribute(child, type_ref, :attribute, ctx, doc, default: fixed))
226
+ push_attribute(parent,
227
+ build_attribute(child, type_ref, :attribute, ctx,
228
+ doc, default: fixed))
217
229
  end
218
230
 
219
231
  # The attribute's value type, as a Symbol. An attribute in a foreign
@@ -225,7 +237,8 @@ module Lutaml
225
237
  uri = foreign_namespace_uri(child)
226
238
  return base unless uri
227
239
 
228
- namespaced_attribute_type(child, base, @register_namespace.call(uri))
240
+ namespaced_attribute_type(child, base,
241
+ @register_namespace.call(uri))
229
242
  end
230
243
 
231
244
  # Give the attribute's value a namespace via a dedicated generated
@@ -300,19 +313,26 @@ module Lutaml
300
313
 
301
314
  def handle_ref(_kind, ref, parent, ctx)
302
315
  target_define = @defines[ref.name]
303
- raise Lutaml::Model::Error, "ref to unknown define: #{ref.name}" unless target_define
316
+ unless target_define
317
+ raise Lutaml::Model::Error,
318
+ "ref to unknown define: #{ref.name}"
319
+ end
304
320
 
305
321
  target_class = compile_define(target_define)
306
322
 
307
323
  if RngHelpers.simple_type?(target_class)
308
- type_ref = Definitions::TypeRef.new(kind: :symbol, value: RngHelpers.type_symbol(target_class.class_name).to_s)
309
- push_attribute(parent, build_ref_attribute(ref, type_ref, ref.name, ctx))
324
+ type_ref = Definitions::TypeRef.new(kind: :symbol,
325
+ value: RngHelpers.type_symbol(target_class.class_name).to_s)
326
+ push_attribute(parent,
327
+ build_ref_attribute(ref, type_ref, ref.name, ctx))
310
328
  elsif RngHelpers.fragment_model?(target_class) && ctx[:collection].nil?
311
329
  push_import(parent, target_class.class_name)
312
330
  else
313
- type_ref = Definitions::TypeRef.new(kind: :class_ref, value: target_class.class_name)
331
+ type_ref = Definitions::TypeRef.new(kind: :class_ref,
332
+ value: target_class.class_name)
314
333
  xml_name = target_class.xml_root.name || ref.name
315
- push_attribute(parent, build_ref_attribute(ref, type_ref, xml_name, ctx))
334
+ push_attribute(parent,
335
+ build_ref_attribute(ref, type_ref, xml_name, ctx))
316
336
  end
317
337
  end
318
338
 
@@ -57,14 +57,16 @@ module Lutaml
57
57
  base = RngCompiler::DATA_TYPE_MAP.fetch(
58
58
  data.type, RngCompiler::DEFAULT_DATA_TYPE
59
59
  )
60
- anonymous_restricted_type(container, base, RngHelpers.facet_from_data(data))
60
+ anonymous_restricted_type(container, base,
61
+ RngHelpers.facet_from_data(data))
61
62
  end
62
63
 
63
64
  def anonymous_from_enum_choice(container)
64
65
  choice = RngHelpers.single(container.choice)
65
66
  return nil unless choice && RngHelpers.pure_value_choice?(choice)
66
67
 
67
- anonymous_restricted_type(container, :string, RngHelpers.facet_from_values(choice.value))
68
+ anonymous_restricted_type(container, :string,
69
+ RngHelpers.facet_from_values(choice.value))
68
70
  end
69
71
 
70
72
  def anonymous_restricted_type(container, base, facet)
@@ -20,8 +20,9 @@ module Lutaml
20
20
  autoload :MemberCollector, "#{__dir__}/rng_compiler/member_collector"
21
21
  autoload :ElementVisitor, "#{__dir__}/rng_compiler/element_visitor"
22
22
  autoload :DefineClassifier, "#{__dir__}/rng_compiler/define_classifier"
23
- autoload :ValueTypeResolver, "#{__dir__}/rng_compiler/value_type_resolver"
24
- autoload :RngHelpers, "#{__dir__}/rng_compiler/rng_helpers"
23
+ autoload :ValueTypeResolver,
24
+ "#{__dir__}/rng_compiler/value_type_resolver"
25
+ autoload :RngHelpers, "#{__dir__}/rng_compiler/rng_helpers"
25
26
 
26
27
  # Map RNG <data type="..."/> values to Lutaml::Model attribute type
27
28
  # symbols. Mirrors Lutaml::Xml::Schema::RelaxngSchema.get_relaxng_type
@@ -59,7 +60,9 @@ module Lutaml
59
60
  classes, namespaces = compile_grammar(grammar)
60
61
 
61
62
  entries =
62
- classes.map { |name, spec| CompiledOutput::Entry.new(name, spec, :model) } +
63
+ classes.map do |name, spec|
64
+ CompiledOutput::Entry.new(name, spec, :model)
65
+ end +
63
66
  namespaces.map { |name, spec| CompiledOutput::Entry.new(name, spec, :namespace) }
64
67
 
65
68
  CompiledOutput.new(
@@ -89,7 +92,8 @@ module Lutaml
89
92
  return rng if rng.is_a?(::Rng::Grammar)
90
93
 
91
94
  if options[:location]
92
- ::Rng.parse(rng, location: options[:location], resolve_external: true)
95
+ ::Rng.parse(rng, location: options[:location],
96
+ resolve_external: true)
93
97
  else
94
98
  ::Rng::Grammar.from_xml(rng)
95
99
  end
@@ -165,7 +169,8 @@ module Lutaml
165
169
  ns = Definitions::Namespace.new(
166
170
  class_name: class_name,
167
171
  uri: uri,
168
- prefix_default: unique_prefix(namespaces, NamespaceNaming.prefix_for(uri)),
172
+ prefix_default: unique_prefix(namespaces,
173
+ NamespaceNaming.prefix_for(uri)),
169
174
  element_form_default: :qualified,
170
175
  )
171
176
  namespaces[class_name] = ns
@@ -29,7 +29,8 @@ module Lutaml
29
29
  array.each_with_index do |element, i|
30
30
  next unless element == builder
31
31
 
32
- array[i] = Array(object.public_send(Utils.snake_case(builder.name)))[index]
32
+ array[i] =
33
+ Array(object.public_send(Utils.snake_case(builder.name)))[index]
33
34
  index += 1
34
35
  end
35
36
  end
@@ -25,7 +25,8 @@ module Lutaml
25
25
  def build(complex_type)
26
26
  model = Definitions::Model.new(
27
27
  class_name: Utils.camel_case(complex_type.name),
28
- xml_root: Definitions::XmlRoot.new(kind: :element, name: complex_type.name),
28
+ xml_root: Definitions::XmlRoot.new(kind: :element,
29
+ name: complex_type.name),
29
30
  mixed: !!complex_type.mixed,
30
31
  namespace_class_name: @parent.namespace_class_name,
31
32
  )
@@ -49,7 +50,8 @@ module Lutaml
49
50
  base_name = group.name || Utils.last_of_split(group.ref)
50
51
  Definitions::Model.new(
51
52
  class_name: Utils.camel_case(base_name),
52
- xml_root: Definitions::XmlRoot.new(kind: :type_name, name: base_name),
53
+ xml_root: Definitions::XmlRoot.new(kind: :type_name,
54
+ name: base_name),
53
55
  members: inner_members,
54
56
  module_wrappable: false,
55
57
  lazy_register: true,
@@ -56,7 +56,8 @@ module Lutaml
56
56
  type: build_type_ref(resolve_element_type(element)),
57
57
  xml_name: element.name,
58
58
  kind: :element,
59
- collection: collection_from_occurs(element.min_occurs, element.max_occurs),
59
+ collection: collection_from_occurs(element.min_occurs,
60
+ element.max_occurs),
60
61
  default: element.default,
61
62
  render_default: !element.default.nil?,
62
63
  render_empty: element_required?(element.min_occurs),
@@ -75,7 +76,9 @@ module Lutaml
75
76
  end
76
77
 
77
78
  def build_choice(choice)
78
- alternatives = ElementOrder.resolved(choice).filter_map { |item| build_member(item) }
79
+ alternatives = ElementOrder.resolved(choice).filter_map do |item|
80
+ build_member(item)
81
+ end
79
82
  Definitions::Choice.new(
80
83
  alternatives: alternatives,
81
84
  header: choice_header(choice),
@@ -97,11 +100,18 @@ module Lutaml
97
100
  # ----- TypeRef ---------------------------------------------------
98
101
 
99
102
  def build_type_ref(raw_type)
100
- return Definitions::TypeRef.new(kind: :symbol, value: "string") if raw_type.nil?
101
- return Definitions::TypeRef.new(kind: :w3c, value: raw_type) if w3c_type?(raw_type)
103
+ if raw_type.nil?
104
+ return Definitions::TypeRef.new(kind: :symbol,
105
+ value: "string")
106
+ end
107
+ if w3c_type?(raw_type)
108
+ return Definitions::TypeRef.new(kind: :w3c,
109
+ value: raw_type)
110
+ end
102
111
 
103
112
  local = Utils.last_of_split(raw_type)
104
- Definitions::TypeRef.new(kind: :symbol, value: Utils.snake_case(local))
113
+ Definitions::TypeRef.new(kind: :symbol,
114
+ value: Utils.snake_case(local))
105
115
  end
106
116
 
107
117
  private
@@ -131,7 +141,8 @@ module Lutaml
131
141
  def resolve_attribute_type(attr)
132
142
  return attr.type if attr.type
133
143
 
134
- register_anonymous_simple_type("ST_#{attr.name}", attr.simple_type)
144
+ register_anonymous_simple_type("ST_#{attr.name}",
145
+ attr.simple_type)
135
146
  end
136
147
 
137
148
  def resolve_element_ref(element)
@@ -143,7 +154,8 @@ module Lutaml
143
154
  type: target.type,
144
155
  xml_name: target.xml_name,
145
156
  kind: :element,
146
- collection: collection_from_occurs(element.min_occurs, element.max_occurs),
157
+ collection: collection_from_occurs(element.min_occurs,
158
+ element.max_occurs),
147
159
  default: target.default,
148
160
  render_default: !target.default.nil?,
149
161
  render_empty: element_required?(element.min_occurs),
@@ -152,28 +164,42 @@ module Lutaml
152
164
 
153
165
  def resolve_element_type(element)
154
166
  return element.type if element.type
155
- return register_anonymous_simple_type("ST_#{element.name}", element.simple_type) if element.simple_type
156
- return register_anonymous_complex_type("CT_#{element.name}", element.complex_type) if element.complex_type
167
+ if element.simple_type
168
+ return register_anonymous_simple_type("ST_#{element.name}",
169
+ element.simple_type)
170
+ end
171
+ if element.complex_type
172
+ return register_anonymous_complex_type("CT_#{element.name}",
173
+ element.complex_type)
174
+ end
157
175
 
158
176
  "string"
159
177
  end
160
178
 
161
179
  def top_level_type_str(item)
162
180
  return item.type if item.respond_to?(:type) && item.type
163
- return register_anonymous_simple_type("ST_#{item.name}", item.simple_type) if item.respond_to?(:simple_type) && item.simple_type
181
+ if item.respond_to?(:simple_type) && item.simple_type
182
+ return register_anonymous_simple_type("ST_#{item.name}",
183
+ item.simple_type)
184
+ end
164
185
 
165
- register_anonymous_complex_type("CT_#{item.name}", item.complex_type) if item.respond_to?(:complex_type) && item.complex_type
186
+ if item.respond_to?(:complex_type) && item.complex_type
187
+ register_anonymous_complex_type("CT_#{item.name}",
188
+ item.complex_type)
189
+ end
166
190
  end
167
191
 
168
192
  def register_anonymous_simple_type(anon_name, anon_node)
169
193
  anon_node.name = anon_name
170
- @parent.simple_types[anon_name] = @parent.build_simple_type(anon_node)
194
+ @parent.simple_types[anon_name] =
195
+ @parent.build_simple_type(anon_node)
171
196
  anon_name
172
197
  end
173
198
 
174
199
  def register_anonymous_complex_type(anon_name, anon_node)
175
200
  anon_node.name = anon_name
176
- @parent.complex_types[anon_name] = @parent.build_complex_type(anon_node)
201
+ @parent.complex_types[anon_name] =
202
+ @parent.build_complex_type(anon_node)
177
203
  anon_name
178
204
  end
179
205
 
@@ -65,7 +65,9 @@ module Lutaml
65
65
  # passed :namespace, every generated complex type gets this
66
66
  # name. Mirrors XmlCompiler::ComplexType#setup_options on main.
67
67
  @namespace_class_name =
68
- requested_uri && @namespace_classes.values.find { |ns| ns.uri == requested_uri }&.class_name
68
+ requested_uri && @namespace_classes.values.find do |ns|
69
+ ns.uri == requested_uri
70
+ end&.class_name
69
71
  end
70
72
 
71
73
  def walk_schemas(schemas)
@@ -79,11 +81,15 @@ module Lutaml
79
81
  end
80
82
 
81
83
  def collect_lookups(schemas)
82
- each_schema_item(schemas) { |item, schema| dispatch_lookup(item, schema) }
84
+ each_schema_item(schemas) do |item, schema|
85
+ dispatch_lookup(item, schema)
86
+ end
83
87
  end
84
88
 
85
89
  def build_complex_types(schemas)
86
- each_schema_item(schemas) { |item, schema| dispatch_complex(item, schema) }
90
+ each_schema_item(schemas) do |item, schema|
91
+ dispatch_complex(item, schema)
92
+ end
87
93
  end
88
94
 
89
95
  def dispatch_lookup(item, schema)
@@ -91,13 +97,17 @@ module Lutaml
91
97
  when Lutaml::Xml::Schema::Xsd::SimpleType
92
98
  @simple_types[item.name] = build_simple_type(item)
93
99
  when Lutaml::Xml::Schema::Xsd::Element
94
- @elements[item.name] = @members_builder.build_top_level_attribute(item, kind: :element)
100
+ @elements[item.name] =
101
+ @members_builder.build_top_level_attribute(item, kind: :element)
95
102
  when Lutaml::Xml::Schema::Xsd::Attribute
96
103
  return if xml_defined_attribute?(schema, item.name)
97
104
 
98
- @attributes[item.name] = @members_builder.build_top_level_attribute(item, kind: :attribute)
105
+ @attributes[item.name] =
106
+ @members_builder.build_top_level_attribute(item,
107
+ kind: :attribute)
99
108
  when Lutaml::Xml::Schema::Xsd::AttributeGroup
100
- @attribute_groups[item.name] = @complex_types_builder.build_attribute_group(item)
109
+ @attribute_groups[item.name] =
110
+ @complex_types_builder.build_attribute_group(item)
101
111
  end
102
112
  end
103
113
 
@@ -117,7 +127,8 @@ module Lutaml
117
127
  next if info[:skippable]
118
128
 
119
129
  str_name = name.to_s
120
- @simple_types[str_name] = @simple_types_builder.build_supported(str_name, info)
130
+ @simple_types[str_name] =
131
+ @simple_types_builder.build_supported(str_name, info)
121
132
  end
122
133
  end
123
134
 
@@ -141,8 +152,13 @@ module Lutaml
141
152
  # Public surface used by the Members sub-builder to register
142
153
  # nested anonymous types it encounters during attribute /
143
154
  # element resolution.
144
- def build_simple_type(simple_type) = @simple_types_builder.build(simple_type)
145
- def build_complex_type(complex_type) = @complex_types_builder.build(complex_type)
155
+ def build_simple_type(simple_type)
156
+ @simple_types_builder.build(simple_type)
157
+ end
158
+
159
+ def build_complex_type(complex_type)
160
+ @complex_types_builder.build(complex_type)
161
+ end
146
162
 
147
163
  private
148
164
 
@@ -150,7 +166,10 @@ module Lutaml
150
166
  # yields each resolved element-order item with its owning schema.
151
167
  def each_schema_item(schemas, &dispatch)
152
168
  schemas.each do |schema|
153
- each_schema_item(schema.include, &dispatch) if schema.include&.any?
169
+ if schema.include&.any?
170
+ each_schema_item(schema.include,
171
+ &dispatch)
172
+ end
154
173
  each_schema_item(schema.import, &dispatch) if schema.import&.any?
155
174
  schema.resolved_element_order.each { |item| yield(item, schema) }
156
175
  end
@@ -9,10 +9,12 @@ module Lutaml
9
9
  module XmlCompiler
10
10
  extend self
11
11
 
12
- autoload :SpecBuilder, "#{__dir__}/xml_compiler/spec_builder"
13
- autoload :RegistryGenerator, "#{__dir__}/xml_compiler/registry_generator"
14
- autoload :SupportedDataTypes, "#{__dir__}/xml_compiler/supported_data_types"
15
- autoload :ElementOrder, "#{__dir__}/xml_compiler/element_order"
12
+ autoload :SpecBuilder, "#{__dir__}/xml_compiler/spec_builder"
13
+ autoload :RegistryGenerator,
14
+ "#{__dir__}/xml_compiler/registry_generator"
15
+ autoload :SupportedDataTypes,
16
+ "#{__dir__}/xml_compiler/supported_data_types"
17
+ autoload :ElementOrder, "#{__dir__}/xml_compiler/element_order"
16
18
 
17
19
  ELEMENT_ORDER_IGNORABLE = %w[import include].freeze
18
20
 
@@ -60,7 +62,10 @@ module Lutaml
60
62
  FileWriter.write(output, dir, registry_generator: RegistryGenerator)
61
63
  true
62
64
  else
63
- ClassLoader.load(output, registry_generator: RegistryGenerator) if options[:load_classes]
65
+ if options[:load_classes]
66
+ ClassLoader.load(output,
67
+ registry_generator: RegistryGenerator)
68
+ end
64
69
  output.sources
65
70
  end
66
71
  end
@@ -188,6 +188,7 @@ module Lutaml
188
188
 
189
189
  attr = Attribute.new(name, type, options)
190
190
  @attributes[name] = attr
191
+ @merged_attributes_cache = nil
191
192
  define_attribute_methods(attr)
192
193
 
193
194
  attr
@@ -115,13 +115,18 @@ module Lutaml
115
115
  # Get all attributes for this model
116
116
  #
117
117
  # Merges class-level attributes with register-specific attributes.
118
+ # Memoized per register: this is on the per-element deserialization
119
+ # hot path, and the merged hash is stable until an attribute, import
120
+ # or register record mutates (those clear @merged_attributes_cache).
118
121
  #
119
122
  # @param register [Symbol, nil] The register context
120
123
  # @return [Hash] The attributes hash
121
124
  def attributes(register = nil)
122
125
  ensure_imports!(register) if finalized?
123
126
  if @register_records&.any?
124
- @attributes.merge(@register_records[extract_register_id(register)][:attributes])
127
+ register_id = extract_register_id(register)
128
+ (@merged_attributes_cache ||= {})[register_id] ||=
129
+ @attributes.merge(@register_records[register_id][:attributes])
125
130
  else
126
131
  @attributes
127
132
  end
@@ -183,6 +188,9 @@ module Lutaml
183
188
  GlobalContext.resolver.clear_cache(register_id)
184
189
  end
185
190
 
191
+ # Clear memoized attribute merge (see .attributes)
192
+ @merged_attributes_cache = nil
193
+
186
194
  # Clear per-Attribute type caches (stale entries from GC'd TypeContext objects)
187
195
  class_attributes.each_value(&:clear_type_cache)
188
196
  @register_records&.each_value do |record|
@@ -121,6 +121,7 @@ module Lutaml
121
121
  @register_records[register_id][:choice_attributes].concat(
122
122
  deep_duplicate_choice_attributes(model, register_id),
123
123
  )
124
+ @merged_attributes_cache = nil
124
125
  end
125
126
  private :register_only_import_model_attributes
126
127
 
@@ -175,18 +175,19 @@ module Lutaml
175
175
  @using_default[attribute_name]
176
176
  end
177
177
 
178
- # rubocop:disable Style/ArgumentsForwarding -- anonymous * requires Ruby 3.2+, but required_ruby_version >= 3.0
179
- def method_missing(method_name, *args)
178
+ def method_missing(method_name, *, &)
180
179
  if method_name.to_s.end_with?("=") && attribute_exist?(method_name)
181
180
  define_singleton_method(method_name) do |value|
182
181
  instance_variable_set(:"@#{method_name.to_s.chomp('=')}", value)
183
182
  end
184
- send(method_name, *args)
183
+ send(method_name, *)
184
+ elsif ::Lutaml::Model::Attribute.default_evaluation? &&
185
+ self.class.respond_to?(method_name)
186
+ self.class.public_send(method_name, *, &)
185
187
  else
186
188
  super
187
189
  end
188
190
  end
189
- # rubocop:enable Style/ArgumentsForwarding
190
191
 
191
192
  def respond_to_missing?(method_name, include_private = false)
192
193
  (method_name.to_s.end_with?("=") && attribute_exist?(method_name)) ||
@@ -131,7 +131,8 @@ module Lutaml
131
131
 
132
132
  def validate_members!(members)
133
133
  unless members.is_a?(::Array) && !members.empty?
134
- raise ArgumentError, "union type must be a non-empty array of types"
134
+ raise ArgumentError,
135
+ "union type must be a non-empty array of types"
135
136
  end
136
137
 
137
138
  resolved = members.map { |member| resolve_member!(member) }
@@ -166,7 +167,9 @@ module Lutaml
166
167
  return :no_match if keys.nil? || keys.empty?
167
168
 
168
169
  field_names = member_field_names(member, format, register)
169
- return :no_match unless keys.all? { |key| field_names.include?(key) }
170
+ return :no_match unless keys.all? do |key|
171
+ field_names.include?(key)
172
+ end
170
173
 
171
174
  # Plain-Ruby assignment (format nil) builds straight from the
172
175
  # attribute hash, mirroring how non-union model attributes accept
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.8.19"
5
+ VERSION = "0.9.1"
6
6
  end
7
7
  end