xml-fu 0.1.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.
- data/.gitignore +19 -0
- data/CHANGELOG.md +8 -0
- data/Gemfile +4 -0
- data/LICENSE +23 -0
- data/README.md +291 -0
- data/Rakefile +2 -0
- data/lib/xml-fu/.FuNode.rb.swp +0 -0
- data/lib/xml-fu/array.rb +98 -0
- data/lib/xml-fu/core_ext/string.rb +14 -0
- data/lib/xml-fu/hash.rb +87 -0
- data/lib/xml-fu/node.rb +143 -0
- data/lib/xml-fu/version.rb +3 -0
- data/lib/xml-fu.rb +53 -0
- data/spec/lib/array_spec.rb +77 -0
- data/spec/lib/hash_spec.rb +61 -0
- data/spec/lib/node_spec.rb +112 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/xmlfu_spec.rb +75 -0
- data/xml-fu.gemspec +29 -0
- metadata +150 -0
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2012 Ryan Johnson
|
2
|
+
Copyright (c) 2010 Daniel Harrington (for logic inspired by Gyoku)
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
# Xml::Fu
|
2
|
+
|
3
|
+
Convert Ruby Hashes to XML
|
4
|
+
|
5
|
+
A hash is meant to be a structured set of data. So is XML. The two are very similar in that they have
|
6
|
+
the capability of nesting information within a tree structure. With XML you have nodes. With Hashes, you
|
7
|
+
have key/value pairs. The value of an XML node is referenced by its parent's name. A hash value is referenced
|
8
|
+
by its key. This basic lesson tells the majority of what you need to know about creating XML via Hashes in
|
9
|
+
Ruby using the XmlFu gem.
|
10
|
+
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
gem 'xml-fu'
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install xml-fu
|
25
|
+
|
26
|
+
|
27
|
+
## Hash Keys
|
28
|
+
|
29
|
+
Hash keys are translated into XML nodes (whether it be a document node or attribute node).
|
30
|
+
|
31
|
+
|
32
|
+
### Translation
|
33
|
+
|
34
|
+
With Ruby, a hash key may be a string or a symbol. XmlFu will convert the symbols into an XML safe name
|
35
|
+
by lower camel-casing them. So :foo\_bar will become "fooBar". You may change the conversion algorithm to your
|
36
|
+
liking by setting the XmlFu.symbol\_conversion\_algorithm to a lambda or proc of your liking.
|
37
|
+
|
38
|
+
|
39
|
+
#### Built-In Algorithms
|
40
|
+
|
41
|
+
* :lower\_camelcase **(default)**
|
42
|
+
* :camelcase
|
43
|
+
* :none (result of :sym.to\_s)
|
44
|
+
* :snake\_case (alias for :none)
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
# Built-in Algorithm
|
48
|
+
XmlFu.symbol_conversion_algorithm(:camelcase)
|
49
|
+
|
50
|
+
XmlFu.xml( :foo => "bar" ) #=> "<foo>bar</foo>"
|
51
|
+
XmlFu.xml( "foo" => "bar" ) #=> "<foo>bar</foo>"
|
52
|
+
# :foo and "foo" both translate to <foo>
|
53
|
+
|
54
|
+
# Custom Algorithm
|
55
|
+
XmlFu.symbol_conversion_algorithm {|sym| sym.downcase }
|
56
|
+
```
|
57
|
+
|
58
|
+
### Types of Nodes
|
59
|
+
|
60
|
+
Because there are multiple types of XML nodes, there are also multiple types of keys to denote them.
|
61
|
+
|
62
|
+
|
63
|
+
#### Self-Closing Nodes (key/)
|
64
|
+
|
65
|
+
By default, XmlFu assumes that all XML nodes will contain closing tags. However, if you want to explicitly
|
66
|
+
create a self-closing node, use the following syntax when you define the key.
|
67
|
+
|
68
|
+
``` ruby
|
69
|
+
XmlFu.xml("foo/" => "bar") #=> <foo/>
|
70
|
+
```
|
71
|
+
|
72
|
+
One thing to take note of this syntax is that XmlFu will ignore ANY value you throw at it if the key syntax
|
73
|
+
denotes a self-closing tag. This is because a self-closing tag cannot have any contents (hence the use for
|
74
|
+
a self-closing tag).
|
75
|
+
|
76
|
+
|
77
|
+
#### Unescaped Content Nodes (key!)
|
78
|
+
|
79
|
+
By default, if you pass a pure string as a value, special characters will be escaped to keep the XML compliant.
|
80
|
+
If you know that the string is valid XML and can be trusted, you can add the exclamation point to the end of
|
81
|
+
the key name to denote that XmlFu should NOT escape special characters in the value.
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
# Default Functionality (Escaped Characters)
|
85
|
+
XmlFu.xml("foo" => "<bar/>") #=> "<foo><bar/></foo>"
|
86
|
+
|
87
|
+
# Unescaped Characters
|
88
|
+
XmlFu.xml("foo!" => "<bar/>") #=> "<foo><bar/></foo>"
|
89
|
+
```
|
90
|
+
|
91
|
+
|
92
|
+
#### Attribute Node (@key)
|
93
|
+
|
94
|
+
Yes, the attributes of an XML node are nodes themselves, so we need a way of defining them. Since XPath syntax
|
95
|
+
uses @ to denote an attribute, so does XmlFu.
|
96
|
+
|
97
|
+
``` ruby
|
98
|
+
XmlFu.xml(:agent => {
|
99
|
+
"@id" => "007",
|
100
|
+
"FirstName" => "James",
|
101
|
+
"LastName" => "Bond"
|
102
|
+
})
|
103
|
+
#=> <agent id="007"><FirstName>James</FirstName><LastName>Bond</LastName></agent>
|
104
|
+
```
|
105
|
+
|
106
|
+
|
107
|
+
## Hash Values
|
108
|
+
|
109
|
+
The value in a key/value pair describes the key/node. Different value types determine the extent of this description.
|
110
|
+
|
111
|
+
|
112
|
+
### Simple Values
|
113
|
+
|
114
|
+
Simple value types describe the contents of the XML node.
|
115
|
+
|
116
|
+
|
117
|
+
#### Strings
|
118
|
+
|
119
|
+
``` ruby
|
120
|
+
XmlFu.xml( :foo => "bar" ) #=> "<foo>bar</foo>"
|
121
|
+
XmlFu.xml( "foo" => "bar" ) #=> "<foo>bar</foo>"
|
122
|
+
```
|
123
|
+
|
124
|
+
|
125
|
+
#### Numbers
|
126
|
+
|
127
|
+
``` ruby
|
128
|
+
XmlFu.xml( :foo => 0 ) #=> "<foo>0</foo>"
|
129
|
+
XmlFu.xml( :pi => 3.14159 ) #=> "<pi>3.14159</pi>"
|
130
|
+
```
|
131
|
+
|
132
|
+
|
133
|
+
#### Nil
|
134
|
+
|
135
|
+
``` ruby
|
136
|
+
XmlFu.xml( :foo => nil ) #=> "<foo xsi:nil=\"true\"/>"
|
137
|
+
```
|
138
|
+
|
139
|
+
|
140
|
+
### Hashes
|
141
|
+
|
142
|
+
Hash are parsed for their translated values prior to returning a XmlFu value.
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
XmlFu.xml(:foo => {:bar => {:biz => "bang"} })
|
146
|
+
#=> "<foo><bar><biz>bang</biz></bar></foo>"
|
147
|
+
```
|
148
|
+
|
149
|
+
#### Content in Hash (=)
|
150
|
+
|
151
|
+
Should you require setting node attributes as well as setting the value of the XML node, you may use the "="
|
152
|
+
key in a nested hash to denote explicit content.
|
153
|
+
|
154
|
+
```ruby
|
155
|
+
XmlFu.xml(:agent => {"@id" => "007", "=" => "James Bond"})
|
156
|
+
#=> "<agent id=\"007\">James Bond</agent>"
|
157
|
+
```
|
158
|
+
|
159
|
+
This key will not get around the self-closing node rule. The only nodes that will be used in this case will be
|
160
|
+
attribute nodes and additional content will be ignored.
|
161
|
+
|
162
|
+
```ruby
|
163
|
+
XmlFu.xml("foo/" => {"@id" => "123", "=" => "You can't see me."})
|
164
|
+
#=> "<foo id=\"123\"/>"
|
165
|
+
```
|
166
|
+
|
167
|
+
|
168
|
+
### Arrays
|
169
|
+
|
170
|
+
Since the value in a key/value pair is (for the most part) used as the contents of a key/node, there are some
|
171
|
+
assumptions that XmlFu makes when dealing with Array values.
|
172
|
+
|
173
|
+
* For a typical key, the contents of the array are considered to be nodes to be contained within the <key> node.
|
174
|
+
|
175
|
+
|
176
|
+
#### Array of Hashes
|
177
|
+
|
178
|
+
``` ruby
|
179
|
+
XmlFu.xml( "SecretAgents" => [
|
180
|
+
{ "agent/" => { "@id"=>"006", "@name"=>"Alec Trevelyan" } },
|
181
|
+
{ "agent/" => { "@id"=>"007", "@name"=>"James Bond" } }
|
182
|
+
])
|
183
|
+
#=> "<SecretAgents><agent name=\"Alec Trevelyan\" id=\"006\"/><agent name=\"James Bond\" id=\"007\"/></SecretAgents>"
|
184
|
+
```
|
185
|
+
|
186
|
+
|
187
|
+
#### Alternate Array of Hashes (key\*)
|
188
|
+
|
189
|
+
There comes a time that you may want to declare the contents of an array as a collection of items denoted by the
|
190
|
+
key name. Using the asterisk (also known for multiplication --- hence multiple keys) we denote that we want a
|
191
|
+
collection of <key> nodes.
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
XmlFu.xml( "person*" => ["Bob", "Sheila"] )
|
195
|
+
#=> "<person>Bob</person><person>Sheila</person>"
|
196
|
+
```
|
197
|
+
|
198
|
+
In this case, the value of "person*" is an array of two names. These names are to be the contents of multiple
|
199
|
+
<person> nodes and the result is a set of sibling XML nodes with no parent.
|
200
|
+
|
201
|
+
How about a more complex example:
|
202
|
+
|
203
|
+
```ruby
|
204
|
+
XmlFu.xml(
|
205
|
+
"person*" => {
|
206
|
+
"@foo" => "bar",
|
207
|
+
"=" => [
|
208
|
+
{"@foo" => "nope", "=" => "Bob"},
|
209
|
+
"Sheila"
|
210
|
+
]
|
211
|
+
}
|
212
|
+
)
|
213
|
+
#=> "<person foo=\"nope\">Bob</person><person foo=\"bar\">Sheila</person>"
|
214
|
+
```
|
215
|
+
|
216
|
+
*This is getting interesting, isn't it?* In this example, we are setting a default "foo" attribute on each of the
|
217
|
+
items in the collection of <person> nodes. However, you'll notice that we overwrote the default "foo" with Bob.
|
218
|
+
|
219
|
+
|
220
|
+
#### Array of Arrays
|
221
|
+
|
222
|
+
Array values are flattened prior to translation, to reduce the need to iterate over nested arrays.
|
223
|
+
|
224
|
+
```ruby
|
225
|
+
XmlFu.xml(
|
226
|
+
:foo => [
|
227
|
+
[{"a/" => nil}, {"b/" => nil}],
|
228
|
+
{"c/" => nil},
|
229
|
+
[
|
230
|
+
[{"d/" => nil}, {"e/" => nil}],
|
231
|
+
{"f/" => nil}
|
232
|
+
]
|
233
|
+
]
|
234
|
+
)
|
235
|
+
#=> "<foo><a/><b/><c/><d/><e/><f/></foo>"
|
236
|
+
```
|
237
|
+
|
238
|
+
:foo in this case, is the parent node of it's contents
|
239
|
+
|
240
|
+
|
241
|
+
#### Array of Mixed Types
|
242
|
+
|
243
|
+
Since with simple values, you cannot infer the value of their node container purely on their value, simple values
|
244
|
+
are currently ignored in arrays and only Hashes are translated.
|
245
|
+
|
246
|
+
``` ruby
|
247
|
+
"foo" => [
|
248
|
+
{:bar => "biz"},
|
249
|
+
nil, # ignored
|
250
|
+
true, # ignored
|
251
|
+
false, # ignored
|
252
|
+
42, # ignored
|
253
|
+
3.14, # ignored
|
254
|
+
"simple string", # ignored
|
255
|
+
['another','array','of','values'] # ignored
|
256
|
+
]
|
257
|
+
#=> "<foo><bar>biz</bar></foo>"
|
258
|
+
```
|
259
|
+
|
260
|
+
|
261
|
+
### Cheat Sheet
|
262
|
+
|
263
|
+
#### Key
|
264
|
+
1. if key denotes self-closing node (key/)
|
265
|
+
* attributes are preserved with Hash values
|
266
|
+
* value and "=" values are ignored
|
267
|
+
2. if key denotes collection (key*) with Array value
|
268
|
+
* Array is flattened
|
269
|
+
* Only Hash and Simple values are translated
|
270
|
+
* Hashes may override default attributes set by parent
|
271
|
+
* **(applies to Array values only)**
|
272
|
+
3. if key denotes contents (key) with Array value
|
273
|
+
* Array is flattened
|
274
|
+
* Only Hash items in array are translated
|
275
|
+
|
276
|
+
#### Value
|
277
|
+
1. if value is Hash:
|
278
|
+
* "@" keys are attributes of the node
|
279
|
+
* "=" key can be used in conjunction with any "@" keys to specify content of node
|
280
|
+
3. if value is simple value:
|
281
|
+
* it is content of <key> node
|
282
|
+
* **unless:** key denotes a self-closing node
|
283
|
+
|
284
|
+
|
285
|
+
## Contributing
|
286
|
+
|
287
|
+
1. Fork it
|
288
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
289
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
290
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
291
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
Binary file
|
data/lib/xml-fu/array.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'xml-fu/hash'
|
3
|
+
require 'xml-fu/node'
|
4
|
+
|
5
|
+
module XmlFu
|
6
|
+
|
7
|
+
# Convert Array to XML String
|
8
|
+
class Array
|
9
|
+
|
10
|
+
# Custom exception class
|
11
|
+
class MissingKeyException < Exception; end
|
12
|
+
|
13
|
+
# Convert Array to XML String of sibling XML nodes
|
14
|
+
# @param [Array] array
|
15
|
+
# @param [Hash] options
|
16
|
+
# @option options [String] :content_type Either "collection" or "content". (defaults to "content")
|
17
|
+
# @option options [String, Symbol] :key Name of node.
|
18
|
+
# @option options [Hash] :attributes Possible hash of attributes to assign to nodes in array.
|
19
|
+
# @return String
|
20
|
+
def self.to_xml(array, options={})
|
21
|
+
each_with_xml(array, options) do |xml, key, item, attributes|
|
22
|
+
case options[:content_type].to_s
|
23
|
+
when "collection"
|
24
|
+
raise(MissingKeyException, "Key name missing for collection") if key.empty?
|
25
|
+
|
26
|
+
case
|
27
|
+
when key[-1,1] == "/"
|
28
|
+
xml << Node.new(key, nil, attributes).to_xml
|
29
|
+
when ::Hash === item
|
30
|
+
xml.tag!(key, attributes) { xml << Hash.to_xml(item,options) }
|
31
|
+
when ::Array === item
|
32
|
+
xml << Array.to_xml(item.flatten,options)
|
33
|
+
else
|
34
|
+
xml << Node.new(key, item, attributes).to_xml
|
35
|
+
end
|
36
|
+
else
|
37
|
+
# Array is content of node rather than collection of node elements
|
38
|
+
case
|
39
|
+
when ::Hash === item
|
40
|
+
xml << Hash.to_xml(item, options)
|
41
|
+
when ::Array === item
|
42
|
+
xml << Array.to_xml(item, options)
|
43
|
+
when XmlFu.infer_simple_value_nodes == true
|
44
|
+
xml << infer_node(item, attributes)
|
45
|
+
else
|
46
|
+
# only act on item if it responds to to_xml
|
47
|
+
xml << item.to_xml if item.respond_to?(:to_xml)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end#self.to_xml
|
52
|
+
|
53
|
+
# Future Functionality - VERY ALPHA STAGE!!!
|
54
|
+
# @todo Add node inferrance functionality
|
55
|
+
# @note Do not use if you want stable functionality
|
56
|
+
# @param item Simple Value
|
57
|
+
# @param [Hash] attributes Hash of attributes to assign to inferred node.
|
58
|
+
def self.infer_node(item, attributes={})
|
59
|
+
node_name = case item.class
|
60
|
+
when "TrueClass"
|
61
|
+
when "FalseClass"
|
62
|
+
"Boolean"
|
63
|
+
else
|
64
|
+
item.class
|
65
|
+
end
|
66
|
+
Node.new(node_name, item, attributes).to_xml
|
67
|
+
end#self.infer_node
|
68
|
+
|
69
|
+
# Convenience function to iterate over array items as well as
|
70
|
+
# providing a single location for logic
|
71
|
+
# @param [Array] arr Array to iterate over
|
72
|
+
# @param [Hash] opts Hash of options to pass to the iteration
|
73
|
+
def self.each_with_xml(arr, opts={})
|
74
|
+
xml = Builder::XmlMarkup.new
|
75
|
+
|
76
|
+
arr.each do |item|
|
77
|
+
key = opts.fetch(:key, "")
|
78
|
+
item_content = item
|
79
|
+
|
80
|
+
# Attributes reuires duplicate or child elements will
|
81
|
+
# contain attributes of their siblings.
|
82
|
+
attributes = (opts[:attributes] ? opts[:attributes].dup : {})
|
83
|
+
|
84
|
+
if item.respond_to?(:keys)
|
85
|
+
filtered = Hash.filter(item)
|
86
|
+
attributes = filtered.last
|
87
|
+
item_content = filtered.first
|
88
|
+
end
|
89
|
+
|
90
|
+
yield xml, key, item_content, attributes
|
91
|
+
end
|
92
|
+
|
93
|
+
xml.target!
|
94
|
+
end#self.each_with_xml
|
95
|
+
|
96
|
+
end#Array
|
97
|
+
|
98
|
+
end#XmlFu
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Redefined parts of the String class to suit our needs in the gem.
|
2
|
+
class String
|
3
|
+
|
4
|
+
# Returns the string in camelcase (with first character lowercase)
|
5
|
+
def lower_camelcase
|
6
|
+
self[0].chr.downcase + self.camelcase[1..-1]
|
7
|
+
end#lower_camelcase
|
8
|
+
|
9
|
+
# Returns the string in camelcase (with the first character uppercase)
|
10
|
+
def camelcase
|
11
|
+
self.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
12
|
+
end#camelcase
|
13
|
+
|
14
|
+
end#String
|
data/lib/xml-fu/hash.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'xml-fu/array'
|
3
|
+
require 'xml-fu/node'
|
4
|
+
|
5
|
+
module XmlFu
|
6
|
+
|
7
|
+
# By definition, a hash is an UNORDERED list of key/value pairs
|
8
|
+
# There's no sense in trying to order the keys.
|
9
|
+
# If order is of concern, use Array.to_xml
|
10
|
+
class Hash
|
11
|
+
|
12
|
+
# Convert Hash to XML String
|
13
|
+
def self.to_xml(hash, options={})
|
14
|
+
each_with_xml hash do |xml, key, value, attributes|
|
15
|
+
# Use symbol conversion algorithm to set tag name
|
16
|
+
tag_name = ( Symbol === key ?
|
17
|
+
XmlFu::Node.symbol_conversion_algorithm.call(key) :
|
18
|
+
key.to_s )
|
19
|
+
|
20
|
+
case
|
21
|
+
when tag_name[-1,1] == "/"
|
22
|
+
xml << Node.new(tag_name, nil, attributes).to_xml
|
23
|
+
when ::Array === value
|
24
|
+
if tag_name[-1,1] == '*'
|
25
|
+
options.merge!({
|
26
|
+
:content_type => "collection",
|
27
|
+
:key => tag_name.chop,
|
28
|
+
:attributes => attributes
|
29
|
+
})
|
30
|
+
# Collection is merely a set of sibling nodes
|
31
|
+
xml << Array.to_xml(value.flatten, options)
|
32
|
+
else
|
33
|
+
# Contents will contain a parent node
|
34
|
+
xml.tag!(tag_name, attributes) { xml << Array.to_xml(value, options) }
|
35
|
+
end
|
36
|
+
when ::Hash === value
|
37
|
+
xml.tag!(tag_name, attributes) { xml << Hash.to_xml(value, options) }
|
38
|
+
else
|
39
|
+
xml << Node.new(tag_name, value, attributes).to_xml
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end#self.to_xml
|
43
|
+
|
44
|
+
|
45
|
+
# Class method to filter out attributes and content
|
46
|
+
# from a given hash
|
47
|
+
def self.filter(hash)
|
48
|
+
attribs = {}
|
49
|
+
content = hash.dup
|
50
|
+
|
51
|
+
content.keys.select{|k| k =~ /^@/ }.each do |k|
|
52
|
+
attribs[k[1..-1]] = content.delete(k)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Use _content value if defined
|
56
|
+
content = content.delete("=") || content
|
57
|
+
|
58
|
+
return [content, attribs]
|
59
|
+
end#self.filter
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Provides a convenience function to iterate over the hash
|
64
|
+
# Logic will filter out attribute and content keys from hash values
|
65
|
+
def self.each_with_xml(hash)
|
66
|
+
xml = Builder::XmlMarkup.new
|
67
|
+
|
68
|
+
hash.each do |key,value|
|
69
|
+
node_value = value
|
70
|
+
node_attrs = {}
|
71
|
+
|
72
|
+
# yank the attribute keys into their own hash
|
73
|
+
if value.respond_to?(:keys)
|
74
|
+
filtered = Hash.filter(value)
|
75
|
+
node_attrs = filtered.last
|
76
|
+
node_value = filtered.first
|
77
|
+
end
|
78
|
+
|
79
|
+
yield xml, key, node_value, node_attrs
|
80
|
+
end
|
81
|
+
|
82
|
+
xml.target!
|
83
|
+
end#self.each_with_xml
|
84
|
+
|
85
|
+
end#Hash
|
86
|
+
|
87
|
+
end#XmlFu
|
data/lib/xml-fu/node.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'builder'
|
2
|
+
require 'cgi'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
require 'xml-fu/core_ext/string'
|
6
|
+
|
7
|
+
module XmlFu
|
8
|
+
|
9
|
+
# Class to contain logic for converting a key/value pair into an XML node
|
10
|
+
class Node
|
11
|
+
|
12
|
+
# Custom exception class
|
13
|
+
class InvalidAttributesException < Exception; end
|
14
|
+
|
15
|
+
# xs:dateTime format.
|
16
|
+
XS_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
17
|
+
|
18
|
+
# default set of algorithms to choose from
|
19
|
+
ALGORITHMS = {
|
20
|
+
:lower_camelcase => lambda { |sym| sym.to_s.lower_camelcase },
|
21
|
+
:camelcase => lambda { |sym| sym.to_s.camelcase },
|
22
|
+
:snakecase => lambda { |sym| sym.to_s },
|
23
|
+
:none => lambda { |sym| sym.to_s }
|
24
|
+
}
|
25
|
+
|
26
|
+
# Class method for retrieving global Symbol-to-string conversion algorithm
|
27
|
+
# @return [lambda]
|
28
|
+
def self.symbol_conversion_algorithm
|
29
|
+
@symbol_conversion_algorithm ||= ALGORITHMS[:lower_camelcase]
|
30
|
+
end#self.symbol_conversion_algorithm
|
31
|
+
|
32
|
+
# Class method for setting global Symbol-to-string conversion algorithm
|
33
|
+
# @param [lambda] algorithm Should accept a symbol as an argument and return a string
|
34
|
+
def self.symbol_conversion_algorithm=(algorithm)
|
35
|
+
algorithm = ALGORITHMS[algorithm] unless algorithm.respond_to?(:call)
|
36
|
+
raise(ArgumentError, "Invalid symbol conversion algorithm") unless algorithm
|
37
|
+
@symbol_conversion_algorithm = algorithm
|
38
|
+
end#self.symbol_conversion_algorithm=
|
39
|
+
|
40
|
+
attr_accessor :escape_xml
|
41
|
+
attr_accessor :self_closing
|
42
|
+
|
43
|
+
# Create XmlFu::Node object
|
44
|
+
# @param [String, Symbol] name Name of node
|
45
|
+
# @param value Simple Value or nil
|
46
|
+
# @param [Hash] attributes Optional hash of attributes to apply to XML Node
|
47
|
+
def initialize(name, value, attributes={})
|
48
|
+
@escape_xml = true
|
49
|
+
@self_closing = false
|
50
|
+
self.attributes = attributes
|
51
|
+
self.value = value
|
52
|
+
self.name = name
|
53
|
+
end#initialize
|
54
|
+
|
55
|
+
attr_reader :attributes
|
56
|
+
def attributes=(val)
|
57
|
+
if ::Hash === val
|
58
|
+
@attributes = val
|
59
|
+
else
|
60
|
+
raise(InvalidAttributesException, "Attempted to set attributes to non-hash value")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_reader :name
|
65
|
+
def name=(val)
|
66
|
+
use_name = val.dup
|
67
|
+
|
68
|
+
use_name = name_parse_special_characters(use_name)
|
69
|
+
|
70
|
+
# TODO: Add additional logic that Gyoku XmlKey puts in place
|
71
|
+
|
72
|
+
# remove ":" if name begins with ":" (i.e. no namespace)
|
73
|
+
use_name = use_name[1..-1] if use_name[0,1] == ":"
|
74
|
+
|
75
|
+
if Symbol === val
|
76
|
+
use_name = self.class.symbol_conversion_algorithm.call(use_name)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Set name to remaining value
|
80
|
+
@name = "#{use_name}"
|
81
|
+
end#name=
|
82
|
+
|
83
|
+
# Converts name into proper XML node name
|
84
|
+
# @param [String, Symbol] val Raw name
|
85
|
+
def name_parse_special_characters(val)
|
86
|
+
use_this = val.dup
|
87
|
+
|
88
|
+
# Will this be a self closing node?
|
89
|
+
if use_this.to_s[-1,1] == '/'
|
90
|
+
@self_closing = true
|
91
|
+
use_this.chop!
|
92
|
+
end
|
93
|
+
|
94
|
+
# Will this node contain escaped XML?
|
95
|
+
if use_this.to_s[-1,1] == '!'
|
96
|
+
@escape_xml = false
|
97
|
+
use_this.chop!
|
98
|
+
end
|
99
|
+
|
100
|
+
# Ensure that we don't have special characters at end of name
|
101
|
+
while ["!","/","*"].include?(use_this.to_s[-1,1]) do
|
102
|
+
use_this.chop!
|
103
|
+
end
|
104
|
+
|
105
|
+
return use_this
|
106
|
+
end#name_parse_special_characters
|
107
|
+
|
108
|
+
# Custom Setter for @value instance method
|
109
|
+
def value=(val)
|
110
|
+
if DateTime === val || Time === val || Date === val
|
111
|
+
@value = val.strftime XS_DATETIME_FORMAT
|
112
|
+
elsif val.respond_to?(:to_datetime)
|
113
|
+
@value = val.to_datetime
|
114
|
+
elsif val.respond_to?(:call)
|
115
|
+
@value = val.call
|
116
|
+
elsif val.nil?
|
117
|
+
@value = nil
|
118
|
+
else
|
119
|
+
@value = val.to_s
|
120
|
+
end
|
121
|
+
end#value=
|
122
|
+
|
123
|
+
# @return [String, nil]
|
124
|
+
# Value can be nil, else it should return a String value.
|
125
|
+
def value
|
126
|
+
return CGI.escapeHTML(@value) if String === @value && @escape_xml
|
127
|
+
return @value
|
128
|
+
end#value
|
129
|
+
|
130
|
+
# Create XML String from XmlFu::Node object
|
131
|
+
def to_xml
|
132
|
+
xml = Builder::XmlMarkup.new
|
133
|
+
case
|
134
|
+
when @self_closing then xml.tag!(@name, @attributes)
|
135
|
+
when @value.nil? then xml.tag!(@name, @attributes.merge!("xsi:nil" => "true"))
|
136
|
+
else xml.tag!(@name, @attributes) { xml << self.value }
|
137
|
+
end
|
138
|
+
xml.target!
|
139
|
+
end#to_xml
|
140
|
+
|
141
|
+
end#Node
|
142
|
+
|
143
|
+
end#XmlFu
|
data/lib/xml-fu.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
#require "xml-fu/version"
|
2
|
+
require "xml-fu/hash"
|
3
|
+
require "xml-fu/array"
|
4
|
+
|
5
|
+
module XmlFu
|
6
|
+
class << self
|
7
|
+
|
8
|
+
@@infer_simple_value_nodes = false
|
9
|
+
|
10
|
+
# Convert construct into XML
|
11
|
+
def xml(construct, options={})
|
12
|
+
case construct
|
13
|
+
when ::Hash then Hash.to_xml( construct.dup, options )
|
14
|
+
when ::Array then Array.to_xml( construct.dup, options )
|
15
|
+
end
|
16
|
+
end#convert
|
17
|
+
|
18
|
+
# @todo Add Nori-like parsing capability to convert XML back into XmlFu-compatible Hash/Array
|
19
|
+
# Parse XML into array of hashes. If XML used as input contains only sibling nodes, output
|
20
|
+
# will be array of hashes corresponding to those sibling nodes.
|
21
|
+
#
|
22
|
+
# <foo/><bar/> => [{"foo/" => ""}, {"bar/" => ""}]
|
23
|
+
#
|
24
|
+
# If XML used as input contains a full document with root node, output will be
|
25
|
+
# an array of one hash (the root node hash)
|
26
|
+
#
|
27
|
+
# <foo><bar/><baz/></foo> => [{"foo" => [{"bar/" => ""},{"baz/" => ""}] }]
|
28
|
+
def hash(xml, options)
|
29
|
+
end
|
30
|
+
|
31
|
+
def configure
|
32
|
+
yield self
|
33
|
+
end
|
34
|
+
|
35
|
+
# Set configuration option to be used with future releases
|
36
|
+
def infer_simple_value_nodes=(val)
|
37
|
+
@@infer_simple_value_nodes = val
|
38
|
+
end
|
39
|
+
|
40
|
+
# Configuration option to be used with future releases
|
41
|
+
# This option should allow for the inferrance of parent node names of simple value types
|
42
|
+
#
|
43
|
+
# Example:
|
44
|
+
# 1 => <Integer>1</Integer>
|
45
|
+
# true => <Boolean>true</Boolean>
|
46
|
+
#
|
47
|
+
# This is disabled by default as it is conflicting with working logic.
|
48
|
+
def infer_simple_value_nodes
|
49
|
+
return @@infer_simple_value_nodes
|
50
|
+
end
|
51
|
+
|
52
|
+
end#class<<self
|
53
|
+
end#XmlFu
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XmlFu::Array do
|
4
|
+
|
5
|
+
describe ".to_xml" do
|
6
|
+
#after do
|
7
|
+
# XmlFu.infer_simple_value_nodes = false
|
8
|
+
#end
|
9
|
+
|
10
|
+
#it "should infer simple value type nodes with configuration turned on" do
|
11
|
+
# mixed_hash = {
|
12
|
+
# "foo" => [
|
13
|
+
# {:bar => "biz"},
|
14
|
+
# nil,
|
15
|
+
# true,
|
16
|
+
# false,
|
17
|
+
# 3.14,
|
18
|
+
# "simple string",
|
19
|
+
# ["another","array","of","values"]
|
20
|
+
# ]
|
21
|
+
# }
|
22
|
+
# XmlFu.infer_simple_value_nodes = true
|
23
|
+
# XmlFu.xml(mixed_hash).should_not == "<foo><bar>biz</bar></foo>"
|
24
|
+
#
|
25
|
+
# XmlFu.infer_simple_value_nodes = false
|
26
|
+
# XmlFu.xml(mixed_hash).should == "<foo><bar>biz</bar></foo>"
|
27
|
+
#
|
28
|
+
# XmlFu.infer_simple_value_nodes = false
|
29
|
+
#end
|
30
|
+
|
31
|
+
it "should flatten nested arrays properly" do
|
32
|
+
hash = {
|
33
|
+
:foo => [
|
34
|
+
[{"a/" => nil}, {"b/" => nil}],
|
35
|
+
{"c/" => nil},
|
36
|
+
[
|
37
|
+
[{"d/" => nil}, {"e/" => nil}],
|
38
|
+
{"f/" => nil}
|
39
|
+
]
|
40
|
+
]
|
41
|
+
}
|
42
|
+
expected = "<foo><a/><b/><c/><d/><e/><f/></foo>"
|
43
|
+
XmlFu.xml(hash).should == expected
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "creating siblings with special key character (*)" do
|
47
|
+
it "should create siblings with special key character" do
|
48
|
+
hash = { "person*" => ["Bob", "Sheila"] }
|
49
|
+
XmlFu.xml(hash).should == "<person>Bob</person><person>Sheila</person>"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should create siblings with mixed attributes" do
|
53
|
+
hash = {
|
54
|
+
"person*" => {
|
55
|
+
"@foo" => "bar",
|
56
|
+
"=" => ["Bob", "Sheila"]
|
57
|
+
}
|
58
|
+
}
|
59
|
+
XmlFu.xml(hash).should == "<person foo=\"bar\">Bob</person><person foo=\"bar\">Sheila</person>"
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should create siblings with complex mixed attributes" do
|
63
|
+
hash = {
|
64
|
+
"person*" => {
|
65
|
+
"@foo" => "bar",
|
66
|
+
"=" => [
|
67
|
+
{"@foo" => "nope", "=" => "Bob"},
|
68
|
+
"Sheila"
|
69
|
+
]
|
70
|
+
}
|
71
|
+
}
|
72
|
+
XmlFu.xml(hash).should == "<person foo=\"nope\">Bob</person><person foo=\"bar\">Sheila</person>"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XmlFu::Hash do
|
4
|
+
|
5
|
+
describe ".to_xml" do
|
6
|
+
it "for a simple Hash" do
|
7
|
+
XmlFu::Hash.to_xml(:some => "body").should == "<some>body</some>"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "for a nested Hash" do
|
11
|
+
XmlFu::Hash.to_xml(:foo => {:bar => "biz" }).should == "<foo><bar>biz</bar></foo>"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "for a hash with multiple keys" do
|
15
|
+
XmlFu::Hash.to_xml(:first => "myself", :second => "the world").should include(
|
16
|
+
"<first>myself</first>",
|
17
|
+
"<second>the world</second>"
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
describe "for a hash value with '=' key defined" do
|
23
|
+
it "should ignore '=' for self-closing tag" do
|
24
|
+
hash = {"foo/" => {"@id" => "1", "=" => "PEEKABOO"}}
|
25
|
+
XmlFu.xml(hash).should == "<foo id=\"1\"/>"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should set additional content using '=' key" do
|
29
|
+
hash = {:foo => {"@id" => "1", "=" => "Hello"}}
|
30
|
+
XmlFu.xml(hash).should == "<foo id=\"1\">Hello</foo>"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "with a key that will contain multiple nodes" do
|
35
|
+
describe "when key explicitly denotes value is a collection" do
|
36
|
+
hash = { "foo*" => ["bar", "biz"] }
|
37
|
+
XmlFu::Hash.to_xml(hash).should == "<foo>bar</foo><foo>biz</foo>"
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "when key denotes value contains children" do
|
41
|
+
it "for array consisting entirely of simple values" do
|
42
|
+
XmlFu::Hash.to_xml(:foo => ["bar", "biz"]).should == "<foo></foo>"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "for array containing mix of simple and complex values" do
|
46
|
+
XmlFu::Hash.to_xml(:foo => ["bar", {:biz => "bang"}]).should == "<foo><biz>bang</biz></foo>"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "for array containing complex values" do
|
50
|
+
hash1 = {:foo => "bar"}
|
51
|
+
hash2 = {:bar => "biz"}
|
52
|
+
XmlFu::Hash.to_xml(:lol => [hash1, hash2]).should == "<lol><foo>bar</foo><bar>biz</bar></lol>"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "for array containing nil values" do
|
56
|
+
XmlFu::Hash.to_xml(:foo => [nil, {:bar => "biz"}]).should == "<foo><bar>biz</bar></foo>"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XmlFu::Node do
|
4
|
+
|
5
|
+
describe "setting instance variables" do
|
6
|
+
it "should correctly remove special characters from a name" do
|
7
|
+
node = XmlFu::Node.new("foo/", "something")
|
8
|
+
node.name.should == "foo"
|
9
|
+
|
10
|
+
node = XmlFu::Node.new("foo*", "something")
|
11
|
+
node.name.should == "foo"
|
12
|
+
|
13
|
+
node = XmlFu::Node.new("foo!", "something")
|
14
|
+
node.name.should == "foo"
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should set self-closing with special name character" do
|
18
|
+
node = XmlFu::Node.new("foo/", "something")
|
19
|
+
node.self_closing.should == true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should set escape_xml with special name character" do
|
23
|
+
node = XmlFu::Node.new("foo!", "something")
|
24
|
+
node.escape_xml.should == false
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should set attributes with a hash" do
|
28
|
+
node = XmlFu::Node.new("foo", "bar", {:this => "that"})
|
29
|
+
node.attributes.should == {:this => "that"}
|
30
|
+
|
31
|
+
lambda { node.attributes = "foo" }.should raise_error(XmlFu::Node::InvalidAttributesException)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be able to set a nil value" do
|
35
|
+
node = XmlFu::Node.new("foo", nil)
|
36
|
+
node.value.should == nil
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should format a Data/Time value to acceptable string value" do
|
40
|
+
formatted_regex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/
|
41
|
+
|
42
|
+
node = XmlFu::Node.new("now", Time.now)
|
43
|
+
node.value.should match formatted_regex
|
44
|
+
|
45
|
+
node = XmlFu::Node.new("today", Date.today)
|
46
|
+
node.value.should match formatted_regex
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should properly convert names without proper namespacing" do
|
50
|
+
node = XmlFu::Node.new(":foo", "bar")
|
51
|
+
node.name.should == "foo"
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should properly preserve namespaceds names" do
|
55
|
+
node = XmlFu::Node.new("foo:bar", "biz")
|
56
|
+
node.name.should == "foo:bar"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "to_xml" do
|
61
|
+
|
62
|
+
describe "should return self-closing nil XML node for nil value" do
|
63
|
+
|
64
|
+
it "provided ANY non-blank name" do
|
65
|
+
nil_foo = "<foo xsi:nil=\"true\"/>"
|
66
|
+
node = XmlFu::Node.new("foo", nil)
|
67
|
+
node.to_xml.should == nil_foo
|
68
|
+
|
69
|
+
node = XmlFu::Node.new("foo!", nil)
|
70
|
+
node.to_xml.should == nil_foo
|
71
|
+
|
72
|
+
node = XmlFu::Node.new("foo*", nil)
|
73
|
+
node.to_xml.should == nil_foo
|
74
|
+
end
|
75
|
+
|
76
|
+
it "with additional attributes provided" do
|
77
|
+
node = XmlFu::Node.new("foo", nil, {:this => "that"})
|
78
|
+
node.to_xml.should == "<foo this=\"that\" xsi:nil=\"true\"/>"
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should escape values by default" do
|
84
|
+
node = XmlFu::Node.new("foo", "<bar/>")
|
85
|
+
node.to_xml.should == "<foo><bar/></foo>"
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should not escape values when provided with a special name" do
|
89
|
+
node = XmlFu::Node.new("foo!", "<bar/>")
|
90
|
+
node.to_xml.should == "<foo><bar/></foo>"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should ignore starred key (key*) for simple values" do
|
94
|
+
node = XmlFu::Node.new("foo*", "bar")
|
95
|
+
node.to_xml.should == "<foo>bar</foo>"
|
96
|
+
|
97
|
+
node = XmlFu::Node.new("pi*", 3.14159)
|
98
|
+
node.to_xml.should == "<pi>3.14159</pi>"
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "when name denotes a self-closing XML node" do
|
102
|
+
it "should ignore tag content/value if it isn't a hash" do
|
103
|
+
node = XmlFu::Node.new("foo/", nil)
|
104
|
+
node.to_xml.should == "<foo/>"
|
105
|
+
|
106
|
+
node = XmlFu::Node.new("foo/", "bar")
|
107
|
+
node.to_xml.should == "<foo/>"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/xmlfu_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XmlFu do
|
4
|
+
describe ".xml" do
|
5
|
+
it "translates a given Hash to XML" do
|
6
|
+
XmlFu.xml( :id => 1 ).should == "<id>1</id>"
|
7
|
+
end
|
8
|
+
|
9
|
+
it "doesn't modify the input hash" do
|
10
|
+
the_hash = {
|
11
|
+
:person => {
|
12
|
+
"@id" => "007",
|
13
|
+
:first_name => "James",
|
14
|
+
:last_name => "Bond"
|
15
|
+
}
|
16
|
+
}
|
17
|
+
original_hash = the_hash.dup
|
18
|
+
|
19
|
+
XmlFu.xml(the_hash)
|
20
|
+
original_hash.should == the_hash
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return correct value based on nested array of hashes" do
|
24
|
+
hash = {
|
25
|
+
"SecretAgents" => [
|
26
|
+
{"agent/" => {"@id"=>"006", "@name"=>"Alec Trevelyan"}},
|
27
|
+
{"agent/" => {"@id"=>"007", "@name"=>"James Bond"}}
|
28
|
+
]
|
29
|
+
}
|
30
|
+
expected = "<SecretAgents><agent name=\"Alec Trevelyan\" id=\"006\"/><agent name=\"James Bond\" id=\"007\"/></SecretAgents>"
|
31
|
+
XmlFu.xml(hash).should == expected
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should return correct value for nested collection of hashes" do
|
35
|
+
hash = {
|
36
|
+
"foo*" => [
|
37
|
+
{"@bar" => "biz"},
|
38
|
+
{"@biz" => "bang"}
|
39
|
+
]
|
40
|
+
}
|
41
|
+
XmlFu.xml(hash).should == "<foo bar=\"biz\"></foo><foo biz=\"bang\"></foo>"
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should ignore nested values for content array" do
|
45
|
+
output = XmlFu.xml("foo/" => [{:bar => "biz"}, {:bar => "biz"}])
|
46
|
+
output.should == "<foo/>"
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should ignore nested keys if they aren't attributes" do
|
50
|
+
output = XmlFu.xml("foo/" => {"bar" => "biz"})
|
51
|
+
output.should == "<foo/>"
|
52
|
+
|
53
|
+
output = XmlFu.xml("foo/" => {"@id" => "0"})
|
54
|
+
output.should == "<foo id=\"0\"/>"
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "configure" do
|
60
|
+
it "yields the XmlFu module" do
|
61
|
+
XmlFu.configure do |xf|
|
62
|
+
xf.should respond_to(:infer_simple_value_nodes)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should set XmlFu Module variable 'infer_simple_value_nodes'" do
|
68
|
+
XmlFu.infer_simple_value_nodes.should == false
|
69
|
+
XmlFu.infer_simple_value_nodes = true
|
70
|
+
XmlFu.infer_simple_value_nodes.should == true
|
71
|
+
XmlFu.infer_simple_value_nodes = false
|
72
|
+
XmlFu.infer_simple_value_nodes.should == false
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/xml-fu.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/xml-fu/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.name = "xml-fu"
|
6
|
+
gem.version = XmlFu::VERSION
|
7
|
+
gem.authors = ["Ryan Johnson"]
|
8
|
+
gem.email = ["rhino.citguy@gmail.com"]
|
9
|
+
gem.homepage = "http://github.com/CITguy/#{gem.name}"
|
10
|
+
gem.summary = %q{Simple Hash/Array to XML generation}
|
11
|
+
gem.description = %q{
|
12
|
+
Inspired by the Gyoku gem for hash to xml conversion,
|
13
|
+
XmlFu is designed to require no meta tagging for
|
14
|
+
node attributes and content. (i.e. no :attributes! and no :order!)
|
15
|
+
}
|
16
|
+
|
17
|
+
gem.rubyforge_project = 'xml-fu'
|
18
|
+
|
19
|
+
gem.add_dependency "builder", ">= 2.1.2"
|
20
|
+
|
21
|
+
gem.add_development_dependency "rspec", ">= 2.4.0"
|
22
|
+
gem.add_development_dependency "autotest"
|
23
|
+
gem.add_development_dependency "mocha", "~> 0.9.9"
|
24
|
+
|
25
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
26
|
+
gem.files = `git ls-files`.split("\n")
|
27
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
28
|
+
gem.require_paths = ["lib"]
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xml-fu
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Ryan Johnson
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-03-13 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: builder
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 15
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 1
|
32
|
+
- 2
|
33
|
+
version: 2.1.2
|
34
|
+
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: rspec
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 31
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 4
|
48
|
+
- 0
|
49
|
+
version: 2.4.0
|
50
|
+
type: :development
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: autotest
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
type: :development
|
65
|
+
version_requirements: *id003
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: mocha
|
68
|
+
prerelease: false
|
69
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
70
|
+
none: false
|
71
|
+
requirements:
|
72
|
+
- - ~>
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
hash: 41
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
- 9
|
78
|
+
- 9
|
79
|
+
version: 0.9.9
|
80
|
+
type: :development
|
81
|
+
version_requirements: *id004
|
82
|
+
description: "\n Inspired by the Gyoku gem for hash to xml conversion, \n XmlFu is designed to require no meta tagging for \n node attributes and content. (i.e. no :attributes! and no :order!)\n "
|
83
|
+
email:
|
84
|
+
- rhino.citguy@gmail.com
|
85
|
+
executables: []
|
86
|
+
|
87
|
+
extensions: []
|
88
|
+
|
89
|
+
extra_rdoc_files: []
|
90
|
+
|
91
|
+
files:
|
92
|
+
- .gitignore
|
93
|
+
- CHANGELOG.md
|
94
|
+
- Gemfile
|
95
|
+
- LICENSE
|
96
|
+
- README.md
|
97
|
+
- Rakefile
|
98
|
+
- lib/xml-fu.rb
|
99
|
+
- lib/xml-fu/.FuNode.rb.swp
|
100
|
+
- lib/xml-fu/array.rb
|
101
|
+
- lib/xml-fu/core_ext/string.rb
|
102
|
+
- lib/xml-fu/hash.rb
|
103
|
+
- lib/xml-fu/node.rb
|
104
|
+
- lib/xml-fu/version.rb
|
105
|
+
- spec/lib/array_spec.rb
|
106
|
+
- spec/lib/hash_spec.rb
|
107
|
+
- spec/lib/node_spec.rb
|
108
|
+
- spec/spec_helper.rb
|
109
|
+
- spec/xmlfu_spec.rb
|
110
|
+
- xml-fu.gemspec
|
111
|
+
homepage: http://github.com/CITguy/xml-fu
|
112
|
+
licenses: []
|
113
|
+
|
114
|
+
post_install_message:
|
115
|
+
rdoc_options: []
|
116
|
+
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
none: false
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
hash: 3
|
125
|
+
segments:
|
126
|
+
- 0
|
127
|
+
version: "0"
|
128
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
hash: 3
|
134
|
+
segments:
|
135
|
+
- 0
|
136
|
+
version: "0"
|
137
|
+
requirements: []
|
138
|
+
|
139
|
+
rubyforge_project: xml-fu
|
140
|
+
rubygems_version: 1.8.10
|
141
|
+
signing_key:
|
142
|
+
specification_version: 3
|
143
|
+
summary: Simple Hash/Array to XML generation
|
144
|
+
test_files:
|
145
|
+
- spec/lib/array_spec.rb
|
146
|
+
- spec/lib/hash_spec.rb
|
147
|
+
- spec/lib/node_spec.rb
|
148
|
+
- spec/spec_helper.rb
|
149
|
+
- spec/xmlfu_spec.rb
|
150
|
+
has_rdoc:
|