module Lutaml::Model::Serialize::ClassMethods
def add_custom_handling_methods_to_model(klass)
def add_custom_handling_methods_to_model(klass) Utils.add_boolean_accessor_if_not_defined(klass, :ordered) Utils.add_boolean_accessor_if_not_defined(klass, :mixed) Utils.add_accessor_if_not_defined(klass, :element_order) Utils.add_accessor_if_not_defined(klass, :encoding) 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, :using_default?) do |attribute_name| @using_default ||= {} !!@using_default[attribute_name] end end
def add_enum_getter_if_not_defined(klass, enum_name, collection)
def add_enum_getter_if_not_defined(klass, enum_name, collection) Utils.add_method_if_not_defined(klass, enum_name) do i = instance_variable_get(:"@#{enum_name}") || [] if !collection && i.is_a?(Array) i.first else i.uniq end end end
def add_enum_methods_to_model(klass, enum_name, values, collection: false)
def add_enum_methods_to_model(klass, enum_name, values, collection: false) add_enum_getter_if_not_defined(klass, enum_name, collection) add_enum_setter_if_not_defined(klass, enum_name, values, collection) return unless values.all?(::String) values.each do |value| Utils.add_method_if_not_defined(klass, "#{value}?") do curr_value = public_send(:"#{enum_name}") if collection curr_value.include?(value) else curr_value == value end end Utils.add_method_if_not_defined(klass, value.to_s) do public_send(:"#{value}?") end Utils.add_method_if_not_defined(klass, "#{value}=") do |val| value_set_for(enum_name) enum_vals = public_send(:"#{enum_name}") enum_vals = if !!val if collection enum_vals << value else [value] end elsif collection enum_vals.delete(value) enum_vals else instance_variable_get(:"@#{enum_name}") - [value] end instance_variable_set(:"@#{enum_name}", enum_vals) end Utils.add_method_if_not_defined(klass, "#{value}!") do public_send(:"#{value}=", true) end end end
def add_enum_setter_if_not_defined(klass, enum_name, _values, collection)
def add_enum_setter_if_not_defined(klass, enum_name, _values, collection) Utils.add_method_if_not_defined(klass, "#{enum_name}=") do |value| value = [] if value.nil? value = [value] if !value.is_a?(Array) value_set_for(enum_name) if collection curr_value = public_send(:"#{enum_name}") instance_variable_set(:"@#{enum_name}", curr_value + value) else instance_variable_set(:"@#{enum_name}", value) end end end
def apply_mappings(doc, format, options = {})
def apply_mappings(doc, format, options = {}) instance = options[:instance] || model.new return instance if Utils.blank?(doc) mappings = mappings_for(format) if mappings.polymorphic_mapping return resolve_polymorphic(doc, format, mappings, instance, options) end # options[:mappings] = mappings.mappings transformer = Lutaml::Model::Config.transformer_for(format) transformer.data_to_model(self, doc, format, options) end
def apply_value_map(value, value_map, attr)
def apply_value_map(value, value_map, attr) if value.nil? value_for_option(value_map[:nil], attr) elsif Utils.empty?(value) value_for_option(value_map[:empty], attr, value) elsif Utils.uninitialized?(value) value_for_option(value_map[:omitted], attr) else value end end
def as(format, instance, options = {})
def as(format, instance, options = {}) if instance.is_a?(Array) return instance.map { |item| public_send(:"as_#{format}", item) } end unless instance.is_a?(model) msg = "argument is a '#{instance.class}' but should be a '#{model}'" raise Lutaml::Model::IncorrectModelError, msg end transformer = Lutaml::Model::Config.transformer_for(format) transformer.model_to_data(self, instance, format, options) end
def attribute(name, type, options = {})
def attribute(name, type, options = {}) if type.is_a?(Hash) options[:method_name] = type[:method] type = nil end attr = Attribute.new(name, type, options) attributes[name] = attr define_attribute_methods(attr) attr end
def cast(value)
def cast(value) value end
def choice(min: 1, max: 1, &block)
def choice(min: 1, max: 1, &block) @choice_attributes << Choice.new(self, min, max).tap do |c| c.instance_eval(&block) end end
def default_mappings(format)
def default_mappings(format) klass = ::Lutaml::Model::Config.mappings_class_for(format) mappings = klass.new mappings.tap do |mapping| attributes&.each_key do |name| mapping.map_element( name.to_s, to: name, ) end mapping.root(to_s.split("::").last) if format == :xml end end
def define_attribute_methods(attr)
def define_attribute_methods(attr) name = attr.name if attr.enum? add_enum_methods_to_model( model, name, attr.options[:values], collection: attr.options[:collection], ) elsif attr.derived? && name != attr.method_name define_method(name) do public_send(attr.method_name) end else define_method(name) do instance_variable_get(:"@#{name}") end define_method(:"#{name}=") do |value| value_set_for(name) instance_variable_set(:"@#{name}", attr.cast_value(value)) end end end
def empty_object(attr)
def empty_object(attr) return [] if attr.collection? "" end
def ensure_utf8(value)
def ensure_utf8(value) case value when String value.encode("UTF-8", invalid: :replace, undef: :replace, replace: "") when Array value.map { |v| ensure_utf8(v) } when Hash value.transform_keys do |k| ensure_utf8(k) end.transform_values do |v| ensure_utf8(v) end else value end end
def enums
def enums attributes.select { |_, attr| attr.enum? } end
def from(format, data, options = {})
def from(format, data, options = {}) return data if Utils.uninitialized?(data) adapter = Lutaml::Model::Config.adapter_for(format) doc = adapter.parse(data, options) public_send(:"of_#{format}", doc, options) end
def handle_key_value_mappings(mapping, format)
def handle_key_value_mappings(mapping, format) @mappings[format] ||= KeyValueMapping.new @mappings[format].mappings.concat(mapping.mappings) end
def import_model(model)
def import_model(model) import_model_with_root_error(model) import_model_attributes(model) import_model_mappings(model) end
def import_model_attributes(model)
def import_model_attributes(model) model.attributes.each_value do |attr| define_attribute_methods(attr) end @choice_attributes.concat(Utils.deep_dup(model.choice_attributes)) @attributes.merge!(Utils.deep_dup(model.attributes)) end
def import_model_mappings(model)
def import_model_mappings(model) import_model_with_root_error(model) Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format| next unless model.mappings.key?(format) mapping = model.mappings_for(format) mapping = Utils.deep_dup(mapping) klass = ::Lutaml::Model::Config.mappings_class_for(format) @mappings[format] ||= klass.new if format == :xml @mappings[format].merge_mapping_attributes(mapping) @mappings[format].merge_mapping_elements(mapping) @mappings[format].merge_elements_sequence(mapping) else @mappings[format].mappings.concat(mapping.mappings) end end end
def import_model_with_root_error(model)
def import_model_with_root_error(model) return unless model.mappings.key?(:xml) && model.root? raise Lutaml::Model::ImportModelWithRootError.new(model) end
def included(base)
def included(base) base.extend(ClassMethods) base.initialize_attrs(self) end
def inherited(subclass)
def inherited(subclass) super subclass.initialize_attrs(self) end
def initialize_attrs(source_class)
def initialize_attrs(source_class) @mappings = Utils.deep_dup(source_class.instance_variable_get(:@mappings)) || {} @attributes = Utils.deep_dup(source_class.instance_variable_get(:@attributes)) || {} @choice_attributes = Utils.deep_dup(source_class.instance_variable_get(:@choice_attributes)) || [] instance_variable_set(:@model, self) end
def key_value(&block)
def key_value(&block) Lutaml::Model::Config::KEY_VALUE_FORMATS.each do |format| mappings[format] ||= KeyValueMapping.new(format) mappings[format].instance_eval(&block) end end
def mappings_for(format)
def mappings_for(format) mappings[format] || default_mappings(format) end
def model(klass = nil)
def model(klass = nil) if klass @model = klass add_custom_handling_methods_to_model(klass) else @model end end
def of(format, doc, options = {})
def of(format, doc, options = {}) if doc.is_a?(Array) return doc.map { |item| send(:"of_#{format}", item) } end if format == :xml raise Lutaml::Model::NoRootMappingError.new(self) unless root? options[:encoding] = doc.encoding end transformer = Lutaml::Model::Config.transformer_for(format) transformer.data_to_model(self, doc, format, options) end
def process_mapping(format, &block)
def process_mapping(format, &block) # klass = Lutaml::Model.const_get("#{format.to_s.capitalize}Mapping") # mappings[format] ||= klass.new # mappings[format].instance_eval(&block) # handle_root_assignment(mappings, format) klass = ::Lutaml::Model::Config.mappings_class_for(format) mappings[format] ||= klass.new mappings[format].instance_eval(&block) if mappings[format].respond_to?(:finalize) mappings[format].finalize(self) end end
def resolve_polymorphic(doc, format, mappings, instance, options = {})
def resolve_polymorphic(doc, format, mappings, instance, options = {}) polymorphic_mapping = mappings.polymorphic_mapping return instance if polymorphic_mapping.polymorphic_map.empty? klass_key = doc[polymorphic_mapping.name] klass_name = polymorphic_mapping.polymorphic_map[klass_key] klass = Object.const_get(klass_name) klass.apply_mappings(doc, format, options) end
def root?
def root? mappings_for(:xml)&.root? end
def to(format, instance, options = {})
def to(format, instance, options = {}) value = public_send(:"as_#{format}", instance, options) adapter = Lutaml::Model::Config.adapter_for(format) options[:mapper_class] = self if format == :xml adapter.new(value).public_send(:"to_#{format}", options) end
def value_for_option(option, attr, empty_value = nil)
def value_for_option(option, attr, empty_value = nil) return nil if option == :nil return empty_value || empty_object(attr) if option == :empty Lutaml::Model::UninitializedClass.instance end