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
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 30487a24277470ff7b3d12193e46262b0fc1cac85461224ed98749c8b6de5104
         | 
| 4 | 
            +
              data.tar.gz: a161a8e5f83a10ec5db5b77a0d5a0abe72b629d0900a4ab040414c2a02c22725
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 2a805f81f93bc0dcb983d83e7f7fc870989f84fb25434de7edee18269c1b96b8572516132e17e3db2e188075e985cb8219ec84257fd380c7abfe4b65b32116af
         | 
| 7 | 
            +
              data.tar.gz: 0243f89d6fb6c5466e29500e810a077096e260b51d53a8a23db7918c7b2d57dc30a9757dd00814a2c497dd552e39bd17e92729c509670dde555a0b508a77013b
         | 
    
        data/.rubocop_todo.yml
    CHANGED
    
    | @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            # This configuration was generated by
         | 
| 2 2 | 
             
            # `rubocop --auto-gen-config`
         | 
| 3 | 
            -
            # on 2024-08- | 
| 3 | 
            +
            # on 2024-08-27 06:48:03 UTC using RuboCop version 1.65.1.
         | 
| 4 4 | 
             
            # The point is for the user to remove these configuration records
         | 
| 5 5 | 
             
            # one by one as the offenses are removed from the code base.
         | 
| 6 6 | 
             
            # Note that changes in the inspected code, or installation of new
         | 
| @@ -14,7 +14,7 @@ Gemspec/RequireMFA: | |
| 14 14 | 
             
              Exclude:
         | 
| 15 15 | 
             
                - 'lutaml-model.gemspec'
         | 
| 16 16 |  | 
| 17 | 
            -
            # Offense count:  | 
| 17 | 
            +
            # Offense count: 57
         | 
| 18 18 | 
             
            # This cop supports safe autocorrection (--autocorrect).
         | 
| 19 19 | 
             
            # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
         | 
| 20 20 | 
             
            # URISchemes: http, https
         | 
| @@ -24,6 +24,7 @@ Layout/LineLength: | |
| 24 24 | 
             
                - 'lib/lutaml/model/comparable_model.rb'
         | 
| 25 25 | 
             
                - 'lib/lutaml/model/serialize.rb'
         | 
| 26 26 | 
             
                - 'lib/lutaml/model/type.rb'
         | 
| 27 | 
            +
                - 'lib/lutaml/model/utils.rb'
         | 
| 27 28 | 
             
                - 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
         | 
| 28 29 | 
             
                - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
         | 
| 29 30 | 
             
                - 'lib/lutaml/model/xml_adapter/xml_document.rb'
         | 
| @@ -43,7 +44,7 @@ Lint/ConstantDefinitionInBlock: | |
| 43 44 | 
             
                - 'spec/lutaml/model/schema/xsd_schema_spec.rb'
         | 
| 44 45 | 
             
                - 'spec/lutaml/model/schema/yaml_schema_spec.rb'
         | 
| 45 46 |  | 
| 46 | 
            -
            # Offense count:  | 
| 47 | 
            +
            # Offense count: 28
         | 
| 47 48 | 
             
            # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
         | 
| 48 49 | 
             
            Metrics/AbcSize:
         | 
| 49 50 | 
             
              Exclude:
         | 
| @@ -56,16 +57,17 @@ Metrics/AbcSize: | |
| 56 57 | 
             
                - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
         | 
| 57 58 | 
             
                - 'lib/lutaml/model/xml_adapter/xml_document.rb'
         | 
| 58 59 |  | 
| 59 | 
            -
            # Offense count:  | 
| 60 | 
            +
            # Offense count: 5
         | 
| 60 61 | 
             
            # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
         | 
| 61 62 | 
             
            # AllowedMethods: refine
         | 
| 62 63 | 
             
            Metrics/BlockLength:
         | 
| 63 | 
            -
              Max:  | 
| 64 | 
            +
              Max: 43
         | 
| 64 65 |  | 
| 65 | 
            -
            # Offense count:  | 
| 66 | 
            +
            # Offense count: 22
         | 
| 66 67 | 
             
            # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
         | 
| 67 68 | 
             
            Metrics/CyclomaticComplexity:
         | 
| 68 69 | 
             
              Exclude:
         | 
| 70 | 
            +
                - 'lib/lutaml/model/attribute.rb'
         | 
| 69 71 | 
             
                - 'lib/lutaml/model/comparable_model.rb'
         | 
| 70 72 | 
             
                - 'lib/lutaml/model/serialize.rb'
         | 
| 71 73 | 
             
                - 'lib/lutaml/model/type.rb'
         | 
| @@ -73,23 +75,23 @@ Metrics/CyclomaticComplexity: | |
| 73 75 | 
             
                - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
         | 
| 74 76 | 
             
                - 'lib/lutaml/model/xml_adapter/xml_document.rb'
         | 
| 75 77 |  | 
| 76 | 
            -
            # Offense count:  | 
| 78 | 
            +
            # Offense count: 36
         | 
| 77 79 | 
             
            # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
         | 
| 78 80 | 
             
            Metrics/MethodLength:
         | 
| 79 | 
            -
              Max:  | 
| 81 | 
            +
              Max: 43
         | 
| 80 82 |  | 
| 81 83 | 
             
            # Offense count: 4
         | 
| 82 84 | 
             
            # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
         | 
| 83 85 | 
             
            Metrics/ParameterLists:
         | 
| 84 86 | 
             
              Max: 9
         | 
| 85 87 |  | 
| 86 | 
            -
            # Offense count:  | 
| 88 | 
            +
            # Offense count: 18
         | 
| 87 89 | 
             
            # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
         | 
| 88 90 | 
             
            Metrics/PerceivedComplexity:
         | 
| 89 91 | 
             
              Exclude:
         | 
| 92 | 
            +
                - 'lib/lutaml/model/attribute.rb'
         | 
| 90 93 | 
             
                - 'lib/lutaml/model/comparable_model.rb'
         | 
| 91 94 | 
             
                - 'lib/lutaml/model/serialize.rb'
         | 
| 92 | 
            -
                - 'lib/lutaml/model/type.rb'
         | 
| 93 95 | 
             
                - 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
         | 
| 94 96 | 
             
                - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
         | 
| 95 97 | 
             
                - 'lib/lutaml/model/xml_adapter/xml_document.rb'
         | 
| @@ -103,7 +105,7 @@ RSpec/ContextWording: | |
| 103 105 | 
             
                - 'spec/lutaml/model/xml_adapter/oga_adapter_spec.rb'
         | 
| 104 106 | 
             
                - 'spec/lutaml/model/xml_adapter/ox_adapter_spec.rb'
         | 
| 105 107 |  | 
| 106 | 
            -
            # Offense count:  | 
| 108 | 
            +
            # Offense count: 76
         | 
| 107 109 | 
             
            # Configuration parameters: CountAsOne.
         | 
| 108 110 | 
             
            RSpec/ExampleLength:
         | 
| 109 111 | 
             
              Max: 57
         | 
| @@ -132,7 +134,7 @@ RSpec/MultipleDescribes: | |
| 132 134 |  | 
| 133 135 | 
             
            # Offense count: 70
         | 
| 134 136 | 
             
            RSpec/MultipleExpectations:
         | 
| 135 | 
            -
              Max:  | 
| 137 | 
            +
              Max: 10
         | 
| 136 138 |  | 
| 137 139 | 
             
            # Offense count: 11
         | 
| 138 140 | 
             
            # Configuration parameters: AllowSubject.
         | 
    
        data/README.adoc
    CHANGED
    
    | @@ -1338,11 +1338,11 @@ class CustomCeramic < Lutaml::Model::Serializable | |
| 1338 1338 | 
             
              end
         | 
| 1339 1339 |  | 
| 1340 1340 | 
             
              def name_to_json(model, value)
         | 
| 1341 | 
            -
                "Masterpiece: #{value}"
         | 
| 1341 | 
            +
                doc["name"] = "Masterpiece: #{value}"
         | 
| 1342 1342 | 
             
              end
         | 
| 1343 1343 |  | 
| 1344 1344 | 
             
              def name_from_json(model, doc)
         | 
| 1345 | 
            -
                 | 
| 1345 | 
            +
                model.name = value.sub(/^JSON Masterpiece: /, '')
         | 
| 1346 1346 | 
             
              end
         | 
| 1347 1347 | 
             
            end
         | 
| 1348 1348 | 
             
            ----
         | 
| @@ -1971,52 +1971,56 @@ class Example < Lutaml::Model::Serializable | |
| 1971 1971 | 
             
                                    from: :description_from_xml }
         | 
| 1972 1972 | 
             
              end
         | 
| 1973 1973 |  | 
| 1974 | 
            -
              def name_to_json( | 
| 1975 | 
            -
                "JSON Masterpiece: #{ | 
| 1974 | 
            +
              def name_to_json(model, doc)
         | 
| 1975 | 
            +
                doc["name"] = "JSON Masterpiece: #{model.name}"
         | 
| 1976 1976 | 
             
              end
         | 
| 1977 1977 |  | 
| 1978 | 
            -
              def name_from_json( | 
| 1979 | 
            -
                 | 
| 1978 | 
            +
              def name_from_json(model, value)
         | 
| 1979 | 
            +
                model.name = value.sub(/^JSON Masterpiece: /, "")
         | 
| 1980 1980 | 
             
              end
         | 
| 1981 1981 |  | 
| 1982 | 
            -
              def color_to_json( | 
| 1983 | 
            -
                 | 
| 1982 | 
            +
              def color_to_json(model, doc)
         | 
| 1983 | 
            +
                doc["color"] = model.color.upcase
         | 
| 1984 1984 | 
             
              end
         | 
| 1985 1985 |  | 
| 1986 | 
            -
              def color_from_json( | 
| 1987 | 
            -
                 | 
| 1986 | 
            +
              def color_from_json(model, value)
         | 
| 1987 | 
            +
                model.color = value.downcase
         | 
| 1988 1988 | 
             
              end
         | 
| 1989 1989 |  | 
| 1990 | 
            -
              def description_to_json( | 
| 1991 | 
            -
                "JSON Description: #{ | 
| 1990 | 
            +
              def description_to_json(model, doc)
         | 
| 1991 | 
            +
                doc["description"] = "JSON Description: #{model.description}"
         | 
| 1992 1992 | 
             
              end
         | 
| 1993 1993 |  | 
| 1994 | 
            -
              def description_from_json( | 
| 1995 | 
            -
                 | 
| 1994 | 
            +
              def description_from_json(model, value)
         | 
| 1995 | 
            +
                model.description = value.sub(/^JSON Description: /, "")
         | 
| 1996 1996 | 
             
              end
         | 
| 1997 1997 |  | 
| 1998 | 
            -
              def name_to_xml( | 
| 1999 | 
            -
                 | 
| 1998 | 
            +
              def name_to_xml(model, parent, doc)
         | 
| 1999 | 
            +
                el = doc.create_element("Name")
         | 
| 2000 | 
            +
                doc.add_text(el, "XML Masterpiece: #{model.name}")
         | 
| 2001 | 
            +
                doc.add_element(parent, el)
         | 
| 2000 2002 | 
             
              end
         | 
| 2001 2003 |  | 
| 2002 | 
            -
              def name_from_xml( | 
| 2003 | 
            -
                value.sub(/^XML Masterpiece: /, "")
         | 
| 2004 | 
            +
              def name_from_xml(model, value)
         | 
| 2005 | 
            +
                model.name = value.sub(/^XML Masterpiece: /, "")
         | 
| 2004 2006 | 
             
              end
         | 
| 2005 2007 |  | 
| 2006 | 
            -
              def color_to_xml( | 
| 2007 | 
            -
                 | 
| 2008 | 
            +
              def color_to_xml(model, parent, doc)
         | 
| 2009 | 
            +
                color_element = doc.create_element("Color")
         | 
| 2010 | 
            +
                doc.add_text(color_element, model.color.upcase)
         | 
| 2011 | 
            +
                doc.add_element(parent, color_element)
         | 
| 2008 2012 | 
             
              end
         | 
| 2009 2013 |  | 
| 2010 | 
            -
              def color_from_xml( | 
| 2011 | 
            -
                value.downcase
         | 
| 2014 | 
            +
              def color_from_xml(model, value)
         | 
| 2015 | 
            +
                model.color = value.downcase
         | 
| 2012 2016 | 
             
              end
         | 
| 2013 2017 |  | 
| 2014 | 
            -
              def description_to_xml( | 
| 2015 | 
            -
                "XML Description: #{ | 
| 2018 | 
            +
              def description_to_xml(model, parent, doc)
         | 
| 2019 | 
            +
                doc.add_text(parent, "XML Description: #{model.description}")
         | 
| 2016 2020 | 
             
              end
         | 
| 2017 2021 |  | 
| 2018 | 
            -
              def description_from_xml( | 
| 2019 | 
            -
                value.sub(/^XML Description: /, "")
         | 
| 2022 | 
            +
              def description_from_xml(model, value)
         | 
| 2023 | 
            +
                model.description = value.join.strip.sub(/^XML Description: /, "")
         | 
| 2020 2024 | 
             
              end
         | 
| 2021 2025 | 
             
            end
         | 
| 2022 2026 | 
             
            ----
         | 
| @@ -40,6 +40,33 @@ module Lutaml | |
| 40 40 | 
             
                  def render_nil?
         | 
| 41 41 | 
             
                    options.fetch(:render_nil, false)
         | 
| 42 42 | 
             
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def serialize(value, format, options = {})
         | 
| 45 | 
            +
                    if value.is_a?(Array)
         | 
| 46 | 
            +
                      value.map do |v|
         | 
| 47 | 
            +
                        serialize(v, format, options)
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    elsif type <= Serialize
         | 
| 50 | 
            +
                      type.hash_representation(value, format, options)
         | 
| 51 | 
            +
                    else
         | 
| 52 | 
            +
                      type.serialize(value)
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                  end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  def cast(value, format, options = {})
         | 
| 57 | 
            +
                    value ||= [] if collection?
         | 
| 58 | 
            +
                    instance = options[:instance]
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    if value.is_a?(Array)
         | 
| 61 | 
            +
                      value.map do |v|
         | 
| 62 | 
            +
                        cast(v, format, instance: instance)
         | 
| 63 | 
            +
                      end
         | 
| 64 | 
            +
                    elsif type <= Serialize && value.is_a?(Hash)
         | 
| 65 | 
            +
                      type.apply_mappings(value, format, options)
         | 
| 66 | 
            +
                    else
         | 
| 67 | 
            +
                      Lutaml::Model::Type.cast(value, type)
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
                  end
         | 
| 43 70 | 
             
                end
         | 
| 44 71 | 
             
              end
         | 
| 45 72 | 
             
            end
         | 
| @@ -84,29 +84,47 @@ module Lutaml | |
| 84 84 |  | 
| 85 85 | 
             
                      define_method(:"from_#{format}") do |data|
         | 
| 86 86 | 
             
                        adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
         | 
| 87 | 
            +
             | 
| 87 88 | 
             
                        doc = adapter.parse(data)
         | 
| 88 | 
            -
                         | 
| 89 | 
            -
                        # apply_content_mapping(doc, mapped_attrs) if format == :xml
         | 
| 90 | 
            -
                        generate_model_object(self, mapped_attrs)
         | 
| 89 | 
            +
                        public_send(:"of_#{format}", doc.to_h)
         | 
| 91 90 | 
             
                      end
         | 
| 92 91 |  | 
| 93 | 
            -
                      define_method(:" | 
| 94 | 
            -
                         | 
| 95 | 
            -
                           | 
| 96 | 
            -
             | 
| 92 | 
            +
                      define_method(:"of_#{format}") do |hash|
         | 
| 93 | 
            +
                        if hash.is_a?(Array)
         | 
| 94 | 
            +
                          return hash.map do |item|
         | 
| 95 | 
            +
                            apply_mappings(item, format)
         | 
| 96 | 
            +
                          end
         | 
| 97 97 | 
             
                        end
         | 
| 98 98 |  | 
| 99 | 
            +
                        apply_mappings(hash, format)
         | 
| 100 | 
            +
                      end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                      define_method(:"to_#{format}") do |instance|
         | 
| 103 | 
            +
                        value = public_send(:"as_#{format}", instance)
         | 
| 99 104 | 
             
                        adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
         | 
| 100 105 |  | 
| 101 106 | 
             
                        if format == :xml
         | 
| 102 107 | 
             
                          xml_options = { mapper_class: self }
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                          adapter.new(instance).public_send(:"to_#{format}", xml_options)
         | 
| 108 | 
            +
                          adapter.new(value).public_send(:"to_#{format}", xml_options)
         | 
| 105 109 | 
             
                        else
         | 
| 106 | 
            -
                           | 
| 107 | 
            -
                          adapter.new(hash).public_send(:"to_#{format}")
         | 
| 110 | 
            +
                          adapter.new(value).public_send(:"to_#{format}")
         | 
| 108 111 | 
             
                        end
         | 
| 109 112 | 
             
                      end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      define_method(:"as_#{format}") do |instance|
         | 
| 115 | 
            +
                        if instance.is_a?(Array)
         | 
| 116 | 
            +
                          return instance.map { |item| public_send(:"as_#{format}", item) }
         | 
| 117 | 
            +
                        end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                        unless instance.is_a?(model)
         | 
| 120 | 
            +
                          msg = "argument is a '#{instance.class}' but should be a '#{model}'"
         | 
| 121 | 
            +
                          raise Lutaml::Model::IncorrectModelError, msg
         | 
| 122 | 
            +
                        end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                        return instance if format == :xml
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                        hash_representation(instance, format)
         | 
| 127 | 
            +
                      end
         | 
| 110 128 | 
             
                    end
         | 
| 111 129 |  | 
| 112 130 | 
             
                    def hash_representation(instance, format, options = {})
         | 
| @@ -118,13 +136,13 @@ module Lutaml | |
| 118 136 | 
             
                        name = rule.to
         | 
| 119 137 | 
             
                        next if except&.include?(name) || (only && !only.include?(name))
         | 
| 120 138 |  | 
| 121 | 
            -
                        next handle_delegate(instance, rule, hash) if rule.delegate
         | 
| 139 | 
            +
                        next handle_delegate(instance, rule, hash, format) if rule.delegate
         | 
| 122 140 |  | 
| 123 | 
            -
                         | 
| 124 | 
            -
             | 
| 125 | 
            -
             | 
| 126 | 
            -
             | 
| 127 | 
            -
             | 
| 141 | 
            +
                        if rule.custom_methods[:to]
         | 
| 142 | 
            +
                          next instance.send(rule.custom_methods[:to], instance, hash)
         | 
| 143 | 
            +
                        end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                        value = instance.send(name)
         | 
| 128 146 |  | 
| 129 147 | 
             
                        next if value.nil? && !rule.render_nil
         | 
| 130 148 |  | 
| @@ -132,68 +150,27 @@ module Lutaml | |
| 132 150 |  | 
| 133 151 | 
             
                        hash[rule.from] = if rule.child_mappings
         | 
| 134 152 | 
             
                                            generate_hash_from_child_mappings(value, rule.child_mappings)
         | 
| 135 | 
            -
                                          elsif value.is_a?(Array)
         | 
| 136 | 
            -
                                            value.map do |v|
         | 
| 137 | 
            -
                                              if attribute.type <= Serialize
         | 
| 138 | 
            -
                                                attribute.type.hash_representation(v, format, options)
         | 
| 139 | 
            -
                                              else
         | 
| 140 | 
            -
                                                attribute.type.serialize(v)
         | 
| 141 | 
            -
                                              end
         | 
| 142 | 
            -
                                            end
         | 
| 143 | 
            -
                                          elsif attribute.type <= Serialize
         | 
| 144 | 
            -
                                            attribute.type.hash_representation(value, format, options)
         | 
| 145 153 | 
             
                                          else
         | 
| 146 | 
            -
                                            attribute. | 
| 154 | 
            +
                                            attribute.serialize(value, format, options)
         | 
| 147 155 | 
             
                                          end
         | 
| 148 156 | 
             
                      end
         | 
| 149 157 | 
             
                    end
         | 
| 150 158 |  | 
| 151 | 
            -
                    def handle_delegate(instance, rule, hash)
         | 
| 159 | 
            +
                    def handle_delegate(instance, rule, hash, format)
         | 
| 152 160 | 
             
                      name = rule.to
         | 
| 153 161 | 
             
                      value = instance.send(rule.delegate).send(name)
         | 
| 154 162 | 
             
                      return if value.nil? && !rule.render_nil
         | 
| 155 163 |  | 
| 156 164 | 
             
                      attribute = instance.send(rule.delegate).class.attributes[name]
         | 
| 157 | 
            -
                      hash[rule.from] =  | 
| 158 | 
            -
                                        when Array
         | 
| 159 | 
            -
                                          value.map do |v|
         | 
| 160 | 
            -
                                            if v.is_a?(Serialize)
         | 
| 161 | 
            -
                                              hash_representation(v, format, options)
         | 
| 162 | 
            -
                                            else
         | 
| 163 | 
            -
                                              attribute.type.serialize(v)
         | 
| 164 | 
            -
                                            end
         | 
| 165 | 
            -
                                          end
         | 
| 166 | 
            -
                                        else
         | 
| 167 | 
            -
                                          if value.is_a?(Serialize)
         | 
| 168 | 
            -
                                            hash_representation(value, format, options)
         | 
| 169 | 
            -
                                          else
         | 
| 170 | 
            -
                                            attribute.type.serialize(value)
         | 
| 171 | 
            -
                                          end
         | 
| 172 | 
            -
                                        end
         | 
| 165 | 
            +
                      hash[rule.from] = attribute.serialize(value, format)
         | 
| 173 166 | 
             
                    end
         | 
| 174 167 |  | 
| 175 168 | 
             
                    def mappings_for(format)
         | 
| 176 169 | 
             
                      mappings[format] || default_mappings(format)
         | 
| 177 170 | 
             
                    end
         | 
| 178 171 |  | 
| 179 | 
            -
                    def generate_model_object(type, mapped_attrs)
         | 
| 180 | 
            -
                      return type.model.new(mapped_attrs) if self == model
         | 
| 181 | 
            -
             | 
| 182 | 
            -
                      instance = type.model.new
         | 
| 183 | 
            -
             | 
| 184 | 
            -
                      type.attributes.each do |name, attr|
         | 
| 185 | 
            -
                        value = attr_value(mapped_attrs, name, attr)
         | 
| 186 | 
            -
             | 
| 187 | 
            -
                        instance.send(:"#{name}=", ensure_utf8(value))
         | 
| 188 | 
            -
                      end
         | 
| 189 | 
            -
             | 
| 190 | 
            -
                      instance
         | 
| 191 | 
            -
                    end
         | 
| 192 | 
            -
             | 
| 193 172 | 
             
                    def attr_value(attrs, name, attr_rule)
         | 
| 194 | 
            -
                      value = if attrs.key?(name)
         | 
| 195 | 
            -
                                attrs[name]
         | 
| 196 | 
            -
                              elsif attrs.key?(name.to_sym)
         | 
| 173 | 
            +
                      value = if attrs.key?(name.to_sym)
         | 
| 197 174 | 
             
                                attrs[name.to_sym]
         | 
| 198 175 | 
             
                              elsif attrs.key?(name.to_s)
         | 
| 199 176 | 
             
                                attrs[name.to_s]
         | 
| @@ -211,8 +188,6 @@ module Lutaml | |
| 211 188 | 
             
                            Lutaml::Model::Type.cast(v, attr_rule.type)
         | 
| 212 189 | 
             
                          end
         | 
| 213 190 | 
             
                        end
         | 
| 214 | 
            -
                      elsif value.is_a?(Hash) && attr_rule.type != Lutaml::Model::Type::Hash
         | 
| 215 | 
            -
                        generate_model_object(attr_rule.type, value)
         | 
| 216 191 | 
             
                      else
         | 
| 217 192 | 
             
                        # TODO: This code is problematic because Type.cast does not know
         | 
| 218 193 | 
             
                        # about all the types.
         | 
| @@ -279,11 +254,13 @@ module Lutaml | |
| 279 254 | 
             
                      hash
         | 
| 280 255 | 
             
                    end
         | 
| 281 256 |  | 
| 282 | 
            -
                    def apply_mappings(doc, format)
         | 
| 283 | 
            -
                       | 
| 257 | 
            +
                    def apply_mappings(doc, format, options = {})
         | 
| 258 | 
            +
                      instance = options[:instance] || model.new
         | 
| 259 | 
            +
                      return instance if !doc || doc.empty?
         | 
| 260 | 
            +
                      return apply_xml_mapping(doc, instance, options) if format == :xml
         | 
| 284 261 |  | 
| 285 262 | 
             
                      mappings = mappings_for(format).mappings
         | 
| 286 | 
            -
                      mappings. | 
| 263 | 
            +
                      mappings.each do |rule|
         | 
| 287 264 | 
             
                        attr = if rule.delegate
         | 
| 288 265 | 
             
                                 attributes[rule.delegate].type.attributes[rule.to]
         | 
| 289 266 | 
             
                               else
         | 
| @@ -292,106 +269,96 @@ module Lutaml | |
| 292 269 |  | 
| 293 270 | 
             
                        raise "Attribute '#{rule.to}' not found in #{self}" unless attr
         | 
| 294 271 |  | 
| 295 | 
            -
                        value = if rule. | 
| 296 | 
            -
                                  new.send(rule.custom_methods[:from], hash, doc)
         | 
| 297 | 
            -
                                elsif doc.key?(rule.name) || doc.key?(rule.name.to_sym)
         | 
| 272 | 
            +
                        value = if doc.key?(rule.name) || doc.key?(rule.name.to_sym)
         | 
| 298 273 | 
             
                                  doc[rule.name] || doc[rule.name.to_sym]
         | 
| 299 274 | 
             
                                else
         | 
| 300 275 | 
             
                                  attr.default
         | 
| 301 276 | 
             
                                end
         | 
| 302 277 |  | 
| 303 | 
            -
                         | 
| 304 | 
            -
             | 
| 305 | 
            -
             | 
| 306 | 
            -
                          value = (value || []).map do |v|
         | 
| 307 | 
            -
                            attr.type <= Serialize ? attr.type.apply_mappings(v, format) : v
         | 
| 308 | 
            -
                          end
         | 
| 309 | 
            -
                        elsif value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash
         | 
| 310 | 
            -
                          value = attr.type.apply_mappings(value, format)
         | 
| 278 | 
            +
                        if rule.custom_methods[:from]
         | 
| 279 | 
            +
                          value = new.send(rule.custom_methods[:from], instance, value) if value && !value.empty?
         | 
| 280 | 
            +
                          next
         | 
| 311 281 | 
             
                        end
         | 
| 312 282 |  | 
| 283 | 
            +
                        value = apply_child_mappings(value, rule.child_mappings)
         | 
| 284 | 
            +
                        value = attr.cast(value, format)
         | 
| 285 | 
            +
             | 
| 313 286 | 
             
                        if rule.delegate
         | 
| 314 | 
            -
                           | 
| 315 | 
            -
             | 
| 287 | 
            +
                          if instance.public_send(rule.delegate).nil?
         | 
| 288 | 
            +
                            instance.public_send(:"#{rule.delegate}=",
         | 
| 289 | 
            +
                                                 attributes[rule.delegate].type.new)
         | 
| 290 | 
            +
                          end
         | 
| 291 | 
            +
                          instance.public_send(rule.delegate).public_send(:"#{rule.to}=",
         | 
| 292 | 
            +
                                                                          value)
         | 
| 316 293 | 
             
                        else
         | 
| 317 | 
            -
                           | 
| 294 | 
            +
                          instance.public_send(:"#{rule.to}=", value)
         | 
| 318 295 | 
             
                        end
         | 
| 319 296 | 
             
                      end
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                      instance
         | 
| 320 299 | 
             
                    end
         | 
| 321 300 |  | 
| 322 | 
            -
                    def apply_xml_mapping(doc,  | 
| 323 | 
            -
                      return unless doc
         | 
| 301 | 
            +
                    def apply_xml_mapping(doc, instance, options = {})
         | 
| 302 | 
            +
                      return instance unless doc
         | 
| 324 303 |  | 
| 325 304 | 
             
                      mappings = mappings_for(:xml).mappings
         | 
| 326 305 |  | 
| 327 306 | 
             
                      if doc.is_a?(Array)
         | 
| 328 307 | 
             
                        raise "May be `collection: true` is" \
         | 
| 329 | 
            -
                              "missing for #{self} in #{caller_class}"
         | 
| 308 | 
            +
                              "missing for #{self} in #{options[:caller_class]}"
         | 
| 330 309 | 
             
                      end
         | 
| 331 310 |  | 
| 332 | 
            -
                       | 
| 333 | 
            -
             | 
| 334 | 
            -
             | 
| 335 | 
            -
             | 
| 336 | 
            -
                      mapping_from = []
         | 
| 311 | 
            +
                      if instance.respond_to?(:ordered=) && doc.is_a?(Lutaml::Model::MappingHash)
         | 
| 312 | 
            +
                        instance.element_order = doc.item_order
         | 
| 313 | 
            +
                        instance.ordered = mappings_for(:xml).mixed_content? || options[:mixed_content]
         | 
| 314 | 
            +
                      end
         | 
| 337 315 |  | 
| 338 | 
            -
                      mappings. | 
| 316 | 
            +
                      mappings.each do |rule|
         | 
| 339 317 | 
             
                        attr = attributes[rule.to]
         | 
| 340 318 | 
             
                        raise "Attribute '#{rule.to}' not found in #{self}" unless attr
         | 
| 341 319 |  | 
| 342 320 | 
             
                        is_content_mapping = rule.name.nil?
         | 
| 321 | 
            +
             | 
| 343 322 | 
             
                        value = if is_content_mapping
         | 
| 344 323 | 
             
                                  doc["text"]
         | 
| 345 324 | 
             
                                else
         | 
| 346 325 | 
             
                                  doc[rule.name.to_s] || doc[rule.name.to_sym]
         | 
| 347 326 | 
             
                                end
         | 
| 348 327 |  | 
| 349 | 
            -
                        if attr.collection?
         | 
| 350 | 
            -
                          if value && !value.is_a?(Array)
         | 
| 351 | 
            -
                            value = [value]
         | 
| 352 | 
            -
                          end
         | 
| 328 | 
            +
                        value = [value].compact if attr.collection? && !value.is_a?(Array)
         | 
| 353 329 |  | 
| 354 | 
            -
             | 
| 355 | 
            -
             | 
| 356 | 
            -
             | 
| 357 | 
            -
                            elsif v.is_a?(Hash)
         | 
| 358 | 
            -
                              v["text"]
         | 
| 359 | 
            -
                            else
         | 
| 360 | 
            -
                              v
         | 
| 361 | 
            -
                            end
         | 
| 330 | 
            +
                        if value.is_a?(Array)
         | 
| 331 | 
            +
                          value = value.map do |v|
         | 
| 332 | 
            +
                            v.is_a?(Hash) && !(attr.type <= Serialize) ? v["text"] : v
         | 
| 362 333 | 
             
                          end
         | 
| 363 | 
            -
                        elsif attr.type <= Serialize
         | 
| 364 | 
            -
                          value =  | 
| 365 | 
            -
                        else
         | 
| 366 | 
            -
                          if value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash
         | 
| 367 | 
            -
                            value = value["text"]
         | 
| 368 | 
            -
                          end
         | 
| 369 | 
            -
             | 
| 370 | 
            -
                          value = attr.type.cast(value) unless is_content_mapping
         | 
| 334 | 
            +
                        elsif !(attr.type <= Serialize) && value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash
         | 
| 335 | 
            +
                          value = value["text"]
         | 
| 371 336 | 
             
                        end
         | 
| 372 337 |  | 
| 373 | 
            -
                         | 
| 374 | 
            -
             | 
| 375 | 
            -
             | 
| 376 | 
            -
             | 
| 377 | 
            -
             | 
| 378 | 
            -
             | 
| 379 | 
            -
             | 
| 380 | 
            -
             | 
| 381 | 
            -
                                else
         | 
| 382 | 
            -
                                  mapping_hash[rule.to]
         | 
| 383 | 
            -
                                end
         | 
| 338 | 
            +
                        unless is_content_mapping
         | 
| 339 | 
            +
                          value = attr.cast(
         | 
| 340 | 
            +
                            value,
         | 
| 341 | 
            +
                            :xml,
         | 
| 342 | 
            +
                            caller_class: self,
         | 
| 343 | 
            +
                            mixed_content: rule.mixed_content,
         | 
| 344 | 
            +
                          )
         | 
| 345 | 
            +
                        end
         | 
| 384 346 |  | 
| 385 | 
            -
                         | 
| 347 | 
            +
                        if rule.custom_methods[:from]
         | 
| 348 | 
            +
                          new.send(rule.custom_methods[:from], instance, value)
         | 
| 349 | 
            +
                        else
         | 
| 350 | 
            +
                          instance.public_send(:"#{rule.to}=", value)
         | 
| 351 | 
            +
                        end
         | 
| 386 352 | 
             
                      end
         | 
| 387 353 |  | 
| 388 | 
            -
                       | 
| 354 | 
            +
                      instance
         | 
| 389 355 | 
             
                    end
         | 
| 390 356 |  | 
| 391 357 | 
             
                    def ensure_utf8(value)
         | 
| 392 358 | 
             
                      case value
         | 
| 393 359 | 
             
                      when String
         | 
| 394 | 
            -
                        value.encode("UTF-8", invalid: :replace, undef: :replace, | 
| 360 | 
            +
                        value.encode("UTF-8", invalid: :replace, undef: :replace,
         | 
| 361 | 
            +
                                              replace: "")
         | 
| 395 362 | 
             
                      when Array
         | 
| 396 363 | 
             
                        value.map { |v| ensure_utf8(v) }
         | 
| 397 364 | 
             
                      when Hash
         | 
| @@ -406,7 +373,7 @@ module Lutaml | |
| 406 373 | 
             
                    end
         | 
| 407 374 | 
             
                  end
         | 
| 408 375 |  | 
| 409 | 
            -
                   | 
| 376 | 
            +
                  attr_accessor :element_order
         | 
| 410 377 |  | 
| 411 378 | 
             
                  def initialize(attrs = {})
         | 
| 412 379 | 
             
                    return unless self.class.attributes
         | 
| @@ -429,6 +396,10 @@ module Lutaml | |
| 429 396 | 
             
                    @ordered
         | 
| 430 397 | 
             
                  end
         | 
| 431 398 |  | 
| 399 | 
            +
                  def ordered=(ordered)
         | 
| 400 | 
            +
                    @ordered = ordered
         | 
| 401 | 
            +
                  end
         | 
| 402 | 
            +
             | 
| 432 403 | 
             
                  def key_exist?(hash, key)
         | 
| 433 404 | 
             
                    hash.key?(key.to_sym) || hash.key?(key.to_s)
         | 
| 434 405 | 
             
                  end
         | 
| @@ -444,7 +415,8 @@ module Lutaml | |
| 444 415 | 
             
                      representation = if format == :xml
         | 
| 445 416 | 
             
                                         self
         | 
| 446 417 | 
             
                                       else
         | 
| 447 | 
            -
                                         self.class.hash_representation(self, format, | 
| 418 | 
            +
                                         self.class.hash_representation(self, format,
         | 
| 419 | 
            +
                                                                        options)
         | 
| 448 420 | 
             
                                       end
         | 
| 449 421 |  | 
| 450 422 | 
             
                      adapter.new(representation).public_send(:"to_#{format}", options)
         | 
| @@ -455,7 +427,8 @@ module Lutaml | |
| 455 427 | 
             
                    self.class.attributes.each do |name, attr|
         | 
| 456 428 | 
             
                      value = send(name)
         | 
| 457 429 | 
             
                      unless self.class.attr_value_valid?(name, value)
         | 
| 458 | 
            -
                        raise Lutaml::Model::InvalidValueError.new(name, value, | 
| 430 | 
            +
                        raise Lutaml::Model::InvalidValueError.new(name, value,
         | 
| 431 | 
            +
                                                                   attr.options[:values])
         | 
| 459 432 | 
             
                      end
         | 
| 460 433 | 
             
                    end
         | 
| 461 434 | 
             
                  end
         | 
    
        data/lib/lutaml/model/type.rb
    CHANGED
    
    | @@ -22,11 +22,13 @@ module Lutaml | |
| 22 22 | 
             
                                 class #{t}                        # class Integer
         | 
| 23 23 | 
             
                        def self.cast(value)            #   def self.cast(value)
         | 
| 24 24 | 
             
                          return if value.nil?          #     return if value.nil?
         | 
| 25 | 
            +
                                                        #
         | 
| 25 26 | 
             
                          Type.cast(value, #{t})        #     Type.cast(value, Integer)
         | 
| 26 27 | 
             
                        end                             #   end
         | 
| 27 28 |  | 
| 28 29 | 
             
                        def self.serialize(value)       #   def self.serialize(value)
         | 
| 29 30 | 
             
                          return if value.nil?          #     return if value.nil?
         | 
| 31 | 
            +
                                                        #
         | 
| 30 32 | 
             
                          Type.serialize(value, #{t})   #     Type.serialize(value, Integer)
         | 
| 31 33 | 
             
                        end                             #   end
         | 
| 32 34 | 
             
                      end                               # end
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Lutaml
         | 
| 4 | 
            +
              module Model
         | 
| 5 | 
            +
                module Utils
         | 
| 6 | 
            +
                  class << self
         | 
| 7 | 
            +
                    # Convert string to camel case
         | 
| 8 | 
            +
                    def camel_case(str)
         | 
| 9 | 
            +
                      return "" if str.nil? || str.empty?
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                      str.split("/").map { |part| camelize_part(part) }.join("::")
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    # Convert string to class name
         | 
| 15 | 
            +
                    def classify(str)
         | 
| 16 | 
            +
                      str = str.to_s.delete(".")
         | 
| 17 | 
            +
                      str = str.sub(/^[a-z\d]*/) { |match| camel_case(match) || match }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                      str.gsub("::", "/").gsub(%r{(?:_|-|(/))([a-z\d]*)}i) do
         | 
| 20 | 
            +
                        word = Regexp.last_match(2)
         | 
| 21 | 
            +
                        substituted = camel_case(word) || word
         | 
| 22 | 
            +
                        Regexp.last_match(1) ? "::#{substituted}" : substituted
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    # Convert string to snake case
         | 
| 27 | 
            +
                    def snake_case(str)
         | 
| 28 | 
            +
                      str = str.to_s.tr(".", "_")
         | 
| 29 | 
            +
                      return str unless /[A-Z-]|::/.match?(str)
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      str.gsub("::", "/")
         | 
| 32 | 
            +
                        .gsub(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) { "#{$1 || $2}_" }
         | 
| 33 | 
            +
                        .tr("-", "_")
         | 
| 34 | 
            +
                        .downcase
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    private
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def camelize_part(part)
         | 
| 40 | 
            +
                      part.gsub(/(?:_|-|^)([a-z\d])/i) { $1.upcase }
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         |