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 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