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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 28be5b537419a6fafec89b07fefe33804b849b9e
4
- data.tar.gz: e4add19ea231c5f6ba8940249797be097803dc1d
3
+ metadata.gz: cc343c81cf2926d463564160710e7170958bd9b6
4
+ data.tar.gz: 038f07bad13be27bb76899e61999a6a4abb00a62
5
5
  SHA512:
6
- metadata.gz: 1b5dea05efe58faa1c195df67b00cf19fa186da0edde4db221d4ea7da6c08ad2be921af7f27e65cd74b80914917eab29750c9cf131e5f4b153f22fb99a54c065
7
- data.tar.gz: 284d2411eee690d48bfa30790a116e08bf9db3f7de7f430388cdbb25b597fbc18a19af31ef486509f87e0d6d959bc738b265901bb93a677d9d834411ba1341a6
6
+ metadata.gz: be1fb4bb139f40a59b54fbc2206bb02154b880206be9ab48a0ccf70523782872a56d6eaac128897fec723aee2b0a80a11b933747ebb553d2ec6c582f666c0d36
7
+ data.tar.gz: c487226c4d30694e94e2a8335b5172c9433ef64dbb18fe688851a07385ae515d04db5ef5ff6ad388a73f8f6a94c1351f70d188b77db5e9d28a50673a883a33e4
data/.simplecov CHANGED
@@ -1,4 +1,3 @@
1
1
  SimpleCov.start do
2
- add_filter "/spec/"
2
+ add_filter "/test/"
3
3
  end
4
-
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
- # Internal dependencies
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.34'
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.4)
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.1.0)
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.2.3.0)
19
- ast (>= 1.1, < 3.0)
24
+ parser (2.3.0.2)
25
+ ast (~> 2.2)
20
26
  powerpack (0.1.1)
21
- rainbow (2.0.0)
27
+ rainbow (2.1.0)
22
28
  rake (10.4.2)
23
- rubocop (0.34.2)
24
- astrolabe (~> 1.3)
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.4)
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 (~> 4.2)
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.34)
55
+ rubocop (~> 0.36)
51
56
  simplecov (~> 0.10)
52
- tilia-uri (~> 1.0)
57
+ tilia-xml!
53
58
  yard (~> 0.8)
54
59
 
55
60
  BUNDLED WITH
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (C) 2015 Jakob Sack (tilia@jakobsack.de)
1
+ Copyright (C) 2015-2016 Jakob Sack (tilia@jakobsack.de)
2
2
 
3
3
  All rights reserved.
4
4
 
data/LICENSE.sabre CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (C) 2009-2015 fruux GmbH (https://fruux.com/)
1
+ Copyright (C) 2009-2016 fruux GmbH (https://fruux.com/)
2
2
 
3
3
  All rights reserved.
4
4
 
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
- protected
46
-
47
- # Backups of previous contexts.
45
+ # This is a list of custom serializers for specific classes.
48
46
  #
49
- # @return [Array]
50
- attr_accessor :context_stack
51
-
52
- public
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
- dup_or_obj.call(@element_map),
74
- dup_or_obj.call(@context_uri),
75
- dup_or_obj.call(@namespace_map)
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
- # TODO: document
91
- def initialize_context_stack_attributes
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