lib/lutaml/model/serialize.rb in lutaml-model-0.3.10 vs lib/lutaml/model/serialize.rb in lutaml-model-0.3.11

- old
+ new

@@ -9,10 +9,11 @@ require_relative "key_value_mapping" require_relative "json_adapter" require_relative "comparable_model" require_relative "schema_location" require_relative "validation" +require_relative "error" module Lutaml module Model module Serialize include ComparableModel @@ -37,27 +38,49 @@ end def model(klass = nil) if klass @model = klass + add_order_handling_methods_to_model(klass) else @model end end + def add_order_handling_methods_to_model(klass) + Utils.add_method_if_not_defined(klass, :ordered=) do |ordered| + @ordered = ordered + end + + Utils.add_method_if_not_defined(klass, :ordered?) do + !!@ordered + end + + Utils.add_method_if_not_defined(klass, :element_order=) do |order| + @element_order = order + end + + Utils.add_method_if_not_defined(klass, :element_order) do + @element_order + end + end + + def cast(value) + value + end + # Define an attribute for the model def attribute(name, type, options = {}) attr = Attribute.new(name, type, options) attributes[name] = attr define_method(name) do instance_variable_get(:"@#{name}") end define_method(:"#{name}=") do |value| - instance_variable_set(:"@#{name}", value) - # validate!(name) + instance_variable_set(:"@#{name}", attr.cast_value(value)) end end Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format| define_method(format) do |&block| @@ -72,21 +95,21 @@ define_method(:"from_#{format}") do |data| adapter = Lutaml::Model::Config.send(:"#{format}_adapter") doc = adapter.parse(data) - public_send(:"of_#{format}", doc.to_h) + public_send(:"of_#{format}", doc) end - define_method(:"of_#{format}") do |hash| - if hash.is_a?(Array) - return hash.map do |item| - apply_mappings(item, format) + define_method(:"of_#{format}") do |doc| + if doc.is_a?(Array) + return doc.map do |item| + send(:"of_#{format}", item) end end - apply_mappings(hash, format) + apply_mappings(doc.to_h, format) end define_method(:"to_#{format}") do |instance| value = public_send(:"as_#{format}", instance) adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter") @@ -183,18 +206,21 @@ end end def default_mappings(format) klass = format == :xml ? XmlMapping : KeyValueMapping + klass.new.tap do |mapping| attributes&.each do |name, attr| mapping.map_element( name.to_s, to: name, render_nil: attr.render_nil?, ) end + + mapping.root(to_s.split("::").last) if format == :xml end end def apply_child_mappings(hash, child_mappings) return hash unless child_mappings @@ -240,52 +266,51 @@ end hash end + def valid_rule?(rule) + attribute = attribute_for_rule(rule) + + !!attribute || rule.custom_methods[:from] + end + + def attribute_for_rule(rule) + return attributes[rule.to] unless rule.delegate + + attributes[rule.delegate].type.attributes[rule.to] + end + def apply_mappings(doc, format, options = {}) instance = options[:instance] || model.new - return instance if !doc || doc.empty? + return instance if Utils.blank?(doc) return apply_xml_mapping(doc, instance, options) if format == :xml mappings = mappings_for(format).mappings mappings.each do |rule| - attr = if rule.delegate - attributes[rule.delegate].type.attributes[rule.to] - else - attributes[rule.to] - end + raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule) - raise "Attribute '#{rule.to}' not found in #{self}" unless attr + attr = attribute_for_rule(rule) value = if doc.key?(rule.name) || doc.key?(rule.name.to_sym) doc[rule.name] || doc[rule.name.to_sym] else attr.default end if rule.custom_methods[:from] - if value && !value.empty? - value = new.send(rule.custom_methods[:from], instance, - value) + if Utils.present?(value) + value = new.send(rule.custom_methods[:from], instance, value) end + next end value = apply_child_mappings(value, rule.child_mappings) value = attr.cast(value, format) - if rule.delegate - if instance.public_send(rule.delegate).nil? - instance.public_send(:"#{rule.delegate}=", - attributes[rule.delegate].type.new) - end - instance.public_send(rule.delegate).public_send(:"#{rule.to}=", - value) - else - instance.public_send(:"#{rule.to}=", value) - end + rule.deserialize(instance, value, attributes, self) end instance end @@ -311,50 +336,59 @@ namespace: doc["__schema_location"][:namespace], ) end mappings.each do |rule| - attr = attributes[rule.to] - raise "Attribute '#{rule.to}' not found in #{self}" unless attr + raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule) - is_content_mapping = rule.name.nil? - - value = if is_content_mapping + value = if rule.content_mapping? doc["text"] else doc[rule.name.to_s] || doc[rule.name.to_sym] end - value = [value].compact if attr.collection? && !value.is_a?(Array) + value = normalize_xml_value(value, rule) + rule.deserialize(instance, value, attributes, self) + end - if value.is_a?(Array) - value = value.map do |v| - v.is_a?(Hash) && !(attr.type <= Serialize) ? v["text"] : v - end - elsif !(attr.type <= Serialize) && value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash - value = value["text"] - end + instance + end - unless is_content_mapping - value = attr.cast( - value, - :xml, - caller_class: self, - mixed_content: rule.mixed_content, - ) - end + def normalize_xml_value(value, rule) + attr = attribute_for_rule(rule) - if rule.custom_methods[:from] - new.send(rule.custom_methods[:from], instance, value) - else - instance.public_send(:"#{rule.to}=", value) - end + value = [value].compact if attr&.collection? && !value.is_a?(Array) + + value = if value.is_a?(Array) + value.map do |v| + text_hash?(attr, v) ? v["text"] : v + end + elsif text_hash?(attr, value) + value["text"] + else + value + end + + if attr && !rule.content_mapping? + value = attr.cast( + value, + :xml, + caller_class: self, + mixed_content: rule.mixed_content, + ) end - instance + value end + def text_hash?(attr, value) + return false unless value.is_a?(Hash) + return value.keys == ["text"] unless attr + + !(attr.type <= Serialize) && attr.type != Lutaml::Model::Type::Hash + end + def ensure_utf8(value) case value when String value.encode("UTF-8", invalid: :replace, undef: :replace, replace: "") @@ -437,10 +471,9 @@ hash[key.to_sym] || hash[key.to_s] end Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format| define_method(:"to_#{format}") do |options = {}| - validate! adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter") representation = if format == :xml self else self.class.hash_representation(self, format,