Class: Lutaml::KeyValue::Transformation

Inherits:
Model::Transformation show all
Includes:
Model::RenderPolicy
Defined in:
lib/lutaml/key_value/transformation.rb,
lib/lutaml/key_value/transformation/rule_compiler.rb,
lib/lutaml/key_value/transformation/value_serializer.rb,
lib/lutaml/key_value/transformation/collection_serializer.rb

Overview

KeyValue-specific transformation implementation.

Transforms model instances into KeyValueElement trees without serialization concerns. This provides the same architectural pattern as Xml::Transformation but for key-value formats (JSON, YAML, TOML).

Architecture:

  • Content Layer: KeyValueElement defines WHAT to serialize

  • Presentation Layer: Adapters define HOW to serialize

This is a critical step toward symmetric OOP architecture across all serialization formats.

Defined Under Namespace

Classes: CollectionSerializer, RuleCompiler, ValueSerializer

Instance Attribute Summary

Attributes inherited from Model::Transformation

#compiled_rules, #format, #model_class, #register

Instance Method Summary collapse

Methods included from Model::RenderPolicy

derived_attribute_for?, #should_skip_delegated_value?

Methods inherited from Model::Transformation

#all_namespaces

Constructor Details

#initialize(model_class, mapping_dsl, format, register) ⇒ Transformation

Initialize serializers before calling super This must happen before super calls freeze

Parameters:

  • model_class (Class)

    The model class to transform

  • mapping_dsl (Mapping)

    The mapping DSL to compile

  • format (Symbol)

    The format (:json, :yaml, :toml)

  • register (Register, nil)

    The register for type resolution



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
# File 'lib/lutaml/key_value/transformation.rb', line 33

def initialize(model_class, mapping_dsl, format, register)
  # Extract register_id before super
  register_id = extract_register_id(register)
  transformation_factory = ->(type_class) {
    build_child_transformation(type_class)
  }

  # Set up serializers before calling super
  # The super will call compile_rules and freeze
  @rule_compiler = RuleCompiler.new(
    model_class: model_class,
    register_id: register_id,
    format: format,
    transformation_factory: transformation_factory,
  )

  @value_serializer = ValueSerializer.new(
    format: format,
    register_id: register_id,
    model_class: model_class,
    transformation_factory: transformation_factory,
  )

  @collection_serializer = CollectionSerializer.new(
    format: format,
    register_id: register_id,
    value_serializer: @value_serializer,
    transformation_factory: transformation_factory,
  )

  super
end

Instance Method Details

#build_child_transformation(type_class, format = self.format, register = self.register) ⇒ Transformation?

Build child transformation for nested model

Parameters:

  • type_class (Class)

    The nested model class

  • format (Symbol) (defaults to: self.format)

    The format

  • register (Register, nil) (defaults to: self.register)

    The register

Returns:



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/lutaml/key_value/transformation.rb', line 87

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

  # Prevent infinite recursion for self-referential models
  compilation_stack = Thread.current[:lutaml_compilation_stack] ||= []
  return nil if compilation_stack.include?(type_class)

  compilation_stack.push(type_class)
  begin
    # Get the mapping for the current format
    mapping = type_class.mappings_for(format,
                                      extract_register_id(register))

    # Create a new Transformation instance with the mapping
    self.class.new(type_class, mapping, format, register)
  ensure
    compilation_stack.pop
  end
end

#extract_register_id(register) ⇒ Symbol?

Extract register_id from register parameter

Parameters:

  • register (Register, Symbol, nil)

    The register

Returns:

  • (Symbol, nil)

    The register ID



70
71
72
73
74
75
76
77
78
79
# File 'lib/lutaml/key_value/transformation.rb', line 70

def extract_register_id(register)
  case register
  when Lutaml::Model::Register
    register.id
  when Symbol
    register
  else
    Lutaml::Model::Config.default_register
  end
end

#transform(model_instance, options = {}) ⇒ Lutaml::KeyValue::DataModel::Element

Transform a model instance into KeyValueElement tree

Parameters:

  • model_instance (Object)

    The model instance to transform

  • options (Hash) (defaults to: {})

    Transformation options (supports :only, :except for filtering)

Returns:



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

def transform(model_instance, options = {})
  # For key-value formats, we typically don't have a named root
  # Instead, we create an anonymous root that holds all attributes
  root = Lutaml::KeyValue::DataModel::Element.new("__root__")

  # Apply each compiled rule (with filtering support)
  compiled_rules.each do |rule|
    # Check if this rule should be applied based on only/except options
    next unless valid_mapping?(rule, options)

    apply_rule(root, rule, model_instance, options)
  end

  if ENV["DEBUG_KEYED_COLLECTION"]
    puts "=== TRANSFORM COMPLETE ==="
    puts "root.children.count: #{root.children.count}"
    root.children.each do |child|
      puts "  child key=#{child.key.inspect}, value=#{child.value.inspect}, to_hash=#{child.to_hash.inspect}"
    end
    puts "root.to_hash: #{root.to_hash.inspect}"
  end

  root
end