tilia-xml 1.2.0.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|