lutaml-model 0.5.4 → 0.6.0
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 +58 -21
- data/Gemfile +1 -0
- data/README.adoc +1112 -264
- data/lib/lutaml/model/attribute.rb +33 -10
- data/lib/lutaml/model/choice.rb +56 -0
- data/lib/lutaml/model/config.rb +1 -0
- data/lib/lutaml/model/error/choice_lower_bound_error.rb +9 -0
- data/lib/lutaml/model/error/choice_upper_bound_error.rb +9 -0
- data/lib/lutaml/model/error/import_model_with_root_error.rb +9 -0
- data/lib/lutaml/model/error/incorrect_sequence_error.rb +9 -0
- data/lib/lutaml/model/error/invalid_choice_range_error.rb +20 -0
- data/lib/lutaml/model/error/no_root_mapping_error.rb +9 -0
- data/lib/lutaml/model/error/no_root_namespace_error.rb +9 -0
- data/lib/lutaml/model/error/unknown_sequence_mapping_error.rb +9 -0
- data/lib/lutaml/model/error.rb +8 -0
- data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +6 -1
- data/lib/lutaml/model/key_value_mapping.rb +3 -1
- data/lib/lutaml/model/key_value_mapping_rule.rb +4 -2
- data/lib/lutaml/model/liquefiable.rb +59 -0
- data/lib/lutaml/model/mapping_hash.rb +1 -1
- data/lib/lutaml/model/mapping_rule.rb +15 -2
- data/lib/lutaml/model/schema/xml_compiler.rb +68 -26
- data/lib/lutaml/model/schema_location.rb +7 -0
- data/lib/lutaml/model/sequence.rb +71 -0
- data/lib/lutaml/model/serialize.rb +125 -35
- data/lib/lutaml/model/type/decimal.rb +0 -4
- data/lib/lutaml/model/type/time.rb +3 -3
- data/lib/lutaml/model/utils.rb +19 -15
- data/lib/lutaml/model/validation.rb +12 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/oga.rb +10 -7
- data/lib/lutaml/model/xml_adapter/builder/ox.rb +20 -13
- data/lib/lutaml/model/xml_adapter/element.rb +32 -0
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +8 -8
- data/lib/lutaml/model/xml_adapter/oga/element.rb +14 -13
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +86 -19
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +19 -15
- data/lib/lutaml/model/xml_adapter/xml_document.rb +74 -13
- data/lib/lutaml/model/xml_adapter/xml_element.rb +57 -3
- data/lib/lutaml/model/xml_mapping.rb +49 -7
- data/lib/lutaml/model/xml_mapping_rule.rb +8 -3
- data/lib/lutaml/model.rb +1 -0
- data/lutaml-model.gemspec +5 -0
- data/spec/benchmarks/xml_parsing_benchmark_spec.rb +75 -0
- data/spec/ceramic_spec.rb +39 -0
- data/spec/fixtures/ceramic.rb +23 -0
- data/spec/fixtures/xml/address_example_260.xsd +9 -0
- data/spec/fixtures/xml/user.xsd +10 -0
- data/spec/lutaml/model/cdata_spec.rb +4 -5
- data/spec/lutaml/model/choice_spec.rb +168 -0
- data/spec/lutaml/model/collection_spec.rb +1 -1
- data/spec/lutaml/model/custom_model_spec.rb +6 -7
- data/spec/lutaml/model/custom_serialization_spec.rb +74 -2
- data/spec/lutaml/model/defaults_spec.rb +3 -1
- data/spec/lutaml/model/delegation_spec.rb +7 -5
- data/spec/lutaml/model/enum_spec.rb +35 -0
- data/spec/lutaml/model/group_spec.rb +160 -0
- data/spec/lutaml/model/inheritance_spec.rb +25 -0
- data/spec/lutaml/model/liquefiable_spec.rb +121 -0
- data/spec/lutaml/model/mixed_content_spec.rb +80 -41
- data/spec/lutaml/model/multiple_mapping_spec.rb +22 -10
- data/spec/lutaml/model/schema/xml_compiler_spec.rb +218 -25
- data/spec/lutaml/model/sequence_spec.rb +216 -0
- data/spec/lutaml/model/transformation_spec.rb +230 -0
- data/spec/lutaml/model/type_spec.rb +138 -31
- data/spec/lutaml/model/utils_spec.rb +32 -0
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -7
- data/spec/lutaml/model/xml_mapping_rule_spec.rb +51 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +143 -112
- metadata +67 -2
@@ -0,0 +1,160 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "lutaml/model"
|
3
|
+
|
4
|
+
module GroupSpec
|
5
|
+
class Ceramic < Lutaml::Model::Serializable
|
6
|
+
attribute :type, :string
|
7
|
+
attribute :name, :string
|
8
|
+
|
9
|
+
xml do
|
10
|
+
no_root
|
11
|
+
map_element :type, to: :type
|
12
|
+
map_element :name, to: :name
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class CeramicCollection < Lutaml::Model::Serializable
|
17
|
+
attribute :ceramic, Ceramic, collection: 1..2
|
18
|
+
|
19
|
+
xml do
|
20
|
+
root "collection"
|
21
|
+
map_element "ceramic", to: :ceramic
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class AttributeValueType < Lutaml::Model::Type::Decimal
|
26
|
+
end
|
27
|
+
|
28
|
+
class GroupOfItems < Lutaml::Model::Serializable
|
29
|
+
attribute :name, :string
|
30
|
+
attribute :type, :string
|
31
|
+
attribute :description, :string
|
32
|
+
attribute :code, :string
|
33
|
+
|
34
|
+
xml do
|
35
|
+
no_root
|
36
|
+
sequence do
|
37
|
+
map_element "name", to: :name
|
38
|
+
map_element "type", to: :type
|
39
|
+
map_element "description", to: :description,
|
40
|
+
namespace: "http://www.sparxsystems.com/profiles/GML/1.0",
|
41
|
+
prefix: "GML"
|
42
|
+
end
|
43
|
+
map_attribute "code", to: :code, namespace: "http://www.example.com", prefix: "ex1"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class ComplexType < Lutaml::Model::Serializable
|
48
|
+
attribute :tag, AttributeValueType
|
49
|
+
attribute :content, :string
|
50
|
+
attribute :group, :string
|
51
|
+
import_model_attributes GroupOfItems
|
52
|
+
|
53
|
+
xml do
|
54
|
+
root "GroupOfItems"
|
55
|
+
map_attribute "tag", to: :tag
|
56
|
+
map_content to: :content
|
57
|
+
map_element :group, to: :group
|
58
|
+
import_model_mappings GroupOfItems
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class SimpleType < Lutaml::Model::Serializable
|
63
|
+
import_model GroupOfItems
|
64
|
+
end
|
65
|
+
|
66
|
+
class GenericType < Lutaml::Model::Serializable
|
67
|
+
import_model_mappings GroupOfItems
|
68
|
+
end
|
69
|
+
|
70
|
+
class GroupWithRoot < Lutaml::Model::Serializable
|
71
|
+
attribute :name, :string
|
72
|
+
|
73
|
+
xml do
|
74
|
+
root "group"
|
75
|
+
map_element :name, to: :name
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
RSpec.describe "Group" do
|
81
|
+
context "with no_root" do
|
82
|
+
let(:mapper) { GroupSpec::CeramicCollection }
|
83
|
+
|
84
|
+
it "raises error if root-less class used directly for parsing" do
|
85
|
+
xml = <<~XML
|
86
|
+
<type>Data</type>
|
87
|
+
<name>Smith</name>
|
88
|
+
XML
|
89
|
+
|
90
|
+
expect { GroupSpec::Ceramic.from_xml(xml) }.to raise_error(
|
91
|
+
Lutaml::Model::NoRootMappingError,
|
92
|
+
"GroupSpec::Ceramic has `no_root`, it allowed only for reusable models",
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
it "raises error if root_less class used for deserializing" do
|
97
|
+
ceramic = GroupSpec::Ceramic.new(type: "Data", name: "Starc")
|
98
|
+
|
99
|
+
expect { ceramic.to_xml }.to raise_error(
|
100
|
+
Lutaml::Model::NoRootMappingError,
|
101
|
+
"GroupSpec::Ceramic has `no_root`, it allowed only for reusable models",
|
102
|
+
)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "correctly get the element of root-less class" do
|
106
|
+
xml = <<~XML
|
107
|
+
<collection>
|
108
|
+
<ceramic>
|
109
|
+
<type>Data</type>
|
110
|
+
</ceramic>
|
111
|
+
</collection>
|
112
|
+
XML
|
113
|
+
|
114
|
+
expect { mapper.from_xml(xml) }.not_to raise_error
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context "with model" do
|
119
|
+
it "import attributes" do
|
120
|
+
expect(GroupSpec::ComplexType.attributes).to include(GroupSpec::GroupOfItems.attributes)
|
121
|
+
end
|
122
|
+
|
123
|
+
it "import mappings in xml block" do
|
124
|
+
expect(GroupSpec::ComplexType.mappings_for(:xml).elements).to include(*GroupSpec::GroupOfItems.mappings_for(:xml).elements)
|
125
|
+
end
|
126
|
+
|
127
|
+
it "import mappings outside xml block" do
|
128
|
+
expect(GroupSpec::GenericType.mappings_for(:xml).elements).to include(*GroupSpec::GroupOfItems.mappings_for(:xml).elements)
|
129
|
+
end
|
130
|
+
|
131
|
+
it "import attributes and mappings in xml block" do
|
132
|
+
expect(GroupSpec::ComplexType.attributes).to include(GroupSpec::GroupOfItems.attributes)
|
133
|
+
expect(GroupSpec::ComplexType.mappings_for(:xml).elements).to include(*GroupSpec::GroupOfItems.mappings_for(:xml).elements)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "import attributes and mappings outside the xml block" do
|
137
|
+
expect(GroupSpec::SimpleType.attributes).to include(GroupSpec::GroupOfItems.attributes)
|
138
|
+
expect(GroupSpec::SimpleType.mappings_for(:xml).elements).to include(*GroupSpec::GroupOfItems.mappings_for(:xml).elements)
|
139
|
+
end
|
140
|
+
|
141
|
+
it "raises error if root is defined on imported class" do
|
142
|
+
expect do
|
143
|
+
Class.new(Lutaml::Model::Serializable) do
|
144
|
+
import_model GroupSpec::GroupWithRoot
|
145
|
+
end
|
146
|
+
end.to raise_error(Lutaml::Model::ImportModelWithRootError, "Cannot import a model `GroupSpec::GroupWithRoot` with a root element")
|
147
|
+
end
|
148
|
+
|
149
|
+
it "raises error if namespace is defined with no_root" do
|
150
|
+
expect do
|
151
|
+
Class.new(Lutaml::Model::Serializable) do
|
152
|
+
xml do
|
153
|
+
no_root
|
154
|
+
namespace "http://www.omg.org/spec/XMI/20131001", "xmi"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end.to raise_error(Lutaml::Model::NoRootNamespaceError, "Cannot assign namespace to `no_root`")
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -34,6 +34,22 @@ module InheritanceSpec
|
|
34
34
|
map_element "gender", to: :age
|
35
35
|
end
|
36
36
|
end
|
37
|
+
|
38
|
+
class ParentWithMapAll < Lutaml::Model::Serializable
|
39
|
+
attribute :id, :string
|
40
|
+
attribute :description, :string
|
41
|
+
|
42
|
+
xml do
|
43
|
+
map_attribute "id", to: :id
|
44
|
+
map_all_content to: :description
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Child < ParentWithMapAll
|
49
|
+
xml do
|
50
|
+
root "child"
|
51
|
+
end
|
52
|
+
end
|
37
53
|
end
|
38
54
|
|
39
55
|
RSpec.describe "Inheritance" do
|
@@ -89,4 +105,13 @@ RSpec.describe "Inheritance" do
|
|
89
105
|
end
|
90
106
|
end
|
91
107
|
end
|
108
|
+
|
109
|
+
context "with map_all in parent" do
|
110
|
+
let(:xml) { "<child id=\"en\">Some <b>bold</b> Content</child>" }
|
111
|
+
|
112
|
+
it "round trip correctly" do
|
113
|
+
parsed = InheritanceSpec::Child.from_xml(xml)
|
114
|
+
expect(parsed.to_xml).to eq(xml)
|
115
|
+
end
|
116
|
+
end
|
92
117
|
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require_relative "../../fixtures/address"
|
3
|
+
|
4
|
+
class LiquefiableClass
|
5
|
+
include Lutaml::Model::Liquefiable
|
6
|
+
|
7
|
+
attr_accessor :name, :value
|
8
|
+
|
9
|
+
def initialize(name, value)
|
10
|
+
@name = name
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def display_name
|
15
|
+
"#{name} (#{value})"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec.describe Lutaml::Model::Liquefiable do
|
20
|
+
before do
|
21
|
+
stub_const("DummyModel", Class.new(LiquefiableClass))
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:dummy) { DummyModel.new("TestName", 42) }
|
25
|
+
|
26
|
+
describe ".register_liquid_drop_class" do
|
27
|
+
context "when drop class does not exist" do
|
28
|
+
it "creates a new drop class" do
|
29
|
+
expect { dummy.class.register_liquid_drop_class }.to change {
|
30
|
+
dummy.class.const_defined?(:DummyModelDrop)
|
31
|
+
}
|
32
|
+
.from(false)
|
33
|
+
.to(true)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "when drop class already exists" do
|
38
|
+
it "raises an error" do
|
39
|
+
dummy.class.register_liquid_drop_class
|
40
|
+
expect { dummy.class.register_liquid_drop_class }.to raise_error(RuntimeError, "DummyModelDrop Already exists!")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe ".drop_class_name" do
|
46
|
+
it "returns the correct drop class name" do
|
47
|
+
expect(dummy.class.drop_class_name).to eq("DummyModelDrop")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe ".drop_class" do
|
52
|
+
context "when drop class exists" do
|
53
|
+
it "returns the drop class" do
|
54
|
+
dummy.class.register_liquid_drop_class
|
55
|
+
expect(dummy.class.drop_class).to eq(DummyModel::DummyModelDrop)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when drop class does not exist" do
|
60
|
+
it "returns nil" do
|
61
|
+
expect(dummy.class.drop_class).to be_nil
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".register_drop_method" do
|
67
|
+
before do
|
68
|
+
dummy.class.register_liquid_drop_class
|
69
|
+
end
|
70
|
+
|
71
|
+
it "defines a method on the drop class" do
|
72
|
+
expect { dummy.class.register_drop_method(:display_name) }.to change {
|
73
|
+
dummy.to_liquid.respond_to?(:display_name)
|
74
|
+
}
|
75
|
+
.from(false)
|
76
|
+
.to(true)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe ".to_liquid" do
|
81
|
+
before do
|
82
|
+
dummy.class.register_liquid_drop_class
|
83
|
+
dummy.class.register_drop_method(:display_name)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "returns an instance of the drop class" do
|
87
|
+
expect(dummy.to_liquid).to be_a(dummy.class.drop_class)
|
88
|
+
end
|
89
|
+
|
90
|
+
it "allows access to registered methods via the drop class" do
|
91
|
+
expect(dummy.to_liquid.display_name).to eq("TestName (42)")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "with serializeable classes" do
|
96
|
+
let(:address) do
|
97
|
+
Address.new(
|
98
|
+
{
|
99
|
+
country: "US",
|
100
|
+
post_code: "12345",
|
101
|
+
person: [Person.new, Person.new],
|
102
|
+
},
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
describe ".to_liquid" do
|
107
|
+
it "returns correct drop object" do
|
108
|
+
expect(address.to_liquid).to be_a(Address::AddressDrop)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "returns array of drops for collection objects" do
|
112
|
+
person_classes = address.to_liquid.person.map(&:class)
|
113
|
+
expect(person_classes).to eq([Person::PersonDrop, Person::PersonDrop])
|
114
|
+
end
|
115
|
+
|
116
|
+
it "returns `US` for country" do
|
117
|
+
expect(address.to_liquid.country).to eq("US")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -467,7 +467,9 @@ RSpec.describe "MixedContent" do
|
|
467
467
|
end
|
468
468
|
|
469
469
|
describe ".from_xml" do
|
470
|
-
let(:expected_content)
|
470
|
+
let(:expected_content) do
|
471
|
+
"Moon&Mars Distanced©its — surface covered & processed"
|
472
|
+
end
|
471
473
|
|
472
474
|
it "deserializes special char mixed content correctly" do
|
473
475
|
parsed = MixedContentSpec::SpecialCharContentWithMixedTrue.from_xml(xml)
|
@@ -476,7 +478,9 @@ RSpec.describe "MixedContent" do
|
|
476
478
|
end
|
477
479
|
|
478
480
|
describe ".to_xml" do
|
479
|
-
let(:expected_xml)
|
481
|
+
let(:expected_xml) do
|
482
|
+
"Moon&Mars Distanced©its — surface covered & processed"
|
483
|
+
end
|
480
484
|
|
481
485
|
it "serializes special char mixed content correctly" do
|
482
486
|
parsed = MixedContentSpec::SpecialCharContentWithMixedTrue.from_xml(xml)
|
@@ -522,9 +526,15 @@ RSpec.describe "MixedContent" do
|
|
522
526
|
end
|
523
527
|
|
524
528
|
describe ".from_xml" do
|
525
|
-
let(:expected_nokogiri_content)
|
526
|
-
|
527
|
-
|
529
|
+
let(:expected_nokogiri_content) do
|
530
|
+
"B <p>R&C</p>\n C <p>J—C</p>\n O <p>A & B </p>\n F <p>Z ©S</p>"
|
531
|
+
end
|
532
|
+
let(:expected_ox_content) do
|
533
|
+
"B <p>R&C</p> C <p>J—C</p> O <p>A & B </p> F <p>Z ©S</p>"
|
534
|
+
end
|
535
|
+
let(:expected_oga_content) do
|
536
|
+
"B <p>R&C</p>\n C <p>J—C</p>\n O <p>A & B </p>\n F <p>Z ©S</p>"
|
537
|
+
end
|
528
538
|
|
529
539
|
it "deserializes special char mixed content correctly" do
|
530
540
|
parsed = MixedContentSpec::SpecialCharContentWithRawAndMixedOption.from_xml(xml)
|
@@ -627,7 +637,9 @@ RSpec.describe "MixedContent" do
|
|
627
637
|
end
|
628
638
|
|
629
639
|
describe ".from_xml" do
|
630
|
-
let(:expected_content)
|
640
|
+
let(:expected_content) do
|
641
|
+
"<computer security> type of operation specified by an access right"
|
642
|
+
end
|
631
643
|
|
632
644
|
it "deserializes special char mixed content correctly" do
|
633
645
|
parsed = MixedContentSpec::TextualSupport.from_xml(xml)
|
@@ -637,8 +649,12 @@ RSpec.describe "MixedContent" do
|
|
637
649
|
end
|
638
650
|
|
639
651
|
describe ".to_xml" do
|
640
|
-
let(:expected_xml)
|
641
|
-
|
652
|
+
let(:expected_xml) do
|
653
|
+
"<TextualSupport>\n <value><computer security> type of operation specified by an access right</value>\n</TextualSupport>"
|
654
|
+
end
|
655
|
+
let(:expected_oga_xml) do
|
656
|
+
"<TextualSupport><value><computer security> type of operation specified by an access right</value></TextualSupport>"
|
657
|
+
end
|
642
658
|
|
643
659
|
it "serializes special char mixed content correctly" do
|
644
660
|
parsed = MixedContentSpec::TextualSupport.from_xml(xml)
|
@@ -659,7 +675,9 @@ RSpec.describe "MixedContent" do
|
|
659
675
|
end
|
660
676
|
|
661
677
|
describe ".from_xml" do
|
662
|
-
let(:expected_content)
|
678
|
+
let(:expected_content) do
|
679
|
+
"∑computer security∏ type of operation specified µ by an access right"
|
680
|
+
end
|
663
681
|
|
664
682
|
it "deserializes special char mixed content correctly" do
|
665
683
|
parsed = MixedContentSpec::HexCode.from_xml(xml)
|
@@ -670,7 +688,9 @@ RSpec.describe "MixedContent" do
|
|
670
688
|
|
671
689
|
describe ".to_xml" do
|
672
690
|
context "when default encoding xml" do
|
673
|
-
let(:expected_default_encoding_xml)
|
691
|
+
let(:expected_default_encoding_xml) do
|
692
|
+
"∑computer security∏ type of operation specified µ by an access right"
|
693
|
+
end
|
674
694
|
|
675
695
|
it "serializes special char mixed content correctly with default encoding: UTF-8" do
|
676
696
|
parsed = MixedContentSpec::HexCode.from_xml(xml)
|
@@ -681,20 +701,36 @@ RSpec.describe "MixedContent" do
|
|
681
701
|
end
|
682
702
|
|
683
703
|
context "when encoding: nil xml" do
|
684
|
-
let(:expected_encoding_nil_nokogiri_xml)
|
685
|
-
|
704
|
+
let(:expected_encoding_nil_nokogiri_xml) do
|
705
|
+
"∑computer security∏ type of ​ operation specified µ by an access right"
|
706
|
+
end
|
707
|
+
let(:expected_encoding_nil_ox_xml) do
|
708
|
+
"<HexCode> \xE2\x88\x91computer security\xE2\x88\x8F type of \xE2\x80\x8B operation specified \xC2\xB5 by an access right </HexCode>\n".force_encoding("ASCII-8BIT")
|
709
|
+
end
|
710
|
+
let(:expected_encoding_nil_oga_xml) do
|
711
|
+
"<HexCode>\n ∑computer security∏ type of operation specified µ by an access right\n</HexCode>"
|
712
|
+
end
|
686
713
|
|
687
714
|
it "serializes special char mixed content correctly with encoding: nil to get hexcode" do
|
688
|
-
parsed = MixedContentSpec::HexCode.from_xml(xml)
|
715
|
+
parsed = MixedContentSpec::HexCode.from_xml(xml, encoding: nil)
|
689
716
|
serialized = parsed.to_xml(encoding: nil)
|
690
717
|
|
691
718
|
expected_output = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
|
692
719
|
expected_encoding_nil_nokogiri_xml
|
693
|
-
|
720
|
+
elsif adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
694
721
|
expected_encoding_nil_ox_xml
|
722
|
+
else
|
723
|
+
expected_encoding_nil_oga_xml
|
695
724
|
end
|
696
725
|
|
697
|
-
expect(
|
726
|
+
expect(parsed.encoding).to be_nil
|
727
|
+
expect(serialized.strip).to include(expected_output.strip)
|
728
|
+
|
729
|
+
if adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
730
|
+
expect(serialized.encoding.to_s).to eq("ASCII-8BIT")
|
731
|
+
else
|
732
|
+
expect(serialized.encoding.to_s).to eq("UTF-8")
|
733
|
+
end
|
698
734
|
end
|
699
735
|
end
|
700
736
|
end
|
@@ -702,7 +738,9 @@ RSpec.describe "MixedContent" do
|
|
702
738
|
|
703
739
|
context "when use encoding in parsing" do
|
704
740
|
context "when use SHIFT-JIS encoding" do
|
705
|
-
let(:fixture)
|
741
|
+
let(:fixture) do
|
742
|
+
File.read(fixture_path("xml/shift_jis.xml"), encoding: "Shift_JIS")
|
743
|
+
end
|
706
744
|
|
707
745
|
describe ".from_xml" do
|
708
746
|
it "verifies the encoding of file read" do
|
@@ -712,10 +750,10 @@ RSpec.describe "MixedContent" do
|
|
712
750
|
it "deserializes SHIFT encoded content correctly with explicit encoding option" do
|
713
751
|
parsed = MixedContentSpec::Shift.from_xml(fixture, encoding: "Shift_JIS")
|
714
752
|
|
715
|
-
expected_content = if adapter_class == Lutaml::Model::XmlAdapter::
|
716
|
-
"\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
|
717
|
-
else
|
753
|
+
expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
|
718
754
|
"手書き英字1"
|
755
|
+
else
|
756
|
+
"\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
|
719
757
|
end
|
720
758
|
|
721
759
|
expect(parsed.field).to include(expected_content)
|
@@ -725,13 +763,12 @@ RSpec.describe "MixedContent" do
|
|
725
763
|
parsed = MixedContentSpec::Shift.from_xml(fixture)
|
726
764
|
|
727
765
|
expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
|
728
|
-
"�菑���p���P"
|
729
|
-
elsif adapter_class == Lutaml::Model::XmlAdapter::OgaAdapter
|
730
766
|
"手書き英字1"
|
731
767
|
else
|
732
|
-
"\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("
|
768
|
+
"\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
|
733
769
|
end
|
734
770
|
|
771
|
+
expect(parsed.encoding).to eq("Shift_JIS")
|
735
772
|
expect(parsed.field).to include(expected_content)
|
736
773
|
end
|
737
774
|
end
|
@@ -748,13 +785,13 @@ RSpec.describe "MixedContent" do
|
|
748
785
|
parsed = MixedContentSpec::Shift.from_xml(fixture, encoding: "Shift_JIS")
|
749
786
|
serialized = parsed.to_xml(encoding: "UTF-8")
|
750
787
|
|
751
|
-
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
788
|
+
parsed_xml = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
|
789
|
+
"手書き英字1"
|
790
|
+
else
|
791
|
+
"\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
|
792
|
+
end
|
756
793
|
|
757
|
-
expect(parsed.field).to include(
|
794
|
+
expect(parsed.field).to include(parsed_xml)
|
758
795
|
expect(parsed.encoding).to eq("Shift_JIS")
|
759
796
|
|
760
797
|
expect(serialized).to include("手書き英字1")
|
@@ -765,10 +802,10 @@ RSpec.describe "MixedContent" do
|
|
765
802
|
parsed = MixedContentSpec::Shift.from_xml(fixture, encoding: "Shift_JIS")
|
766
803
|
serialized = parsed.to_xml(encoding: "Shift_JIS")
|
767
804
|
|
768
|
-
expected_xml = if adapter_class == Lutaml::Model::XmlAdapter::
|
769
|
-
"\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
|
770
|
-
else
|
805
|
+
expected_xml = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
|
771
806
|
"手書き英字1"
|
807
|
+
else
|
808
|
+
"\x8E\xE8\x8F\x91\x82\xAB\x89p\x8E\x9A\x82P".force_encoding("Shift_JIS")
|
772
809
|
end
|
773
810
|
|
774
811
|
expect(parsed.field).to include(expected_xml)
|
@@ -788,16 +825,18 @@ RSpec.describe "MixedContent" do
|
|
788
825
|
expect(serialized.encoding.to_s).to eq("Shift_JIS")
|
789
826
|
end
|
790
827
|
|
791
|
-
it "serializes SHIFT-JIS content
|
828
|
+
it "serializes SHIFT-JIS content correctly bcz xml.encoding used during parsing" do
|
792
829
|
parsed = MixedContentSpec::Shift.from_xml(fixture)
|
793
830
|
serialized = parsed.to_xml(encoding: "Shift_JIS")
|
831
|
+
|
794
832
|
expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
|
795
|
-
"<root>\n <FieldName
|
833
|
+
"<root>\n <FieldName>手書き英字1</FieldName>\n <FieldName>123456</FieldName>\n</root>".encode("Shift_JIS")
|
796
834
|
elsif adapter_class == Lutaml::Model::XmlAdapter::OxAdapter
|
797
|
-
"<root>\n <FieldName
|
835
|
+
"<root>\n <FieldName>手書き英字1</FieldName>\n <FieldName>123456</FieldName>\n</root>\n".encode("Shift_JIS")
|
798
836
|
else
|
799
837
|
"<root><FieldName>手書き英字1</FieldName><FieldName>123456</FieldName></root>".encode("Shift_JIS")
|
800
838
|
end
|
839
|
+
|
801
840
|
expect(serialized).to eq(expected_content)
|
802
841
|
end
|
803
842
|
|
@@ -829,27 +868,27 @@ RSpec.describe "MixedContent" do
|
|
829
868
|
it "deserializes latin encoded content correctly" do
|
830
869
|
parsed = MixedContentSpec::Latin.from_xml(fixture, encoding: "ISO-8859-1")
|
831
870
|
|
832
|
-
expected_content = if adapter_class == Lutaml::Model::XmlAdapter::
|
833
|
-
["M\xFCller".force_encoding("ISO-8859-1"), "Jos\xE9".force_encoding("ISO-8859-1")]
|
834
|
-
else
|
871
|
+
expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
|
835
872
|
["Müller", "José"]
|
873
|
+
else
|
874
|
+
["M\xFCller".force_encoding("ISO-8859-1"), "Jos\xE9".force_encoding("ISO-8859-1")]
|
836
875
|
end
|
837
876
|
|
877
|
+
expect(parsed.encoding).to eq("ISO-8859-1")
|
838
878
|
expect(parsed.from).to eq(expected_content[0])
|
839
879
|
expect(parsed.the).to eq(expected_content[1])
|
840
880
|
end
|
841
881
|
|
842
|
-
it "deserializes latin encoded content
|
882
|
+
it "deserializes latin encoded content correctly, bcz xml.encoding used for parsing" do
|
843
883
|
parsed = MixedContentSpec::Latin.from_xml(fixture)
|
844
884
|
|
845
885
|
expected_content = if adapter_class == Lutaml::Model::XmlAdapter::NokogiriAdapter
|
846
|
-
["M�ller", "Jos�"]
|
847
|
-
elsif adapter_class == Lutaml::Model::XmlAdapter::OgaAdapter
|
848
886
|
["Müller", "José"]
|
849
887
|
else
|
850
|
-
["M\xFCller", "Jos\xE9"]
|
888
|
+
["M\xFCller".force_encoding("ISO-8859-1"), "Jos\xE9".force_encoding("ISO-8859-1")]
|
851
889
|
end
|
852
890
|
|
891
|
+
expect(parsed.encoding).to eq("ISO-8859-1")
|
853
892
|
expect(parsed.from).to eq(expected_content[0])
|
854
893
|
expect(parsed.the).to eq(expected_content[1])
|
855
894
|
end
|
@@ -106,7 +106,7 @@ module MultipleMapping
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def name_from_xml(model, value)
|
109
|
-
model.full_name = value.sub(/^XML Model: /, "")
|
109
|
+
model.full_name = value.text.sub(/^XML Model: /, "")
|
110
110
|
end
|
111
111
|
|
112
112
|
def color_to_xml(model, parent, doc)
|
@@ -116,7 +116,7 @@ module MultipleMapping
|
|
116
116
|
end
|
117
117
|
|
118
118
|
def color_from_xml(model, value)
|
119
|
-
model.color = value.downcase
|
119
|
+
model.color = value.text.downcase
|
120
120
|
end
|
121
121
|
|
122
122
|
def size_to_xml(model, parent, doc)
|
@@ -126,7 +126,7 @@ module MultipleMapping
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def size_from_xml(model, value)
|
129
|
-
model.size = (value.to_i || 0) - 10
|
129
|
+
model.size = (value.text.to_i || 0) - 10
|
130
130
|
end
|
131
131
|
|
132
132
|
def desc_to_xml(model, parent, doc)
|
@@ -136,7 +136,7 @@ module MultipleMapping
|
|
136
136
|
end
|
137
137
|
|
138
138
|
def desc_from_xml(model, value)
|
139
|
-
model.description = value.sub(/^XML Description: /, "")
|
139
|
+
model.description = value.text.sub(/^XML Description: /, "")
|
140
140
|
end
|
141
141
|
end
|
142
142
|
end
|
@@ -144,8 +144,12 @@ end
|
|
144
144
|
RSpec.describe MultipleMapping do
|
145
145
|
context "with key-value formats" do
|
146
146
|
context "with YAML format" do
|
147
|
-
let(:yaml_with_name)
|
148
|
-
|
147
|
+
let(:yaml_with_name) do
|
148
|
+
"product_name: Coffee Maker\ndescription: Premium coffee maker"
|
149
|
+
end
|
150
|
+
let(:yaml_with_desc) do
|
151
|
+
"---\nname: Coffee Maker\ndesc: Premium coffee maker\n"
|
152
|
+
end
|
149
153
|
|
150
154
|
it "handles bidirectional conversion" do
|
151
155
|
product1 = MultipleMapping::Product.from_yaml(yaml_with_name)
|
@@ -161,8 +165,12 @@ RSpec.describe MultipleMapping do
|
|
161
165
|
end
|
162
166
|
|
163
167
|
context "with JSON format" do
|
164
|
-
let(:json_with_name)
|
165
|
-
|
168
|
+
let(:json_with_name) do
|
169
|
+
'{"product_name":"Coffee Maker","description":"Premium coffee maker"}'
|
170
|
+
end
|
171
|
+
let(:json_with_desc) do
|
172
|
+
'{"name":"Coffee Maker","desc":"Premium coffee maker"}'
|
173
|
+
end
|
166
174
|
|
167
175
|
it "handles bidirectional conversion" do
|
168
176
|
product1 = MultipleMapping::Product.from_json(json_with_name)
|
@@ -254,8 +262,12 @@ RSpec.describe MultipleMapping do
|
|
254
262
|
|
255
263
|
context "with CustomModel" do
|
256
264
|
context "with JSON format" do
|
257
|
-
let(:json_with_alternate)
|
258
|
-
|
265
|
+
let(:json_with_alternate) do
|
266
|
+
'{"custom_name":"JSON Model: Vase","shade":"BLUE","dimension":22,"description":"JSON Description: A beautiful ceramic vase"}'
|
267
|
+
end
|
268
|
+
let(:json_with_standard) do
|
269
|
+
'{"name":"JSON Model: Vase","color":"BLUE","size":22,"desc":"JSON Description: A beautiful ceramic vase"}'
|
270
|
+
end
|
259
271
|
|
260
272
|
it "handles bidirectional conversion with custom methods" do
|
261
273
|
model1 = MultipleMapping::CustomModel.from_json(json_with_alternate)
|