tilia-xml 1.2.0.2 → 1.3.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/.simplecov +1 -2
- data/CHANGELOG.sabre.md +21 -0
- data/Gemfile +2 -7
- data/Gemfile.lock +22 -17
- data/LICENSE +1 -1
- data/LICENSE.sabre +1 -1
- data/lib/tilia/xml.rb +3 -0
- data/lib/tilia/xml/context_stack_trait.rb +27 -23
- data/lib/tilia/xml/deserializer.rb +241 -0
- data/lib/tilia/xml/element/base.rb +2 -43
- data/lib/tilia/xml/element/cdata.rb +1 -26
- data/lib/tilia/xml/element/elements.rb +4 -66
- data/lib/tilia/xml/element/key_value.rb +3 -65
- data/lib/tilia/xml/element/uri.rb +2 -45
- data/lib/tilia/xml/element/xml_fragment.rb +2 -42
- data/lib/tilia/xml/reader.rb +33 -28
- data/lib/tilia/xml/serializer.rb +194 -0
- data/lib/tilia/xml/service.rb +98 -14
- data/lib/tilia/xml/version.rb +1 -1
- data/lib/tilia/xml/writer.rb +42 -88
- data/lib/tilia/xml/xml_deserializable.rb +4 -4
- data/lib/tilia/xml/xml_serializable.rb +3 -3
- data/test/test_helper.rb +77 -0
- data/test/xml/context_stack_test.rb +0 -3
- data/test/xml/deserializer/enum_test.rb +55 -0
- data/test/xml/deserializer/key_value_test.rb +64 -0
- data/test/xml/deserializer/repeating_elements_test.rb +33 -0
- data/test/xml/deserializer/value_object_test.rb +163 -0
- data/test/xml/reader_test.rb +35 -16
- data/test/xml/serializer/enum_test.rb +36 -0
- data/test/xml/serializer/repeating_elements_test.rb +37 -0
- data/test/xml/service_test.rb +68 -0
- data/test/xml/writer_test.rb +120 -17
- data/tilia-xml.gemspec +4 -4
- metadata +23 -9
@@ -5,7 +5,7 @@ module Tilia
|
|
5
5
|
module XmlDeserializable
|
6
6
|
# The deserialize method is called during xml parsing.
|
7
7
|
#
|
8
|
-
# This method is called
|
8
|
+
# This method is called statictlly, this is because in theory this method
|
9
9
|
# may be used as a type of constructor, or factory method.
|
10
10
|
#
|
11
11
|
# Often you want to return an instance of the current class, but you are
|
@@ -20,9 +20,9 @@ module Tilia
|
|
20
20
|
# $reader->parseInnerTree() will parse the entire sub-tree, and advance to
|
21
21
|
# the next element.
|
22
22
|
#
|
23
|
-
# @param [Reader]
|
24
|
-
# @return
|
25
|
-
def xml_deserialize(
|
23
|
+
# @param [Reader] reader
|
24
|
+
# @return [Object]
|
25
|
+
def xml_deserialize(reader)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -3,7 +3,7 @@ module Tilia
|
|
3
3
|
# Objects implementing XmlSerializable can control how they are represented in
|
4
4
|
# Xml.
|
5
5
|
module XmlSerializable
|
6
|
-
# The xmlSerialize
|
6
|
+
# The xmlSerialize method is called during xml writing.
|
7
7
|
#
|
8
8
|
# Use the $writer argument to write its own xml serialization.
|
9
9
|
#
|
@@ -18,9 +18,9 @@ module Tilia
|
|
18
18
|
#
|
19
19
|
# If you are opening new elements, you must also close them again.
|
20
20
|
#
|
21
|
-
# @param [Writer]
|
21
|
+
# @param [Writer] writer
|
22
22
|
# @return [void]
|
23
|
-
def xml_serialize(
|
23
|
+
def xml_serialize(writer)
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
data/test/test_helper.rb
CHANGED
@@ -2,3 +2,80 @@ require 'simplecov'
|
|
2
2
|
require 'minitest/autorun'
|
3
3
|
|
4
4
|
require 'tilia/xml'
|
5
|
+
|
6
|
+
# Extend the assertions
|
7
|
+
require 'minitest/assertions'
|
8
|
+
module Minitest
|
9
|
+
module Assertions
|
10
|
+
def assert_xml_equal(expected, actual, message = nil)
|
11
|
+
assert(
|
12
|
+
Hash.from_xml(actual) == Hash.from_xml(expected),
|
13
|
+
message || ">>> expected:\n#{expected}\n<<<\n>>> got:\n#{actual}\n<<<"
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
def assert_instance_equal(expected, actual, message = nil)
|
18
|
+
assert(
|
19
|
+
compare_instances(expected, actual),
|
20
|
+
message || ">>> expected:\n#{expected.inspect}\n<<<\n>>> got:\n#{actual.inspect}\n<<<"
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
def assert_has_key(key, hash, message = nil)
|
25
|
+
assert(
|
26
|
+
hash.key?(key),
|
27
|
+
message || "expected #{hash.inspect} to have key #{key.inspect}"
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def assert_v_object_equals(expected, actual, message = nil)
|
32
|
+
get_obj = lambda do |input|
|
33
|
+
input = input.read if input.respond_to?(:read)
|
34
|
+
|
35
|
+
input = Tilia::VObject::Reader.read(input) if input.is_a?(String)
|
36
|
+
|
37
|
+
unless input.is_a?(Tilia::VObject::Component)
|
38
|
+
raise ArgumentError, 'Input must be a string, stream or VObject component'
|
39
|
+
end
|
40
|
+
|
41
|
+
input.delete('PRODID')
|
42
|
+
if input.is_a?(Tilia::VObject::Component::VCalendar) && input['CALSCALE'].to_s == 'GREGORIAN'
|
43
|
+
input.delete('CALSCALE')
|
44
|
+
end
|
45
|
+
input
|
46
|
+
end
|
47
|
+
|
48
|
+
assert_equal(
|
49
|
+
get_obj.call(expected).serialize,
|
50
|
+
get_obj.call(actual).serialize,
|
51
|
+
message
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def compare_instances(a, b)
|
58
|
+
return true if b.__id__ == a.__id__
|
59
|
+
|
60
|
+
# check class
|
61
|
+
return false unless a.class == b.class
|
62
|
+
|
63
|
+
# Instance variables should be the same
|
64
|
+
return false unless a.instance_variables.sort == b.instance_variables.sort
|
65
|
+
|
66
|
+
# compare all instance variables
|
67
|
+
a.instance_variables.each do |var|
|
68
|
+
if a.instance_variable_get(var) == a
|
69
|
+
# Referencing self
|
70
|
+
return false unless b.instance_variable_get(var) == b
|
71
|
+
else
|
72
|
+
unless a.instance_variable_get(var) == b.instance_variable_get(var) ||
|
73
|
+
compare_instances(a.instance_variable_get(var), b.instance_variable_get(var))
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tilia
|
4
|
+
module Xml
|
5
|
+
module Deserializer
|
6
|
+
class EnumTest < Minitest::Test
|
7
|
+
def test_deserialize
|
8
|
+
service = Service.new
|
9
|
+
service.element_map['{urn:test}root'] = Deserializer.method(:enum)
|
10
|
+
|
11
|
+
xml = <<XML
|
12
|
+
<?xml version="1.0"?>
|
13
|
+
<root xmlns="urn:test">
|
14
|
+
<foo1/>
|
15
|
+
<foo2/>
|
16
|
+
</root>
|
17
|
+
XML
|
18
|
+
|
19
|
+
result = service.parse(xml)
|
20
|
+
|
21
|
+
expected = [
|
22
|
+
'{urn:test}foo1',
|
23
|
+
'{urn:test}foo2'
|
24
|
+
]
|
25
|
+
|
26
|
+
assert_equal(expected, result)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_deserialize_default_namespace
|
30
|
+
service = Service.new
|
31
|
+
service.element_map['{urn:test}root'] = lambda do |reader|
|
32
|
+
return Deserializer.enum(reader, 'urn:test')
|
33
|
+
end
|
34
|
+
|
35
|
+
xml = <<XML
|
36
|
+
<?xml version="1.0"?>
|
37
|
+
<root xmlns="urn:test">
|
38
|
+
<foo1/>
|
39
|
+
<foo2/>
|
40
|
+
</root>
|
41
|
+
XML
|
42
|
+
|
43
|
+
result = service.parse(xml)
|
44
|
+
|
45
|
+
expected = [
|
46
|
+
'foo1',
|
47
|
+
'foo2'
|
48
|
+
]
|
49
|
+
|
50
|
+
assert_equal(expected, result)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tilia
|
4
|
+
module Xml
|
5
|
+
module Deserializer
|
6
|
+
class KeyValueTest < Minitest::Test
|
7
|
+
def test_key_value
|
8
|
+
input = <<BLA
|
9
|
+
<?xml version="1.0"?>
|
10
|
+
<root xmlns="http://sabredav.org/ns">
|
11
|
+
<struct>
|
12
|
+
<elem1 />
|
13
|
+
<elem2>hi</elem2>
|
14
|
+
<elem3 xmlns="http://sabredav.org/another-ns">
|
15
|
+
<elem4>foo</elem4>
|
16
|
+
<elem5>foo & bar</elem5>
|
17
|
+
</elem3>
|
18
|
+
</struct>
|
19
|
+
</root>
|
20
|
+
BLA
|
21
|
+
|
22
|
+
reader = Reader.new
|
23
|
+
reader.element_map = {
|
24
|
+
'{http://sabredav.org/ns}struct' => lambda do |reader|
|
25
|
+
return Deserializer.key_value(reader, 'http://sabredav.org/ns')
|
26
|
+
end
|
27
|
+
}
|
28
|
+
reader.xml(input)
|
29
|
+
output = reader.parse
|
30
|
+
|
31
|
+
assert_equal(
|
32
|
+
{
|
33
|
+
'name' => '{http://sabredav.org/ns}root',
|
34
|
+
'value' => [
|
35
|
+
{
|
36
|
+
'name' => '{http://sabredav.org/ns}struct',
|
37
|
+
'value' => {
|
38
|
+
'elem1' => nil,
|
39
|
+
'elem2' => 'hi',
|
40
|
+
'{http://sabredav.org/another-ns}elem3' => [
|
41
|
+
{
|
42
|
+
'name' => '{http://sabredav.org/another-ns}elem4',
|
43
|
+
'value' => 'foo',
|
44
|
+
'attributes' => {}
|
45
|
+
},
|
46
|
+
{
|
47
|
+
'name' => '{http://sabredav.org/another-ns}elem5',
|
48
|
+
'value' => 'foo & bar',
|
49
|
+
'attributes' => {}
|
50
|
+
}
|
51
|
+
]
|
52
|
+
},
|
53
|
+
'attributes' => {}
|
54
|
+
}
|
55
|
+
],
|
56
|
+
'attributes' => {}
|
57
|
+
},
|
58
|
+
output
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tilia
|
4
|
+
module Xml
|
5
|
+
module Deserializer
|
6
|
+
class RepeatingElementsTest < Minitest::Test
|
7
|
+
def test_read
|
8
|
+
service = Service.new
|
9
|
+
service.element_map['{urn:test}collection'] = lambda do |reader|
|
10
|
+
return Deserializer.repeating_elements(reader, '{urn:test}item')
|
11
|
+
end
|
12
|
+
|
13
|
+
xml = <<XML
|
14
|
+
<?xml version="1.0"?>
|
15
|
+
<collection xmlns="urn:test">
|
16
|
+
<item>foo</item>
|
17
|
+
<item>bar</item>
|
18
|
+
</collection>
|
19
|
+
XML
|
20
|
+
|
21
|
+
result = service.parse(xml)
|
22
|
+
|
23
|
+
expected = [
|
24
|
+
'foo',
|
25
|
+
'bar'
|
26
|
+
]
|
27
|
+
|
28
|
+
assert_equal(expected, result)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
module Tilia
|
4
|
+
module Xml
|
5
|
+
module Deserializer
|
6
|
+
class ValueObjectTest < Minitest::Test
|
7
|
+
def test_deserialize_value_object
|
8
|
+
input = <<XML
|
9
|
+
<?xml version="1.0"?>
|
10
|
+
<foo xmlns="urn:foo">
|
11
|
+
<firstName>Harry</firstName>
|
12
|
+
<lastName>Turtle</lastName>
|
13
|
+
</foo>
|
14
|
+
XML
|
15
|
+
|
16
|
+
reader = Reader.new
|
17
|
+
reader.xml(input)
|
18
|
+
reader.element_map = {
|
19
|
+
'{urn:foo}foo' => lambda do |reader|
|
20
|
+
return Deserializer.value_object(reader, TestVo, 'urn:foo')
|
21
|
+
end
|
22
|
+
}
|
23
|
+
|
24
|
+
output = reader.parse
|
25
|
+
|
26
|
+
vo = TestVo.new
|
27
|
+
vo.first_name = 'Harry'
|
28
|
+
vo.last_name = 'Turtle'
|
29
|
+
|
30
|
+
expected = {
|
31
|
+
'name' => '{urn:foo}foo',
|
32
|
+
'value' => vo,
|
33
|
+
'attributes' => []
|
34
|
+
}
|
35
|
+
|
36
|
+
assert_instance_equal(
|
37
|
+
expected,
|
38
|
+
output
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_deserialize_value_object_ignored_element
|
43
|
+
input = <<XML
|
44
|
+
<?xml version="1.0"?>
|
45
|
+
<foo xmlns="urn:foo">
|
46
|
+
<firstName>Harry</firstName>
|
47
|
+
<lastName>Turtle</lastName>
|
48
|
+
<email>harry@example.org</email>
|
49
|
+
</foo>
|
50
|
+
XML
|
51
|
+
|
52
|
+
reader = Reader.new
|
53
|
+
reader.xml(input)
|
54
|
+
reader.element_map = {
|
55
|
+
'{urn:foo}foo' => lambda do |reader|
|
56
|
+
return Deserializer.value_object(reader, TestVo, 'urn:foo')
|
57
|
+
end
|
58
|
+
}
|
59
|
+
|
60
|
+
output = reader.parse
|
61
|
+
|
62
|
+
vo = TestVo.new
|
63
|
+
vo.first_name = 'Harry'
|
64
|
+
vo.last_name = 'Turtle'
|
65
|
+
|
66
|
+
expected = {
|
67
|
+
'name' => '{urn:foo}foo',
|
68
|
+
'value' => vo,
|
69
|
+
'attributes' => []
|
70
|
+
}
|
71
|
+
|
72
|
+
assert_instance_equal(
|
73
|
+
expected,
|
74
|
+
output
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_deserialize_value_object_auto_array
|
79
|
+
input = <<XML
|
80
|
+
<?xml version="1.0"?>
|
81
|
+
<foo xmlns="urn:foo">
|
82
|
+
<firstName>Harry</firstName>
|
83
|
+
<lastName>Turtle</lastName>
|
84
|
+
<link>http://example.org/</link>
|
85
|
+
<link>http://example.net/</link>
|
86
|
+
</foo>
|
87
|
+
XML
|
88
|
+
|
89
|
+
reader = Reader.new
|
90
|
+
reader.xml(input)
|
91
|
+
reader.element_map = {
|
92
|
+
'{urn:foo}foo' => lambda do |reader|
|
93
|
+
return Deserializer.value_object(reader, TestVo, 'urn:foo')
|
94
|
+
end
|
95
|
+
}
|
96
|
+
|
97
|
+
output = reader.parse
|
98
|
+
|
99
|
+
vo = TestVo.new
|
100
|
+
vo.first_name = 'Harry'
|
101
|
+
vo.last_name = 'Turtle'
|
102
|
+
vo.link = [
|
103
|
+
'http://example.org/',
|
104
|
+
'http://example.net/'
|
105
|
+
]
|
106
|
+
|
107
|
+
expected = {
|
108
|
+
'name' => '{urn:foo}foo',
|
109
|
+
'value' => vo,
|
110
|
+
'attributes' => []
|
111
|
+
}
|
112
|
+
|
113
|
+
assert_instance_equal(
|
114
|
+
expected,
|
115
|
+
output
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
def test_deserialize_value_object_empty
|
120
|
+
input = <<XML
|
121
|
+
<?xml version="1.0"?>
|
122
|
+
<foo xmlns="urn:foo" />
|
123
|
+
XML
|
124
|
+
|
125
|
+
reader = Reader.new
|
126
|
+
reader.xml(input)
|
127
|
+
reader.element_map = {
|
128
|
+
'{urn:foo}foo' => lambda do |reader|
|
129
|
+
return Deserializer.value_object(reader, TestVo, 'urn:foo')
|
130
|
+
end
|
131
|
+
}
|
132
|
+
|
133
|
+
output = reader.parse
|
134
|
+
|
135
|
+
vo = TestVo.new
|
136
|
+
|
137
|
+
expected = {
|
138
|
+
'name' => '{urn:foo}foo',
|
139
|
+
'value' => vo,
|
140
|
+
'attributes' => []
|
141
|
+
}
|
142
|
+
|
143
|
+
assert_instance_equal(
|
144
|
+
expected,
|
145
|
+
output
|
146
|
+
)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
class TestVo
|
151
|
+
attr_accessor :first_name
|
152
|
+
attr_accessor :last_name
|
153
|
+
attr_accessor :link
|
154
|
+
|
155
|
+
def initialize
|
156
|
+
@first_name = nil
|
157
|
+
@last_name = nil
|
158
|
+
@link = []
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|