lutaml-model 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +14 -12
- data/README.adoc +30 -26
- data/lib/lutaml/model/attribute.rb +27 -0
- data/lib/lutaml/model/serialize.rb +100 -127
- data/lib/lutaml/model/type.rb +2 -0
- data/lib/lutaml/model/utils.rb +45 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +73 -0
- data/lib/lutaml/model/xml_adapter/builder/ox.rb +89 -0
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +19 -92
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +22 -94
- data/lib/lutaml/model/xml_adapter/xml_document.rb +111 -7
- data/lib/lutaml/model/yaml_adapter/standard_yaml_adapter.rb +0 -12
- data/lib/lutaml/model.rb +1 -0
- metadata +5 -2
    
        data/lib/lutaml/model/version.rb
    CHANGED
    
    
| @@ -0,0 +1,73 @@ | |
| 1 | 
            +
            module Lutaml
         | 
| 2 | 
            +
              module Model
         | 
| 3 | 
            +
                module XmlAdapter
         | 
| 4 | 
            +
                  module Builder
         | 
| 5 | 
            +
                    class Nokogiri
         | 
| 6 | 
            +
                      def self.build(options = {})
         | 
| 7 | 
            +
                        if block_given?
         | 
| 8 | 
            +
                          ::Nokogiri::XML::Builder.new(options) do |xml|
         | 
| 9 | 
            +
                            yield(new(xml))
         | 
| 10 | 
            +
                          end
         | 
| 11 | 
            +
                        else
         | 
| 12 | 
            +
                          new(::Nokogiri::XML::Builder.new(options))
         | 
| 13 | 
            +
                        end
         | 
| 14 | 
            +
                      end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      attr_reader :xml
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      def initialize(xml)
         | 
| 19 | 
            +
                        @xml = xml
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      def create_element(name, attributes = {})
         | 
| 23 | 
            +
                        xml.doc.create_element(name, attributes)
         | 
| 24 | 
            +
                      end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                      def add_element(element, child)
         | 
| 27 | 
            +
                        element.add_child(child)
         | 
| 28 | 
            +
                      end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                      def create_and_add_element(element_name, prefix: nil, attributes: {})
         | 
| 31 | 
            +
                        add_namespace_prefix(prefix) if prefix
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                        if block_given?
         | 
| 34 | 
            +
                          public_send(element_name, attributes) do
         | 
| 35 | 
            +
                            yield(self)
         | 
| 36 | 
            +
                          end
         | 
| 37 | 
            +
                        else
         | 
| 38 | 
            +
                          public_send(element_name, attributes)
         | 
| 39 | 
            +
                        end
         | 
| 40 | 
            +
                      end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                      def add_text(element, text)
         | 
| 43 | 
            +
                        if element.is_a?(self.class)
         | 
| 44 | 
            +
                          element = element.xml.parent
         | 
| 45 | 
            +
                        end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                        add_element(element, ::Nokogiri::XML::Text.new(text.to_s, xml.doc))
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                      def add_namespace_prefix(prefix)
         | 
| 51 | 
            +
                        xml[prefix] if prefix
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                        self
         | 
| 54 | 
            +
                      end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      def method_missing(method_name, *args)
         | 
| 57 | 
            +
                        if block_given?
         | 
| 58 | 
            +
                          xml.public_send(method_name, *args) do
         | 
| 59 | 
            +
                            yield(xml)
         | 
| 60 | 
            +
                          end
         | 
| 61 | 
            +
                        else
         | 
| 62 | 
            +
                          xml.public_send(method_name, *args)
         | 
| 63 | 
            +
                        end
         | 
| 64 | 
            +
                      end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      def respond_to_missing?(method_name, include_private = false)
         | 
| 67 | 
            +
                        xml.respond_to?(method_name) || super
         | 
| 68 | 
            +
                      end
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
                end
         | 
| 72 | 
            +
              end
         | 
| 73 | 
            +
            end
         | 
| @@ -0,0 +1,89 @@ | |
| 1 | 
            +
            module Lutaml
         | 
| 2 | 
            +
              module Model
         | 
| 3 | 
            +
                module XmlAdapter
         | 
| 4 | 
            +
                  module Builder
         | 
| 5 | 
            +
                    class Ox
         | 
| 6 | 
            +
                      def self.build(options = {})
         | 
| 7 | 
            +
                        if block_given?
         | 
| 8 | 
            +
                          ::Ox::Builder.new(options) do |xml|
         | 
| 9 | 
            +
                            yield(new(xml))
         | 
| 10 | 
            +
                          end
         | 
| 11 | 
            +
                        else
         | 
| 12 | 
            +
                          new(::Ox::Builder.new(options))
         | 
| 13 | 
            +
                        end
         | 
| 14 | 
            +
                      end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      attr_reader :xml
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      def initialize(xml)
         | 
| 19 | 
            +
                        @xml = xml
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                      def create_element(name, attributes = {})
         | 
| 23 | 
            +
                        if block_given?
         | 
| 24 | 
            +
                          xml.element(name, attributes) do |element|
         | 
| 25 | 
            +
                            yield(self.class.new(element))
         | 
| 26 | 
            +
                          end
         | 
| 27 | 
            +
                        else
         | 
| 28 | 
            +
                          xml.element(name, attributes)
         | 
| 29 | 
            +
                        end
         | 
| 30 | 
            +
                      end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      def add_element(element, child)
         | 
| 33 | 
            +
                        element << child
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                      def create_and_add_element(element_name, prefix: nil, attributes: {})
         | 
| 37 | 
            +
                        prefixed_name = if prefix
         | 
| 38 | 
            +
                                          "#{prefix}:#{element_name}"
         | 
| 39 | 
            +
                                        else
         | 
| 40 | 
            +
                                          element_name
         | 
| 41 | 
            +
                                        end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                        if block_given?
         | 
| 44 | 
            +
                          xml.element(prefixed_name, attributes) do |element|
         | 
| 45 | 
            +
                            yield(self.class.new(element))
         | 
| 46 | 
            +
                          end
         | 
| 47 | 
            +
                        else
         | 
| 48 | 
            +
                          xml.element(prefixed_name, attributes)
         | 
| 49 | 
            +
                        end
         | 
| 50 | 
            +
                      end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                      def <<(text)
         | 
| 53 | 
            +
                        xml.text(text)
         | 
| 54 | 
            +
                      end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      def add_text(element, text)
         | 
| 57 | 
            +
                        element << text
         | 
| 58 | 
            +
                      end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                      # Add XML namespace to document
         | 
| 61 | 
            +
                      #
         | 
| 62 | 
            +
                      # Ox doesn't support XML namespaces so this method does nothing.
         | 
| 63 | 
            +
                      def add_namespace_prefix(_prefix)
         | 
| 64 | 
            +
                        # :noop:
         | 
| 65 | 
            +
                        self
         | 
| 66 | 
            +
                      end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                      def parent
         | 
| 69 | 
            +
                        xml
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                      def method_missing(method_name, *args)
         | 
| 73 | 
            +
                        if block_given?
         | 
| 74 | 
            +
                          xml.public_send(method_name, *args) do
         | 
| 75 | 
            +
                            yield(xml)
         | 
| 76 | 
            +
                          end
         | 
| 77 | 
            +
                        else
         | 
| 78 | 
            +
                          xml.public_send(method_name, *args)
         | 
| 79 | 
            +
                        end
         | 
| 80 | 
            +
                      end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                      def respond_to_missing?(method_name, include_private = false)
         | 
| 83 | 
            +
                        xml.respond_to?(method_name) || super
         | 
| 84 | 
            +
                      end
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
            end
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            require "nokogiri"
         | 
| 2 2 | 
             
            require_relative "xml_document"
         | 
| 3 | 
            +
            require_relative "builder/nokogiri"
         | 
| 3 4 |  | 
| 4 5 | 
             
            module Lutaml
         | 
| 5 6 | 
             
              module Model
         | 
| @@ -12,12 +13,13 @@ module Lutaml | |
| 12 13 | 
             
                    end
         | 
| 13 14 |  | 
| 14 15 | 
             
                    def to_xml(options = {})
         | 
| 15 | 
            -
                      builder = Nokogiri | 
| 16 | 
            +
                      builder = Builder::Nokogiri.build(encoding: "UTF-8") do |xml|
         | 
| 16 17 | 
             
                        if root.is_a?(Lutaml::Model::XmlAdapter::NokogiriElement)
         | 
| 17 18 | 
             
                          root.to_xml(xml)
         | 
| 18 19 | 
             
                        else
         | 
| 19 20 | 
             
                          mapper_class = options[:mapper_class] || @root.class
         | 
| 20 | 
            -
                          options[:xml_attributes] = | 
| 21 | 
            +
                          options[:xml_attributes] =
         | 
| 22 | 
            +
                            build_namespace_attributes(mapper_class)
         | 
| 21 23 | 
             
                          build_element(xml, @root, options)
         | 
| 22 24 | 
             
                        end
         | 
| 23 25 | 
             
                      end
         | 
| @@ -31,59 +33,6 @@ module Lutaml | |
| 31 33 |  | 
| 32 34 | 
             
                    private
         | 
| 33 35 |  | 
| 34 | 
            -
                    def build_unordered_element(xml, element, options = {})
         | 
| 35 | 
            -
                      mapper_class = options[:mapper_class] || element.class
         | 
| 36 | 
            -
                      xml_mapping = mapper_class.mappings_for(:xml)
         | 
| 37 | 
            -
                      return xml unless xml_mapping
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                      attributes = options[:xml_attributes] ||= {}
         | 
| 40 | 
            -
                      attributes = build_attributes(element,
         | 
| 41 | 
            -
                                                    xml_mapping).merge(attributes)&.compact
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                      prefixed_xml = if options.key?(:namespace_prefix)
         | 
| 44 | 
            -
                                       options[:namespace_prefix] ? xml[options[:namespace_prefix]] : xml
         | 
| 45 | 
            -
                                     elsif xml_mapping.namespace_prefix
         | 
| 46 | 
            -
                                       xml[xml_mapping.namespace_prefix]
         | 
| 47 | 
            -
                                     else
         | 
| 48 | 
            -
                                       xml
         | 
| 49 | 
            -
                                     end
         | 
| 50 | 
            -
             | 
| 51 | 
            -
                      tag_name = options[:tag_name] || xml_mapping.root_element
         | 
| 52 | 
            -
                      prefixed_xml.public_send(tag_name, attributes) do
         | 
| 53 | 
            -
                        if options.key?(:namespace_prefix) && !options[:namespace_prefix]
         | 
| 54 | 
            -
                          xml.parent.namespace = nil
         | 
| 55 | 
            -
                        end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                        xml_mapping.elements.each do |element_rule|
         | 
| 58 | 
            -
                          attribute_def = attribute_definition_for(element, element_rule, mapper_class: mapper_class)
         | 
| 59 | 
            -
                          value = attribute_value_for(element, element_rule)
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                          next if value.nil? && !element_rule.render_nil?
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                          nsp_xml = element_rule.prefix ? xml[element_rule.prefix] : xml
         | 
| 64 | 
            -
             | 
| 65 | 
            -
                          if attribute_def.collection?
         | 
| 66 | 
            -
                            value.each do |v|
         | 
| 67 | 
            -
                              add_to_xml(nsp_xml, v, attribute_def, element_rule)
         | 
| 68 | 
            -
                            end
         | 
| 69 | 
            -
                          elsif !value.nil? || element_rule.render_nil?
         | 
| 70 | 
            -
                            add_to_xml(nsp_xml, value, attribute_def, element_rule)
         | 
| 71 | 
            -
                          end
         | 
| 72 | 
            -
                        end
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                        if (content_rule = xml_mapping.content_mapping)
         | 
| 75 | 
            -
                          text = element.send(content_rule.to)
         | 
| 76 | 
            -
                          text = text.join if text.is_a?(Array)
         | 
| 77 | 
            -
             | 
| 78 | 
            -
                          if content_rule.custom_methods[:to]
         | 
| 79 | 
            -
                            text = @root.send(content_rule.custom_methods[:to], @root, text)
         | 
| 80 | 
            -
                          end
         | 
| 81 | 
            -
             | 
| 82 | 
            -
                          prefixed_xml.text text
         | 
| 83 | 
            -
                        end
         | 
| 84 | 
            -
                      end
         | 
| 85 | 
            -
                    end
         | 
| 86 | 
            -
             | 
| 87 36 | 
             
                    def build_ordered_element(xml, element, options = {})
         | 
| 88 37 | 
             
                      mapper_class = options[:mapper_class] || element.class
         | 
| 89 38 | 
             
                      xml_mapping = mapper_class.mappings_for(:xml)
         | 
| @@ -114,49 +63,27 @@ module Lutaml | |
| 114 63 | 
             
                          element_rule = xml_mapping.find_by_name(name)
         | 
| 115 64 | 
             
                          next if element_rule.nil?
         | 
| 116 65 |  | 
| 117 | 
            -
                          attribute_def = attribute_definition_for(element, element_rule, | 
| 66 | 
            +
                          attribute_def = attribute_definition_for(element, element_rule,
         | 
| 67 | 
            +
                                                                   mapper_class: mapper_class)
         | 
| 118 68 | 
             
                          value = attribute_value_for(element, element_rule)
         | 
| 119 | 
            -
                          nsp_xml = element_rule.prefix ? xml[element_rule.prefix] : xml
         | 
| 120 69 |  | 
| 121 70 | 
             
                          if element_rule == xml_mapping.content_mapping
         | 
| 122 71 | 
             
                            text = element.send(xml_mapping.content_mapping.to)
         | 
| 123 72 | 
             
                            text = text[curr_index] if text.is_a?(Array)
         | 
| 124 73 |  | 
| 125 74 | 
             
                            prefixed_xml.text text
         | 
| 126 | 
            -
                          elsif attribute_def.collection?
         | 
| 127 | 
            -
                            add_to_xml(nsp_xml, value[curr_index], attribute_def,
         | 
| 128 | 
            -
                                       element_rule)
         | 
| 129 75 | 
             
                          elsif !value.nil? || element_rule.render_nil?
         | 
| 130 | 
            -
                             | 
| 131 | 
            -
             | 
| 132 | 
            -
             | 
| 133 | 
            -
             | 
| 134 | 
            -
             | 
| 135 | 
            -
             | 
| 136 | 
            -
             | 
| 137 | 
            -
             | 
| 138 | 
            -
             | 
| 139 | 
            -
             | 
| 140 | 
            -
             | 
| 141 | 
            -
                      if value && (attribute&.type&.<= Lutaml::Model::Serialize)
         | 
| 142 | 
            -
                        handle_nested_elements(
         | 
| 143 | 
            -
                          xml,
         | 
| 144 | 
            -
                          value,
         | 
| 145 | 
            -
                          rule: rule,
         | 
| 146 | 
            -
                          attribute: attribute,
         | 
| 147 | 
            -
                        )
         | 
| 148 | 
            -
                      else
         | 
| 149 | 
            -
                        xml.public_send(rule.name) do
         | 
| 150 | 
            -
                          if !value.nil?
         | 
| 151 | 
            -
                            serialized_value = attribute.type.serialize(value)
         | 
| 152 | 
            -
             | 
| 153 | 
            -
                            if attribute.type == Lutaml::Model::Type::Hash
         | 
| 154 | 
            -
                              serialized_value.each do |key, val|
         | 
| 155 | 
            -
                                xml.public_send(key) { xml.text val }
         | 
| 156 | 
            -
                              end
         | 
| 157 | 
            -
                            else
         | 
| 158 | 
            -
                              xml.text(serialized_value)
         | 
| 159 | 
            -
                            end
         | 
| 76 | 
            +
                            value = value[curr_index] if attribute_def.collection?
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                            add_to_xml(
         | 
| 79 | 
            +
                              xml,
         | 
| 80 | 
            +
                              element_rule.prefix,
         | 
| 81 | 
            +
                              value,
         | 
| 82 | 
            +
                              options.merge(
         | 
| 83 | 
            +
                                attribute: attribute_def,
         | 
| 84 | 
            +
                                rule: element_rule,
         | 
| 85 | 
            +
                              ),
         | 
| 86 | 
            +
                            )
         | 
| 160 87 | 
             
                          end
         | 
| 161 88 | 
             
                        end
         | 
| 162 89 | 
             
                      end
         | 
| @@ -205,7 +132,7 @@ module Lutaml | |
| 205 132 | 
             
                    end
         | 
| 206 133 |  | 
| 207 134 | 
             
                    def to_xml(builder = nil)
         | 
| 208 | 
            -
                      builder ||= Nokogiri | 
| 135 | 
            +
                      builder ||= Builder::Nokogiri.build
         | 
| 209 136 |  | 
| 210 137 | 
             
                      if name == "text"
         | 
| 211 138 | 
             
                        builder.text(text)
         | 
| @@ -232,7 +159,7 @@ module Lutaml | |
| 232 159 | 
             
                      end
         | 
| 233 160 | 
             
                    end
         | 
| 234 161 |  | 
| 235 | 
            -
                    def build_attributes(node)
         | 
| 162 | 
            +
                    def build_attributes(node, _options = {})
         | 
| 236 163 | 
             
                      attrs = node.attributes.transform_values(&:value)
         | 
| 237 164 |  | 
| 238 165 | 
             
                      attrs.merge(build_namespace_attributes(node))
         | 
| @@ -1,5 +1,6 @@ | |
| 1 1 | 
             
            require "ox"
         | 
| 2 2 | 
             
            require_relative "xml_document"
         | 
| 3 | 
            +
            require_relative "builder/ox"
         | 
| 3 4 |  | 
| 4 5 | 
             
            module Lutaml
         | 
| 5 6 | 
             
              module Model
         | 
| @@ -12,7 +13,7 @@ module Lutaml | |
| 12 13 | 
             
                    end
         | 
| 13 14 |  | 
| 14 15 | 
             
                    def to_xml(options = {})
         | 
| 15 | 
            -
                      builder = Ox | 
| 16 | 
            +
                      builder = Builder::Ox.build
         | 
| 16 17 |  | 
| 17 18 | 
             
                      if @root.is_a?(Lutaml::Model::XmlAdapter::OxElement)
         | 
| 18 19 | 
             
                        @root.to_xml(builder)
         | 
| @@ -22,66 +23,12 @@ module Lutaml | |
| 22 23 | 
             
                        build_element(builder, @root, options)
         | 
| 23 24 | 
             
                      end
         | 
| 24 25 |  | 
| 25 | 
            -
                       | 
| 26 | 
            -
                      xml_data = builder.to_s
         | 
| 26 | 
            +
                      xml_data = builder.xml.to_s
         | 
| 27 27 | 
             
                      options[:declaration] ? declaration(options) + xml_data : xml_data
         | 
| 28 28 | 
             
                    end
         | 
| 29 29 |  | 
| 30 30 | 
             
                    private
         | 
| 31 31 |  | 
| 32 | 
            -
                    def build_unordered_element(builder, element, options = {})
         | 
| 33 | 
            -
                      mapper_class = options[:mapper_class] || element.class
         | 
| 34 | 
            -
                      xml_mapping = mapper_class.mappings_for(:xml)
         | 
| 35 | 
            -
                      return xml unless xml_mapping
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                      attributes = build_attributes(element, xml_mapping).compact
         | 
| 38 | 
            -
             | 
| 39 | 
            -
                      tag_name = options[:tag_name] || xml_mapping.root_element
         | 
| 40 | 
            -
                      prefixed_name = if options.key?(:namespace_prefix)
         | 
| 41 | 
            -
                                        [options[:namespace_prefix], tag_name].compact.join(":")
         | 
| 42 | 
            -
                                      elsif xml_mapping.namespace_prefix
         | 
| 43 | 
            -
                                        "#{xml_mapping.namespace_prefix}:#{tag_name}"
         | 
| 44 | 
            -
                                      else
         | 
| 45 | 
            -
                                        tag_name
         | 
| 46 | 
            -
                                      end
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                      builder.element(prefixed_name, attributes) do |el|
         | 
| 49 | 
            -
                        xml_mapping.elements.each do |element_rule|
         | 
| 50 | 
            -
                          attribute_def = attribute_definition_for(element, element_rule, mapper_class: mapper_class)
         | 
| 51 | 
            -
                          value = attribute_value_for(element, element_rule)
         | 
| 52 | 
            -
             | 
| 53 | 
            -
                          val = if attribute_def.collection?
         | 
| 54 | 
            -
                                  value
         | 
| 55 | 
            -
                                elsif value || element_rule.render_nil?
         | 
| 56 | 
            -
                                  [value]
         | 
| 57 | 
            -
                                else
         | 
| 58 | 
            -
                                  []
         | 
| 59 | 
            -
                                end
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                          val.each do |v|
         | 
| 62 | 
            -
                            if attribute_def&.type&.<= Lutaml::Model::Serialize
         | 
| 63 | 
            -
                              handle_nested_elements(el, v, rule: element_rule, attribute: attribute_def)
         | 
| 64 | 
            -
                            else
         | 
| 65 | 
            -
                              builder.element(element_rule.prefixed_name) do |el|
         | 
| 66 | 
            -
                                el.text(attribute_def.type.serialize(v)) if v
         | 
| 67 | 
            -
                              end
         | 
| 68 | 
            -
                            end
         | 
| 69 | 
            -
                          end
         | 
| 70 | 
            -
                        end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                        if (content_rule = xml_mapping.content_mapping)
         | 
| 73 | 
            -
                          text = element.send(xml_mapping.content_mapping.to)
         | 
| 74 | 
            -
                          text = text.join if text.is_a?(Array)
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                          if content_rule.custom_methods[:to]
         | 
| 77 | 
            -
                            text = @root.send(content_rule.custom_methods[:to], @root, text)
         | 
| 78 | 
            -
                          end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
                          el.text text
         | 
| 81 | 
            -
                        end
         | 
| 82 | 
            -
                      end
         | 
| 83 | 
            -
                    end
         | 
| 84 | 
            -
             | 
| 85 32 | 
             
                    def build_ordered_element(builder, element, options = {})
         | 
| 86 33 | 
             
                      mapper_class = options[:mapper_class] || element.class
         | 
| 87 34 | 
             
                      xml_mapping = mapper_class.mappings_for(:xml)
         | 
| @@ -90,7 +37,8 @@ module Lutaml | |
| 90 37 | 
             
                      attributes = build_attributes(element, xml_mapping).compact
         | 
| 91 38 |  | 
| 92 39 | 
             
                      tag_name = options[:tag_name] || xml_mapping.root_element
         | 
| 93 | 
            -
                      builder. | 
| 40 | 
            +
                      builder.create_and_add_element(tag_name,
         | 
| 41 | 
            +
                                                     attributes: attributes) do |el|
         | 
| 94 42 | 
             
                        index_hash = {}
         | 
| 95 43 |  | 
| 96 44 | 
             
                        element.element_order.each do |name|
         | 
| @@ -99,47 +47,27 @@ module Lutaml | |
| 99 47 |  | 
| 100 48 | 
             
                          element_rule = xml_mapping.find_by_name(name)
         | 
| 101 49 |  | 
| 102 | 
            -
                          attribute_def = attribute_definition_for(element, element_rule, | 
| 50 | 
            +
                          attribute_def = attribute_definition_for(element, element_rule,
         | 
| 51 | 
            +
                                                                   mapper_class: mapper_class)
         | 
| 103 52 | 
             
                          value = attribute_value_for(element, element_rule)
         | 
| 104 53 |  | 
| 105 54 | 
             
                          if element_rule == xml_mapping.content_mapping
         | 
| 106 55 | 
             
                            text = element.send(xml_mapping.content_mapping.to)
         | 
| 107 56 | 
             
                            text = text[curr_index] if text.is_a?(Array)
         | 
| 108 57 |  | 
| 109 | 
            -
                            el. | 
| 110 | 
            -
                          elsif attribute_def.collection?
         | 
| 111 | 
            -
                            add_to_xml(el, value[curr_index], attribute_def, element_rule)
         | 
| 58 | 
            +
                            el.add_text(el, text)
         | 
| 112 59 | 
             
                          elsif !value.nil? || element_rule.render_nil?
         | 
| 113 | 
            -
                             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 117 | 
            -
             | 
| 118 | 
            -
             | 
| 119 | 
            -
             | 
| 120 | 
            -
             | 
| 121 | 
            -
             | 
| 122 | 
            -
             | 
| 123 | 
            -
             | 
| 124 | 
            -
                      if value && (attribute&.type&.<= Lutaml::Model::Serialize)
         | 
| 125 | 
            -
                        handle_nested_elements(
         | 
| 126 | 
            -
                          xml,
         | 
| 127 | 
            -
                          value,
         | 
| 128 | 
            -
                          rule: rule,
         | 
| 129 | 
            -
                          attribute: attribute,
         | 
| 130 | 
            -
                        )
         | 
| 131 | 
            -
                      else
         | 
| 132 | 
            -
                        xml.element(rule.name) do |el|
         | 
| 133 | 
            -
                          if !value.nil?
         | 
| 134 | 
            -
                            serialized_value = attribute.type.serialize(value)
         | 
| 135 | 
            -
             | 
| 136 | 
            -
                            if attribute.type == Lutaml::Model::Type::Hash
         | 
| 137 | 
            -
                              serialized_value.each do |key, val|
         | 
| 138 | 
            -
                                el.element(key) { |child_el| child_el.text val }
         | 
| 139 | 
            -
                              end
         | 
| 140 | 
            -
                            else
         | 
| 141 | 
            -
                              el.text(serialized_value)
         | 
| 142 | 
            -
                            end
         | 
| 60 | 
            +
                            value = value[curr_index] if attribute_def.collection?
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                            add_to_xml(
         | 
| 63 | 
            +
                              el,
         | 
| 64 | 
            +
                              nil,
         | 
| 65 | 
            +
                              value,
         | 
| 66 | 
            +
                              options.merge(
         | 
| 67 | 
            +
                                attribute: attribute_def,
         | 
| 68 | 
            +
                                rule: element_rule,
         | 
| 69 | 
            +
                              ),
         | 
| 70 | 
            +
                            )
         | 
| 143 71 | 
             
                          end
         | 
| 144 72 | 
             
                        end
         | 
| 145 73 | 
             
                      end
         | 
| @@ -188,13 +116,13 @@ module Lutaml | |
| 188 116 | 
             
                    end
         | 
| 189 117 |  | 
| 190 118 | 
             
                    def to_xml(builder = nil)
         | 
| 191 | 
            -
                      builder ||= Ox | 
| 119 | 
            +
                      builder ||= Builder::Ox.build
         | 
| 192 120 | 
             
                      attrs = build_attributes(self)
         | 
| 193 121 |  | 
| 194 122 | 
             
                      if text?
         | 
| 195 | 
            -
                        builder. | 
| 123 | 
            +
                        builder.add_text(builder, text)
         | 
| 196 124 | 
             
                      else
         | 
| 197 | 
            -
                        builder. | 
| 125 | 
            +
                        builder.create_and_add_element(name, attributes: attrs) do |el|
         | 
| 198 126 | 
             
                          children.each { |child| child.to_xml(el) }
         | 
| 199 127 | 
             
                        end
         | 
| 200 128 | 
             
                      end
         | 
| @@ -42,21 +42,24 @@ module Lutaml | |
| 42 42 | 
             
                      @root.order
         | 
| 43 43 | 
             
                    end
         | 
| 44 44 |  | 
| 45 | 
            -
                    def handle_nested_elements(builder, value,  | 
| 46 | 
            -
                       | 
| 45 | 
            +
                    def handle_nested_elements(builder, value, options = {})
         | 
| 46 | 
            +
                      element_options = build_options_for_nested_elements(options)
         | 
| 47 47 |  | 
| 48 48 | 
             
                      case value
         | 
| 49 49 | 
             
                      when Array
         | 
| 50 | 
            -
                        value.each { |val| build_element(builder, val,  | 
| 50 | 
            +
                        value.each { |val| build_element(builder, val, element_options) }
         | 
| 51 51 | 
             
                      else
         | 
| 52 | 
            -
                        build_element(builder, value,  | 
| 52 | 
            +
                        build_element(builder, value, element_options)
         | 
| 53 53 | 
             
                      end
         | 
| 54 54 | 
             
                    end
         | 
| 55 55 |  | 
| 56 | 
            -
                    def build_options_for_nested_elements( | 
| 56 | 
            +
                    def build_options_for_nested_elements(options = {})
         | 
| 57 | 
            +
                      attribute = options.delete(:attribute)
         | 
| 58 | 
            +
                      rule = options.delete(:rule)
         | 
| 59 | 
            +
             | 
| 57 60 | 
             
                      return {} unless rule
         | 
| 58 61 |  | 
| 59 | 
            -
                      options = {}
         | 
| 62 | 
            +
                      # options = {}
         | 
| 60 63 |  | 
| 61 64 | 
             
                      options[:namespace_prefix] = rule.prefix if rule&.namespace_set?
         | 
| 62 65 | 
             
                      options[:mixed_content] = rule.mixed_content
         | 
| @@ -97,6 +100,103 @@ module Lutaml | |
| 97 100 | 
             
                      end
         | 
| 98 101 | 
             
                    end
         | 
| 99 102 |  | 
| 103 | 
            +
                    def add_to_xml(xml, prefix, value, options = {})
         | 
| 104 | 
            +
                      if value.is_a?(Array)
         | 
| 105 | 
            +
                        value.each do |item|
         | 
| 106 | 
            +
                          add_to_xml(xml, prefix, item, options)
         | 
| 107 | 
            +
                        end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                        return
         | 
| 110 | 
            +
                      end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                      attribute = options[:attribute]
         | 
| 113 | 
            +
                      rule = options[:rule]
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                      if rule.custom_methods[:to]
         | 
| 116 | 
            +
                        @root.send(rule.custom_methods[:to], @root, xml.parent, xml)
         | 
| 117 | 
            +
                        return
         | 
| 118 | 
            +
                      end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                      if value && (attribute&.type&.<= Lutaml::Model::Serialize)
         | 
| 121 | 
            +
                        handle_nested_elements(
         | 
| 122 | 
            +
                          xml,
         | 
| 123 | 
            +
                          value,
         | 
| 124 | 
            +
                          options.merge({ rule: rule, attribute: attribute }),
         | 
| 125 | 
            +
                        )
         | 
| 126 | 
            +
                      else
         | 
| 127 | 
            +
                        xml.create_and_add_element(rule.name, prefix: prefix) do
         | 
| 128 | 
            +
                          if !value.nil?
         | 
| 129 | 
            +
                            serialized_value = attribute.type.serialize(value)
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                            if attribute.type == Lutaml::Model::Type::Hash
         | 
| 132 | 
            +
                              serialized_value.each do |key, val|
         | 
| 133 | 
            +
                                xml.create_and_add_element(key) do |element|
         | 
| 134 | 
            +
                                  element.text(val)
         | 
| 135 | 
            +
                                end
         | 
| 136 | 
            +
                              end
         | 
| 137 | 
            +
                            else
         | 
| 138 | 
            +
                              xml.add_text(xml, serialized_value)
         | 
| 139 | 
            +
                            end
         | 
| 140 | 
            +
                          end
         | 
| 141 | 
            +
                        end
         | 
| 142 | 
            +
                      end
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    def build_unordered_element(xml, element, options = {})
         | 
| 146 | 
            +
                      mapper_class = options[:mapper_class] || element.class
         | 
| 147 | 
            +
                      xml_mapping = mapper_class.mappings_for(:xml)
         | 
| 148 | 
            +
                      return xml unless xml_mapping
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                      attributes = options[:xml_attributes] ||= {}
         | 
| 151 | 
            +
                      attributes = build_attributes(element,
         | 
| 152 | 
            +
                                                    xml_mapping, options).merge(attributes)&.compact
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                      prefix = if options.key?(:namespace_prefix)
         | 
| 155 | 
            +
                                 options[:namespace_prefix]
         | 
| 156 | 
            +
                               elsif xml_mapping.namespace_prefix
         | 
| 157 | 
            +
                                 xml_mapping.namespace_prefix
         | 
| 158 | 
            +
                               end
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                      prefixed_xml = xml.add_namespace_prefix(prefix)
         | 
| 161 | 
            +
                      tag_name = options[:tag_name] || xml_mapping.root_element
         | 
| 162 | 
            +
             | 
| 163 | 
            +
                      xml.create_and_add_element(tag_name, prefix: prefix,
         | 
| 164 | 
            +
                                                           attributes: attributes) do
         | 
| 165 | 
            +
                        if options.key?(:namespace_prefix) && !options[:namespace_prefix]
         | 
| 166 | 
            +
                          xml.add_namespace_prefix(nil)
         | 
| 167 | 
            +
                        end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
                        xml_mapping.elements.each do |element_rule|
         | 
| 170 | 
            +
                          attribute_def = attribute_definition_for(element, element_rule,
         | 
| 171 | 
            +
                                                                   mapper_class: mapper_class)
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                          value = attribute_value_for(element, element_rule)
         | 
| 174 | 
            +
             | 
| 175 | 
            +
                          next if value.nil? && !element_rule.render_nil?
         | 
| 176 | 
            +
             | 
| 177 | 
            +
                          value = [value] if attribute_def.collection? && !value.is_a?(Array)
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                          add_to_xml(
         | 
| 180 | 
            +
                            xml,
         | 
| 181 | 
            +
                            element_rule.prefix,
         | 
| 182 | 
            +
                            value,
         | 
| 183 | 
            +
                            options.merge({ attribute: attribute_def, rule: element_rule }),
         | 
| 184 | 
            +
                          )
         | 
| 185 | 
            +
                        end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                        if (content_rule = xml_mapping.content_mapping)
         | 
| 188 | 
            +
                          if content_rule.custom_methods[:to]
         | 
| 189 | 
            +
                            @root.send(content_rule.custom_methods[:to], element,
         | 
| 190 | 
            +
                                       prefixed_xml.parent, prefixed_xml)
         | 
| 191 | 
            +
                          else
         | 
| 192 | 
            +
                            text = element.send(content_rule.to)
         | 
| 193 | 
            +
                            text = text.join if text.is_a?(Array)
         | 
| 194 | 
            +
                            prefixed_xml.add_text(xml, text)
         | 
| 195 | 
            +
                          end
         | 
| 196 | 
            +
                        end
         | 
| 197 | 
            +
                      end
         | 
| 198 | 
            +
                    end
         | 
| 199 | 
            +
             | 
| 100 200 | 
             
                    def ordered?(element, options = {})
         | 
| 101 201 | 
             
                      return false unless element.respond_to?(:element_order)
         | 
| 102 202 | 
             
                      return element.ordered? if element.respond_to?(:ordered?)
         | 
| @@ -142,10 +242,12 @@ module Lutaml | |
| 142 242 | 
             
                      attrs
         | 
| 143 243 | 
             
                    end
         | 
| 144 244 |  | 
| 145 | 
            -
                    def build_attributes(element, xml_mapping)
         | 
| 245 | 
            +
                    def build_attributes(element, xml_mapping, options = {})
         | 
| 146 246 | 
             
                      attrs = namespace_attributes(xml_mapping)
         | 
| 147 247 |  | 
| 148 248 | 
             
                      xml_mapping.attributes.each_with_object(attrs) do |mapping_rule, hash|
         | 
| 249 | 
            +
                        next if options[:except]&.include?(mapping_rule.to)
         | 
| 250 | 
            +
             | 
| 149 251 | 
             
                        if mapping_rule.namespace
         | 
| 150 252 | 
             
                          hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
         | 
| 151 253 | 
             
                        end
         | 
| @@ -154,6 +256,8 @@ module Lutaml | |
| 154 256 | 
             
                      end
         | 
| 155 257 |  | 
| 156 258 | 
             
                      xml_mapping.elements.each_with_object(attrs) do |mapping_rule, hash|
         | 
| 259 | 
            +
                        next if options[:except]&.include?(mapping_rule.to)
         | 
| 260 | 
            +
             | 
| 157 261 | 
             
                        if mapping_rule.namespace
         | 
| 158 262 | 
             
                          hash["xmlns:#{mapping_rule.prefix}"] = mapping_rule.namespace
         | 
| 159 263 | 
             
                        end
         | 
| @@ -16,18 +16,6 @@ module Lutaml | |
| 16 16 | 
             
                    def to_yaml(options = {})
         | 
| 17 17 | 
             
                      YAML.dump(@attributes, options)
         | 
| 18 18 | 
             
                    end
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                    # TODO: Is this really needed?
         | 
| 21 | 
            -
                    def self.to_yaml(attributes, *args)
         | 
| 22 | 
            -
                      new(attributes).to_yaml(*args)
         | 
| 23 | 
            -
                    end
         | 
| 24 | 
            -
             | 
| 25 | 
            -
                    # TODO: Is this really needed?
         | 
| 26 | 
            -
                    def self.from_yaml(yaml, klass)
         | 
| 27 | 
            -
                      data = parse(yaml)
         | 
| 28 | 
            -
                      mapped_attrs = klass.send(:apply_mappings, data, :yaml)
         | 
| 29 | 
            -
                      klass.new(mapped_attrs)
         | 
| 30 | 
            -
                    end
         | 
| 31 19 | 
             
                  end
         | 
| 32 20 | 
             
                end
         | 
| 33 21 | 
             
              end
         | 
    
        data/lib/lutaml/model.rb
    CHANGED