Module: Lutaml::Xml::TransformationSupport::RuleCompiler

Included in:
Lutaml::Xml::Transformation
Defined in:
lib/lutaml/xml/transformation/rule_compiler.rb

Overview

Module for compiling XML mapping DSL into pre-compiled rules.

Transforms mapping DSL definitions into CompiledRule objects that can be used during serialization without triggering type resolution.

Instance Method Summary collapse

Instance Method Details

#build_child_transformation(type_class, register) ⇒ Transformation?

Build child transformation for nested model

Parameters:

  • type_class (Class)

    The nested model class

  • register (Register, nil)

    The register

Returns:



223
224
225
226
227
# File 'lib/lutaml/xml/transformation/rule_compiler.rb', line 223

def build_child_transformation(type_class, register)
  return nil unless type_class.is_a?(Class) && type_class.include?(Lutaml::Model::Serialize)

  type_class.transformation_for(:xml, register)
end

#build_value_transformer(mapping_rule, attr) ⇒ Proc, ...

Build value transformer from mapping rule and attribute

Parameters:

  • mapping_rule (Xml::MappingRule)

    The mapping rule

  • attr (Attribute, nil)

    The attribute definition (nil for custom methods)

Returns:

  • (Proc, Hash, nil)

    Value transformer



234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/lutaml/xml/transformation/rule_compiler.rb', line 234

def build_value_transformer(mapping_rule, attr)
  mapping_transform = mapping_rule.transform

  attr_transform = if attr.nil?
                     nil
                   else
                     attr.transform
                   end

  if mapping_transform && !mapping_transform.empty?
    return mapping_transform
  end

  if attr_transform && !attr_transform.empty?
    return attr_transform
  end

  nil
end

#compile_attribute_rule(mapping_rule, model_class, register_id, _register) ⇒ CompiledRule?

Compile an attribute mapping rule

Parameters:

  • mapping_rule (Xml::MappingRule)

    The mapping rule to compile

  • model_class (Class)

    The model class

  • register_id (Symbol, nil)

    The register ID

  • register (Register, nil)

    The register object

Returns:

  • (CompiledRule, nil)

    Compiled rule or nil



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/lutaml/xml/transformation/rule_compiler.rb', line 125

def compile_attribute_rule(mapping_rule, model_class, register_id,
_register)
  # Access custom_methods early to check if we need to infer attribute name
  custom_methods_value = mapping_rule.custom_methods

  # Get attribute name from mapping rule, or infer from custom methods
  attr_name = infer_attribute_name(mapping_rule, custom_methods_value,
                                   model_class, register_id)
  return nil unless attr_name

  # Handle delegated attributes
  if mapping_rule.delegate
    return compile_delegated_attribute_rule(
      mapping_rule, model_class, register_id,
      attr_name, custom_methods_value
    )
  end

  # Get attribute definition from model class (non-delegated)
  attr = model_class.attributes(register_id)&.[](attr_name)

  # For custom methods without a real attribute, create the rule anyway
  if attr.nil? && !custom_methods_value.empty?
    return compile_custom_method_attribute_rule(
      mapping_rule, attr_name, custom_methods_value
    )
  end

  return nil unless attr

  compile_standard_attribute_rule(mapping_rule, attr, attr_name,
                                  register_id, custom_methods_value)
end

#compile_content_rule(mapping_rule, model_class, register_id) ⇒ CompiledRule?

Compile a content mapping rule

Parameters:

  • mapping_rule (Xml::MappingRule)

    The content mapping rule

  • model_class (Class)

    The model class

  • register_id (Symbol, nil)

    The register ID

Returns:

  • (CompiledRule, nil)

    Compiled rule or nil



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/lutaml/xml/transformation/rule_compiler.rb', line 165

def compile_content_rule(mapping_rule, model_class, register_id)
  custom_methods_value = mapping_rule.custom_methods

  # Get attribute name from mapping rule, or use placeholder for custom methods
  attr_name = mapping_rule.to
  if (attr_name.nil? || attr_name.empty?) && !custom_methods_value.empty?
    attr_name = :__content__
  end

  return nil unless attr_name

  attr = model_class.attributes(register_id)&.[](attr_name)

  # For custom methods without a real attribute
  if attr.nil? && !custom_methods_value.empty?
    return build_content_rule(mapping_rule, attr_name, nil,
                              custom_methods_value)
  end

  return nil unless attr

  build_content_rule(mapping_rule, attr_name, attr.type(register_id),
                     nil)
end

#compile_element_rule(mapping_rule, model_class, register_id, register) ⇒ CompiledRule?

Compile an element mapping rule

Parameters:

  • mapping_rule (Xml::MappingRule)

    The mapping rule to compile

  • model_class (Class)

    The model class

  • register_id (Symbol, nil)

    The register ID

  • register (Register, nil)

    The register object

Returns:

  • (CompiledRule, nil)

    Compiled rule or nil



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/lutaml/xml/transformation/rule_compiler.rb', line 84

def compile_element_rule(mapping_rule, model_class, register_id,
register)
  # Access custom_methods early to check if we need to infer attribute name
  custom_methods_value = mapping_rule.custom_methods

  # Get attribute name from mapping rule, or infer from custom methods
  attr_name = infer_attribute_name(mapping_rule, custom_methods_value,
                                   model_class, register_id)
  return nil unless attr_name

  # Handle delegated attributes
  if mapping_rule.delegate
    return compile_delegated_element_rule(
      mapping_rule, model_class, register_id, register,
      attr_name, custom_methods_value
    )
  end

  # Get attribute definition from model class (non-delegated)
  attr = model_class.attributes(register_id)&.[](attr_name)

  # For custom methods without a real attribute, create the rule anyway
  if attr.nil? && !custom_methods_value.empty?
    return compile_custom_method_element_rule(
      mapping_rule, attr_name, custom_methods_value
    )
  end

  return nil unless attr

  compile_standard_element_rule(mapping_rule, attr, attr_name,
                                register_id, register, custom_methods_value)
end

#compile_raw_rule(mapping_rule, model_class, register_id) ⇒ CompiledRule?

Compile a raw mapping rule (map_all directive)

Parameters:

  • mapping_rule (Xml::MappingRule)

    The raw mapping rule

  • model_class (Class)

    The model class

  • register_id (Symbol, nil)

    The register ID

Returns:

  • (CompiledRule, nil)

    Compiled rule or nil



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/lutaml/xml/transformation/rule_compiler.rb', line 196

def compile_raw_rule(mapping_rule, model_class, register_id)
  attr_name = mapping_rule.to
  return nil unless attr_name

  attr = model_class.attributes(register_id)&.[](attr_name)
  return nil unless attr

  value_transformer = build_value_transformer(mapping_rule, attr)
  value_map = mapping_rule.raw_value_map

  ::Lutaml::Model::CompiledRule.new(
    attribute_name: attr_name,
    serialized_name: nil,
    attribute_type: attr.type(register_id),
    value_transformer: value_transformer,
    mapping_type: :raw,
    render_default: mapping_rule.render_default,
    value_map: value_map,
    custom_methods: mapping_rule.custom_methods,
  )
end

#compile_rules(mapping_dsl, model_class, register_id, register) ⇒ Array<CompiledRule>

Compile XML mapping DSL into pre-compiled rules

Parameters:

  • mapping_dsl (Xml::Mapping)

    The XML mapping to compile

  • model_class (Class)

    The model class

  • register_id (Symbol, nil)

    The register ID

  • register (Register, nil)

    The register object

Returns:

  • (Array<CompiledRule>)

    Array of compiled transformation rules



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'lib/lutaml/xml/transformation/rule_compiler.rb', line 18

def compile_rules(mapping_dsl, model_class, register_id, register)
  return [] unless mapping_dsl

  rules = []

  # Compile element mappings
  elements_to_compile = if register_id && register_id != :default &&
      mapping_dsl.elements(register_id).any?
                          mapping_dsl.elements(register_id)
                        else
                          mapping_dsl.elements
                        end
  elements_to_compile.each do |mapping_rule|
    rule = compile_element_rule(mapping_rule, model_class, register_id,
                                register)
    rules << rule if rule
  end

  # Compile attribute mappings
  attributes_to_compile = if register_id && register_id != :default &&
      mapping_dsl.attributes(register_id).any?
                            mapping_dsl.attributes(register_id)
                          else
                            mapping_dsl.attributes
                          end
  attributes_to_compile.each do |mapping_rule|
    rule = compile_attribute_rule(mapping_rule, model_class,
                                  register_id, register)
    rules << rule if rule
  end

  # Compile content mapping if present
  if mapping_dsl.content_mapping
    rule = compile_content_rule(mapping_dsl.content_mapping,
                                model_class, register_id)
    rules << rule if rule
  end

  # Compile raw mapping (map_all directive) if present
  if mapping_dsl.raw_mapping
    rule = compile_raw_rule(mapping_dsl.raw_mapping, model_class,
                            register_id)
    rules << rule if rule
  end

  # Add a pseudo-rule for root namespace if present
  # This ensures root namespace is included in all_namespaces
  if mapping_dsl.namespace_class
    rules << ::Lutaml::Model::CompiledRule.new(
      attribute_name: :__root_namespace__,
      serialized_name: "__root__",
      namespace_class: mapping_dsl.namespace_class,
      mapping_type: :root_namespace,
    )
  end

  rules.compact
end