xml-fu 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|