Module: Lutaml::Model::Serialize::Initialization

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

Overview

Handles initialization and namespace methods for Serialize::ClassMethods

Extracted from serialize.rb to improve code organization. Provides methods for class initialization and namespace handling.

Instance Method Summary collapse

Instance Method Details

#add_custom_handling_methods_to_model(klass) ⇒ Object

Add custom handling methods to a model class

Parameters:

  • klass (Class)

    The model class



223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
# File 'lib/lutaml/model/serialize/initialization.rb', line 223

def add_custom_handling_methods_to_model(klass)
  Utils.add_method_if_not_defined(klass,
                                  :using_default_for) do |attribute_name|
    @using_default ||= {}
    @using_default[attribute_name] = true
  end

  Utils.add_method_if_not_defined(klass,
                                  :value_set_for) do |attribute_name|
    @using_default ||= {}
    @using_default[attribute_name] = false
  end

  Utils.add_method_if_not_defined(klass,
                                  :values_set_for) do |attribute_names|
    @using_default ||= {}
    attribute_names.each { |name| @using_default[name] = false }
  end

  Utils.add_method_if_not_defined(klass,
                                  :using_default?) do |attribute_name|
    @using_default ||= {}
    !!@using_default[attribute_name]
  end

  # Hook for format-specific model methods (e.g., XML adds ordered, mixed, element_order)
  add_format_specific_model_methods(klass)
end

#add_format_specific_model_methods(_klass) ⇒ Object

Hook for format-specific model methods. XML overrides via FormatConversion prepend to add XML accessors.

Parameters:

  • _klass (Class)

    The model class



256
257
258
# File 'lib/lutaml/model/serialize/initialization.rb', line 256

def add_format_specific_model_methods(_klass)
  # No-op by default
end

#allocate_for_deserialization(register = nil) ⇒ Object

Allocate an instance for deserialization without calling initialize.

Skips the expensive initialize_attributes pass (which iterates all attributes to set defaults). The XML mapping pipeline sets values directly via rule.deserialize instead. Uses Hash.new(true) as the default for @using_default so that using_default? returns true for all attributes until value_set_for is called.

Parameters:

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

    The register context

Returns:

  • (Object)

    The allocated instance



324
325
326
327
328
329
# File 'lib/lutaml/model/serialize/initialization.rb', line 324

def allocate_for_deserialization(register = nil)
  instance = allocate
  register_id = extract_register_id(register)
  instance.finalize_deserialization(register_id)
  instance
end

#attributes(register = nil) ⇒ Hash

Get all attributes for this model

Merges class-level attributes with register-specific attributes.

Parameters:

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

    The register context

Returns:

  • (Hash)

    The attributes hash



121
122
123
124
125
126
127
128
# File 'lib/lutaml/model/serialize/initialization.rb', line 121

def attributes(register = nil)
  ensure_imports!(register) if finalized?
  if @register_records&.any?
    @attributes.merge(@register_records[extract_register_id(register)][:attributes])
  else
    @attributes
  end
end

#cast(value) ⇒ Object

Cast a value (pass-through implementation)

Parameters:

  • value (Object)

    The value to cast

Returns:

  • (Object)

    The same value



264
265
266
# File 'lib/lutaml/model/serialize/initialization.rb', line 264

def cast(value)
  value
end

#choice(min: 1, max: 1, format: nil, &block) ⇒ Object

Define a choice constraint

Parameters:

  • min (Integer) (defaults to: 1)

    Minimum number of choices

  • max (Integer) (defaults to: 1)

    Maximum number of choices

  • block (Proc)

    The choice definition block



291
292
293
294
295
296
# File 'lib/lutaml/model/serialize/initialization.rb', line 291

def choice(min: 1, max: 1, format: nil, &block)
  @choice_attributes << Choice.new(self, min, max,
                                   format: format).tap do |c|
    c.instance_eval(&block)
  end
end

#choice_attributes(register = nil) ⇒ Array

Get all choice attributes for this model

Merges class-level choice attributes with register-specific choice attributes.

Parameters:

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

    The register context

Returns:

  • (Array)

    The choice attributes array



143
144
145
146
147
148
149
150
# File 'lib/lutaml/model/serialize/initialization.rb', line 143

def choice_attributes(register = nil)
  ensure_imports!(register) if finalized?
  if @register_records&.any?
    @choice_attributes + @register_records[extract_register_id(register)][:choice_attributes]
  else
    @choice_attributes
  end
end

#class_attributesHash

Raw class-level attributes without register merging. Used by initialize_attrs during class inheritance.

Returns:

  • (Hash)

    The raw attributes hash



133
134
135
# File 'lib/lutaml/model/serialize/initialization.rb', line 133

def class_attributes
  @attributes
end

#clear_cache(register_id = nil) ⇒ Object

Clear all cached data for this model class

Centralized caching (Phase 11.5):

  • Type caches: GlobalContext.resolver

  • Mapping caches: TransformationRegistry

  • Transformation caches: TransformationRegistry

Parameters:

  • register_id (Symbol, nil) (defaults to: nil)

    If provided, only clear cache for this specific context



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/lutaml/model/serialize/initialization.rb', line 180

def clear_cache(register_id = nil)
  # Clear centralized type cache in GlobalContext.resolver
  if defined?(Lutaml::Model::GlobalContext)
    GlobalContext.resolver.clear_cache(register_id)
  end

  # Clear per-Attribute type caches (stale entries from GC'd TypeContext objects)
  class_attributes.each_value(&:clear_type_cache)
  @register_records&.each_value do |record|
    record[:attributes]&.each_value(&:clear_type_cache)
  end

  # Clear centralized mapping and transformation caches
  # (Single Source of Truth - no longer uses instance variables)
  TransformationRegistry.instance.clear

  # Clear Transform cache (uses class identity as key)
  Transform.invalidate_for(self, register_id)

  # Clear import resolution guard flags so imports can be re-resolved
  instance_variables.each do |ivar|
    ivar_s = ivar.to_s
    remove_instance_variable(ivar) if ivar_s.start_with?("@_imports_resolved_") ||
      ivar_s == "@_register_methods_defined"
  end
end

#deep_duplicate_choice_attributes(source_class, register = nil) ⇒ Array

Deep duplicate choice attributes from a source class

Parameters:

  • source_class (Class)

    The source class

Returns:

  • (Array)

    The duplicated choice attributes



108
109
110
111
112
113
# File 'lib/lutaml/model/serialize/initialization.rb', line 108

def deep_duplicate_choice_attributes(source_class, register = nil)
  choice_attrs = Array(source_class.choice_attributes)
  choice_attrs.map do |choice_attr|
    choice_attr.deep_duplicate(self, register)
  end
end

#ensure_format_mapping_imports!(_register = nil) ⇒ Object

Hook for format-specific mapping import resolution. Override in format modules (e.g., XML prepends to resolve XML mapping imports).

Parameters:

  • _register (Symbol, nil) (defaults to: nil)

    The register context



168
169
170
# File 'lib/lutaml/model/serialize/initialization.rb', line 168

def ensure_format_mapping_imports!(_register = nil)
  # No-op by default; XML overrides via prepend
end

#ensure_imports!(register = nil) ⇒ Object

Ensure all imports are resolved

Parameters:

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

    The register context



155
156
157
158
159
160
161
162
# File 'lib/lutaml/model/serialize/initialization.rb', line 155

def ensure_imports!(register = nil)
  ensure_model_imports!(register)
  ensure_choice_imports!(register)
  ensure_restrict_attributes!(register)
  # Hook for format-specific mapping import resolution.
  # XML overrides this to call mappings[:xml]&.ensure_mappings_imported!(register)
  ensure_format_mapping_imports!(register)
end

#ensure_register_methods_defined(register_id) ⇒ Object

Define register-specific attribute methods on the class itself.

Called once per (class, register) combination. Replaces per-instance singleton class allocation with class-level method definitions, preserving Ruby’s inline method cache optimization.

Parameters:

  • register_id (Symbol)

    The register ID



338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# File 'lib/lutaml/model/serialize/initialization.rb', line 338

def ensure_register_methods_defined(register_id)
  return if register_id == :default

  @_register_methods_defined ||= {}
  return if @_register_methods_defined[register_id]

  reg_record = register_records[register_id]
  return unless reg_record

  default_attrs = class_attributes || {}
  reg_record_attrs = reg_record[:attributes] || {}

  reg_record_attrs.each do |name, attr|
    next if default_attrs.key?(name)
    next if method_defined?(name, false)

    if attr.collection?
      define_collection_register_methods(name)
    else
      define_scalar_register_methods(name)
    end
  end

  @_register_methods_defined[register_id] = true
end

#included(base) ⇒ Object

Handle inclusion by extending with ClassMethods

Parameters:

  • base (Class)

    The including class



84
85
86
87
# File 'lib/lutaml/model/serialize/initialization.rb', line 84

def included(base)
  base.extend(ClassMethods)
  base.initialize_attrs(self)
end

#inherited(subclass) ⇒ Object

Handle inheritance by copying parent’s configuration

Parameters:

  • subclass (Class)

    The inheriting class



76
77
78
79
# File 'lib/lutaml/model/serialize/initialization.rb', line 76

def inherited(subclass)
  super
  subclass.initialize_attrs(self)
end

#initialize_attrs(source_class) ⇒ Object

Initialize class attributes from a source class

Parameters:

  • source_class (Class)

    The source class to copy from



92
93
94
95
96
97
98
99
100
101
102
# File 'lib/lutaml/model/serialize/initialization.rb', line 92

def initialize_attrs(source_class)
  @mappings = Utils.deep_dup(source_class.mappings) || {}
  @attributes = Utils.deep_dup(source_class.class_attributes) || {}
  @choice_attributes = deep_duplicate_choice_attributes(source_class)
  @register_records = Utils.deep_dup(
    source_class.register_records,
  ) || ::Hash.new do |hash, key|
         hash[key] = { attributes: {}, choice_attributes: [] }
       end
  model(self)
end

#lutaml_default_registerSymbol?

Get or set the default register for this Model class.

Override in subclasses to specify a preferred default register context. This allows versioned schemas (e.g., MML v2, v3) to use their own register by default when instances are created without explicit register.

Examples:

module Mml
  class V2Base < Lutaml::Model::Serializable
    def self.lutaml_default_register
      :mml_v2
    end
  end
end

class Mml::V2::Math < V2Base
  # Mml::V2::Math.new uses :mml_v2 by default
end

Returns:

  • (Symbol, nil)

    The default register ID or nil to use Config.default_register



69
70
71
# File 'lib/lutaml/model/serialize/initialization.rb', line 69

def lutaml_default_register
  nil
end

#model(klass = nil) ⇒ Class

Get or set the model class

Parameters:

  • klass (Class, nil) (defaults to: nil)

    The model class to set

Returns:

  • (Class)

    The model class



211
212
213
214
215
216
217
218
# File 'lib/lutaml/model/serialize/initialization.rb', line 211

def model(klass = nil)
  if klass
    @model = klass
    add_custom_handling_methods_to_model(klass)
  else
    @model
  end
end

#namespace(_ns_class = nil) ⇒ Class?

Class-level namespace getter/setter.

No-op by default. When XML format is loaded, this is overridden via prepend to provide XML namespace handling.

Parameters:

  • _ns_class (Class, nil) (defaults to: nil)

    Namespace class (handled by format modules)

Returns:

  • (Class, nil)

    the namespace class



18
19
20
# File 'lib/lutaml/model/serialize/initialization.rb', line 18

def namespace(_ns_class = nil)
  @namespace_class
end

#namespace_prefixString?

Get the default namespace prefix for this Model

Returns:

  • (String, nil)

    the namespace prefix



32
33
34
# File 'lib/lutaml/model/serialize/initialization.rb', line 32

def namespace_prefix
  nil
end

#namespace_uriString?

Get the namespace URI for this Model

Returns:

  • (String, nil)

    the namespace URI



25
26
27
# File 'lib/lutaml/model/serialize/initialization.rb', line 25

def namespace_uri
  nil
end

#reference_resolvable?Boolean

Whether instances of this class should be registered in the global Store for reference resolution. Defaults to true for backward compatibility. Use ‘skip_reference_registration` to opt out for classes that never participate in cross-referencing.

Returns:

  • (Boolean)


272
273
274
275
276
# File 'lib/lutaml/model/serialize/initialization.rb', line 272

def reference_resolvable?
  return true unless instance_variable_defined?(:@skip_reference_registration)

  !@skip_reference_registration
end

#register(name) ⇒ Symbol?

Convert register parameter to symbol

Parameters:

  • name (Symbol, String, nil)

    The register name

Returns:

  • (Symbol, nil)

    The register name as a symbol



310
311
312
# File 'lib/lutaml/model/serialize/initialization.rb', line 310

def register(name)
  name&.to_sym
end

#register_record(register_id = nil) ⇒ Hash?

Get the register record for a specific register ID

Parameters:

  • register_id (Symbol, nil) (defaults to: nil)

    The register context

Returns:

  • (Hash, nil)

    The register record hash or nil



302
303
304
# File 'lib/lutaml/model/serialize/initialization.rb', line 302

def register_record(register_id = nil)
  register_records[extract_register_id(register_id)]
end

#set_register_context(register_id) ⇒ void

This method returns an undefined value.

Set the register context for this Model class.

This is called by Register#register_model to ensure the class knows its register context for proper instance initialization.

Parameters:

  • register_id (Symbol)

    The register ID



43
44
45
46
47
# File 'lib/lutaml/model/serialize/initialization.rb', line 43

def set_register_context(register_id)
  return if instance_variable_defined?(:@register)

  @register = register_id
end

#skip_reference_registrationObject

Opt out of Store registration for this class. Instances will not be tracked in the global Store, saving memory and registration overhead for classes that are never resolved by reference (no xml_id or similar attributes).



282
283
284
# File 'lib/lutaml/model/serialize/initialization.rb', line 282

def skip_reference_registration
  @skip_reference_registration = true
end