xml-mapping_extensions 0.3.5 → 0.3.6

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: 1f632745c95852e892e50841d1dae290012d7941
4
- data.tar.gz: 308acdeaa42233881b291b2adce912641e50a993
3
+ metadata.gz: e6415479b9ea2414249436f8273893c81b8bfbdb
4
+ data.tar.gz: 3ada581c17b547c1961e48dd72003a4bcba3c03a
5
5
  SHA512:
6
- metadata.gz: 3280dae9565abd98b0a8800ae66d217097ded31f275dde511b5c652760ad54f27462940b92b7ab539cb1550755af34773879e0fe10b38c866d97e0d290ddb140
7
- data.tar.gz: 0885775aa138a6dacf348f572f9843a97994c036abeab239a36d75816ef19719af29803e54ff57889078d752f47dce67fbeb590e0c40e8c700ea1e14d858794e
6
+ metadata.gz: fb643f42be329656e36d04acf0f759fbc0edcc8636147c2164182f8a7182ef6415b43fcab66331a7b88ff8b00e696155632a6b2fb3e4e133fb84e211e0c63f21
7
+ data.tar.gz: 54ab830f4b26b5b56d6d7afe3e5ca8b8d4f8b5feec004ea89b84bb0aa609b5c801a1fcd67d37601dff0fd94fba99cac8dcf5d6b07d44c0930607ab4bf867d4bd
data/CHANGES.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## 0.3.6 (2 May 2016)
2
+
3
+ - Added `XML::MappingExtensions::Namespace`, with corresponding extension module
4
+ `XML::MappingExtensions::NamespacedElement`, to support more or less proper XML namespace
5
+ URLs and prefixing
6
+
1
7
  ## 0.3.5 (28 April 2016)
2
8
 
3
9
  - `DateNode`: Fix issues with `Date.xmlschema` misbehaving in a Rails / ActiveSupport environment
data/README.md CHANGED
@@ -8,6 +8,19 @@
8
8
  Additional mapping nodes and other utility code for working with
9
9
  [XML::Mapping](http://multi-io.github.io/xml-mapping/).
10
10
 
11
+ ### Table of contents
12
+
13
+ - [Extension methods](#extension-methods)
14
+ - [Custom nodes](#custom-nodes)
15
+ - [Abstract nodes](#abstract-nodes)
16
+ - [Example](#example)
17
+ - [Provided implementations](#provided-implementations)
18
+ - [Example](#example-1)
19
+ - [Reading XML:](#reading-xml)
20
+ - [Writing XML:](#writing-xml)
21
+ - [Namespaces](#namespaces)
22
+
23
+
11
24
  ## Extension methods
12
25
 
13
26
  This gem adds two methods, `write_xml` and `parse_xml`, to XML mapping instances and classes respectively, to reduce
@@ -153,12 +166,7 @@ elem.time = Time.utc(2000, 1, 1, 2, 34, 56)
153
166
  elem.uri = URI('http://example.org')
154
167
  elem.mime_type = MIME::Types['text/plain'].first
155
168
 
156
- xml = elem.save_to_xml
157
-
158
- formatter = REXML::Formatters::Pretty.new
159
- formatter.compact = true
160
-
161
- puts(formatter.write(xml, ""))
169
+ puts(elem.write_xml)
162
170
  ```
163
171
 
164
172
  Outputs:
@@ -172,3 +180,52 @@ Outputs:
172
180
  <mime_type>text/plain</mime_type>
173
181
  </my_elem>
174
182
  ```
183
+
184
+ ## Namespaces
185
+
186
+ The `Namespace` class encapsulates an XML namespace:
187
+
188
+ ```ruby
189
+ namespace = Namespace.new(uri: 'http://example.org/px', schema_location: 'http://example.org/px.xsd')
190
+ ```
191
+
192
+ Setting a namespace on a mapped object will cause that namespace to be written out when the object is saved
193
+ to XML:
194
+
195
+ ```ruby
196
+ obj = MyElem.new(...)
197
+ obj.namespace = namespace
198
+
199
+ puts obj.write_xml
200
+ ```
201
+
202
+ Outputs:
203
+
204
+ ```xml
205
+ <element xmlns='http://example.org/px/' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://example.org/px.xsd' attribute='123'>
206
+ element text
207
+ <child>child 1</child>
208
+ <child>child 2</child>
209
+ </element>
210
+ ```
211
+
212
+ Setting a `prefix` attribute on the namespace will set the prefix on each element in the output:
213
+
214
+ ```ruby
215
+ namespace = Namespace.new(prefix: 'px', uri: 'http://example.org/px', schema_location: 'http://example.org/px.xsd')
216
+
217
+ obj = MyElem.new(...)
218
+ obj.namespace = namespace
219
+
220
+ puts obj.write_xml
221
+ ```
222
+
223
+ Outputs:
224
+
225
+ ```xml
226
+ <px:element xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='http://example.org/px.xsd' xmlns:px='http://example.org/px/' attribute='123'>
227
+ element text
228
+ <px:child>child 1</px:child>
229
+ <px:child>child 2</px:child>
230
+ </px:element>
231
+ ```
@@ -0,0 +1,85 @@
1
+ require 'uri'
2
+
3
+ module XML
4
+ module MappingExtensions
5
+
6
+ # Encapsulates an XML namespace with a URI, schema location, and optional prefix.
7
+ class Namespace
8
+
9
+ # @return [String] the string form of the namespace URI
10
+ attr_reader :uri
11
+
12
+ # @return [String, nil] the namespace prefix
13
+ attr_reader :prefix
14
+
15
+ # @return [String] the schema location URI(s), as a space-separated string list
16
+ attr_reader :schema_location
17
+
18
+ # Creates a new {Namespace}
19
+ # @param uri [URI, String] the namespace URI
20
+ # @param prefix [String, nil] the namespace prefix
21
+ # @param schema_location [String] the schema location(s)
22
+ def initialize(uri:, prefix: nil, schema_location:)
23
+ @uri = uri.to_s
24
+ @prefix = prefix
25
+ @schema_location = schema_location.to_s
26
+ end
27
+
28
+ # Sets `uri` as the default (no-prefix) namespace on `elem`, with
29
+ # `schema_location` as the schema location.
30
+ # @param elem [REXML::Element] The element to set the namespace on
31
+ def set_default_namespace(elem) # rubocop:disable Style/AccessorMethodName
32
+ elem.add_namespace(uri)
33
+ elem.add_namespace('xsi', 'http://www.w3.org/2001/XMLSchema-instance')
34
+ elem.add_attribute('xsi:schemaLocation', schema_location)
35
+ end
36
+
37
+ # Sets `prefix` as the prefix for namespace `uri` on the specified document
38
+ # root element, and all its descendants that have that namespace.
39
+ # @param root [REXML::Element] The document root to set the namespace on
40
+ def set_prefix(root) # rubocop:disable Style/AccessorMethodName
41
+ return unless prefix
42
+ set_prefix_recursive(root)
43
+ root.add_namespace(nil) if root.attributes['xmlns'] == uri # clear the no-prefix namespace
44
+ root.add_namespace(prefix, uri)
45
+ end
46
+
47
+ private
48
+
49
+ def set_prefix_recursive(elem) # rubocop:disable Style/AccessorMethodName
50
+ return unless elem.namespace == uri
51
+ # name= with a prefixed name sets namespace by side effect and is the only way to actually output the prefix
52
+ elem.name = "#{prefix}:#{elem.name}"
53
+ elem.each_element { |e| set_prefix_recursive(e) }
54
+ end
55
+ end
56
+ end
57
+
58
+ # Patches `XML::Mapping` to add a namespace attribute and write the namespace
59
+ # out when saving to XML.
60
+ module NamespacedElement
61
+
62
+ # @return [Namespace, nil] the namespace, if any
63
+ attr_accessor :namespace
64
+
65
+ # Overrides `XML::Mapping#pre_save` to set the XML namespace and schema location
66
+ # on the generated element.
67
+ def pre_save(options = { mapping: :_default })
68
+ xml = super(options)
69
+ namespace.set_default_namespace(xml) if namespace
70
+ xml
71
+ end
72
+
73
+ # Overrides `XML::Mapping#save_to_xml` to set the XML namespace prefix on
74
+ # the generated element, and all its descendants that have that namespace.
75
+ def save_to_xml(options = { mapping: :_default })
76
+ xml = super(options)
77
+ namespace.set_prefix(xml) if namespace
78
+ xml
79
+ end
80
+ end
81
+
82
+ module Mapping
83
+ prepend NamespacedElement
84
+ end
85
+ end
@@ -1,6 +1,6 @@
1
1
  module XML
2
2
  module MappingExtensions
3
3
  # The version of this gem
4
- VERSION = '0.3.5'
4
+ VERSION = '0.3.6'
5
5
  end
6
6
  end
@@ -3,7 +3,7 @@ require 'tempfile'
3
3
 
4
4
  module XML
5
5
  module Mapping
6
- class ParseXMLSpecElement
6
+ class MXSpecObject
7
7
  include ::XML::Mapping
8
8
  include Comparable
9
9
 
@@ -29,17 +29,55 @@ module XML
29
29
 
30
30
  describe '#write_xml' do
31
31
  it 'writes an XML string' do
32
- elem = ParseXMLSpecElement.new
33
- elem.attribute = 123
34
- elem.text = 'element text'
35
- elem.children = ['child 1', 'child 2']
36
- expected_xml = elem.save_to_xml
37
- xml_string = elem.write_xml
32
+ obj = MXSpecObject.new
33
+ obj.attribute = 123
34
+ obj.text = 'element text'
35
+ obj.children = ['child 1', 'child 2']
36
+ expected_xml = obj.save_to_xml
37
+ xml_string = obj.write_xml
38
38
  expect(xml_string).to be_a(String)
39
39
  expect(xml_string).to be_xml(expected_xml)
40
40
  end
41
41
  end
42
42
 
43
+ describe ':namespace' do
44
+ it 'sets the namespace on save' do
45
+ uri = 'http://example.org/px/'
46
+ schema_location = 'http://example.org/px.xsd'
47
+ namespace = MappingExtensions::Namespace.new(uri: uri, prefix: 'px', schema_location: schema_location)
48
+
49
+ obj = MXSpecObject.new
50
+ obj.attribute = 123
51
+ obj.text = 'element text'
52
+ obj.children = ['child 1', 'child 2']
53
+ obj.namespace = namespace
54
+
55
+ namespace_attribs = "xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='#{schema_location}' xmlns:px='#{uri}'"
56
+ expected = "<px:element #{namespace_attribs} attribute='123'>element text<px:child>child 1</px:child><px:child>child 2</px:child></px:element>"
57
+
58
+ expect(obj.save_to_xml).to be_xml(expected)
59
+ puts(obj.write_xml)
60
+ end
61
+
62
+ it 'works without prefixes' do
63
+ uri = 'http://example.org/px/'
64
+ schema_location = 'http://example.org/px.xsd'
65
+ namespace = MappingExtensions::Namespace.new(uri: uri, schema_location: schema_location)
66
+
67
+ obj = MXSpecObject.new
68
+ obj.attribute = 123
69
+ obj.text = 'element text'
70
+ obj.children = ['child 1', 'child 2']
71
+ obj.namespace = namespace
72
+
73
+ namespace_attribs = "xmlns='#{uri}' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xsi:schemaLocation='#{schema_location}'"
74
+ expected = "<element #{namespace_attribs} attribute='123'>element text<child>child 1</child><child>child 2</child></element>"
75
+
76
+ expect(obj.save_to_xml).to be_xml(expected)
77
+ puts(obj.write_xml)
78
+ end
79
+ end
80
+
43
81
  module ClassMethods
44
82
  describe '#parse_xml' do
45
83
 
@@ -51,28 +89,28 @@ module XML
51
89
  </element>'
52
90
  @xml_document = REXML::Document.new(@xml_string)
53
91
  @xml_element = @xml_document.root
54
- @expected_element = ParseXMLSpecElement.load_from_xml(@xml_element)
92
+ @expected_element = MXSpecObject.load_from_xml(@xml_element)
55
93
  end
56
94
 
57
95
  it 'parses a String' do
58
- elem = ParseXMLSpecElement.parse_xml(@xml_string)
59
- expect(elem).to eq(@expected_element)
96
+ obj = MXSpecObject.parse_xml(@xml_string)
97
+ expect(obj).to eq(@expected_element)
60
98
  end
61
99
 
62
100
  it 'parses a REXML::Document' do
63
- elem = ParseXMLSpecElement.parse_xml(@xml_document)
64
- expect(elem).to eq(@expected_element)
101
+ obj = MXSpecObject.parse_xml(@xml_document)
102
+ expect(obj).to eq(@expected_element)
65
103
  end
66
104
 
67
105
  it 'parses a REXML::Element' do
68
- elem = ParseXMLSpecElement.parse_xml(@xml_element)
69
- expect(elem).to eq(@expected_element)
106
+ obj = MXSpecObject.parse_xml(@xml_element)
107
+ expect(obj).to eq(@expected_element)
70
108
  end
71
109
 
72
110
  it 'parses an IO' do
73
111
  xml_io = StringIO.new(@xml_string)
74
- elem = ParseXMLSpecElement.parse_xml(xml_io)
75
- expect(elem).to eq(@expected_element)
112
+ obj = MXSpecObject.parse_xml(xml_io)
113
+ expect(obj).to eq(@expected_element)
76
114
  end
77
115
 
78
116
  it 'parses a file' do
@@ -80,8 +118,8 @@ module XML
80
118
  begin
81
119
  xml_file.write(@xml_string)
82
120
  xml_file.rewind
83
- elem = ParseXMLSpecElement.parse_xml(xml_file)
84
- expect(elem).to eq(@expected_element)
121
+ obj = MXSpecObject.parse_xml(xml_file)
122
+ expect(obj).to eq(@expected_element)
85
123
  ensure
86
124
  xml_file.close(true)
87
125
  end
@@ -0,0 +1,18 @@
1
+ require 'spec_helper'
2
+
3
+ module XML
4
+ module MappingExtensions
5
+ describe Namespace do
6
+ describe '#set_default_namespace' do
7
+ it 'sets the default namespace'
8
+ it 'sets the schema location'
9
+ end
10
+
11
+ describe '#set_prefix' do
12
+ it 'sets the prefix'
13
+ it 'clears the no-prefix namespace, if previously present'
14
+ it 'leaves an unrelated no-prefix namespace intact'
15
+ end
16
+ end
17
+ end
18
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: xml-mapping_extensions
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.5
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Moles
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-28 00:00:00.000000000 Z
11
+ date: 2016-05-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mime-types
@@ -190,6 +190,7 @@ files:
190
190
  - lib/xml/mapping_extensions.rb
191
191
  - lib/xml/mapping_extensions/date_node.rb
192
192
  - lib/xml/mapping_extensions/mime_type_node.rb
193
+ - lib/xml/mapping_extensions/namespace.rb
193
194
  - lib/xml/mapping_extensions/node_base.rb
194
195
  - lib/xml/mapping_extensions/time_node.rb
195
196
  - lib/xml/mapping_extensions/typesafe_enum_node.rb
@@ -202,6 +203,7 @@ files:
202
203
  - spec/unit/xml/mapping_extensions/date_node_spec.rb
203
204
  - spec/unit/xml/mapping_extensions/mapping_extensions_spec.rb
204
205
  - spec/unit/xml/mapping_extensions/mime_type_node_spec.rb
206
+ - spec/unit/xml/mapping_extensions/namespace_spec.rb
205
207
  - spec/unit/xml/mapping_extensions/node_base_spec.rb
206
208
  - spec/unit/xml/mapping_extensions/time_node_spec.rb
207
209
  - spec/unit/xml/mapping_extensions/typesafe_enum_node_spec.rb
@@ -239,6 +241,7 @@ test_files:
239
241
  - spec/unit/xml/mapping_extensions/date_node_spec.rb
240
242
  - spec/unit/xml/mapping_extensions/mapping_extensions_spec.rb
241
243
  - spec/unit/xml/mapping_extensions/mime_type_node_spec.rb
244
+ - spec/unit/xml/mapping_extensions/namespace_spec.rb
242
245
  - spec/unit/xml/mapping_extensions/node_base_spec.rb
243
246
  - spec/unit/xml/mapping_extensions/time_node_spec.rb
244
247
  - spec/unit/xml/mapping_extensions/typesafe_enum_node_spec.rb