xml-fu 0.1.9 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +82 -16
- data/lib/xml-fu.rb +48 -54
- data/lib/xml-fu/array.rb +8 -30
- data/lib/xml-fu/configuration.rb +58 -0
- data/lib/xml-fu/hash.rb +46 -40
- data/lib/xml-fu/node.rb +14 -36
- data/lib/xml-fu/open_struct.rb +17 -0
- data/lib/xml-fu/version.rb +1 -1
- data/spec/lib/configuration_spec.rb +24 -0
- data/spec/lib/open_struct_spec.rb +37 -0
- data/spec/lib/xml-fu_spec.rb +6 -7
- data/spec/xmlfu_spec.rb +159 -76
- metadata +12 -6
data/README.md
CHANGED
@@ -25,6 +25,23 @@ Or install it yourself as:
|
|
25
25
|
|
26
26
|
$ gem install xml-fu
|
27
27
|
|
28
|
+
## BREAKING CHANGES in 0.2.0
|
29
|
+
|
30
|
+
Configuration was reworked to be more flexible and provide a centralized object for configuration. As such,
|
31
|
+
the configuration options have been moved off of the XmlFu module into the XmlFu.config object. The XmlFu.configure
|
32
|
+
method still works the same for setting configurations, but for reading configuration variables, you need to go
|
33
|
+
through the XmlFu.config object.
|
34
|
+
|
35
|
+
* ADDED
|
36
|
+
* support for OpenStruct conversion
|
37
|
+
* REMOVED
|
38
|
+
* infer_simple_value_nodes configuration option
|
39
|
+
* XmlFu::Array.infer_node
|
40
|
+
* XmlFu.parse()
|
41
|
+
* XmlFu is for generation of XML, not parsing.
|
42
|
+
* MOVED
|
43
|
+
* configuration options moved to XmlFu.config
|
44
|
+
|
28
45
|
|
29
46
|
## Hash Keys
|
30
47
|
|
@@ -34,15 +51,16 @@ Hash keys are translated into XML nodes (whether it be a document node or attrib
|
|
34
51
|
### Key Translation
|
35
52
|
|
36
53
|
With Ruby, a hash key may be a string or a symbol. **Strings will be preserved** as they may
|
37
|
-
contain node namespacing ("foo:Bar" would need preserved rather than converted).
|
38
|
-
|
39
|
-
|
40
|
-
|
54
|
+
contain node namespacing ("foo:Bar" would need preserved rather than converted). There are some
|
55
|
+
exceptions to this rule (especially with special key syntax discussed in *Types of Nodes* and the
|
56
|
+
like). Symbols will be converted into an XML safe name by lower camel-casing them. So :foo\_bar
|
57
|
+
will become "fooBar". You may change the conversion algorithm to your liking by setting the
|
58
|
+
XmlFu.config.symbol_conversion_algorithm to a lambda or proc of your liking.
|
41
59
|
|
42
60
|
|
43
61
|
#### Built-In Algorithms
|
44
62
|
|
45
|
-
For a complete list, reference XmlFu::
|
63
|
+
For a complete list, reference XmlFu::Configuration::ALGORITHMS
|
46
64
|
|
47
65
|
* :camelcase
|
48
66
|
* :downcase
|
@@ -57,27 +75,27 @@ XmlFu.xml( :foo_bar => "bang" ) #=> "<fooBar>bang</fooBar>"
|
|
57
75
|
|
58
76
|
|
59
77
|
# Built-in Algorithms (:camelcase)
|
60
|
-
XmlFu.symbol_conversion_algorithm = :camelcase
|
78
|
+
XmlFu.config.symbol_conversion_algorithm = :camelcase
|
61
79
|
XmlFu.xml( :Foo_Bar => "bang" ) #=> "<FooBar>bang</FooBar>"
|
62
80
|
XmlFu.xml( :foo_bar => "bang" ) #=> "<FooBar>bang</FooBar>"
|
63
81
|
|
64
82
|
|
65
83
|
# Built-in Algorithms (:downcase)
|
66
|
-
XmlFu.symbol_conversion_algorithm = :downcase
|
84
|
+
XmlFu.config.symbol_conversion_algorithm = :downcase
|
67
85
|
XmlFu.xml( :foo_bar => "bang" ) #=> "<foo_bar>bang</foo_bar>"
|
68
86
|
XmlFu.xml( :Foo_Bar => "bang" ) #=> "<foo_bar>bang</foo_bar>"
|
69
87
|
XmlFu.xml( :FOO => "bar" ) #=> "<foo>bar</foo>"
|
70
88
|
|
71
89
|
|
72
90
|
# Built-in Algorithms (:upcase)
|
73
|
-
XmlFu.symbol_conversion_algorithm = :upcase
|
91
|
+
XmlFu.config.symbol_conversion_algorithm = :upcase
|
74
92
|
XmlFu.xml( :foo_bar => "bang" ) #=> "<FOO_BAR>bang</FOO_BAR>"
|
75
93
|
XmlFu.xml( :Foo_Bar => "bang" ) #=> "<FOO_BAR>bang</FOO_BAR>"
|
76
94
|
XmlFu.xml( :foo => "bar" ) #=> "<FOO>bar</FOO>"
|
77
95
|
|
78
96
|
|
79
97
|
# Custom Algorithm
|
80
|
-
XmlFu.symbol_conversion_algorithm = lambda {|sym| sym.do_something_special }
|
98
|
+
XmlFu.config.symbol_conversion_algorithm = lambda {|sym| sym.do_something_special }
|
81
99
|
```
|
82
100
|
|
83
101
|
### Types of Nodes
|
@@ -119,6 +137,11 @@ XmlFu.xml("foo!" => "<bar/>") #=> "<foo><bar/></foo>"
|
|
119
137
|
Yes, the attributes of an XML node are nodes themselves, so we need a way of defining them. Since XPath syntax
|
120
138
|
uses @ to denote an attribute, so does XmlFu.
|
121
139
|
|
140
|
+
**Note**: Keep in mind, because of the way that XML::Builder accepts an attributes hash and the way that
|
141
|
+
Ruby stores and retrieves values from a Hash, that attributes won't always be generated in the same
|
142
|
+
order. We can only guarantee that the attributes will be present, not in any specific order.
|
143
|
+
|
144
|
+
|
122
145
|
``` ruby
|
123
146
|
XmlFu.xml(:agent => {
|
124
147
|
"@id" => "007",
|
@@ -190,6 +213,14 @@ XmlFu.xml("foo/" => {"@id" => "123", "=" => "You can't see me."})
|
|
190
213
|
```
|
191
214
|
|
192
215
|
|
216
|
+
### OpenStructs
|
217
|
+
|
218
|
+
Since version 0.2.0, support has been added for converting an OpenStruct object. OpenStruct objects behave
|
219
|
+
similar to Hashes, but they do not allow the flexibility that Hashes provide when naming keys/methods. As such,
|
220
|
+
the advanced naming capabilities are not available with OpenStruct objects and key conversion will go through
|
221
|
+
XmlFu.config.symbol_conversion_algorithm.
|
222
|
+
|
223
|
+
|
193
224
|
### Arrays
|
194
225
|
|
195
226
|
Since the value in a key/value pair is (for the most part) used as the contents of a key/node, there are some
|
@@ -271,20 +302,55 @@ are currently ignored in arrays and only Hashes are translated.
|
|
271
302
|
```ruby
|
272
303
|
"foo" => [
|
273
304
|
{:bar => "biz"},
|
274
|
-
nil,
|
275
|
-
true,
|
276
|
-
false,
|
277
|
-
42,
|
278
|
-
3.14,
|
279
|
-
"simple string",
|
305
|
+
nil, # ignored
|
306
|
+
true, # ignored
|
307
|
+
false, # ignored
|
308
|
+
42, # ignored
|
309
|
+
3.14, # ignored
|
310
|
+
"simple string", # ignored
|
280
311
|
['another','array','of','values'] # ignored
|
281
312
|
]
|
282
313
|
#=> "<foo><bar>biz</bar></foo>"
|
283
314
|
```
|
284
315
|
|
285
316
|
## Options
|
317
|
+
|
318
|
+
The following options are available to pass to XmlFu.xml(obj, options).
|
319
|
+
|
286
320
|
* **:instruct** => true
|
287
|
-
* Adds <
|
321
|
+
* Adds <?xml version="1.0" encoding="UTF-8"?> to generated XML
|
322
|
+
* This will be overridden by XmlFu.config.include_xml_declaration
|
323
|
+
|
324
|
+
|
325
|
+
## Configuration
|
326
|
+
```
|
327
|
+
XmlFu.configure do |config|
|
328
|
+
config.symbol_conversion_algorithm = :default # (:lower_camelcase)
|
329
|
+
config.fail_on_invalid_construct = false # (false)
|
330
|
+
config.include_xml_declaration = nil # (nil)
|
331
|
+
end
|
332
|
+
```
|
333
|
+
|
334
|
+
### symbol_conversion_algorithm
|
335
|
+
|
336
|
+
This is used to convert symbol keys in a Hash to Strings.
|
337
|
+
|
338
|
+
|
339
|
+
### fail_on_invalid_construct
|
340
|
+
|
341
|
+
When an unsupported object is passed to XmlFu.xml(), the default action is to return nil as
|
342
|
+
the result. When fail_on_invalid_construct is enabled, XmlFu.xml() will raise an ArgumentError
|
343
|
+
to denote that the passed object is not supported rather than fail silently.
|
344
|
+
|
345
|
+
### include_xml_declaration
|
346
|
+
|
347
|
+
Deals with adding/excluding <?xml version="1.0" encoding="UTF-8"?> to generated XML
|
348
|
+
|
349
|
+
* When enabled, ALWAYS adds declaration to XML
|
350
|
+
* When disabled, NEVER adds declaration to XML
|
351
|
+
* When nil, control given to XmlFu.xml :instruct option (default)
|
352
|
+
|
353
|
+
|
288
354
|
|
289
355
|
|
290
356
|
## Cheat Sheet
|
data/lib/xml-fu.rb
CHANGED
@@ -1,75 +1,69 @@
|
|
1
1
|
#require "xml-fu/version"
|
2
2
|
require "xml-fu/hash"
|
3
|
+
require "xml-fu/open_struct"
|
3
4
|
require "xml-fu/array"
|
4
5
|
require "xml-fu/markup"
|
6
|
+
require "xml-fu/configuration"
|
5
7
|
|
6
8
|
module XmlFu
|
7
|
-
class << self
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
case construct
|
12
|
-
when ::Hash then Hash.to_xml( construct.dup, options )
|
13
|
-
when ::Array then Array.to_xml( construct.dup, options )
|
14
|
-
else nil
|
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 parse(xml=nil, options={})
|
29
|
-
parsed_xml = xml
|
10
|
+
# Convert construct into XML
|
11
|
+
def self.xml(construct, options={})
|
30
12
|
|
31
|
-
|
13
|
+
# Override options for xml_declaration if config is present
|
14
|
+
unless self.config.include_xml_declaration.nil?
|
15
|
+
options[:instruct] = self.config.include_xml_declaration
|
32
16
|
end
|
33
17
|
|
34
|
-
|
35
|
-
|
18
|
+
case construct
|
19
|
+
when ::OpenStruct then
|
20
|
+
OpenStruct.to_xml( construct.dup, options )
|
21
|
+
when ::Hash then
|
22
|
+
Hash.to_xml( construct.dup, options )
|
23
|
+
when ::Array then
|
24
|
+
Array.to_xml( construct.dup, options )
|
25
|
+
else
|
26
|
+
if construct.respond_to?(:to_xml)
|
27
|
+
construct.to_xml
|
28
|
+
else
|
29
|
+
# Options have been exhausted
|
30
|
+
if self.config.fail_on_invalid_construct
|
31
|
+
raise ArgumentError, "Invalid construct"
|
32
|
+
else
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
36
|
end
|
37
|
+
end#self.xml
|
37
38
|
|
38
|
-
################################################################################
|
39
|
-
## CONFIGURATIONS
|
40
|
-
################################################################################
|
41
39
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
40
|
+
# Used to determine if the top-level #xml method recognizes the passed object
|
41
|
+
def self.recognized_object?(obj)
|
42
|
+
case obj
|
43
|
+
when ::Hash then return true
|
44
|
+
when ::Array then return true
|
45
|
+
when ::OpenStruct then return true
|
46
|
+
else
|
47
|
+
return true if obj.respond_to?(:to_xml)
|
48
|
+
return false
|
47
49
|
end
|
50
|
+
end#self.recognized_object?
|
48
51
|
|
49
|
-
# Configuration option to be used with future releases
|
50
|
-
# This option should allow for the inferrance of parent node names of simple value types
|
51
|
-
#
|
52
|
-
# Example:
|
53
|
-
# 1 => <Integer>1</Integer>
|
54
|
-
# true => <Boolean>true</Boolean>
|
55
|
-
#
|
56
|
-
# This is disabled by default as it is conflicting with working logic.
|
57
|
-
def infer_simple_value_nodes
|
58
|
-
return @@infer_simple_value_nodes
|
59
|
-
end
|
60
52
|
|
53
|
+
################################################################################
|
54
|
+
## CONFIGURATIONS
|
55
|
+
################################################################################
|
56
|
+
|
57
|
+
# Modify configuration for library
|
58
|
+
# @yield [Configuration]
|
59
|
+
def self.configure
|
60
|
+
yield self.config if block_given?
|
61
|
+
end#configure
|
61
62
|
|
62
|
-
# @param formula_enum Formula Enumeration
|
63
|
-
# @return [lambda]
|
64
|
-
def symbol_conversion_algorithm=( formula_enum=nil, &block )
|
65
|
-
XmlFu::Node.symbol_conversion_algorithm = (formula_enum ? formula_enum : block)
|
66
|
-
end#symbol_conversion_algorithm
|
67
63
|
|
64
|
+
def self.config
|
65
|
+
@@config ||= Configuration.new
|
66
|
+
end#self.config
|
68
67
|
|
69
|
-
# @return [lambda]
|
70
|
-
def symbol_conversion_algorithm
|
71
|
-
XmlFu::Node.symbol_conversion_algorithm
|
72
|
-
end#symbol_conversion_algorithm
|
73
68
|
|
74
|
-
end#class<<self
|
75
69
|
end#XmlFu
|
data/lib/xml-fu/array.rb
CHANGED
@@ -22,46 +22,24 @@ module XmlFu
|
|
22
22
|
case options[:content_type].to_s
|
23
23
|
when "collection"
|
24
24
|
raise(MissingKeyException, "Key name missing for collection") if key.empty?
|
25
|
-
|
26
|
-
case
|
27
|
-
when ::Array
|
25
|
+
|
26
|
+
case item
|
27
|
+
when ::Array then
|
28
28
|
xml << Array.to_xml(item.flatten,options)
|
29
29
|
else
|
30
30
|
xml << Node.new(key, item, attributes).to_xml
|
31
31
|
end
|
32
32
|
else
|
33
33
|
# Array is content of node rather than collection of node elements
|
34
|
-
|
35
|
-
|
36
|
-
xml << Hash.to_xml(item, options)
|
37
|
-
when ::Array === item
|
38
|
-
xml << Array.to_xml(item, options)
|
39
|
-
when XmlFu.infer_simple_value_nodes == true
|
40
|
-
xml << infer_node(item, attributes)
|
41
|
-
when item.respond_to?(:to_xml)
|
42
|
-
xml << item.to_xml
|
34
|
+
if XmlFu.recognized_object?(item)
|
35
|
+
xml << XmlFu.xml(item, options)
|
43
36
|
else
|
44
|
-
# unknown xml
|
37
|
+
# unknown xml transformation
|
45
38
|
end
|
46
39
|
end
|
47
40
|
end
|
48
41
|
end#self.to_xml
|
49
42
|
|
50
|
-
# Future Functionality - VERY ALPHA STAGE!!!
|
51
|
-
# @todo Add node inferrance functionality
|
52
|
-
# @note Do not use if you want stable functionality
|
53
|
-
# @param item Simple Value
|
54
|
-
# @param [Hash] attributes Hash of attributes to assign to inferred node.
|
55
|
-
def self.infer_node(item, attributes={})
|
56
|
-
node_name = case item.class
|
57
|
-
when "TrueClass"
|
58
|
-
when "FalseClass"
|
59
|
-
"Boolean"
|
60
|
-
else
|
61
|
-
item.class
|
62
|
-
end
|
63
|
-
Node.new(node_name, item, attributes).to_xml
|
64
|
-
end#self.infer_node
|
65
43
|
|
66
44
|
# Convenience function to iterate over array items as well as
|
67
45
|
# providing a single location for logic
|
@@ -74,7 +52,7 @@ module XmlFu
|
|
74
52
|
key = opts.fetch(:key, "")
|
75
53
|
item_content = item
|
76
54
|
|
77
|
-
# Attributes reuires duplicate or child elements will
|
55
|
+
# Attributes reuires duplicate or child elements will
|
78
56
|
# contain attributes of their siblings.
|
79
57
|
attributes = (opts[:attributes] ? opts[:attributes].dup : {})
|
80
58
|
|
@@ -84,7 +62,7 @@ module XmlFu
|
|
84
62
|
end
|
85
63
|
|
86
64
|
item_name = ( Symbol === key ?
|
87
|
-
XmlFu
|
65
|
+
XmlFu.config.symbol_conversion_algorithm.call(key) :
|
88
66
|
key.to_s )
|
89
67
|
|
90
68
|
yield xml, item_name, item_content, attributes
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module XmlFu
|
2
|
+
class Configuration
|
3
|
+
# @attr [lambda] symbol_conversion_algorithm (:lower_camelcase)
|
4
|
+
# symbol-to-string conversion algorithm
|
5
|
+
attr_reader :symbol_conversion_algorithm # define my own writer
|
6
|
+
|
7
|
+
# @attr [Boolean] fail_on_invalid_construct (false)
|
8
|
+
# By default, XmlFu works for converting Hash, Array, and OpenStruct objects. Any other
|
9
|
+
# type of object will be ignored and the result would return an empty string. With this option
|
10
|
+
# enabled, XmlFu will raise an exception if an unsupported object is attempted to be
|
11
|
+
# converted.
|
12
|
+
attr_accessor :fail_on_invalid_construct
|
13
|
+
|
14
|
+
# @attr [Boolean, nil] include_xml_declaration (nil)
|
15
|
+
# If set, will override XmlFu.xml :istruct option for toggling the XML declaration for
|
16
|
+
# the generated output.
|
17
|
+
attr_accessor :include_xml_declaration
|
18
|
+
|
19
|
+
|
20
|
+
ALGORITHMS = {
|
21
|
+
:camelcase => lambda { |sym| sym.to_s.camelcase },
|
22
|
+
:downcase => lambda { |sym| sym.to_s.downcase },
|
23
|
+
:lower_camelcase => lambda { |sym| sym.to_s.lower_camelcase }, # DEFAULT
|
24
|
+
:none => lambda { |sym| sym.to_s },
|
25
|
+
:upcase => lambda { |sym| sym.to_s.upcase }
|
26
|
+
}
|
27
|
+
|
28
|
+
|
29
|
+
# Set default values
|
30
|
+
def initialize
|
31
|
+
@symbol_conversion_algorithm = ALGORITHMS[:lower_camelcase]
|
32
|
+
@fail_on_invalid_construct = false
|
33
|
+
@include_xml_declaration = nil
|
34
|
+
end#initialize
|
35
|
+
|
36
|
+
|
37
|
+
# Method for setting global Symbol-to-string conversion algorithm
|
38
|
+
# @param [symbol, lambda] algorithm
|
39
|
+
# Can be symbol corresponding to predefined algorithm or a lambda that accepts a symbol
|
40
|
+
# as an argument and returns a string
|
41
|
+
def symbol_conversion_algorithm=(algorithm)
|
42
|
+
raise(ArgumentError, "Missing symbol conversion algorithm") unless algorithm
|
43
|
+
|
44
|
+
if algorithm.respond_to?(:call)
|
45
|
+
@symbol_conversion_algorithm = algorithm
|
46
|
+
else
|
47
|
+
if algorithm == :default
|
48
|
+
@symbol_conversion_algorithm = ALGORITHMS[:lower_camelcase]
|
49
|
+
elsif ALGORITHMS.keys.include?(algorithm)
|
50
|
+
@symbol_conversion_algorithm = ALGORITHMS[algorithm]
|
51
|
+
else
|
52
|
+
raise(ArgumentError, "Invalid symbol conversion algorithm")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end#symbol_conversion_algorithm
|
56
|
+
|
57
|
+
end#class
|
58
|
+
end#module
|
data/lib/xml-fu/hash.rb
CHANGED
@@ -9,61 +9,67 @@ module XmlFu
|
|
9
9
|
# If order is of concern, use Array.to_xml
|
10
10
|
class Hash
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
# Convert Hash to XML String
|
13
|
+
def self.to_xml(hash, options={})
|
14
|
+
each_with_xml hash, options do |xml, name, value, attributes|
|
15
|
+
case value
|
16
|
+
when ::OpenStruct then
|
17
|
+
node = Node.new( name, OpenStruct.to_xml(value, options) )
|
18
|
+
# We want the value returned from OpenStruct.to_xml NOT to be escaped
|
19
|
+
node.escape_xml = false
|
20
|
+
|
21
|
+
xml << node.to_xml
|
22
|
+
else
|
17
23
|
xml << Node.new(name, value, attributes).to_xml
|
18
24
|
end
|
19
|
-
end
|
25
|
+
end
|
26
|
+
end#self.to_xml
|
20
27
|
|
21
28
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
29
|
+
# Class method to filter out attributes and content
|
30
|
+
# from a given hash
|
31
|
+
def self.filter(hash)
|
32
|
+
attribs = {}
|
33
|
+
content = hash.dup
|
27
34
|
|
28
|
-
|
29
|
-
|
30
|
-
|
35
|
+
content.keys.select{|k| k =~ /^@/ }.each do |k|
|
36
|
+
attribs[k[1..-1]] = content.delete(k)
|
37
|
+
end
|
31
38
|
|
32
|
-
|
33
|
-
|
39
|
+
# Use _content value if defined
|
40
|
+
content = content.delete("=") || content
|
34
41
|
|
35
|
-
|
36
|
-
|
42
|
+
return [content, attribs]
|
43
|
+
end#self.filter
|
37
44
|
|
38
|
-
|
45
|
+
private
|
39
46
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
47
|
+
# Provides a convenience function to iterate over the hash
|
48
|
+
# Logic will filter out attribute and content keys from hash values
|
49
|
+
def self.each_with_xml(hash, opts={})
|
50
|
+
xml = XmlFu::Markup.new(opts)
|
44
51
|
|
45
|
-
|
46
|
-
|
47
|
-
|
52
|
+
hash.each do |key,value|
|
53
|
+
node_value = value
|
54
|
+
node_attrs = {}
|
48
55
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
56
|
+
# yank the attribute keys into their own hash
|
57
|
+
if value.respond_to?(:keys)
|
58
|
+
filtered = Hash.filter(value)
|
59
|
+
node_value, node_attrs = filtered.first, filtered.last
|
60
|
+
end
|
54
61
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
62
|
+
# Use symbol conversion algorithm to set tag name
|
63
|
+
node_name = ( Symbol === key ?
|
64
|
+
XmlFu.config.symbol_conversion_algorithm.call(key) :
|
65
|
+
key.to_s )
|
59
66
|
|
60
|
-
|
61
|
-
|
67
|
+
yield xml, node_name, node_value, node_attrs
|
68
|
+
end
|
62
69
|
|
63
|
-
|
64
|
-
|
70
|
+
xml.target!
|
71
|
+
end#self.each_with_xml
|
65
72
|
|
66
|
-
end#class << self
|
67
73
|
|
68
74
|
end#Hash
|
69
75
|
|
data/lib/xml-fu/node.rb
CHANGED
@@ -15,35 +15,6 @@ module XmlFu
|
|
15
15
|
# xs:dateTime format.
|
16
16
|
XS_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
|
17
17
|
|
18
|
-
# default set of algorithms to choose from
|
19
|
-
ALGORITHMS = {
|
20
|
-
:camelcase => lambda { |sym| sym.to_s.camelcase },
|
21
|
-
:downcase => lambda { |sym| sym.to_s.downcase },
|
22
|
-
:lower_camelcase => lambda { |sym| sym.to_s.lower_camelcase }, # DEFAULT
|
23
|
-
:none => lambda { |sym| sym.to_s },
|
24
|
-
:upcase => lambda { |sym| sym.to_s.upcase }
|
25
|
-
}
|
26
|
-
|
27
|
-
# Class method for retrieving global Symbol-to-string conversion algorithm
|
28
|
-
# @return [lambda]
|
29
|
-
def self.symbol_conversion_algorithm
|
30
|
-
@symbol_conversion_algorithm ||= ALGORITHMS[:lower_camelcase]
|
31
|
-
end#self.symbol_conversion_algorithm
|
32
|
-
|
33
|
-
# Class method for setting global Symbol-to-string conversion algorithm
|
34
|
-
# @param [symbol, lambda] algorithm
|
35
|
-
# Can be symbol corresponding to predefined algorithm or a lambda that accepts a symbol
|
36
|
-
# as an argument and returns a string
|
37
|
-
def self.symbol_conversion_algorithm=(algorithm)
|
38
|
-
if algorithm == :default
|
39
|
-
algorithm = ALGORITHMS[:lower_camelcase]
|
40
|
-
else
|
41
|
-
algorithm = ALGORITHMS[algorithm] unless algorithm.respond_to?(:call)
|
42
|
-
end
|
43
|
-
raise(ArgumentError, "Invalid symbol conversion algorithm") unless algorithm
|
44
|
-
@symbol_conversion_algorithm = algorithm
|
45
|
-
end#self.symbol_conversion_algorithm=
|
46
|
-
|
47
18
|
attr_accessor :escape_xml
|
48
19
|
attr_accessor :self_closing
|
49
20
|
attr_accessor :content_type
|
@@ -61,6 +32,7 @@ module XmlFu
|
|
61
32
|
self.name = name
|
62
33
|
end#initialize
|
63
34
|
|
35
|
+
|
64
36
|
attr_reader :attributes
|
65
37
|
def attributes=(val)
|
66
38
|
if ::Hash === val
|
@@ -70,6 +42,7 @@ module XmlFu
|
|
70
42
|
end
|
71
43
|
end
|
72
44
|
|
45
|
+
|
73
46
|
attr_reader :name
|
74
47
|
def name=(val)
|
75
48
|
use_name = val.dup
|
@@ -80,13 +53,14 @@ module XmlFu
|
|
80
53
|
use_name = use_name[1..-1] if use_name[0,1] == ":"
|
81
54
|
|
82
55
|
if Symbol === val
|
83
|
-
use_name =
|
56
|
+
use_name = XmlFu.config.symbol_conversion_algorithm.call(use_name)
|
84
57
|
end
|
85
58
|
|
86
59
|
# Set name to remaining value
|
87
60
|
@name = "#{use_name}"
|
88
61
|
end#name=
|
89
62
|
|
63
|
+
|
90
64
|
# Converts name into proper XML node name
|
91
65
|
# @param [String, Symbol] val Raw name
|
92
66
|
def name_parse_special_characters(val)
|
@@ -116,15 +90,17 @@ module XmlFu
|
|
116
90
|
return use_this
|
117
91
|
end#name_parse_special_characters
|
118
92
|
|
93
|
+
|
119
94
|
# Custom Setter for @value instance method
|
120
95
|
def value=(val)
|
121
96
|
case val
|
122
|
-
when ::String
|
123
|
-
when ::Hash
|
124
|
-
when ::Array
|
125
|
-
when ::
|
126
|
-
when ::
|
127
|
-
when ::
|
97
|
+
when ::String then @value = val.to_s
|
98
|
+
when ::Hash then @value = val
|
99
|
+
when ::Array then @value = val
|
100
|
+
when ::OpenStruct then @value = val
|
101
|
+
when ::DateTime then @value = val.strftime XS_DATETIME_FORMAT
|
102
|
+
when ::Time then @value = val.strftime XS_DATETIME_FORMAT
|
103
|
+
when ::Date then @value = val.strftime XS_DATETIME_FORMAT
|
128
104
|
else
|
129
105
|
if val.respond_to?(:to_datetime)
|
130
106
|
@value = val.to_datetime
|
@@ -140,6 +116,7 @@ module XmlFu
|
|
140
116
|
@value = val.to_s
|
141
117
|
end#value=
|
142
118
|
|
119
|
+
|
143
120
|
# @return [String, nil]
|
144
121
|
# Value can be nil, else it should return a String value.
|
145
122
|
def value
|
@@ -147,6 +124,7 @@ module XmlFu
|
|
147
124
|
return @value
|
148
125
|
end#value
|
149
126
|
|
127
|
+
|
150
128
|
# Create XML String from XmlFu::Node object
|
151
129
|
def to_xml
|
152
130
|
xml = Builder::XmlMarkup.new
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
require 'builder'
|
3
|
+
require 'xml-fu/node'
|
4
|
+
|
5
|
+
module XmlFu
|
6
|
+
# An OpenStruct object behaves similar to a Hash in that it has key/value pairs,
|
7
|
+
# but is more restrictive with key names. As such, it is near impossible to set "attribute"
|
8
|
+
# keys on an OpenStruct object.
|
9
|
+
class OpenStruct
|
10
|
+
|
11
|
+
def self.to_xml(obj, options={})
|
12
|
+
Hash.to_xml(obj.marshal_dump, options)
|
13
|
+
end#self.to_xml
|
14
|
+
|
15
|
+
end#class
|
16
|
+
|
17
|
+
end#module
|
data/lib/xml-fu/version.rb
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe XmlFu::Configuration do
|
4
|
+
subject { XmlFu::Configuration }
|
5
|
+
|
6
|
+
it "should have ::ALGORITHMS" do
|
7
|
+
lambda { subject::ALGORITHMS }.should_not raise_exception
|
8
|
+
subject::ALGORITHMS.should_not be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "instance" do
|
12
|
+
subject { XmlFu::Configuration.new }
|
13
|
+
|
14
|
+
[ :symbol_conversion_algorithm,
|
15
|
+
:symbol_conversion_algorithm=,
|
16
|
+
:fail_on_invalid_construct,
|
17
|
+
:include_xml_declaration
|
18
|
+
].each do |m|
|
19
|
+
it "should respond_to '#{m}'" do
|
20
|
+
subject.should respond_to(m)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end#instance
|
24
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
describe XmlFu::OpenStruct do
|
5
|
+
let(:klass) { XmlFu::OpenStruct }
|
6
|
+
|
7
|
+
describe ".to_xml" do
|
8
|
+
subject { OpenStruct.new({:foo => "bar"}) }
|
9
|
+
|
10
|
+
it "should result in correct output" do
|
11
|
+
klass.to_xml(subject).should == "<foo>bar</foo>"
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with complex nesting" do
|
15
|
+
subject do
|
16
|
+
OpenStruct.new({
|
17
|
+
:foo => [
|
18
|
+
OpenStruct.new({ :bar => "bang" }),
|
19
|
+
{ :bang => :biz },
|
20
|
+
{ "number*" => [
|
21
|
+
{ "@name" => "pi", "=" => 3.14159 }
|
22
|
+
]}
|
23
|
+
]
|
24
|
+
})
|
25
|
+
end
|
26
|
+
|
27
|
+
# NOTE: (TL;DR) Cannot guarantee attribute order, can only guarantee attribute presence
|
28
|
+
#
|
29
|
+
# Due to the way XML::Builder iterates over attributes, the order of the attributes
|
30
|
+
# cannot be guaranteed. This is because of the way that ruby stores and iterates
|
31
|
+
# over a Hash, and it is different depending on the version of Ruby being used.
|
32
|
+
it "should result in correct output" do
|
33
|
+
klass.to_xml(subject).should == '<foo><bar>bang</bar><bang>biz</bang><number name="pi">3.14159</number></foo>'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end#.to_xml
|
37
|
+
end
|
data/spec/lib/xml-fu_spec.rb
CHANGED
@@ -2,12 +2,12 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe XmlFu do
|
4
4
|
after(:each) do
|
5
|
-
XmlFu.symbol_conversion_algorithm = :default
|
5
|
+
XmlFu.config.symbol_conversion_algorithm = :default
|
6
6
|
end
|
7
7
|
|
8
8
|
describe "with default symbol conversion algorithm" do
|
9
9
|
before(:each) do
|
10
|
-
XmlFu.symbol_conversion_algorithm = :default
|
10
|
+
XmlFu.config.symbol_conversion_algorithm = :default
|
11
11
|
end
|
12
12
|
|
13
13
|
{
|
@@ -24,7 +24,7 @@ describe XmlFu do
|
|
24
24
|
|
25
25
|
describe "with built-in :camelcase algorithm" do
|
26
26
|
before(:each) do
|
27
|
-
XmlFu.symbol_conversion_algorithm = :camelcase
|
27
|
+
XmlFu.config.symbol_conversion_algorithm = :camelcase
|
28
28
|
end
|
29
29
|
|
30
30
|
{
|
@@ -41,7 +41,7 @@ describe XmlFu do
|
|
41
41
|
|
42
42
|
describe "with built-in :downcase algorithm" do
|
43
43
|
before(:each) do
|
44
|
-
XmlFu.symbol_conversion_algorithm = :downcase
|
44
|
+
XmlFu.config.symbol_conversion_algorithm = :downcase
|
45
45
|
end
|
46
46
|
|
47
47
|
{
|
@@ -58,7 +58,7 @@ describe XmlFu do
|
|
58
58
|
|
59
59
|
describe "with built-in :upcase algorithm" do
|
60
60
|
before(:each) do
|
61
|
-
XmlFu.symbol_conversion_algorithm = :upcase
|
61
|
+
XmlFu.config.symbol_conversion_algorithm = :upcase
|
62
62
|
end
|
63
63
|
|
64
64
|
{
|
@@ -71,5 +71,4 @@ describe XmlFu do
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end#:upcase
|
74
|
-
|
75
|
-
end
|
74
|
+
end#XmlFu
|
data/spec/xmlfu_spec.rb
CHANGED
@@ -2,97 +2,180 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe XmlFu do
|
4
4
|
describe ".xml" do
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
context "(default configuration)" do
|
6
|
+
context "with an unsupported construct as argument" do
|
7
|
+
it "should return nil" do
|
8
|
+
XmlFu.xml(1).should == nil
|
9
|
+
XmlFu.xml(nil).should == nil
|
10
|
+
XmlFu.xml(Object.new).should == nil
|
11
|
+
end
|
12
|
+
end
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
+
it "translates a given Hash to XML" do
|
15
|
+
XmlFu.xml({:id => 1}).should == "<id>1</id>"
|
16
|
+
end
|
14
17
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
it "doesn't modify the input hash" do
|
19
|
+
the_hash = {
|
20
|
+
:person => {
|
21
|
+
"@id" => "007",
|
22
|
+
:first_name => "James",
|
23
|
+
:last_name => "Bond"
|
24
|
+
}
|
21
25
|
}
|
22
|
-
|
23
|
-
original_hash = the_hash.dup
|
26
|
+
original_hash = the_hash.dup
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
+
XmlFu.xml(the_hash)
|
29
|
+
original_hash.should == the_hash
|
30
|
+
end
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
32
|
+
it "should return correct value based on nested array of hashes" do
|
33
|
+
hash = {
|
34
|
+
"SecretAgents" => [
|
35
|
+
{"agent/" => {"@name"=>"Alec Trevelyan"}},
|
36
|
+
{"agent/" => {"@name"=>"James Bond"}}
|
37
|
+
]
|
38
|
+
}
|
39
|
+
expected = '<SecretAgents><agent name="Alec Trevelyan"/><agent name="James Bond"/></SecretAgents>'
|
40
|
+
XmlFu.xml(hash).should == expected
|
41
|
+
end
|
39
42
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
it "should return correct value for nested collection of hashes" do
|
44
|
+
hash = {
|
45
|
+
"foo*" => [
|
46
|
+
{"@bar" => "biz"},
|
47
|
+
{"@biz" => "bang"}
|
48
|
+
]
|
49
|
+
}
|
50
|
+
XmlFu.xml(hash).should == '<foo bar="biz"></foo><foo biz="bang"></foo>'
|
51
|
+
end
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
53
|
+
it "should return list of self-closing nodes" do
|
54
|
+
hash = {
|
55
|
+
"foo/*" => [
|
56
|
+
{"@bar" => "biz"},
|
57
|
+
{"@biz" => "bang"}
|
58
|
+
]
|
59
|
+
}
|
60
|
+
XmlFu.xml(hash).should == '<foo bar="biz"/><foo biz="bang"/>'
|
61
|
+
end
|
59
62
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
63
|
+
it "should ignore nested values for content array" do
|
64
|
+
output = XmlFu.xml("foo/" => [{:bar => "biz"}, {:bar => "biz"}])
|
65
|
+
output.should == "<foo/>"
|
66
|
+
end
|
64
67
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
+
it "should ignore nested keys if they aren't attributes" do
|
69
|
+
output = XmlFu.xml("foo/" => {"bar" => "biz"})
|
70
|
+
output.should == "<foo/>"
|
68
71
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
72
|
+
output = XmlFu.xml("foo/" => {"@id" => "0"})
|
73
|
+
output.should == '<foo id="0"/>'
|
74
|
+
end
|
75
|
+
end#default configuration
|
73
76
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
77
|
+
context "with 'fail_on_invalid_construct' enabled" do
|
78
|
+
before(:each) do
|
79
|
+
XmlFu.configure do |config|
|
80
|
+
config.fail_on_invalid_construct = true
|
81
|
+
end
|
78
82
|
end
|
79
|
-
end
|
80
83
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
context "with an unsupported construct as argument" do
|
85
|
+
it "should raise an ArgumentError exception" do
|
86
|
+
lambda { XmlFu.xml(1) }.should raise_exception
|
87
|
+
lambda { XmlFu.xml(nil) }.should raise_exception
|
88
|
+
lambda { XmlFu.xml(Object.new) }.should raise_exception
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end#fail_on_invalid_construct enabled
|
92
|
+
|
93
|
+
context "with 'include_xml_declaration" do
|
94
|
+
context "set TRUE" do
|
95
|
+
before(:each) do
|
96
|
+
XmlFu.configure do |config|
|
97
|
+
config.include_xml_declaration = true
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should ALWAYS output the xml declaration" do
|
102
|
+
XmlFu.xml({:foo => "bar"}).should == '<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>'
|
103
|
+
XmlFu.xml({:foo => "bar"}, {:instruct => false}).should == '<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>'
|
104
|
+
XmlFu.xml({:foo => "bar"}, {:instruct => true}).should == '<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>'
|
105
|
+
end
|
106
|
+
end#enabled
|
107
|
+
|
108
|
+
context "set FALSE" do
|
109
|
+
before(:each) do
|
110
|
+
XmlFu.configure do |config|
|
111
|
+
config.include_xml_declaration = false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should NEVER output the xml declaration" do
|
116
|
+
XmlFu.xml({:foo => "bar"}).should == '<foo>bar</foo>'
|
117
|
+
XmlFu.xml({:foo => "bar"}, {:instruct => false}).should == '<foo>bar</foo>'
|
118
|
+
XmlFu.xml({:foo => "bar"}, {:instruct => true}).should == '<foo>bar</foo>'
|
119
|
+
end
|
120
|
+
end#enabled
|
121
|
+
|
122
|
+
context "set NIL" do
|
123
|
+
before(:each) do
|
124
|
+
XmlFu.configure do |config|
|
125
|
+
config.include_xml_declaration = nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should output if option says so" do
|
130
|
+
XmlFu.xml({:foo => "bar"}).should == '<foo>bar</foo>'
|
131
|
+
XmlFu.xml({:foo => "bar"}, {:instruct => false}).should == '<foo>bar</foo>'
|
132
|
+
XmlFu.xml({:foo => "bar"}, {:instruct => true}).should == '<?xml version="1.0" encoding="UTF-8"?><foo>bar</foo>'
|
133
|
+
end
|
134
|
+
end
|
87
135
|
end
|
88
|
-
end
|
136
|
+
end#.xml
|
137
|
+
|
138
|
+
describe ".recognized_object?" do
|
139
|
+
describe "should be true" do
|
140
|
+
it { XmlFu.recognized_object?(Hash.new).should be_true }
|
141
|
+
it { XmlFu.recognized_object?(Array.new).should be_true }
|
142
|
+
it { XmlFu.recognized_object?(OpenStruct.new).should be_true }
|
143
|
+
|
144
|
+
context "if object responds to :to_xml" do
|
145
|
+
subject do
|
146
|
+
obj = Object.new
|
147
|
+
def obj.to_xml; end
|
148
|
+
obj
|
149
|
+
end
|
150
|
+
it { XmlFu.recognized_object?(subject).should be_true }
|
151
|
+
end
|
152
|
+
end#should be true
|
153
|
+
|
154
|
+
describe "should be false" do
|
155
|
+
it { XmlFu.recognized_object?(1).should_not be_true }
|
156
|
+
it { XmlFu.recognized_object?("foobar").should_not be_true }
|
157
|
+
it { XmlFu.recognized_object?(1.23).should_not be_true }
|
158
|
+
context "if object does not respond to :to_xml" do
|
159
|
+
it { XmlFu.recognized_object?(Object.new).should_not be_true }
|
160
|
+
end
|
161
|
+
end#should be false
|
162
|
+
end#.recognized_object?
|
163
|
+
|
164
|
+
describe ".configure" do
|
165
|
+
context "yielded value" do
|
166
|
+
it "should be the same object as .config()" do
|
167
|
+
yielded = nil
|
168
|
+
XmlFu.configure { |c| yielded = c }
|
169
|
+
yielded.should == XmlFu.config
|
170
|
+
end
|
171
|
+
end#yielded value
|
172
|
+
end#.configure
|
89
173
|
|
90
|
-
describe ".
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
::Array === XmlFu.parse("foo", {:bar => true})
|
174
|
+
describe ".config" do
|
175
|
+
subject { XmlFu.config }
|
176
|
+
it "should be a XmlFu::Configuration object" do
|
177
|
+
subject.should be_a_kind_of(XmlFu::Configuration)
|
95
178
|
end
|
96
|
-
end
|
179
|
+
end#.config
|
97
180
|
|
98
181
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xml-fu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 2
|
9
|
+
- 0
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ryan Johnson
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2013-
|
18
|
+
date: 2013-09-24 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: builder
|
@@ -83,14 +83,18 @@ files:
|
|
83
83
|
- Rakefile
|
84
84
|
- lib/xml-fu.rb
|
85
85
|
- lib/xml-fu/array.rb
|
86
|
+
- lib/xml-fu/configuration.rb
|
86
87
|
- lib/xml-fu/core_ext/string.rb
|
87
88
|
- lib/xml-fu/hash.rb
|
88
89
|
- lib/xml-fu/markup.rb
|
89
90
|
- lib/xml-fu/node.rb
|
91
|
+
- lib/xml-fu/open_struct.rb
|
90
92
|
- lib/xml-fu/version.rb
|
91
93
|
- spec/lib/array_spec.rb
|
94
|
+
- spec/lib/configuration_spec.rb
|
92
95
|
- spec/lib/hash_spec.rb
|
93
96
|
- spec/lib/node_spec.rb
|
97
|
+
- spec/lib/open_struct_spec.rb
|
94
98
|
- spec/lib/xml-fu_spec.rb
|
95
99
|
- spec/lib/xml_spec.rb
|
96
100
|
- spec/spec_helper.rb
|
@@ -125,14 +129,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
129
|
requirements: []
|
126
130
|
|
127
131
|
rubyforge_project: xml-fu
|
128
|
-
rubygems_version: 1.8.
|
132
|
+
rubygems_version: 1.8.26
|
129
133
|
signing_key:
|
130
134
|
specification_version: 3
|
131
135
|
summary: Simple Hash/Array to XML generation
|
132
136
|
test_files:
|
133
137
|
- spec/lib/array_spec.rb
|
138
|
+
- spec/lib/configuration_spec.rb
|
134
139
|
- spec/lib/hash_spec.rb
|
135
140
|
- spec/lib/node_spec.rb
|
141
|
+
- spec/lib/open_struct_spec.rb
|
136
142
|
- spec/lib/xml-fu_spec.rb
|
137
143
|
- spec/lib/xml_spec.rb
|
138
144
|
- spec/spec_helper.rb
|