lutaml-model 0.3.30 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +34 -18
  3. data/README.adoc +172 -8
  4. data/lib/lutaml/model/attribute.rb +6 -2
  5. data/lib/lutaml/model/key_value_mapping.rb +0 -1
  6. data/lib/lutaml/model/key_value_mapping_rule.rb +3 -1
  7. data/lib/lutaml/model/mapping_rule.rb +14 -2
  8. data/lib/lutaml/model/serialize.rb +174 -61
  9. data/lib/lutaml/model/type/decimal.rb +5 -0
  10. data/lib/lutaml/model/type/time.rb +4 -4
  11. data/lib/lutaml/model/utils.rb +1 -1
  12. data/lib/lutaml/model/validation.rb +6 -2
  13. data/lib/lutaml/model/version.rb +1 -1
  14. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +1 -0
  15. data/lib/lutaml/model/xml_adapter/builder/oga.rb +180 -0
  16. data/lib/lutaml/model/xml_adapter/builder/ox.rb +1 -0
  17. data/lib/lutaml/model/xml_adapter/oga/document.rb +20 -0
  18. data/lib/lutaml/model/xml_adapter/oga/element.rb +117 -0
  19. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +77 -44
  20. data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -9
  21. data/lib/lutaml/model/xml_mapping.rb +0 -1
  22. data/lib/lutaml/model/xml_mapping_rule.rb +16 -4
  23. data/spec/address_spec.rb +1 -0
  24. data/spec/fixtures/sample_model.rb +7 -0
  25. data/spec/lutaml/model/custom_model_spec.rb +47 -1
  26. data/spec/lutaml/model/custom_serialization_spec.rb +16 -0
  27. data/spec/lutaml/model/enum_spec.rb +131 -0
  28. data/spec/lutaml/model/included_spec.rb +192 -0
  29. data/spec/lutaml/model/mixed_content_spec.rb +48 -32
  30. data/spec/lutaml/model/multiple_mapping_spec.rb +329 -0
  31. data/spec/lutaml/model/ordered_content_spec.rb +1 -1
  32. data/spec/lutaml/model/render_nil_spec.rb +3 -2
  33. data/spec/lutaml/model/serializable_spec.rb +3 -3
  34. data/spec/lutaml/model/type/boolean_spec.rb +62 -0
  35. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -11
  36. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +1 -1
  37. data/spec/lutaml/model/xml_adapter_spec.rb +2 -2
  38. data/spec/lutaml/model/xml_mapping_spec.rb +24 -9
  39. data/spec/sample_model_spec.rb +114 -0
  40. metadata +9 -2
@@ -0,0 +1,192 @@
1
+ require "spec_helper"
2
+ require "lutaml/model"
3
+
4
+ module IncludedSpec
5
+ class Base
6
+ include Lutaml::Model::Serialize
7
+
8
+ attribute :text, Lutaml::Model::Type::String
9
+ attribute :id, Lutaml::Model::Type::String
10
+ attribute :name, Lutaml::Model::Type::String
11
+
12
+ xml do
13
+ map_content to: :text
14
+ map_attribute "id", to: :id
15
+ map_element "name", to: :name
16
+ end
17
+ end
18
+
19
+ class Implementation1
20
+ include Lutaml::Model::Serialize
21
+
22
+ attribute :text, Lutaml::Model::Type::String
23
+ attribute :id, Lutaml::Model::Type::String
24
+ attribute :name, Lutaml::Model::Type::String
25
+ attribute :age, Lutaml::Model::Type::Integer
26
+
27
+ xml do
28
+ root "impl_one"
29
+ map_content to: :text
30
+ map_attribute "id", to: :id
31
+ map_element "name", to: :name
32
+ map_element "age", to: :age
33
+ end
34
+ end
35
+
36
+ class Implementation2
37
+ include Lutaml::Model::Serialize
38
+
39
+ attribute :text, Lutaml::Model::Type::String
40
+ attribute :id, Lutaml::Model::Type::String
41
+ attribute :name, Lutaml::Model::Type::String
42
+ attribute :age, Lutaml::Model::Type::Integer
43
+
44
+ xml do
45
+ root "impl_two"
46
+ map_content to: :text
47
+ map_attribute "id", to: :id
48
+ map_element "name", to: :name
49
+ map_element "gender", to: :age
50
+ end
51
+ end
52
+
53
+ module ParentClass
54
+ include Lutaml::Model::Serialize
55
+
56
+ attribute :parent_name, Lutaml::Model::Type::String
57
+ attribute :parent_id, Lutaml::Model::Type::String
58
+
59
+ xml do
60
+ root "parent"
61
+ map_attribute "id", to: :parent_id
62
+ map_element "parent_name", to: :parent_name
63
+ end
64
+ end
65
+
66
+ module ChildClass
67
+ include ParentClass
68
+
69
+ attribute :child_name, Lutaml::Model::Type::String
70
+ attribute :child_type, Lutaml::Model::Type::String
71
+ attribute :child_text, Lutaml::Model::Type::String
72
+
73
+ xml do
74
+ root "child"
75
+ map_attribute "type", to: :child_type
76
+ map_element "child_name", to: :child_name
77
+ map_content to: :child_text
78
+ end
79
+ end
80
+
81
+ class GrandChildClass
82
+ include ChildClass
83
+
84
+ attribute :grandchild_name, Lutaml::Model::Type::String
85
+ attribute :grandchild_version, Lutaml::Model::Type::String
86
+ attribute :grandchild_text, Lutaml::Model::Type::String
87
+
88
+ xml do
89
+ root "grandchild"
90
+ map_attribute "version", to: :grandchild_version
91
+ map_element "grandchild_name", to: :grandchild_name
92
+ map_content to: :grandchild_text
93
+ end
94
+ end
95
+ end
96
+
97
+ RSpec.describe "Included" do
98
+ subject(:impl_object) do
99
+ IncludedSpec::Implementation1.new(
100
+ {
101
+ text: "Some text",
102
+ name: "John Doe",
103
+ id: "foobar",
104
+ age: 30,
105
+ },
106
+ )
107
+ end
108
+
109
+ let(:expected_xml) do
110
+ '<impl_one id="foobar"><name>John Doe</name><age>30</age>Some text</impl_one>'
111
+ end
112
+
113
+ it "uses included module attributes" do
114
+ expect(impl_object.to_xml(pretty: true)).to eq(expected_xml)
115
+ end
116
+
117
+ context "with multiple implementing classes" do
118
+ describe "Implementation1" do
119
+ let(:impl1) { IncludedSpec::Implementation1 }
120
+
121
+ it "has correct mappings" do
122
+ expect(impl1.mappings_for(:xml).mappings.count).to eq(4)
123
+ end
124
+
125
+ it "has correct attributes" do
126
+ expect(impl1.attributes.count).to eq(4)
127
+ end
128
+
129
+ it "has correct model" do
130
+ expect(impl1.model).to eq(impl1)
131
+ end
132
+ end
133
+
134
+ describe "Implementation2" do
135
+ let(:impl2) { IncludedSpec::Implementation2 }
136
+
137
+ it "has correct mappings" do
138
+ expect(impl2.mappings_for(:xml).mappings.count).to eq(4)
139
+ end
140
+
141
+ it "has correct attributes" do
142
+ expect(impl2.attributes.count).to eq(4)
143
+ end
144
+
145
+ it "has correct model" do
146
+ expect(impl2.model).to eq(impl2)
147
+ end
148
+ end
149
+ end
150
+
151
+ context "with nested module inclusion" do
152
+ let(:grandchild) do
153
+ IncludedSpec::GrandChildClass.new(
154
+ parent_name: "Parent Name",
155
+ parent_id: "P123",
156
+ child_name: "Child Name",
157
+ child_type: "Type A",
158
+ child_text: "Child Text",
159
+ grandchild_name: "GrandChild Name",
160
+ grandchild_version: "1.0",
161
+ grandchild_text: "GrandChild Text",
162
+ )
163
+ end
164
+
165
+ it "inherits attributes through the chain" do
166
+ expect(IncludedSpec::GrandChildClass.attributes.keys).to include(
167
+ :parent_name, :parent_id,
168
+ :child_name, :child_type, :child_text,
169
+ :grandchild_name, :grandchild_version, :grandchild_text
170
+ )
171
+ end
172
+
173
+ it "maintains correct XML mappings through inheritance" do
174
+ expected_xml = <<~XML
175
+ <grandchild id="P123" type="Type A" version="1.0">
176
+ <parent_name>Parent Name</parent_name>
177
+ <child_name>Child Name</child_name>
178
+ <grandchild_name>GrandChild Name</grandchild_name>
179
+ GrandChild Text
180
+ </grandchild>
181
+ XML
182
+
183
+ expect(grandchild.to_xml(pretty: true)).to be_equivalent_to(expected_xml)
184
+ end
185
+
186
+ it "preserves separate mapping configurations" do
187
+ expect(IncludedSpec::ParentClass.mappings_for(:xml).root_element).to eq("parent")
188
+ expect(IncludedSpec::ChildClass.mappings_for(:xml).root_element).to eq("child")
189
+ expect(IncludedSpec::GrandChildClass.mappings_for(:xml).root_element).to eq("grandchild")
190
+ end
191
+ end
192
+ end
@@ -524,15 +524,12 @@ RSpec.describe "MixedContent" do
524
524
  describe ".from_xml" do
525
525
  let(:expected_nokogiri_content) { "B <p>R&amp;C</p>\n C <p>J&#x2014;C</p>\n O <p>A &amp; B </p>\n F <p>Z &#xA9;S</p>" }
526
526
  let(:expected_ox_content) { "B <p>R&amp;C</p>\n C <p>J—C</p>\n O <p>A &amp; B </p>\n F <p>Z ©S</p>" }
527
+ let(:expected_oga_content) { "B <p>R&amp;C</p>\n C <p>J—C</p>\n O <p>A &amp; B </p>\n F <p>Z ©S</p>" }
527
528
 
528
529
  it "deserializes special char mixed content correctly" do
529
530
  parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
530
- expected_content = expected_nokogiri_content
531
-
532
- if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
533
- expected_content = expected_ox_content
534
- parsed.special.force_encoding("UTF-8")
535
- end
531
+ expected_content = public_send(:"expected_#{adapter_class.type}_content")
532
+ parsed.special.force_encoding("UTF-8") unless adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
536
533
 
537
534
  expect(parsed.special.strip).to eq(expected_content)
538
535
  end
@@ -562,12 +559,23 @@ RSpec.describe "MixedContent" do
562
559
  XML
563
560
  end
564
561
 
562
+ let(:expected_oga_xml) do
563
+ <<~XML
564
+ <SpecialCharContentWithRawOptionAndMixedOption>
565
+ <special> B &lt;p&gt;R&amp;C&lt;/p&gt;
566
+ C &lt;p&gt;J—C&lt;/p&gt;
567
+ O &lt;p&gt;A &amp; B &lt;/p&gt;
568
+ F &lt;p&gt;Z ©S&lt;/p&gt;
569
+ </special>
570
+ </SpecialCharContentWithRawOptionAndMixedOption>
571
+ XML
572
+ end
573
+
565
574
  it "serializes special char mixed content correctly" do
566
575
  parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
567
576
  serialized = parsed.to_xml
568
577
 
569
- expected_output = adapter_class == Lutaml::Model::XmlAdapter::OxAdapter ? expected_ox_xml : expected_nokogiri_xml
570
- expect(serialized).to be_equivalent_to(expected_output)
578
+ expect(serialized).to be_equivalent_to(send(:"expected_#{adapter_class.type}_xml"))
571
579
  end
572
580
  end
573
581
  end
@@ -590,21 +598,21 @@ RSpec.describe "MixedContent" do
590
598
  it "deserializes special char mixed content correctly" do
591
599
  parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
592
600
 
593
- expected_output = adapter_class == Lutaml::Model::XmlAdapter::OxAdapter ? expected_ox_content : expected_nokogiri_content
601
+ expected_output = adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter ? expected_nokogiri_content : expected_ox_content
594
602
  expect(parsed.special.strip).to eq(expected_output)
595
603
  end
596
604
  end
597
605
 
598
606
  describe ".to_xml" do
599
607
  let(:expected_nokogiri_xml) { "B &lt;p&gt;R&lt;/p&gt;" }
608
+ let(:expected_oga_xml) { "B &lt;p&gt;R&amp;C&lt;/p&gt;" }
600
609
  let(:expected_ox_xml) { "B &lt;p&gt;R&amp;amp;C&lt;/p&gt;" }
601
610
 
602
611
  it "serializes special char mixed content correctly" do
603
612
  parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
604
613
  serialized = parsed.to_xml
605
614
 
606
- expected_output = adapter_class == Lutaml::Model::XmlAdapter::OxAdapter ? expected_ox_xml : expected_nokogiri_xml
607
- expect(serialized).to include(expected_output)
615
+ expect(serialized).to include(send(:"expected_#{adapter_class.type}_xml"))
608
616
  end
609
617
  end
610
618
  end
@@ -630,12 +638,13 @@ RSpec.describe "MixedContent" do
630
638
 
631
639
  describe ".to_xml" do
632
640
  let(:expected_xml) { "<TextualSupport>\n <value>&lt;computer security&gt; type of operation specified by an access right</value>\n</TextualSupport>" }
641
+ let(:expected_oga_xml) { "<TextualSupport><value>&lt;computer security&gt; type of operation specified by an access right</value></TextualSupport>" }
633
642
 
634
643
  it "serializes special char mixed content correctly" do
635
644
  parsed = MixedContentSpec::TextualSupport.from_xml(xml)
636
645
  serialized = parsed.to_xml
637
-
638
- expect(serialized.strip).to include(expected_xml)
646
+ expected = adapter_class.type == "oga" ? expected_oga_xml : expected_xml
647
+ expect(serialized.strip).to include(expected)
639
648
  end
640
649
  end
641
650
  end
@@ -679,10 +688,10 @@ RSpec.describe "MixedContent" do
679
688
  parsed = MixedContentSpec::HexCode.from_xml(xml)
680
689
  serialized = parsed.to_xml(encoding: nil)
681
690
 
682
- expected_output = if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
683
- expected_encoding_nil_ox_xml
684
- else
691
+ expected_output = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
685
692
  expected_encoding_nil_nokogiri_xml
693
+ else
694
+ expected_encoding_nil_ox_xml
686
695
  end
687
696
 
688
697
  expect(serialized.strip).to include(expected_output)
@@ -715,10 +724,12 @@ RSpec.describe "MixedContent" do
715
724
  it "deserializes SHIFT encoded content incorrectly without explicit encoding option" do
716
725
  parsed = MixedContentSpec::Shift.from_xml(fixture)
717
726
 
718
- expected_content = if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
719
- "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("UTF-8")
720
- else
727
+ expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
721
728
  "�菑���p���P"
729
+ elsif adapter_class == Lutaml::Model::XmlAdapter::OgaAdapter
730
+ "手書き英字1"
731
+ else
732
+ "\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("UTF-8")
722
733
  end
723
734
 
724
735
  expect(parsed.field).to include(expected_content)
@@ -729,8 +740,8 @@ RSpec.describe "MixedContent" do
729
740
  it "serializes SHIFT-JIS encoding content correctly reading from file" do
730
741
  parsed = MixedContentSpec::Shift.from_xml(fixture, encoding: "Shift_JIS")
731
742
  serialized = parsed.to_xml
732
-
733
- expect(serialized.strip).to eq(fixture.strip)
743
+ expected = adapter_class.type == "oga" ? fixture.gsub(/\s+/, "") : fixture.strip
744
+ expect(serialized.strip).to eq(expected)
734
745
  end
735
746
 
736
747
  it "serializes SHIFT encoded content correctly with explicit encoding option both in parsing and deserializing" do
@@ -780,13 +791,13 @@ RSpec.describe "MixedContent" do
780
791
  it "serializes SHIFT-JIS content incorrectly bcz no encoding provided during parsing" do
781
792
  parsed = MixedContentSpec::Shift.from_xml(fixture)
782
793
  serialized = parsed.to_xml(encoding: "Shift_JIS")
783
-
784
- expected_content = if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
794
+ expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
795
+ "<root>\n <FieldName>&#65533;&#33745;&#65533;&#65533;&#65533;p&#65533;&#65533;&#65533;P</FieldName>\n <FieldName>123456</FieldName>\n</root>"
796
+ elsif adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
785
797
  "<root>\n <FieldName>\x8E菑\x82\xAB\x89p\x8E\x9A\x82P</FieldName>\n <FieldName>123456</FieldName>\n</root>\n"
786
798
  else
787
- "<root>\n <FieldName>&#65533;&#33745;&#65533;&#65533;&#65533;p&#65533;&#65533;&#65533;P</FieldName>\n <FieldName>123456</FieldName>\n</root>"
799
+ "<root><FieldName>手書き英字1</FieldName><FieldName>123456</FieldName></root>".encode("Shift_JIS")
788
800
  end
789
-
790
801
  expect(serialized).to eq(expected_content)
791
802
  end
792
803
 
@@ -831,10 +842,12 @@ RSpec.describe "MixedContent" do
831
842
  it "deserializes latin encoded content incorrectly" do
832
843
  parsed = MixedContentSpec::Latin.from_xml(fixture)
833
844
 
834
- expected_content = if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
835
- ["M\xFCller", "Jos\xE9"]
836
- else
845
+ expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
837
846
  ["M�ller", "Jos�"]
847
+ elsif adapter_class == Lutaml::Model::XmlAdapter::OgaAdapter
848
+ ["Müller", "José"]
849
+ else
850
+ ["M\xFCller", "Jos\xE9"]
838
851
  end
839
852
 
840
853
  expect(parsed.from).to eq(expected_content[0])
@@ -846,8 +859,12 @@ RSpec.describe "MixedContent" do
846
859
  it "serializes latin encoded content correctly" do
847
860
  parsed = MixedContentSpec::Latin.from_xml(fixture, encoding: "ISO-8859-1")
848
861
  serialized = parsed.to_xml
849
-
850
- expect(serialized.strip).to eq("<note>\n <to>Jos\xE9</to>\n <from>M\xFCller</from>\n <heading>Reminder</heading>\n</note>".force_encoding("ISO-8859-1"))
862
+ expected_xml = if adapter_class == Lutaml::Model::XmlAdapter::OgaAdapter
863
+ "<note><to>Jos\xE9</to><from>M\xFCller</from><heading>Reminder</heading></note>"
864
+ else
865
+ "<note>\n <to>Jos\xE9</to>\n <from>M\xFCller</from>\n <heading>Reminder</heading>\n</note>"
866
+ end
867
+ expect(serialized.strip).to eq(expected_xml.force_encoding("ISO-8859-1"))
851
868
  end
852
869
  end
853
870
  end
@@ -868,8 +885,7 @@ RSpec.describe "MixedContent" do
868
885
  it_behaves_like "mixed content behavior", described_class
869
886
  end
870
887
 
871
- # Not implemented yet
872
- xdescribe Lutaml::Model::XmlAdapter::OgaAdapter do
888
+ describe Lutaml::Model::XmlAdapter::OgaAdapter do
873
889
  it_behaves_like "mixed content behavior", described_class
874
890
  end
875
891
  end