xml-mapping_extensions 0.3.5 → 0.3.6

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