Module: Lutaml::Model::Serialize
- Includes:
- ComparableModel, Liquefiable, Registrable, Builder, Validation
- Included in:
- Serializable
- Defined in:
- lib/lutaml/model/serialize.rb,
lib/lutaml/model/serialize/builder.rb,
lib/lutaml/model/serialize/model_import.rb,
lib/lutaml/model/serialize/enum_handling.rb,
lib/lutaml/model/serialize/value_mapping.rb,
lib/lutaml/model/serialize/initialization.rb,
lib/lutaml/model/serialize/format_conversion.rb,
lib/lutaml/model/serialize/conversion_caching.rb,
lib/lutaml/model/serialize/attribute_definition.rb,
lib/lutaml/model/serialize/transformation_builder.rb,
lib/lutaml/model/serialize/deserialization_context.rb
Defined Under Namespace
Modules: AttributeDefinition, Builder, ClassMethods, ConversionCaching, EnumHandling, FormatConversion, Initialization, ModelImport, TransformationBuilder, ValueMapping
Classes: DeserializationContext
Constant Summary
collapse
- DEFAULT_VALUE_MAP =
Performance: Pre-computed default value map to avoid per-call allocations
{
omitted: :nil,
nil: :nil,
empty: :empty,
}.freeze
- LAZY_EMPTY_COLLECTION =
Shared frozen sentinel for lazy collection initialization.
The getter materializes a real Array on first access.
[].freeze
- LAZY_IVAR =
Ivar symbols for lazy collections, memoized per attribute name:
the "@name" interpolation allocated twice per collection read
(491k on the ISO-13849 serialize profile).
{}.compare_by_identity
- NO_ARG =
Sentinel distinguishing "getter called with no argument" from
builder-syntax g.attr(value) where value may be nil. Compiled
getters default to it instead of a *args splat.
Object.new.freeze
- INTERNAL_ATTRIBUTES =
%i[@using_default @lutaml_register @lutaml_parent @lutaml_root
@register_records].freeze
- GENERATOR_STATE_KEY =
Ruby's JSON generator hands #to_json its own JSON::State instead of an
options hash. It carries no LutaML options, but it does carry the
surrounding indent context, so it travels IN BAND under a private key
rather than replacing the options hash. Wrapping rather than returning
early keeps every later step -- register propagation, root-mapping
validation, Collection's collection: true merge -- working on a real
Hash. json 3.0 removed JSON::State#[] and rejects unknown keys in
State#merge, so nothing may treat it as a Hash.
:_generator_state
Constants included
from Validation
Validation::VALIDATING_KEY
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
-
#attr_value(attrs, name, attribute) ⇒ Object
-
#attribute_exist?(name) ⇒ Boolean
-
#define_singleton_attribute_methods ⇒ Object
Ensure register-specific attribute methods are defined on the class.
-
#extract_register_id(attrs, options) ⇒ Object
-
#finalize_deserialization(register) ⇒ Object
Complete deserialization initialization after allocation.
-
#init_deserialization_state(register) ⇒ Object
Initialize instance state for fast deserialization path.
-
#initialize(attrs = {}, options = {}) ⇒ Object
-
#key_exist?(hash, key) ⇒ Boolean
-
#key_value(hash, key) ⇒ Object
-
#method_missing(method_name, *args, &block) ⇒ Object
-
#prepare_instance_format_options(_format, _options) ⇒ Object
Hook for format-specific instance-level options preparation.
-
#pretty_print_instance_variables ⇒ Object
-
#register_in_reference_store ⇒ Object
-
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
-
#to_format(format, options = {}) ⇒ Object
-
#to_yaml_hash ⇒ Object
-
#using_default?(attribute_name) ⇒ Boolean
-
#using_default_for(attribute_name) ⇒ Object
-
#validate_attribute!(attr_name) ⇒ Object
-
#validate_root_mapping!(_format, _options) ⇒ Object
Hook for format-specific root mapping validation.
-
#value_map(options) ⇒ Object
-
#value_set_for(attribute_name) ⇒ Object
-
#values_set_for(attribute_names) ⇒ Object
#to_liquid
Methods included from Validation
#collect_validation_errors, #element_order, #format_element_sequences, new_registry, #order_names, #validate, validate, validate!, #validate!, #validate_helper, #validate_sequence!, visiting?
#already_compared?, #attributes_hash, #calculate_hash, #comparison_key, #eql?, #hash, #same_class?
Methods included from Builder
#mixed_content?, #order_tracking_enabled?, #ordered?, #record_mutation, #record_mutation_collection
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(method_name, *args, &block) ⇒ Object
229
230
231
232
233
234
235
236
237
238
239
240
241
|
# File 'lib/lutaml/model/serialize.rb', line 229
def method_missing(method_name, *args, &block)
if method_name.to_s.end_with?("=") && attribute_exist?(method_name)
define_singleton_method(method_name) do |value|
instance_variable_set(:"@#{method_name.to_s.chomp('=')}", value)
end
send(method_name, *args)
elsif ::Lutaml::Model::Attribute.default_evaluation? &&
self.class.respond_to?(method_name)
self.class.public_send(method_name, *args, &block)
else
super
end
end
|
Instance Attribute Details
#lutaml_parent ⇒ Object
Returns the value of attribute lutaml_parent.
143
144
145
|
# File 'lib/lutaml/model/serialize.rb', line 143
def lutaml_parent
@lutaml_parent
end
|
#lutaml_register ⇒ Object
Returns the value of attribute lutaml_register.
143
144
145
|
# File 'lib/lutaml/model/serialize.rb', line 143
def lutaml_register
@lutaml_register
end
|
#lutaml_root ⇒ Object
Returns the value of attribute lutaml_root.
143
144
145
|
# File 'lib/lutaml/model/serialize.rb', line 143
def lutaml_root
@lutaml_root
end
|
Class Method Details
.included(base) ⇒ Object
49
50
51
52
|
# File 'lib/lutaml/model/serialize.rb', line 49
def self.included(base)
base.extend(ClassMethods)
base.initialize_attrs(base)
end
|
68
69
70
71
72
73
74
|
# File 'lib/lutaml/model/serialize.rb', line 68
def self.register_format_mapping_method(format)
method_name = format == :hash ? :hsh : format
::Lutaml::Model::Serialize::ClassMethods.define_method(method_name) do |*args, &block|
process_mapping(format, *args, &block)
end
end
|
76
77
78
79
80
81
82
83
84
|
# File 'lib/lutaml/model/serialize.rb', line 76
def self.register_from_format_method(format)
ClassMethods.define_method(:"from_#{format}") do |data, options = {}|
from(format, data, options)
end
ClassMethods.define_method(:"of_#{format}") do |doc, options = {}|
of(format, doc, options)
end
end
|
.register_stream_methods(format) ⇒ Object
lutaml-model#436: multi-document streams. from_<f>_stream
parses each document separately; discriminator: receives each
parsed document hash and returns the model class to build with,
enabling polymorphic streams. to_<f>_stream joins instances
into one stream document.
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
# File 'lib/lutaml/model/serialize.rb', line 91
def self.register_stream_methods(format)
ClassMethods.define_method(:"from_#{format}_stream") do |data, options = {}|
docs = Lutaml::Model::Serialize.stream_documents(data, format, options)
discriminator = options[:discriminator]
docs.map do |doc|
target = discriminator ? discriminator.call(doc) : self
target.send(:"of_#{format}", doc, options)
end
end
ClassMethods.define_method(:"to_#{format}_stream") do |instances, options = {}|
instances.map do |instance|
instance.send(:"to_#{format}", options)
end.join("---\n")
end
end
|
129
130
131
132
133
134
135
136
137
138
139
140
141
|
# File 'lib/lutaml/model/serialize.rb', line 129
def self.register_to_format_method(format)
ClassMethods.define_method(:"to_#{format}") do |instance, options = {}|
to(format, instance, options)
end
ClassMethods.define_method(:"as_#{format}") do |instance, options = {}|
as(format, instance, options)
end
define_method(:"to_#{format}") do |options = {}|
to_format(format, options)
end
end
|
.stream_documents(data, format, options) ⇒ Object
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
# File 'lib/lutaml/model/serialize.rb', line 108
def self.stream_documents(data, format, options)
require "yaml"
if YAML.respond_to?(:load_stream, true)
begin
docs = YAML.load_stream(data,
aliases: options.fetch(:aliases, true))
rescue ArgumentError
docs = YAML.load_stream(data)
end
docs.is_a?(Array) ? docs.compact : [docs].compact
else
data.split(/^---\s*$/).filter_map do |chunk|
chunk.strip.empty? ? nil : YAML.safe_load(chunk)
end
end
rescue StandardError => e
raise Lutaml::Model::InvalidFormatError.new(format, e.message)
end
|
.wrap_generator_state(options) ⇒ Object
289
290
291
292
293
|
# File 'lib/lutaml/model/serialize.rb', line 289
def self.wrap_generator_state(options)
return options if options.is_a?(::Hash)
{ GENERATOR_STATE_KEY => options }
end
|
Instance Method Details
#attr_value(attrs, name, attribute) ⇒ Object
199
200
201
202
203
|
# File 'lib/lutaml/model/serialize.rb', line 199
def attr_value(attrs, name, attribute)
value = Utils.fetch_str_or_sym(attrs, name,
attribute.default(lutaml_register, self))
attribute.cast_value(value, lutaml_register)
end
|
#attribute_exist?(name) ⇒ Boolean
248
249
250
251
252
|
# File 'lib/lutaml/model/serialize.rb', line 248
def attribute_exist?(name)
name = name.to_s.chomp("=").to_sym if name.end_with?("=")
self.class.attributes(lutaml_register).key?(name)
end
|
#define_singleton_attribute_methods ⇒ Object
Ensure register-specific attribute methods are defined on the class.
Delegates to the class method which defines methods once per
(class, register) combination instead of per-instance singleton methods.
332
333
334
|
# File 'lib/lutaml/model/serialize.rb', line 332
def define_singleton_attribute_methods
self.class.ensure_register_methods_defined(lutaml_register)
end
|
181
182
183
184
|
# File 'lib/lutaml/model/serialize.rb', line 181
def (attrs, options)
register = attrs&.dig(:lutaml_register) || options&.dig(:register)
self.class.(register)
end
|
#finalize_deserialization(register) ⇒ Object
Complete deserialization initialization after allocation.
Called by allocate_for_deserialization to set up instance state,
define register-specific methods, and register in the reference store.
175
176
177
178
179
|
# File 'lib/lutaml/model/serialize.rb', line 175
def finalize_deserialization(register)
init_deserialization_state(register)
define_singleton_attribute_methods
register_in_reference_store
end
|
#init_deserialization_state(register) ⇒ Object
Initialize instance state for fast deserialization path.
Called by allocate_for_deserialization instead of initialize.
Uses nil for @using_default to mean "all attributes use default" —
no hash allocation needed until value_set_for is called.
160
161
162
163
164
165
166
167
168
169
170
|
# File 'lib/lutaml/model/serialize.rb', line 160
def init_deserialization_state(register)
@using_default = nil
@lutaml_register = register
public_send(self.class.compiled_state_defaults_name!(self.class.(register)))
end
|
#initialize(attrs = {}, options = {}) ⇒ Object
145
146
147
148
149
150
151
152
153
154
|
# File 'lib/lutaml/model/serialize.rb', line 145
def initialize(attrs = {}, options = {})
@using_default = {}
@lutaml_register = (attrs, options)
return unless self.class.attributes(@lutaml_register)
initialize_attributes(attrs, options)
define_singleton_attribute_methods
register_in_reference_store
end
|
#key_exist?(hash, key) ⇒ Boolean
260
261
262
|
# File 'lib/lutaml/model/serialize.rb', line 260
def key_exist?(hash, key)
hash.key?(key.to_sym) || hash.key?(key.to_s)
end
|
#key_value(hash, key) ⇒ Object
264
265
266
|
# File 'lib/lutaml/model/serialize.rb', line 264
def key_value(hash, key)
hash[key.to_sym] || hash[key.to_s]
end
|
Hook for format-specific instance-level options preparation.
XML overrides via InstanceMethods prepend.
316
317
318
|
# File 'lib/lutaml/model/serialize.rb', line 316
def prepare_instance_format_options(_format, _options)
end
|
#pretty_print_instance_variables ⇒ Object
268
269
270
271
272
273
|
# File 'lib/lutaml/model/serialize.rb', line 268
def pretty_print_instance_variables
reference_attributes = instance_variables.select do |var|
var.to_s.end_with?("_ref")
end
(instance_variables - INTERNAL_ATTRIBUTES - reference_attributes).sort
end
|
#register_in_reference_store ⇒ Object
336
337
338
|
# File 'lib/lutaml/model/serialize.rb', line 336
def register_in_reference_store
Lutaml::Model::Store.register(self) if self.class.reference_resolvable?
end
|
#respond_to_missing?(method_name, include_private = false) ⇒ Boolean
243
244
245
246
|
# File 'lib/lutaml/model/serialize.rb', line 243
def respond_to_missing?(method_name, include_private = false)
(method_name.to_s.end_with?("=") && attribute_exist?(method_name)) ||
super
end
|
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
|
# File 'lib/lutaml/model/serialize.rb', line 295
def to_format(format, options = {})
options = Lutaml::Model::Serialize.wrap_generator_state(options)
validate_root_mapping!(format, options)
options[:register] ||= lutaml_register if lutaml_register
prepare_instance_format_options(format, options)
self.class.to(format, self, options)
end
|
#to_yaml_hash ⇒ Object
275
276
277
|
# File 'lib/lutaml/model/serialize.rb', line 275
def to_yaml_hash
self.class.as_yaml(self)
end
|
#using_default?(attribute_name) ⇒ Boolean
222
223
224
225
226
227
|
# File 'lib/lutaml/model/serialize.rb', line 222
def using_default?(attribute_name)
return true if @using_default.nil?
@using_default[attribute_name]
end
|
#using_default_for(attribute_name) ⇒ Object
205
206
207
208
|
# File 'lib/lutaml/model/serialize.rb', line 205
def using_default_for(attribute_name)
@using_default ||= ::Hash.new(true)
@using_default[attribute_name] = true
end
|
#validate_attribute!(attr_name) ⇒ Object
254
255
256
257
258
|
# File 'lib/lutaml/model/serialize.rb', line 254
def validate_attribute!(attr_name)
attr = self.class.attributes[attr_name]
value = instance_variable_get(:"@#{attr_name}")
attr.validate_value!(value, lutaml_register, instance_object: self)
end
|
#validate_root_mapping!(_format, _options) ⇒ Object
Hook for format-specific root mapping validation.
XML overrides via InstanceMethods prepend.
325
326
327
|
# File 'lib/lutaml/model/serialize.rb', line 325
def validate_root_mapping!(_format, _options)
end
|
#value_map(options) ⇒ Object
186
187
188
189
190
191
192
193
194
195
196
197
|
# File 'lib/lutaml/model/serialize.rb', line 186
def value_map(options)
return DEFAULT_VALUE_MAP if options.equal?(Type::Value::EMPTY_OPTIONS)
return DEFAULT_VALUE_MAP if options.empty?
{
omitted: options[:omitted] || :nil,
nil: options[:nil] || :nil,
empty: options[:empty] || :empty,
}
end
|
#value_set_for(attribute_name) ⇒ Object
210
211
212
213
214
215
|
# File 'lib/lutaml/model/serialize.rb', line 210
def value_set_for(attribute_name)
@using_default ||= ::Hash.new(true)
@using_default[attribute_name] = false
end
|
#values_set_for(attribute_names) ⇒ Object
217
218
219
220
|
# File 'lib/lutaml/model/serialize.rb', line 217
def values_set_for(attribute_names)
@using_default ||= ::Hash.new(true)
attribute_names.each { |name| @using_default[name] = false }
end
|