lutaml-model 0.6.7 → 0.7.1

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.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-repos-todo.json +7 -0
  3. data/.github/workflows/dependent-repos.json +17 -9
  4. data/.rubocop_todo.yml +18 -33
  5. data/README.adoc +4380 -2557
  6. data/lib/lutaml/model/attribute.rb +94 -15
  7. data/lib/lutaml/model/choice.rb +7 -0
  8. data/lib/lutaml/model/comparable_model.rb +48 -9
  9. data/lib/lutaml/model/error/collection_count_out_of_range_error.rb +1 -1
  10. data/lib/lutaml/model/error/polymorphic_error.rb +9 -0
  11. data/lib/lutaml/model/error.rb +1 -0
  12. data/lib/lutaml/model/mapping/json_mapping.rb +17 -0
  13. data/lib/lutaml/model/{key_value_mapping.rb → mapping/key_value_mapping.rb} +58 -14
  14. data/lib/lutaml/model/{key_value_mapping_rule.rb → mapping/key_value_mapping_rule.rb} +18 -2
  15. data/lib/lutaml/model/mapping/mapping_rule.rb +299 -0
  16. data/lib/lutaml/model/mapping/toml_mapping.rb +25 -0
  17. data/lib/lutaml/model/{xml_mapping.rb → mapping/xml_mapping.rb} +97 -15
  18. data/lib/lutaml/model/{xml_mapping_rule.rb → mapping/xml_mapping_rule.rb} +20 -3
  19. data/lib/lutaml/model/mapping/yaml_mapping.rb +17 -0
  20. data/lib/lutaml/model/mapping.rb +14 -0
  21. data/lib/lutaml/model/schema/xml_compiler.rb +15 -15
  22. data/lib/lutaml/model/sequence.rb +2 -2
  23. data/lib/lutaml/model/serialize.rb +247 -97
  24. data/lib/lutaml/model/type/date.rb +1 -1
  25. data/lib/lutaml/model/type/date_time.rb +2 -2
  26. data/lib/lutaml/model/type/hash.rb +1 -1
  27. data/lib/lutaml/model/type/time.rb +2 -2
  28. data/lib/lutaml/model/type/time_without_date.rb +2 -2
  29. data/lib/lutaml/model/uninitialized_class.rb +64 -0
  30. data/lib/lutaml/model/utils.rb +14 -0
  31. data/lib/lutaml/model/validation.rb +1 -0
  32. data/lib/lutaml/model/version.rb +1 -1
  33. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +1 -1
  34. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +1 -1
  35. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +1 -1
  36. data/lib/lutaml/model/xml_adapter/xml_document.rb +38 -17
  37. data/lib/lutaml/model/xml_adapter/xml_element.rb +17 -7
  38. data/lib/lutaml/model.rb +1 -0
  39. data/spec/benchmarks/xml_parsing_benchmark_spec.rb +3 -3
  40. data/spec/fixtures/person.rb +5 -5
  41. data/spec/lutaml/model/attribute_spec.rb +37 -1
  42. data/spec/lutaml/model/cdata_spec.rb +2 -2
  43. data/spec/lutaml/model/collection_spec.rb +50 -2
  44. data/spec/lutaml/model/comparable_model_spec.rb +92 -27
  45. data/spec/lutaml/model/defaults_spec.rb +1 -1
  46. data/spec/lutaml/model/enum_spec.rb +1 -1
  47. data/spec/lutaml/model/group_spec.rb +316 -14
  48. data/spec/lutaml/model/key_value_mapping_spec.rb +41 -3
  49. data/spec/lutaml/model/polymorphic_spec.rb +348 -0
  50. data/spec/lutaml/model/render_empty_spec.rb +194 -0
  51. data/spec/lutaml/model/render_nil_spec.rb +206 -22
  52. data/spec/lutaml/model/simple_model_spec.rb +9 -9
  53. data/spec/lutaml/model/value_map_spec.rb +240 -0
  54. data/spec/lutaml/model/xml/namespace/nested_with_explicit_namespace_spec.rb +85 -0
  55. data/spec/lutaml/model/xml/xml_element_spec.rb +93 -0
  56. data/spec/lutaml/model/xml_mapping_rule_spec.rb +102 -2
  57. data/spec/lutaml/model/xml_mapping_spec.rb +45 -3
  58. data/spec/sample_model_spec.rb +3 -3
  59. metadata +20 -8
  60. data/lib/lutaml/model/mapping_rule.rb +0 -109
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+
5
+ RSpec.describe "XmlElement" do
6
+ describe "#text" do
7
+ context "when only text is present" do
8
+ let(:element) { create_element("name", text: "something") }
9
+
10
+ it "returns text" do
11
+ expect(element.text).to eq("something")
12
+ end
13
+ end
14
+
15
+ context "when single text node is present" do
16
+ let(:element) do
17
+ create_element(
18
+ "name",
19
+ children: [
20
+ create_element("text", text: "John Doe"),
21
+ ],
22
+ )
23
+ end
24
+
25
+ it "returns single string" do
26
+ expect(element.text).to eq("John Doe")
27
+ end
28
+ end
29
+
30
+ context "when multiple text nodes are present" do
31
+ let(:element) do
32
+ create_element(
33
+ "name",
34
+ children: [
35
+ create_element("text", text: "John"),
36
+ create_element("text", text: "\n"),
37
+ create_element("text", text: "Doe"),
38
+ ],
39
+ )
40
+ end
41
+
42
+ it "returns all text elements array" do
43
+ expect(element.text).to eq(["John", "\n", "Doe"])
44
+ end
45
+ end
46
+ end
47
+
48
+ describe "#cdata" do
49
+ context "when only cdata is present" do
50
+ let(:element) { create_element("name", text: "something") }
51
+
52
+ it "returns cdata" do
53
+ expect(element.cdata).to eq("something")
54
+ end
55
+ end
56
+
57
+ context "when single cdata node is present" do
58
+ let(:element) do
59
+ create_element(
60
+ "name",
61
+ children: [
62
+ create_element("#cdata-section", text: "John Doe"),
63
+ ],
64
+ )
65
+ end
66
+
67
+ it "returns single string" do
68
+ expect(element.cdata).to eq("John Doe")
69
+ end
70
+ end
71
+
72
+ context "when multiple cdata nodes are present" do
73
+ let(:element) do
74
+ create_element(
75
+ "name",
76
+ children: [
77
+ create_element("#cdata-section", text: "John"),
78
+ create_element("#cdata-section", text: "\n"),
79
+ create_element("#cdata-section", text: "Doe"),
80
+ ],
81
+ )
82
+ end
83
+
84
+ it "returns all cdata elements array" do
85
+ expect(element.cdata).to eq(["John", "\n", "Doe"])
86
+ end
87
+ end
88
+ end
89
+
90
+ def create_element(name, attributes: {}, children: [], text: "")
91
+ Lutaml::Model::XmlAdapter::XmlElement.new(name, attributes, children, text)
92
+ end
93
+ end
@@ -3,8 +3,6 @@ require "spec_helper"
3
3
  require "lutaml/model/xml_adapter/ox_adapter"
4
4
  require "lutaml/model/xml_adapter/oga_adapter"
5
5
 
6
- require_relative "../../../lib/lutaml/model/xml_mapping_rule"
7
-
8
6
  def content_to_xml(model, parent, doc)
9
7
  content = model.all_content.sub(/^<div>/, "").sub(/<\/div>$/, "")
10
8
  doc.add_xml_fragment(parent, content)
@@ -15,6 +13,108 @@ def content_from_xml(model, value)
15
13
  end
16
14
 
17
15
  RSpec.describe Lutaml::Model::XmlMappingRule do
16
+ describe "#namespaced_name" do
17
+ let(:namespaced_name) do
18
+ mapping_rule.namespaced_name
19
+ end
20
+
21
+ context "when attribute name is string 'lang'" do
22
+ let(:mapping_rule) do
23
+ described_class.new("lang", to: :lang, prefix: "xml")
24
+ end
25
+
26
+ it "returns `xml:lang`" do
27
+ expect(namespaced_name).to eq("xml:lang")
28
+ end
29
+ end
30
+
31
+ context "when attribute name is symbol ':lang'" do
32
+ let(:mapping_rule) do
33
+ described_class.new(:lang, to: :lang, prefix: "xml")
34
+ end
35
+
36
+ it "returns `xml:lang`" do
37
+ expect(namespaced_name).to eq("xml:lang")
38
+ end
39
+ end
40
+
41
+ context "when namespace is explicitly set" do
42
+ let(:mapping_rule) do
43
+ described_class.new(
44
+ "explicit_namespace",
45
+ to: :explicit_namespace,
46
+ namespace: "http://test",
47
+ namespace_set: true,
48
+ )
49
+ end
50
+
51
+ it "returns `http://test:explicit_namespace`" do
52
+ expect(namespaced_name).to eq("http://test:explicit_namespace")
53
+ end
54
+ end
55
+
56
+ context "when namespace is explicitly set to nil" do
57
+ let(:mapping_rule) do
58
+ described_class.new(
59
+ "explicit_namespace",
60
+ to: :explicit_namespace,
61
+ namespace: nil,
62
+ namespace_set: true,
63
+ default_namespace: "http://default",
64
+ )
65
+ end
66
+
67
+ it "returns name without namespace" do
68
+ expect(namespaced_name).to eq("explicit_namespace")
69
+ end
70
+ end
71
+
72
+ context "when attribute has namespace set" do
73
+ let(:mapping_rule) do
74
+ described_class.new(
75
+ "attribute",
76
+ to: :attribute,
77
+ attribute: true,
78
+ namespace: "http://test",
79
+ default_namespace: "http://default",
80
+ )
81
+ end
82
+
83
+ it "returns `http://test:attribute`" do
84
+ expect(namespaced_name).to eq("http://test:attribute")
85
+ end
86
+ end
87
+
88
+ context "when attribute has no namespace set" do
89
+ let(:mapping_rule) do
90
+ described_class.new(
91
+ "attribute",
92
+ to: :attribute,
93
+ attribute: true,
94
+ default_namespace: "http://default",
95
+ )
96
+ end
97
+
98
+ it "does not use default namespace" do
99
+ expect(namespaced_name).to eq("attribute")
100
+ end
101
+ end
102
+
103
+ context "when default_namespace is set" do
104
+ let(:mapping_rule) do
105
+ described_class.new(
106
+ "default_namespace",
107
+ to: :default_namespace,
108
+ default_namespace: "http://default",
109
+ )
110
+ end
111
+
112
+ it "returns `default_namespace:name` if not an attribute" do
113
+ expect(namespaced_name).to eq("http://default:default_namespace")
114
+ end
115
+ end
116
+ end
117
+
18
118
  context "with Xml Mapping Rule" do
19
119
  let(:orig_mapping_rule) do
20
120
  described_class.new(
@@ -3,9 +3,6 @@ require "spec_helper"
3
3
  require "lutaml/model/xml_adapter/ox_adapter"
4
4
  require "lutaml/model/xml_adapter/oga_adapter"
5
5
 
6
- require_relative "../../../lib/lutaml/model/xml_mapping"
7
- require_relative "../../../lib/lutaml/model/xml_mapping_rule"
8
-
9
6
  # Define a sample class for testing map_content
10
7
  class Italic < Lutaml::Model::Serializable
11
8
  attribute :text, Lutaml::Model::Type::String, collection: true
@@ -445,6 +442,7 @@ RSpec.describe Lutaml::Model::XmlMapping do
445
442
 
446
443
  it "parse and serialize model correctly" do
447
444
  parsed = XmlMapping::OwnedComment.from_xml(xml_with_attribute)
445
+
448
446
  serialized = parsed.to_xml
449
447
 
450
448
  expect(serialized).to be_equivalent_to(xml_with_attribute)
@@ -1248,6 +1246,50 @@ RSpec.describe Lutaml::Model::XmlMapping do
1248
1246
  end
1249
1247
  end
1250
1248
  end
1249
+
1250
+ describe "validation errors" do
1251
+ let(:mapping) { Lutaml::Model::XmlMapping.new }
1252
+
1253
+ it "raises error when neither :to nor :with provided" do
1254
+ expect do
1255
+ mapping.map_element("test")
1256
+ end.to raise_error(
1257
+ Lutaml::Model::IncorrectMappingArgumentsError,
1258
+ ":to or :with argument is required for mapping 'test'",
1259
+ )
1260
+ end
1261
+
1262
+ it "raises error when :with is missing :to or :from keys" do
1263
+ expect do
1264
+ mapping.map_element("test", with: { to: "value" })
1265
+ end.to raise_error(
1266
+ Lutaml::Model::IncorrectMappingArgumentsError,
1267
+ ":with argument for mapping 'test' requires :to and :from keys",
1268
+ )
1269
+ end
1270
+
1271
+ describe "map_attribute validations" do
1272
+ it "raises error for invalid :with argument" do
1273
+ expect do
1274
+ mapping.map_attribute("test", with: { from: "value" })
1275
+ end.to raise_error(
1276
+ Lutaml::Model::IncorrectMappingArgumentsError,
1277
+ ":with argument for mapping 'test' requires :to and :from keys",
1278
+ )
1279
+ end
1280
+ end
1281
+
1282
+ describe "map_content validations" do
1283
+ it "raises error when no :to provided" do
1284
+ expect do
1285
+ mapping.map_content
1286
+ end.to raise_error(
1287
+ Lutaml::Model::IncorrectMappingArgumentsError,
1288
+ ":to or :with argument is required for mapping 'content'",
1289
+ )
1290
+ end
1291
+ end
1292
+ end
1251
1293
  end
1252
1294
 
1253
1295
  describe Lutaml::Model::XmlAdapter::NokogiriAdapter do
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
  require_relative "fixtures/sample_model"
3
3
 
4
4
  RSpec.describe SampleModel do
5
- let(:model) { described_class.new(attributes) }
5
+ let(:model) { described_class.new(attributes, { omitted: :omitted }) }
6
6
  let(:attributes_yaml) do
7
7
  {
8
8
  "name" => "John Doe",
@@ -37,7 +37,7 @@ RSpec.describe SampleModel do
37
37
  end
38
38
 
39
39
  describe "default values" do
40
- let(:model) { described_class.new }
40
+ let(:model) { described_class.new({}, { omitted: :omitted }) }
41
41
 
42
42
  it "sets default name" do
43
43
  expect(model.name).to eq("Anonymous")
@@ -52,7 +52,7 @@ RSpec.describe SampleModel do
52
52
  end
53
53
 
54
54
  it "sets default tags" do
55
- expect(model.tags).to eq([])
55
+ expect(model.tags).to be_uninitialized
56
56
  end
57
57
 
58
58
  it "sets default preferences" do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lutaml-model
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.7
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-02-24 00:00:00.000000000 Z
11
+ date: 2025-03-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -76,6 +76,7 @@ executables:
76
76
  extensions: []
77
77
  extra_rdoc_files: []
78
78
  files:
79
+ - ".github/workflows/dependent-repos-todo.json"
79
80
  - ".github/workflows/dependent-repos.json"
80
81
  - ".github/workflows/dependent-tests.yml"
81
82
  - ".github/workflows/rake.yml"
@@ -116,6 +117,7 @@ files:
116
117
  - lib/lutaml/model/error/no_root_mapping_error.rb
117
118
  - lib/lutaml/model/error/no_root_namespace_error.rb
118
119
  - lib/lutaml/model/error/pattern_not_matched_error.rb
120
+ - lib/lutaml/model/error/polymorphic_error.rb
119
121
  - lib/lutaml/model/error/type/invalid_value_error.rb
120
122
  - lib/lutaml/model/error/type_error.rb
121
123
  - lib/lutaml/model/error/type_not_enabled_error.rb
@@ -128,12 +130,18 @@ files:
128
130
  - lib/lutaml/model/json_adapter/json_object.rb
129
131
  - lib/lutaml/model/json_adapter/multi_json_adapter.rb
130
132
  - lib/lutaml/model/json_adapter/standard_json_adapter.rb
131
- - lib/lutaml/model/key_value_mapping.rb
132
- - lib/lutaml/model/key_value_mapping_rule.rb
133
133
  - lib/lutaml/model/liquefiable.rb
134
134
  - lib/lutaml/model/loggable.rb
135
+ - lib/lutaml/model/mapping.rb
136
+ - lib/lutaml/model/mapping/json_mapping.rb
137
+ - lib/lutaml/model/mapping/key_value_mapping.rb
138
+ - lib/lutaml/model/mapping/key_value_mapping_rule.rb
139
+ - lib/lutaml/model/mapping/mapping_rule.rb
140
+ - lib/lutaml/model/mapping/toml_mapping.rb
141
+ - lib/lutaml/model/mapping/xml_mapping.rb
142
+ - lib/lutaml/model/mapping/xml_mapping_rule.rb
143
+ - lib/lutaml/model/mapping/yaml_mapping.rb
135
144
  - lib/lutaml/model/mapping_hash.rb
136
- - lib/lutaml/model/mapping_rule.rb
137
145
  - lib/lutaml/model/schema.rb
138
146
  - lib/lutaml/model/schema/json_schema.rb
139
147
  - lib/lutaml/model/schema/json_schema_parser.rb
@@ -163,6 +171,7 @@ files:
163
171
  - lib/lutaml/model/type/time.rb
164
172
  - lib/lutaml/model/type/time_without_date.rb
165
173
  - lib/lutaml/model/type/value.rb
174
+ - lib/lutaml/model/uninitialized_class.rb
166
175
  - lib/lutaml/model/utils.rb
167
176
  - lib/lutaml/model/validation.rb
168
177
  - lib/lutaml/model/version.rb
@@ -180,8 +189,6 @@ files:
180
189
  - lib/lutaml/model/xml_adapter/xml_document.rb
181
190
  - lib/lutaml/model/xml_adapter/xml_element.rb
182
191
  - lib/lutaml/model/xml_adapter/xml_namespace.rb
183
- - lib/lutaml/model/xml_mapping.rb
184
- - lib/lutaml/model/xml_mapping_rule.rb
185
192
  - lib/lutaml/model/yaml_adapter.rb
186
193
  - lib/lutaml/model/yaml_adapter/standard_yaml_adapter.rb
187
194
  - lib/lutaml/model/yaml_adapter/yaml_document.rb
@@ -230,6 +237,8 @@ files:
230
237
  - spec/lutaml/model/multiple_mapping_spec.rb
231
238
  - spec/lutaml/model/namespace_spec.rb
232
239
  - spec/lutaml/model/ordered_content_spec.rb
240
+ - spec/lutaml/model/polymorphic_spec.rb
241
+ - spec/lutaml/model/render_empty_spec.rb
233
242
  - spec/lutaml/model/render_nil_spec.rb
234
243
  - spec/lutaml/model/root_mappings_spec.rb
235
244
  - spec/lutaml/model/schema/json_schema_spec.rb
@@ -256,8 +265,11 @@ files:
256
265
  - spec/lutaml/model/type_spec.rb
257
266
  - spec/lutaml/model/utils_spec.rb
258
267
  - spec/lutaml/model/validation_spec.rb
268
+ - spec/lutaml/model/value_map_spec.rb
259
269
  - spec/lutaml/model/with_child_mapping_spec.rb
260
270
  - spec/lutaml/model/xml/derived_attributes_spec.rb
271
+ - spec/lutaml/model/xml/namespace/nested_with_explicit_namespace_spec.rb
272
+ - spec/lutaml/model/xml/xml_element_spec.rb
261
273
  - spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb
262
274
  - spec/lutaml/model/xml_adapter/oga_adapter_spec.rb
263
275
  - spec/lutaml/model/xml_adapter/ox_adapter_spec.rb
@@ -290,7 +302,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
290
302
  - !ruby/object:Gem::Version
291
303
  version: '0'
292
304
  requirements: []
293
- rubygems_version: 3.3.27
305
+ rubygems_version: 3.5.22
294
306
  signing_key:
295
307
  specification_version: 4
296
308
  summary: LutaML creating data models in Ruby
@@ -1,109 +0,0 @@
1
- module Lutaml
2
- module Model
3
- class MappingRule
4
- attr_reader :name,
5
- :to,
6
- :render_nil,
7
- :render_default,
8
- :attribute,
9
- :custom_methods,
10
- :delegate,
11
- :transform
12
-
13
- def initialize(
14
- name,
15
- to:,
16
- render_nil: false,
17
- render_default: false,
18
- with: {},
19
- attribute: false,
20
- delegate: nil,
21
- root_mappings: nil,
22
- transform: {}
23
- )
24
- @name = name
25
- @to = to
26
- @render_nil = render_nil
27
- @render_default = render_default
28
- @custom_methods = with
29
- @attribute = attribute
30
- @delegate = delegate
31
- @root_mappings = root_mappings
32
- @transform = transform
33
- end
34
-
35
- alias from name
36
- alias render_nil? render_nil
37
- alias render_default? render_default
38
- alias attribute? attribute
39
-
40
- def render?(value)
41
- render_nil? || (!value.nil? && !Utils.empty_collection?(value))
42
- end
43
-
44
- def serialize_attribute(model, element, doc)
45
- if custom_methods[:to]
46
- model.send(custom_methods[:to], model, element, doc)
47
- end
48
- end
49
-
50
- def to_value_for(model)
51
- if delegate
52
- model.public_send(delegate).public_send(to)
53
- else
54
- return if to.nil?
55
-
56
- model.public_send(to)
57
- end
58
- end
59
-
60
- def serialize(model, parent = nil, doc = nil)
61
- if custom_methods[:to]
62
- model.send(custom_methods[:to], model, parent, doc)
63
- else
64
- to_value_for(model)
65
- end
66
- end
67
-
68
- def deserialize(model, value, attributes, mapper_class = nil)
69
- if custom_methods[:from]
70
- mapper_class.new.send(custom_methods[:from], model, value) unless value.nil?
71
- elsif delegate
72
- if model.public_send(delegate).nil?
73
- model.public_send(:"#{delegate}=", attributes[delegate].type.new)
74
- end
75
-
76
- model.public_send(delegate).public_send(:"#{to}=", value)
77
- elsif transform_method = transform[:import] || attributes[to].transform_import_method
78
- model.public_send(:"#{to}=", transform_method.call(value))
79
- else
80
- model.public_send(:"#{to}=", value)
81
- end
82
- end
83
-
84
- def using_custom_methods?
85
- !custom_methods.empty?
86
- end
87
-
88
- def multiple_mappings?
89
- name.is_a?(Array)
90
- end
91
-
92
- def raw_mapping?
93
- name == Constants::RAW_MAPPING_KEY
94
- end
95
-
96
- def eql?(other)
97
- other.class == self.class &&
98
- instance_variables.all? do |var|
99
- instance_variable_get(var) == other.instance_variable_get(var)
100
- end
101
- end
102
- alias == eql?
103
-
104
- def deep_dup
105
- raise NotImplementedError, "Subclasses must implement `deep_dup`."
106
- end
107
- end
108
- end
109
- end