Module: Lutaml::Model::Serialize::TransformationBuilder

Included in:
ClassMethods
Defined in:
lib/lutaml/model/serialize/transformation_builder.rb

Overview

Handles transformation building for Serialize::ClassMethods

Extracted from serialize.rb to improve code organization. Provides methods for building transformations and processing types.

Instance Method Summary collapse

Instance Method Details

#build_transformation(format, register_id) ⇒ Transformation

Build a new transformation instance for the format

This method creates a pre-compiled transformation by:

  1. Ensuring all mappings are imported

  2. Getting the mapping DSL for the format

  3. Creating format-specific transformation class

Parameters:

  • format (Symbol)

    The format (:xml, :json, :yaml, etc.)

  • register_id (Symbol)

    The register ID

Returns:



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/lutaml/model/serialize/transformation_builder.rb', line 49

def build_transformation(format, register_id)
  # Get the mapping DSL for this format
  mapping_dsl = mappings_for(format, register_id)

  # Pass register_id directly (Attribute#type handles Symbol)

  # Create format-specific transformation using registered builders
  # XML builder is registered by Lutaml::Xml at load time via
  # TransformationRegistry.register_builder(:xml, ...)
  case format
  when :json, :yaml, :toml, :hash
    # Key-value formats use KeyValue::Transformation (symmetric OOP architecture)
    Lutaml::KeyValue::Transformation.new(self, mapping_dsl,
                                         format, register_id)
  else
    # For other formats (including :xml), use registered builder or return mapping_dsl
    builder = TransformationRegistry.builder_for(format)
    if builder
      builder.build(self, mapping_dsl, format, register_id)
    else
      mapping_dsl
    end
  end
end

#process_reference_type(type, options) ⇒ Array

Process a reference type specification

Parameters:

  • type (Hash)

    The type specification

  • options (Hash)

    The options hash to update

Returns:

  • (Array)

    Tuple of [type, options]



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/lutaml/model/serialize/transformation_builder.rb', line 102

def process_reference_type(type, options)
  ref_spec = type[:ref] || type["ref"]
  validate_reference_spec!(ref_spec)

  model_class, key_attr = ref_spec
  options[:ref_model_class] = model_class
  options[:ref_key_attribute] = key_attr
  type = Lutaml::Model::Type::Reference

  [type, options]
end

#process_type_hash(type, options) ⇒ Array

Process a type hash (for reference types)

Parameters:

  • type (Hash)

    The type specification

  • options (Hash)

    The options hash to update

Returns:

  • (Array)

    Tuple of [type, options]



79
80
81
82
83
84
85
86
87
# File 'lib/lutaml/model/serialize/transformation_builder.rb', line 79

def process_type_hash(type, options)
  if reference_type?(type)
    type, options = process_reference_type(type, options)
  else
    type = nil
  end

  [type, options]
end

#reference_type?(type) ⇒ Boolean

Check if a type hash specifies a reference type

Parameters:

  • type (Hash)

    The type specification

Returns:

  • (Boolean)

    True if this is a reference type



93
94
95
# File 'lib/lutaml/model/serialize/transformation_builder.rb', line 93

def reference_type?(type)
  type.key?(:ref) || type.key?("ref")
end

#transformation_for(format, register = nil) ⇒ Transformation?

Get or build a pre-compiled transformation for the specified format

Transformations are cached centrally in TransformationRegistry singleton to ensure compilation happens only once per model class and format.

CRITICAL: Uses TransformationRegistry for cycle detection to prevent infinite recursion on self-referential models (e.g., Address.address: Address)

Architecture (Phase 5.1 refactoring):

  • Model classes define mappings (declarative DSL) - stateless

  • TransformationRegistry manages ALL transformation caches - single source of truth

  • Transformation objects execute serialization - runtime instances

Register resolution: If the caller passes a parent register (e.g., :default) but this class declares its own ‘lutaml_default_register` (e.g., :mml_v2), the child’s register takes precedence. This ensures cross-register embedding works transparently — the parent doesn’t need to know the child’s register.

Parameters:

  • format (Symbol)

    The format (:xml, :json, :yaml, etc.)

  • register (Symbol, Register, nil) (defaults to: nil)

    The register for type resolution

Returns:

  • (Transformation, nil)

    The pre-compiled transformation, or nil if cycle detected



32
33
34
35
36
37
# File 'lib/lutaml/model/serialize/transformation_builder.rb', line 32

def transformation_for(format, register = nil)
  resolved_register = Lutaml::Model::Register.resolve_for_child(self,
                                                                register)
  TransformationRegistry.instance.get_or_build_transformation(self,
                                                              format, resolved_register)
end

#validate_reference_spec!(ref_spec) ⇒ Object

Validate a reference specification

Parameters:

  • ref_spec (Object)

    The reference specification

Raises:

  • (ArgumentError)

    If the specification is invalid



118
119
120
121
122
123
# File 'lib/lutaml/model/serialize/transformation_builder.rb', line 118

def validate_reference_spec!(ref_spec)
  return if ref_spec.is_a?(Array) && ref_spec.length == 2

  raise ArgumentError,
        "ref: syntax requires an array [model_class, key_attribute]"
end