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
-
#add_custom_handling_methods_to_model(klass) ⇒ Object
Add custom handling methods to a model class.
-
#add_format_specific_model_methods(_klass) ⇒ Object
Hook for format-specific model methods.
-
#allocate_for_deserialization(register = nil) ⇒ Object
Allocate an instance for deserialization without calling initialize.
-
#attributes(register = nil) ⇒ Hash
Get all attributes for this model.
-
#cast(value) ⇒ Object
Cast a value (pass-through implementation).
-
#choice(min: 1, max: 1, format: nil, &block) ⇒ Object
Define a choice constraint.
-
#choice_attributes(register = nil) ⇒ Array
Get all choice attributes for this model.
-
#class_attributes ⇒ Hash
Raw class-level attributes without register merging.
-
#clear_cache(register_id = nil) ⇒ Object
Clear all cached data for this model class.
-
#deep_duplicate_choice_attributes(source_class, register = nil) ⇒ Array
Deep duplicate choice attributes from a source class.
-
#ensure_format_mapping_imports!(_register = nil) ⇒ Object
Hook for format-specific mapping import resolution.
-
#ensure_imports!(register = nil) ⇒ Object
Ensure all imports are resolved.
-
#ensure_register_methods_defined(register_id) ⇒ Object
Define register-specific attribute methods on the class itself.
-
#included(base) ⇒ Object
Handle inclusion by extending with ClassMethods.
-
#inherited(subclass) ⇒ Object
Handle inheritance by copying parent's configuration.
-
#initialize_attrs(source_class) ⇒ Object
Initialize class attributes from a source class.
-
#lutaml_default_register ⇒ Symbol?
Get or set the default register for this Model class.
-
#model(klass = nil) ⇒ Class
Get or set the model class.
-
#namespace(_ns_class = nil) ⇒ Class?
Class-level namespace getter/setter.
-
#namespace_prefix ⇒ String?
Get the default namespace prefix for this Model.
-
#namespace_uri ⇒ String?
Get the namespace URI for this Model.
-
#reference_resolvable? ⇒ Boolean
Whether instances of this class should be registered in the global Store for reference resolution.
-
#register(name) ⇒ Symbol?
Convert register parameter to symbol.
-
#register_record(register_id = nil) ⇒ Hash?
Get the register record for a specific register ID.
-
#set_register_context(register_id) ⇒ void
Set the register context for this Model class.
-
#skip_reference_registration ⇒ Object
Opt out of Store registration for this class.
Instance Method Details
#add_custom_handling_methods_to_model(klass) ⇒ Object
Add custom handling methods to a model class
245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 245 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.
278 279 280 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 278 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.
346 347 348 349 350 351 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 346 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. Memoized per register: this is on the per-element deserialization hot path, and the merged hash is stable until an attribute, import or register record mutates (those clear @merged_attributes_cache).
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 124 def attributes(register = nil) # Cache hit first: a filled entry can only exist after imports # were ensured for that register (the fill path below runs # ensure_imports! first), and mutations that invalidate the # merge clear @merged_attributes_cache. The lookup was walked # per rule application (751k per ISO-13849 parse). if @register_records&.any? register_id = extract_register_id(register) cached = (@merged_attributes_cache ||= {})[register_id] return cached if cached ensure_imports!(register) if finalized? # ensure_imports! may re-entrantly run clear_cache (restrict / # import resolution), which nils @merged_attributes_cache — # compute the merge first and re-materialize the memo store # at write time (#815: standalone to_xml on register-bound # models crashed with nil[] here). merged = @attributes.merge(@register_records[register_id][:attributes]) (@merged_attributes_cache ||= {})[register_id] = merged else ensure_imports!(register) if finalized? @attributes end end |
#cast(value) ⇒ Object
Cast a value (pass-through implementation)
286 287 288 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 286 def cast(value) value end |
#choice(min: 1, max: 1, format: nil, &block) ⇒ Object
Define a choice constraint
313 314 315 316 317 318 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 313 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.
162 163 164 165 166 167 168 169 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 162 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_attributes ⇒ Hash
Raw class-level attributes without register merging. Used by initialize_attrs during class inheritance.
152 153 154 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 152 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
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 199 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 memoized attribute merge (see .attributes) @merged_attributes_cache = nil # 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
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).
187 188 189 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 187 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
174 175 176 177 178 179 180 181 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 174 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.
360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 360 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
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
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
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_register ⇒ Symbol?
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.
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
233 234 235 236 237 238 239 240 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 233 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.
18 19 20 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 18 def namespace(_ns_class = nil) @namespace_class end |
#namespace_prefix ⇒ String?
Get the default namespace prefix for this Model
32 33 34 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 32 def namespace_prefix nil end |
#namespace_uri ⇒ String?
Get the namespace URI for this Model
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.
294 295 296 297 298 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 294 def reference_resolvable? return true unless instance_variable_defined?(:@skip_reference_registration) !@skip_reference_registration end |
#register(name) ⇒ Symbol?
Convert register parameter to symbol
332 333 334 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 332 def register(name) name&.to_sym end |
#register_record(register_id = nil) ⇒ Hash?
Get the register record for a specific register ID
324 325 326 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 324 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.
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_registration ⇒ Object
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).
304 305 306 |
# File 'lib/lutaml/model/serialize/initialization.rb', line 304 def skip_reference_registration @skip_reference_registration = true end |