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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc343c81cf2926d463564160710e7170958bd9b6
|
4
|
+
data.tar.gz: 038f07bad13be27bb76899e61999a6a4abb00a62
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be1fb4bb139f40a59b54fbc2206bb02154b880206be9ab48a0ccf70523782872a56d6eaac128897fec723aee2b0a80a11b933747ebb553d2ec6c582f666c0d36
|
7
|
+
data.tar.gz: c487226c4d30694e94e2a8335b5172c9433ef64dbb18fe688851a07385ae515d04db5ef5ff6ad388a73f8f6a94c1351f70d188b77db5e9d28a50673a883a33e4
|
data/.simplecov
CHANGED
data/CHANGELOG.sabre.md
CHANGED
@@ -1,6 +1,27 @@
|
|
1
1
|
ChangeLog
|
2
2
|
=========
|
3
3
|
|
4
|
+
1.3.0 (2015-12-29)
|
5
|
+
------------------
|
6
|
+
|
7
|
+
* The `Service` class adds a new `mapValueObject` method which provides basic
|
8
|
+
capabilities to map between ValueObjects and XML.
|
9
|
+
* #61: You can now specify serializers for specific classes, allowing you
|
10
|
+
separate the object you want to serialize from the serializer. This uses the
|
11
|
+
`$classMap` property which is defined on both the `Service` and `Writer`.
|
12
|
+
* It's now possible to pass an array of possible root elements to
|
13
|
+
`Sabre\Xml\Service::expect()`.
|
14
|
+
* Moved some parsing logic to `Reader::getDeserializerForElementName()`,
|
15
|
+
so people with more advanced use-cases can implement their own logic there.
|
16
|
+
* #63: When serializing elements using arrays, the `value` key in the array is
|
17
|
+
now optional.
|
18
|
+
* #62: Added a `keyValue` deserializer function. This can be used instead of
|
19
|
+
the `Element\KeyValue` class and is a lot more flexible. (@staabm)
|
20
|
+
* Also added an `enum` deserializer function to replace
|
21
|
+
`Element\Elements`.
|
22
|
+
* Using an empty string for a namespace prefix now has the same effect as
|
23
|
+
`null`.
|
24
|
+
|
4
25
|
1.2.0 (2015-08-30)
|
5
26
|
------------------
|
6
27
|
|
data/Gemfile
CHANGED
@@ -1,15 +1,10 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
|
4
|
-
gem 'tilia-uri', '~> 1.0'
|
5
|
-
|
6
|
-
# External dependencies
|
7
|
-
gem 'activesupport', '~> 4.2'
|
8
|
-
gem 'libxml-ruby', '~> 2.8'
|
3
|
+
gemspec
|
9
4
|
|
10
5
|
# Testing
|
11
6
|
gem 'rake'
|
12
7
|
gem 'minitest', '~> 5.8'
|
13
8
|
gem 'simplecov', '~> 0.10'
|
14
|
-
gem 'rubocop', '~> 0.
|
9
|
+
gem 'rubocop', '~> 0.36'
|
15
10
|
gem 'yard', '~> 0.8'
|
data/Gemfile.lock
CHANGED
@@ -1,31 +1,37 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tilia-xml (1.3.0)
|
5
|
+
activesupport (>= 4.0)
|
6
|
+
libxml-ruby (>= 2.8)
|
7
|
+
tilia-uri (~> 1.0, >= 1.0.1.1)
|
8
|
+
|
1
9
|
GEM
|
2
10
|
remote: https://rubygems.org/
|
3
11
|
specs:
|
4
|
-
activesupport (4.2.
|
12
|
+
activesupport (4.2.5.1)
|
5
13
|
i18n (~> 0.7)
|
6
14
|
json (~> 1.7, >= 1.7.7)
|
7
15
|
minitest (~> 5.1)
|
8
16
|
thread_safe (~> 0.3, >= 0.3.4)
|
9
17
|
tzinfo (~> 1.1)
|
10
|
-
ast (2.
|
11
|
-
astrolabe (1.3.1)
|
12
|
-
parser (~> 2.2)
|
18
|
+
ast (2.2.0)
|
13
19
|
docile (1.1.5)
|
14
20
|
i18n (0.7.0)
|
15
21
|
json (1.8.3)
|
16
22
|
libxml-ruby (2.8.0)
|
17
23
|
minitest (5.8.1)
|
18
|
-
parser (2.
|
19
|
-
ast (
|
24
|
+
parser (2.3.0.2)
|
25
|
+
ast (~> 2.2)
|
20
26
|
powerpack (0.1.1)
|
21
|
-
rainbow (2.
|
27
|
+
rainbow (2.1.0)
|
22
28
|
rake (10.4.2)
|
23
|
-
rubocop (0.
|
24
|
-
|
25
|
-
parser (>= 2.2.2.5, < 3.0)
|
29
|
+
rubocop (0.37.0)
|
30
|
+
parser (>= 2.3.0.2, < 3.0)
|
26
31
|
powerpack (~> 0.1)
|
27
32
|
rainbow (>= 1.99.1, < 3.0)
|
28
|
-
ruby-progressbar (~> 1.
|
33
|
+
ruby-progressbar (~> 1.7)
|
34
|
+
unicode-display_width (~> 0.3)
|
29
35
|
ruby-progressbar (1.7.5)
|
30
36
|
simplecov (0.10.0)
|
31
37
|
docile (~> 1.1.0)
|
@@ -33,23 +39,22 @@ GEM
|
|
33
39
|
simplecov-html (~> 0.10.0)
|
34
40
|
simplecov-html (0.10.0)
|
35
41
|
thread_safe (0.3.5)
|
36
|
-
tilia-uri (1.0.1)
|
37
|
-
activesupport (
|
42
|
+
tilia-uri (1.0.1.1)
|
43
|
+
activesupport (>= 4.0)
|
38
44
|
tzinfo (1.2.2)
|
39
45
|
thread_safe (~> 0.1)
|
46
|
+
unicode-display_width (0.3.1)
|
40
47
|
yard (0.8.7.6)
|
41
48
|
|
42
49
|
PLATFORMS
|
43
50
|
ruby
|
44
51
|
|
45
52
|
DEPENDENCIES
|
46
|
-
activesupport (~> 4.2)
|
47
|
-
libxml-ruby (~> 2.8)
|
48
53
|
minitest (~> 5.8)
|
49
54
|
rake
|
50
|
-
rubocop (~> 0.
|
55
|
+
rubocop (~> 0.36)
|
51
56
|
simplecov (~> 0.10)
|
52
|
-
tilia-
|
57
|
+
tilia-xml!
|
53
58
|
yard (~> 0.8)
|
54
59
|
|
55
60
|
BUNDLED WITH
|
data/LICENSE
CHANGED
data/LICENSE.sabre
CHANGED
data/lib/tilia/xml.rb
CHANGED
@@ -12,15 +12,18 @@ module Tilia
|
|
12
12
|
require 'tilia/xml/xml_deserializable'
|
13
13
|
require 'tilia/xml/xml_serializable'
|
14
14
|
require 'tilia/xml/context_stack_trait'
|
15
|
+
require 'tilia/xml/deserializer'
|
15
16
|
require 'tilia/xml/element'
|
16
17
|
require 'tilia/xml/parse_exception'
|
17
18
|
require 'tilia/xml/lib_xml_exception'
|
18
19
|
require 'tilia/xml/reader'
|
20
|
+
require 'tilia/xml/serializer'
|
19
21
|
require 'tilia/xml/service'
|
20
22
|
require 'tilia/xml/version'
|
21
23
|
require 'tilia/xml/writer'
|
22
24
|
end
|
23
25
|
|
26
|
+
# Container to avoid pass-by-reference quirks
|
24
27
|
class Box
|
25
28
|
attr_accessor :value
|
26
29
|
|
@@ -42,14 +42,23 @@ module Tilia
|
|
42
42
|
# @return [Hash]
|
43
43
|
attr_accessor :namespace_map
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
# Backups of previous contexts.
|
45
|
+
# This is a list of custom serializers for specific classes.
|
48
46
|
#
|
49
|
-
#
|
50
|
-
|
51
|
-
|
52
|
-
|
47
|
+
# The writer may use this if you attempt to serialize an object with a
|
48
|
+
# class that does not implement XmlSerializable.
|
49
|
+
#
|
50
|
+
# Instead it will look at this classmap to see if there is a custom
|
51
|
+
# serializer here. This is useful if you don't want your value objects
|
52
|
+
# to be responsible for serializing themselves.
|
53
|
+
#
|
54
|
+
# The keys in this classmap need to be fully qualified PHP class names,
|
55
|
+
# the values must be callbacks. The callbacks take two arguments. The
|
56
|
+
# writer class, and the value that must be written.
|
57
|
+
#
|
58
|
+
# function (Writer writer, object value)
|
59
|
+
#
|
60
|
+
# @return [Hash]
|
61
|
+
attr_accessor :class_map
|
53
62
|
|
54
63
|
# Create a new "context".
|
55
64
|
#
|
@@ -59,20 +68,11 @@ module Tilia
|
|
59
68
|
#
|
60
69
|
# @return [void]
|
61
70
|
def push_context
|
62
|
-
dup_or_obj = lambda do |obj|
|
63
|
-
if obj.nil?
|
64
|
-
nil
|
65
|
-
elsif obj.is_a? Fixnum
|
66
|
-
obj
|
67
|
-
else
|
68
|
-
obj.dup
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
71
|
@context_stack << [
|
73
|
-
|
74
|
-
|
75
|
-
|
72
|
+
@element_map.deep_dup,
|
73
|
+
@context_uri.deep_dup,
|
74
|
+
@namespace_map.deep_dup,
|
75
|
+
@class_map.deep_dup
|
76
76
|
]
|
77
77
|
end
|
78
78
|
|
@@ -83,16 +83,20 @@ module Tilia
|
|
83
83
|
(
|
84
84
|
@element_map,
|
85
85
|
@context_uri,
|
86
|
-
@namespace_map
|
86
|
+
@namespace_map,
|
87
|
+
@class_map
|
87
88
|
) = @context_stack.pop
|
88
89
|
end
|
89
90
|
|
90
|
-
#
|
91
|
-
def
|
91
|
+
# Initializes instance variables
|
92
|
+
def initialize(*args)
|
92
93
|
@element_map = {}
|
93
94
|
@namespace_map = {}
|
94
95
|
@context_uri = ''
|
95
96
|
@context_stack = []
|
97
|
+
@class_map = {}
|
98
|
+
|
99
|
+
super
|
96
100
|
end
|
97
101
|
end
|
98
102
|
end
|
@@ -0,0 +1,241 @@
|
|
1
|
+
module Tilia
|
2
|
+
module Xml
|
3
|
+
# This class provides a number of 'deserializer' helper functions.
|
4
|
+
# These can be used to easily specify custom deserializers for specific
|
5
|
+
# XML elements.
|
6
|
+
#
|
7
|
+
# You can either use these functions from within the element_map in the
|
8
|
+
# Service or Reader class, or you can call them from within your own
|
9
|
+
# deserializer functions.
|
10
|
+
module Deserializer
|
11
|
+
# The 'keyValue' deserializer parses all child elements, and outputs them as
|
12
|
+
# a "key=>value" array.
|
13
|
+
#
|
14
|
+
# For example, keyvalue will parse:
|
15
|
+
#
|
16
|
+
# <?xml version="1.0"?>
|
17
|
+
# <s:root xmlns:s="http://sabredav.org/ns">
|
18
|
+
# <s:elem1>value1</s:elem1>
|
19
|
+
# <s:elem2>value2</s:elem2>
|
20
|
+
# <s:elem3 />
|
21
|
+
# </s:root>
|
22
|
+
#
|
23
|
+
# Into:
|
24
|
+
#
|
25
|
+
# [
|
26
|
+
# "{http://sabredav.org/ns}elem1" => "value1",
|
27
|
+
# "{http://sabredav.org/ns}elem2" => "value2",
|
28
|
+
# "{http://sabredav.org/ns}elem3" => null,
|
29
|
+
# ]
|
30
|
+
#
|
31
|
+
# If you specify the 'namespace' argument, the deserializer will remove
|
32
|
+
# the namespaces of the keys that match that namespace.
|
33
|
+
#
|
34
|
+
# For example, if you call keyValue like this:
|
35
|
+
#
|
36
|
+
# key_value(reader, 'http://sabredav.org/ns')
|
37
|
+
#
|
38
|
+
# it's output will instead be:
|
39
|
+
#
|
40
|
+
# [
|
41
|
+
# "elem1" => "value1",
|
42
|
+
# "elem2" => "value2",
|
43
|
+
# "elem3" => null,
|
44
|
+
# ]
|
45
|
+
#
|
46
|
+
# Attributes will be removed from the top-level elements. If elements with
|
47
|
+
# the same name appear twice in the list, only the last one will be kept.
|
48
|
+
#
|
49
|
+
#
|
50
|
+
# @param [Reader] reader
|
51
|
+
# @param [String, nil] namespace
|
52
|
+
# @return [Hash]
|
53
|
+
def self.key_value(reader, namespace = nil)
|
54
|
+
# If there's no children, we don't do anything.
|
55
|
+
if reader.empty_element?
|
56
|
+
reader.next
|
57
|
+
return {}
|
58
|
+
end
|
59
|
+
|
60
|
+
values = {}
|
61
|
+
|
62
|
+
reader.read
|
63
|
+
loop do
|
64
|
+
if reader.node_type == ::LibXML::XML::Reader::TYPE_ELEMENT
|
65
|
+
if namespace && reader.namespace_uri == namespace
|
66
|
+
values[reader.local_name] = reader.parse_current_element['value']
|
67
|
+
else
|
68
|
+
clark = reader.clark
|
69
|
+
values[clark] = reader.parse_current_element['value']
|
70
|
+
end
|
71
|
+
else
|
72
|
+
reader.read
|
73
|
+
end
|
74
|
+
|
75
|
+
break if reader.node_type == ::LibXML::XML::Reader::TYPE_END_ELEMENT
|
76
|
+
end
|
77
|
+
|
78
|
+
reader.read
|
79
|
+
|
80
|
+
values
|
81
|
+
end
|
82
|
+
|
83
|
+
# The 'enum' deserializer parses elements into a simple list
|
84
|
+
# without values or attributes.
|
85
|
+
#
|
86
|
+
# For example, Elements will parse:
|
87
|
+
#
|
88
|
+
# <?xml version="1.0"? >
|
89
|
+
# <s:root xmlns:s="http://sabredav.org/ns">
|
90
|
+
# <s:elem1 />
|
91
|
+
# <s:elem2 />
|
92
|
+
# <s:elem3 />
|
93
|
+
# <s:elem4>content</s:elem4>
|
94
|
+
# <s:elem5 attr="val" />
|
95
|
+
# </s:root>
|
96
|
+
#
|
97
|
+
# Into:
|
98
|
+
#
|
99
|
+
# [
|
100
|
+
# "{http://sabredav.org/ns}elem1",
|
101
|
+
# "{http://sabredav.org/ns}elem2",
|
102
|
+
# "{http://sabredav.org/ns}elem3",
|
103
|
+
# "{http://sabredav.org/ns}elem4",
|
104
|
+
# "{http://sabredav.org/ns}elem5",
|
105
|
+
# ]
|
106
|
+
#
|
107
|
+
# This is useful for 'enum'-like structures.
|
108
|
+
#
|
109
|
+
# If the namespace argument is specified, it will strip the namespace
|
110
|
+
# for all elements that match that.
|
111
|
+
#
|
112
|
+
# For example,
|
113
|
+
#
|
114
|
+
# enum(reader, 'http://sabredav.org/ns')
|
115
|
+
#
|
116
|
+
# would return:
|
117
|
+
#
|
118
|
+
# [
|
119
|
+
# "elem1",
|
120
|
+
# "elem2",
|
121
|
+
# "elem3",
|
122
|
+
# "elem4",
|
123
|
+
# "elem5",
|
124
|
+
# ]
|
125
|
+
#
|
126
|
+
# @param [Reader] reader
|
127
|
+
# @param [String, nil] namespace
|
128
|
+
# @return [Array<String>]
|
129
|
+
def self.enum(reader, namespace = nil)
|
130
|
+
# If there's no children, we don't do anything.
|
131
|
+
if reader.empty_element?
|
132
|
+
reader.next
|
133
|
+
return []
|
134
|
+
end
|
135
|
+
|
136
|
+
reader.read
|
137
|
+
current_depth = reader.depth
|
138
|
+
|
139
|
+
values = []
|
140
|
+
loop do
|
141
|
+
unless reader.node_type == ::LibXML::XML::Reader::TYPE_ELEMENT
|
142
|
+
break unless reader.depth >= current_depth && reader.next
|
143
|
+
next
|
144
|
+
end
|
145
|
+
|
146
|
+
values << if namespace && namespace == reader.namespace_uri
|
147
|
+
reader.local_name
|
148
|
+
else
|
149
|
+
reader.clark
|
150
|
+
end
|
151
|
+
|
152
|
+
break unless reader.depth >= current_depth && reader.next
|
153
|
+
end
|
154
|
+
|
155
|
+
reader.next
|
156
|
+
values
|
157
|
+
end
|
158
|
+
|
159
|
+
# The valueObject deserializer turns an xml element into a PHP object of
|
160
|
+
# a specific class.
|
161
|
+
#
|
162
|
+
# This is primarily used by the mapValueObject function from the Service
|
163
|
+
# class, but it can also easily be used for more specific situations.
|
164
|
+
#
|
165
|
+
# @param [Reader] reader
|
166
|
+
# @param [Class] klass
|
167
|
+
# @param [String] namespace
|
168
|
+
# @return object
|
169
|
+
def self.value_object(reader, klass, namespace)
|
170
|
+
value_object = klass.new
|
171
|
+
|
172
|
+
if reader.empty_element?
|
173
|
+
reader.next
|
174
|
+
return {}
|
175
|
+
end
|
176
|
+
|
177
|
+
default_properties = {}
|
178
|
+
value_object.instance_variables.each do |name|
|
179
|
+
default_properties[name] = value_object.instance_variable_get(name)
|
180
|
+
end
|
181
|
+
|
182
|
+
reader.read
|
183
|
+
loop do
|
184
|
+
if reader.node_type == ::LibXML::XML::Reader::TYPE_ELEMENT && reader.namespace_uri == namespace
|
185
|
+
var_name = "@#{reader.local_name}".to_sym
|
186
|
+
if default_properties.key?(var_name)
|
187
|
+
if default_properties[var_name].is_a?(Array)
|
188
|
+
value_object.instance_variable_get(var_name) << reader.parse_current_element['value']
|
189
|
+
else
|
190
|
+
value_object.instance_variable_set(var_name, reader.parse_current_element['value'])
|
191
|
+
end
|
192
|
+
else
|
193
|
+
# Ignore property
|
194
|
+
reader.next
|
195
|
+
end
|
196
|
+
else
|
197
|
+
reader.read
|
198
|
+
end
|
199
|
+
|
200
|
+
break unless reader.node_type != ::LibXML::XML::Reader::TYPE_END_ELEMENT
|
201
|
+
end
|
202
|
+
|
203
|
+
reader.read
|
204
|
+
value_object
|
205
|
+
end
|
206
|
+
|
207
|
+
# This deserializer helps you deserialize xml structures that look like
|
208
|
+
# this:
|
209
|
+
#
|
210
|
+
# <collection>
|
211
|
+
# <item>...</item>
|
212
|
+
# <item>...</item>
|
213
|
+
# <item>...</item>
|
214
|
+
# </collection>
|
215
|
+
#
|
216
|
+
# Many XML documents use patterns like that, and this deserializer
|
217
|
+
# allow you to get all the 'items' as an array.
|
218
|
+
#
|
219
|
+
# In that previous example, you would register the deserializer as such:
|
220
|
+
#
|
221
|
+
# reader.element_map['{}collection'] = function(reader) {
|
222
|
+
# return repeating_elements(reader, '{}item')
|
223
|
+
# }
|
224
|
+
#
|
225
|
+
# The repeatingElements deserializer simply returns everything as an array.
|
226
|
+
#
|
227
|
+
# @param [Reader] reader
|
228
|
+
# @param [String] child_element_name Element name in clark-notation
|
229
|
+
# @return Array
|
230
|
+
def self.repeating_elements(reader, child_element_name)
|
231
|
+
result = []
|
232
|
+
|
233
|
+
reader.parse_get_elements.each do |element|
|
234
|
+
result << element['value'] if element['name'] == child_element_name
|
235
|
+
end
|
236
|
+
|
237
|
+
result
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|