lutaml-model 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +34 -18
  3. data/README.adoc +166 -8
  4. data/lib/lutaml/model/key_value_mapping.rb +0 -1
  5. data/lib/lutaml/model/key_value_mapping_rule.rb +3 -1
  6. data/lib/lutaml/model/mapping_rule.rb +14 -2
  7. data/lib/lutaml/model/serialize.rb +67 -52
  8. data/lib/lutaml/model/type/decimal.rb +5 -0
  9. data/lib/lutaml/model/version.rb +1 -1
  10. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +1 -0
  11. data/lib/lutaml/model/xml_adapter/builder/oga.rb +180 -0
  12. data/lib/lutaml/model/xml_adapter/builder/ox.rb +1 -0
  13. data/lib/lutaml/model/xml_adapter/oga/document.rb +20 -0
  14. data/lib/lutaml/model/xml_adapter/oga/element.rb +117 -0
  15. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +77 -44
  16. data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -9
  17. data/lib/lutaml/model/xml_mapping.rb +0 -1
  18. data/lib/lutaml/model/xml_mapping_rule.rb +16 -4
  19. data/spec/address_spec.rb +1 -0
  20. data/spec/fixtures/sample_model.rb +7 -0
  21. data/spec/lutaml/model/custom_model_spec.rb +47 -1
  22. data/spec/lutaml/model/included_spec.rb +192 -0
  23. data/spec/lutaml/model/mixed_content_spec.rb +48 -32
  24. data/spec/lutaml/model/multiple_mapping_spec.rb +329 -0
  25. data/spec/lutaml/model/ordered_content_spec.rb +1 -1
  26. data/spec/lutaml/model/render_nil_spec.rb +3 -0
  27. data/spec/lutaml/model/serializable_spec.rb +1 -1
  28. data/spec/lutaml/model/type/boolean_spec.rb +62 -0
  29. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -11
  30. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +1 -1
  31. data/spec/lutaml/model/xml_adapter_spec.rb +2 -2
  32. data/spec/lutaml/model/xml_mapping_spec.rb +24 -9
  33. data/spec/sample_model_spec.rb +114 -0
  34. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fa60d5495804e6bfc0203ba1a237f38b89b9626d036beeb2be8ca7aebb22426f
4
- data.tar.gz: 63d8d18127bee85cfaa73973f57554bc56385805dfeea1dad343c552277089d6
3
+ metadata.gz: b1472bd6c2746013aba5d315d47dc5e484cfa54f6f537ab5d613b81f4f63109e
4
+ data.tar.gz: 45c48daa9b4a0f7b98d04bb8174f6cfe298c59564b714b866f838b7d2bf6464b
5
5
  SHA512:
6
- metadata.gz: 322a29941e7685a6e118d083f1b6e11ce6b8abc1cacb5508dd501f874c5ea3f2dc6f0e6719ce4895deb31c877319a3afe0e20d0820a81b8fd60d9fe59c408472
7
- data.tar.gz: ce087acefc221ed41180658b8c40d4cff5c0196c208647b9d1c5d0d39799849860abf80b981fe753b30a1176ca8633ed696440beeddef5a4493c1e829340f250
6
+ metadata.gz: 94f772a0efbc35332c95fb55729c8d781d4168ff61784af79d2f09388c93263144b378d8485e578f43aeb0386b2c02d4769285c782dffaa2c5d4dd6f0f34fa70
7
+ data.tar.gz: 4dd7279a62a8835e1f7c908590cc653d693f9a0225a388eda89bfa02cdcc7fb9a934bbf8d273f57d93d9764c86b867ae42a7aab2a87a4322c89698fa207343d0
data/.rubocop_todo.yml CHANGED
@@ -1,14 +1,14 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2024-12-03 11:13:42 UTC using RuboCop version 1.66.1.
3
+ # on 2025-01-07 08:52:47 UTC using RuboCop version 1.69.2.
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
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 224
9
+ # Offense count: 292
10
10
  # This cop supports safe autocorrection (--autocorrect).
11
- # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
11
+ # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
12
12
  # URISchemes: http, https
13
13
  Layout/LineLength:
14
14
  Enabled: false
@@ -31,7 +31,7 @@ Lint/DuplicateMethods:
31
31
  Exclude:
32
32
  - 'lib/lutaml/model/type/float.rb'
33
33
 
34
- # Offense count: 37
34
+ # Offense count: 40
35
35
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
36
36
  Metrics/AbcSize:
37
37
  Exclude:
@@ -42,18 +42,20 @@ Metrics/AbcSize:
42
42
  - 'lib/lutaml/model/schema/xsd_schema.rb'
43
43
  - 'lib/lutaml/model/serialize.rb'
44
44
  - 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
45
+ - 'lib/lutaml/model/xml_adapter/oga/element.rb'
46
+ - 'lib/lutaml/model/xml_adapter/oga_adapter.rb'
45
47
  - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
46
48
  - 'lib/lutaml/model/xml_adapter/xml_document.rb'
47
49
  - 'lib/lutaml/model/xml_mapping.rb'
48
50
  - 'lib/lutaml/model/xml_mapping_rule.rb'
49
51
 
50
- # Offense count: 6
52
+ # Offense count: 9
51
53
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
52
54
  # AllowedMethods: refine
53
55
  Metrics/BlockLength:
54
56
  Max: 46
55
57
 
56
- # Offense count: 28
58
+ # Offense count: 32
57
59
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
58
60
  Metrics/CyclomaticComplexity:
59
61
  Exclude:
@@ -61,21 +63,23 @@ Metrics/CyclomaticComplexity:
61
63
  - 'lib/lutaml/model/comparable_model.rb'
62
64
  - 'lib/lutaml/model/serialize.rb'
63
65
  - 'lib/lutaml/model/type/integer.rb'
66
+ - 'lib/lutaml/model/xml_adapter/builder/oga.rb'
64
67
  - 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
68
+ - 'lib/lutaml/model/xml_adapter/oga_adapter.rb'
65
69
  - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
66
70
  - 'lib/lutaml/model/xml_adapter/xml_document.rb'
67
71
 
68
- # Offense count: 54
72
+ # Offense count: 63
69
73
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
70
74
  Metrics/MethodLength:
71
- Max: 46
75
+ Max: 44
72
76
 
73
77
  # Offense count: 7
74
78
  # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
75
79
  Metrics/ParameterLists:
76
- Max: 14
80
+ Max: 15
77
81
 
78
- # Offense count: 25
82
+ # Offense count: 30
79
83
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
80
84
  Metrics/PerceivedComplexity:
81
85
  Exclude:
@@ -83,10 +87,19 @@ Metrics/PerceivedComplexity:
83
87
  - 'lib/lutaml/model/comparable_model.rb'
84
88
  - 'lib/lutaml/model/serialize.rb'
85
89
  - 'lib/lutaml/model/type/integer.rb'
90
+ - 'lib/lutaml/model/xml_adapter/builder/oga.rb'
91
+ - 'lib/lutaml/model/xml_adapter/builder/ox.rb'
86
92
  - 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
93
+ - 'lib/lutaml/model/xml_adapter/oga_adapter.rb'
87
94
  - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
88
95
  - 'lib/lutaml/model/xml_adapter/xml_document.rb'
89
96
 
97
+ # Offense count: 1
98
+ # This cop supports unsafe autocorrection (--autocorrect-all).
99
+ Performance/MapCompact:
100
+ Exclude:
101
+ - 'lib/lutaml/model/serialize.rb'
102
+
90
103
  # Offense count: 8
91
104
  # Configuration parameters: Prefixes, AllowedPatterns.
92
105
  # Prefixes: when, with, without
@@ -105,7 +118,7 @@ RSpec/DescribedClass:
105
118
  Exclude:
106
119
  - 'spec/lutaml/model/xml_mapping_spec.rb'
107
120
 
108
- # Offense count: 133
121
+ # Offense count: 139
109
122
  # Configuration parameters: CountAsOne.
110
123
  RSpec/ExampleLength:
111
124
  Max: 54
@@ -135,7 +148,7 @@ RSpec/MultipleDescribes:
135
148
  - 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
136
149
  - 'spec/lutaml/model/xml_adapter_spec.rb'
137
150
 
138
- # Offense count: 157
151
+ # Offense count: 172
139
152
  RSpec/MultipleExpectations:
140
153
  Max: 14
141
154
 
@@ -144,23 +157,19 @@ RSpec/MultipleExpectations:
144
157
  RSpec/MultipleMemoizedHelpers:
145
158
  Max: 9
146
159
 
147
- # Offense count: 15
160
+ # Offense count: 24
148
161
  # Configuration parameters: AllowedGroups.
149
162
  RSpec/NestedGroups:
150
163
  Max: 4
151
164
 
152
- # Offense count: 13
165
+ # Offense count: 9
153
166
  RSpec/PendingWithoutReason:
154
167
  Exclude:
155
- - 'spec/lutaml/model/mixed_content_spec.rb'
156
168
  - 'spec/lutaml/model/type/date_time_spec.rb'
157
169
  - 'spec/lutaml/model/type/integer_spec.rb'
158
170
  - 'spec/lutaml/model/type/time_spec.rb'
159
171
  - 'spec/lutaml/model/type/time_without_date_spec.rb'
160
172
  - 'spec/lutaml/model/validation_spec.rb'
161
- - 'spec/lutaml/model/xml_adapter/oga_adapter_spec.rb'
162
- - 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
163
- - 'spec/lutaml/model/xml_adapter_spec.rb'
164
173
 
165
174
  # Offense count: 1
166
175
  RSpec/RemoveConst:
@@ -209,3 +218,10 @@ Style/MissingRespondToMissing:
209
218
  Style/OptionalBooleanParameter:
210
219
  Exclude:
211
220
  - 'lib/lutaml/model/comparable_model.rb'
221
+
222
+ # Offense count: 1
223
+ # This cop supports unsafe autocorrection (--autocorrect-all).
224
+ # Configuration parameters: Mode.
225
+ Style/StringConcatenation:
226
+ Exclude:
227
+ - 'spec/lutaml/model/xml_mapping_spec.rb'
data/README.adoc CHANGED
@@ -2131,6 +2131,121 @@ end
2131
2131
 
2132
2132
  === Advanced attribute mapping
2133
2133
 
2134
+ ==== Multiple mappings to single attribute
2135
+
2136
+ The mapping methods support multiple names mapping to a single attribute using
2137
+ an array of names.
2138
+
2139
+ Syntax:
2140
+
2141
+ [source,ruby]
2142
+ ----
2143
+ json | yaml | toml | key_value do
2144
+ map ["name1", "name2"], to: :attribute_name
2145
+ end
2146
+
2147
+ xml do
2148
+ map_element ["name1", "name2"], to: :attribute_name
2149
+ map_attribute ["attr1", "attr2"], to: :attribute_name
2150
+ end
2151
+ ----
2152
+
2153
+ When serializing, the first element in the array of mapped names is always used
2154
+ as the output name.
2155
+
2156
+
2157
+ .Using multiple names to map to a single attribute
2158
+ [example]
2159
+ ====
2160
+ [source,ruby]
2161
+ ----
2162
+ class CustomModel < Lutaml::Model::Serializable
2163
+ attribute :full_name, Lutaml::Model::Type::String
2164
+ attribute :color, Lutaml::Model::Type::String
2165
+ attribute :id, Lutaml::Model::Type::String
2166
+
2167
+ json do
2168
+ map ["name", "custom_name"], with: { to: :name_to_json, from: :name_from_json }
2169
+ map ["color", "shade"], with: { to: :color_to_json, from: :color_from_json }
2170
+ end
2171
+
2172
+ xml do
2173
+ root "CustomModel"
2174
+ map_element ["name", "custom-name"], with: { to: :name_to_xml, from: :name_from_xml }
2175
+ map_element ["color", "shade"], with: { to: :color_to_xml, from: :color_from_xml }
2176
+ map_attribute ["id", "identifier"], to: :id
2177
+ end
2178
+
2179
+ # Custom methods for JSON
2180
+ def name_to_json(model, doc)
2181
+ doc["name"] = "JSON Model: #{model.full_name}"
2182
+ end
2183
+
2184
+ def name_from_json(model, value)
2185
+ model.full_name = value&.sub(/^JSON Model: /, "")
2186
+ end
2187
+
2188
+ def color_to_json(model, doc)
2189
+ doc["color"] = model.color.upcase
2190
+ end
2191
+
2192
+ def color_from_json(model, value)
2193
+ model.color = value&.downcase
2194
+ end
2195
+
2196
+ # Custom methods for XML
2197
+ def name_to_xml(model, parent, doc)
2198
+ el = doc.create_element("name")
2199
+ doc.add_text(el, "XML Model: #{model.full_name}")
2200
+ doc.add_element(parent, el)
2201
+ end
2202
+
2203
+ def name_from_xml(model, value)
2204
+ model.full_name = value.sub(/^XML Model: /, "")
2205
+ end
2206
+
2207
+ def color_to_xml(model, parent, doc)
2208
+ el = doc.create_element("color")
2209
+ doc.add_text(el, model.color.upcase)
2210
+ doc.add_element(parent, el)
2211
+ end
2212
+
2213
+ def color_from_xml(model, value)
2214
+ model.color = value.downcase
2215
+ end
2216
+ end
2217
+ ----
2218
+
2219
+ For JSON:
2220
+ [source,json]
2221
+ ----
2222
+ {
2223
+ "custom_name": "JSON Model: Vase",
2224
+ "shade": "BLUE",
2225
+ "identifier": "123"
2226
+ }
2227
+ ----
2228
+
2229
+ For XML:
2230
+ [source,xml]
2231
+ ----
2232
+ <CustomModel id="123">
2233
+ <name>XML Model: Vase</name>
2234
+ <color>BLUE</color>
2235
+ </CustomModel>
2236
+ ----
2237
+
2238
+ [source,ruby]
2239
+ ----
2240
+ > model = CustomModel.from_json(json)
2241
+ > model.full_name
2242
+ > # "Vase"
2243
+ > model.color
2244
+ > # "blue"
2245
+ ----
2246
+ ====
2247
+
2248
+
2134
2249
  ==== Attribute mapping delegation
2135
2250
 
2136
2251
  Delegate attribute mappings to nested objects using the `delegate` option.
@@ -2641,6 +2756,13 @@ klin.validate
2641
2756
  Lutaml::Model uses an adapter pattern to support multiple libraries for each
2642
2757
  serialization format.
2643
2758
 
2759
+ Lutaml::Model supports the following serialization formats:
2760
+
2761
+ * XML (https://www.w3.org/TR/xmlschema-1/[W3C XML Schema (Second Edition)], XML 1.0)
2762
+ * YAML (https://yaml.org/[YAML version 1.2])
2763
+ * JSON (https://www.ecma-international.org/publications-and-standards/standards/ecma-404/[ECMA-404 The JSON Data Interchange Standard], unofficial link: https://www.json.org[JSON])
2764
+ * TOML (https://toml.io/en[TOML version 1.0])
2765
+
2644
2766
  You will need to specify the configuration for the adapter you want to use. The
2645
2767
  easiest way is to copy and paste the following configuration into your code.
2646
2768
 
@@ -2684,9 +2806,25 @@ NOTE: By default `yaml_adapter_type` and `json_adapter_type` are set to
2684
2806
 
2685
2807
  Lutaml::Model supports the following XML adapters:
2686
2808
 
2687
- * Nokogiri (default)
2688
- * Oga (optional, plain Ruby suitable for Opal/JS)
2689
- * Ox (optional)
2809
+ Nokogiri::
2810
+ (default)
2811
+ Popular `libxml` based XML parser for Ruby.
2812
+ Requires native extensions (i.e. compiled C code).
2813
+ Requires the `nokogiri` gem.
2814
+
2815
+ Oga::
2816
+ (optional)
2817
+ Pure Ruby XML parser.
2818
+ Does not require native extensions and is suitable for
2819
+ https://opalrb.com[Opal] (Ruby on JavaScript).
2820
+ Requires the `oga` gem.
2821
+
2822
+ Ox::
2823
+ (optional)
2824
+ Fast XML parser and object serializer for Ruby, implemented partially in C.
2825
+ Requires native extensions (i.e. compiled C code).
2826
+ Requires the `ox` gem.
2827
+
2690
2828
 
2691
2829
  .Using the Nokogiri XML adapter
2692
2830
  [source,ruby]
@@ -2726,7 +2864,10 @@ end
2726
2864
 
2727
2865
  Lutaml::Model supports only one YAML adapter.
2728
2866
 
2729
- * YAML (default)
2867
+ YAML::
2868
+ (default)
2869
+ The Psych YAML parser and emitter for Ruby.
2870
+ Included in the Ruby standard library.
2730
2871
 
2731
2872
  .Using the YAML adapter
2732
2873
  [source,ruby]
@@ -2745,8 +2886,15 @@ end
2745
2886
 
2746
2887
  Lutaml::Model supports the following JSON adapters:
2747
2888
 
2748
- * JSON (default)
2749
- * MultiJson (optional)
2889
+ JSON::
2890
+ (default)
2891
+ The standard JSON library for Ruby.
2892
+ Included in the Ruby standard library.
2893
+
2894
+ MultiJson::
2895
+ (optional)
2896
+ A gem that provides a common interface to multiple JSON libraries.
2897
+ Requires the `multi_json` gem.
2750
2898
 
2751
2899
  .Using the JSON adapter
2752
2900
  [source,ruby]
@@ -2774,8 +2922,18 @@ end
2774
2922
 
2775
2923
  Lutaml::Model supports the following TOML adapters:
2776
2924
 
2777
- * Toml-rb (default)
2778
- * Tomlib (optional)
2925
+ Toml-rb::
2926
+ (default)
2927
+ A TOML parser and serializer for Ruby that is compatible with the TOML v1.0.0
2928
+ specification.
2929
+ Requires the `toml-rb` gem.
2930
+
2931
+ Tomlib::
2932
+ (optional)
2933
+ Toml-rb fork that is compatible with the TOML v1.0.0 specification, but with
2934
+ additional features.
2935
+ Requires the `tomlib` gem.
2936
+
2779
2937
 
2780
2938
  .Using the Toml-rb adapter
2781
2939
  [source,ruby]
@@ -19,7 +19,6 @@ module Lutaml
19
19
  child_mappings: nil
20
20
  )
21
21
  validate!(name, to, with)
22
-
23
22
  @mappings << KeyValueMappingRule.new(
24
23
  name,
25
24
  to: to,
@@ -12,7 +12,8 @@ module Lutaml
12
12
  render_default: false,
13
13
  with: {},
14
14
  delegate: nil,
15
- child_mappings: nil
15
+ child_mappings: nil,
16
+ id: nil
16
17
  )
17
18
  super(
18
19
  name,
@@ -21,6 +22,7 @@ module Lutaml
21
22
  render_default: render_default,
22
23
  with: with,
23
24
  delegate: delegate,
25
+ id: id
24
26
  )
25
27
 
26
28
  @child_mappings = child_mappings
@@ -16,7 +16,8 @@ module Lutaml
16
16
  render_default: false,
17
17
  with: {},
18
18
  attribute: false,
19
- delegate: nil
19
+ delegate: nil,
20
+ id: nil
20
21
  )
21
22
  @name = name
22
23
  @to = to
@@ -25,6 +26,7 @@ module Lutaml
25
26
  @custom_methods = with
26
27
  @attribute = attribute
27
28
  @delegate = delegate
29
+ @id = id
28
30
  end
29
31
 
30
32
  alias from name
@@ -42,6 +44,8 @@ module Lutaml
42
44
  if delegate
43
45
  model.public_send(delegate).public_send(to)
44
46
  else
47
+ return if to.nil?
48
+
45
49
  model.public_send(to)
46
50
  end
47
51
  end
@@ -56,7 +60,7 @@ module Lutaml
56
60
 
57
61
  def deserialize(model, value, attributes, mapper_class = nil)
58
62
  if custom_methods[:from]
59
- mapper_class.new.send(custom_methods[:from], model, value)
63
+ mapper_class.new.send(custom_methods[:from], model, value) unless value.nil?
60
64
  elsif delegate
61
65
  if model.public_send(delegate).nil?
62
66
  model.public_send(:"#{delegate}=", attributes[delegate].type.new)
@@ -68,6 +72,14 @@ module Lutaml
68
72
  end
69
73
  end
70
74
 
75
+ def using_custom_methods?
76
+ !custom_methods.empty?
77
+ end
78
+
79
+ def multiple_mappings?
80
+ name.is_a?(Array)
81
+ end
82
+
71
83
  def deep_dup
72
84
  raise NotImplementedError, "Subclasses must implement `deep_dup`."
73
85
  end
@@ -21,6 +21,7 @@ module Lutaml
21
21
 
22
22
  def self.included(base)
23
23
  base.extend(ClassMethods)
24
+ base.initialize_attrs(base)
24
25
  end
25
26
 
26
27
  module ClassMethods
@@ -28,14 +29,18 @@ module Lutaml
28
29
 
29
30
  def inherited(subclass)
30
31
  super
32
+ subclass.initialize_attrs(self)
33
+ end
31
34
 
32
- @mappings ||= {}
33
- @attributes ||= {}
35
+ def included(base)
36
+ base.extend(ClassMethods)
37
+ base.initialize_attrs(self)
38
+ end
34
39
 
35
- subclass.instance_variable_set(:@attributes,
36
- Utils.deep_dup(@attributes))
37
- subclass.instance_variable_set(:@mappings, Utils.deep_dup(@mappings))
38
- subclass.instance_variable_set(:@model, subclass)
40
+ def initialize_attrs(source_class)
41
+ @mappings = Utils.deep_dup(source_class.instance_variable_get(:@mappings)) || {}
42
+ @attributes = Utils.deep_dup(source_class.instance_variable_get(:@attributes)) || {}
43
+ instance_variable_set(:@model, self)
39
44
  end
40
45
 
41
46
  def model(klass = nil)
@@ -264,15 +269,18 @@ module Lutaml
264
269
 
265
270
  value = instance.send(name)
266
271
 
267
- next if Utils.blank?(value) && !rule.render_nil
268
-
269
272
  attribute = attributes[name]
270
273
 
271
- hash[rule.from.to_s] = if rule.child_mappings
272
- generate_hash_from_child_mappings(value, rule.child_mappings)
273
- else
274
- attribute.serialize(value, format, options)
275
- end
274
+ value = if rule.child_mappings
275
+ generate_hash_from_child_mappings(value, rule.child_mappings)
276
+ else
277
+ attribute.serialize(value, format, options)
278
+ end
279
+
280
+ next if Utils.blank?(value) && !rule.render_nil
281
+
282
+ rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
283
+ hash[rule_from_name] = value
276
284
  end
277
285
  end
278
286
 
@@ -282,39 +290,14 @@ module Lutaml
282
290
  return if value.nil? && !rule.render_nil
283
291
 
284
292
  attribute = instance.send(rule.delegate).class.attributes[name]
285
- hash[rule.from.to_s] = attribute.serialize(value, format)
293
+ rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
294
+ hash[rule_from_name] = attribute.serialize(value, format)
286
295
  end
287
296
 
288
297
  def mappings_for(format)
289
298
  mappings[format] || default_mappings(format)
290
299
  end
291
300
 
292
- def attr_value(attrs, name, attr_rule)
293
- value = if attrs.key?(name.to_sym)
294
- attrs[name.to_sym]
295
- elsif attrs.key?(name.to_s)
296
- attrs[name.to_s]
297
- else
298
- attr_rule.default
299
- end
300
-
301
- if attr_rule.collection? || value.is_a?(Array)
302
- (value || []).map do |v|
303
- if v.is_a?(Hash)
304
- attr_rule.type.new(v)
305
- else
306
- # TODO: This code is problematic because Type.cast does not know
307
- # about all the types.
308
- Lutaml::Model::Type.cast(v, attr_rule.type)
309
- end
310
- end
311
- else
312
- # TODO: This code is problematic because Type.cast does not know
313
- # about all the types.
314
- Lutaml::Model::Type.cast(value, attr_rule.type)
315
- end
316
- end
317
-
318
301
  def default_mappings(format)
319
302
  klass = format == :xml ? XmlMapping : KeyValueMapping
320
303
 
@@ -413,8 +396,7 @@ module Lutaml
413
396
  mappings = mappings_for(:xml).mappings
414
397
 
415
398
  if doc.is_a?(Array)
416
- raise "May be `collection: true` is" \
417
- "missing for #{self} in #{options[:caller_class]}"
399
+ raise "May be `collection: true` is missing for #{self} in #{options[:caller_class]}"
418
400
  end
419
401
 
420
402
  if instance.respond_to?(:ordered=) && doc.is_a?(Lutaml::Model::MappingHash)
@@ -438,17 +420,18 @@ module Lutaml
438
420
 
439
421
  attr = attribute_for_rule(rule)
440
422
 
423
+ namespaced_names = rule.namespaced_names(options[:default_namespace])
424
+
441
425
  value = if rule.raw_mapping?
442
426
  doc.node.inner_xml
443
427
  elsif rule.content_mapping?
444
428
  doc[rule.content_key]
445
- elsif doc.key_exist?(rule.namespaced_name(options[:default_namespace]))
446
- doc.fetch(rule.namespaced_name(options[:default_namespace]))
429
+ elsif key = (namespaced_names & doc.keys).first
430
+ doc[key]
447
431
  else
448
432
  defaults_used << rule.to
449
433
  attr&.default || rule.to_value_for(instance)
450
434
  end
451
-
452
435
  value = normalize_xml_value(value, rule, attr, options)
453
436
  rule.deserialize(instance, value, attributes, self)
454
437
  end
@@ -467,13 +450,19 @@ module Lutaml
467
450
 
468
451
  attr = attribute_for_rule(rule)
469
452
 
470
- value = if doc.key?(rule.name.to_s) || doc.key?(rule.name.to_sym)
471
- doc[rule.name.to_s] || doc[rule.name.to_sym]
472
- else
473
- attr&.default
474
- end
453
+ names = rule.multiple_mappings? ? rule.name : [rule.name]
454
+
455
+ value = names.collect do |rule_name|
456
+ if doc.key?(rule_name.to_s)
457
+ doc[rule_name.to_s]
458
+ elsif doc.key?(rule_name.to_sym)
459
+ doc[rule_name.to_sym]
460
+ else
461
+ attr&.default
462
+ end
463
+ end.compact.first
475
464
 
476
- if rule.custom_methods[:from]
465
+ if rule.using_custom_methods?
477
466
  if Utils.present?(value)
478
467
  value = new.send(rule.custom_methods[:from], instance, value)
479
468
  end
@@ -567,7 +556,7 @@ module Lutaml
567
556
 
568
557
  self.class.attributes.each do |name, attr|
569
558
  value = if attrs.key?(name) || attrs.key?(name.to_s)
570
- self.class.attr_value(attrs, name, attr)
559
+ attr_value(attrs, name, attr)
571
560
  else
572
561
  using_default_for(name)
573
562
  attr.default
@@ -584,6 +573,32 @@ module Lutaml
584
573
  end
585
574
  end
586
575
 
576
+ def attr_value(attrs, name, attr_rule)
577
+ value = if attrs.key?(name.to_sym)
578
+ attrs[name.to_sym]
579
+ elsif attrs.key?(name.to_s)
580
+ attrs[name.to_s]
581
+ else
582
+ attr_rule.default
583
+ end
584
+
585
+ if attr_rule.collection? || value.is_a?(Array)
586
+ (value || []).map do |v|
587
+ if v.is_a?(Hash)
588
+ attr_rule.type.new(v)
589
+ else
590
+ # TODO: This code is problematic because Type.cast does not know
591
+ # about all the types.
592
+ Lutaml::Model::Type.cast(v, attr_rule.type)
593
+ end
594
+ end
595
+ else
596
+ # TODO: This code is problematic because Type.cast does not know
597
+ # about all the types.
598
+ Lutaml::Model::Type.cast(value, attr_rule.type)
599
+ end
600
+ end
601
+
587
602
  def using_default_for(attribute_name)
588
603
  @using_default[attribute_name] = true
589
604
  end
@@ -36,6 +36,11 @@ module Lutaml
36
36
  raise TypeNotEnabledError.new("Decimal", value)
37
37
  end
38
38
  end
39
+
40
+ # Override to avoid serializing ruby object in YAML
41
+ def to_yaml
42
+ value&.to_s("F")
43
+ end
39
44
  end
40
45
  end
41
46
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.4.0"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
@@ -39,6 +39,7 @@ module Lutaml
39
39
  )
40
40
  add_namespace_prefix(prefix)
41
41
 
42
+ element_name = element_name.first if element_name.is_a?(Array)
42
43
  element_name = "#{element_name}_" if respond_to?(element_name)
43
44
 
44
45
  if block_given?