xml-fu 0.1.9 → 0.2.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/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). Symbols will
38
- be converted into an XML safe name by lower camel-casing them. So :foo\_bar will become "fooBar".
39
- You may change the conversion algorithm to your liking by setting the
40
- XmlFu.symbol\_conversion\_algorithm to a lambda or proc of your liking.
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::Node::ALGORITHMS
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, # ignored
275
- true, # ignored
276
- false, # ignored
277
- 42, # ignored
278
- 3.14, # ignored
279
- "simple string", # ignored
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 &lt;xml version="1.0" encoding="UTF-8"?&gt; to generated XML
321
+ * Adds &lt;?xml version="1.0" encoding="UTF-8"?&gt; 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 &lt;?xml version="1.0" encoding="UTF-8"?&gt; 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
@@ -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
- # Convert construct into XML
10
- def xml(construct, options={})
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
- return Array(parsed_xml)
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
- def configure
35
- yield self
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
- @@infer_simple_value_nodes = false
43
-
44
- # Set configuration option to be used with future releases
45
- def infer_simple_value_nodes=(val)
46
- @@infer_simple_value_nodes = val
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
@@ -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 === item
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
- case
35
- when ::Hash === item
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 tranfromation process
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::Node.symbol_conversion_algorithm.call(key) :
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
@@ -9,61 +9,67 @@ module XmlFu
9
9
  # If order is of concern, use Array.to_xml
10
10
  class Hash
11
11
 
12
- class << self
13
-
14
- # Convert Hash to XML String
15
- def to_xml(hash, options={})
16
- each_with_xml hash, options do |xml, name, value, attributes|
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#to_xml
25
+ end
26
+ end#self.to_xml
20
27
 
21
28
 
22
- # Class method to filter out attributes and content
23
- # from a given hash
24
- def filter(hash)
25
- attribs = {}
26
- content = hash.dup
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
- content.keys.select{|k| k =~ /^@/ }.each do |k|
29
- attribs[k[1..-1]] = content.delete(k)
30
- end
35
+ content.keys.select{|k| k =~ /^@/ }.each do |k|
36
+ attribs[k[1..-1]] = content.delete(k)
37
+ end
31
38
 
32
- # Use _content value if defined
33
- content = content.delete("=") || content
39
+ # Use _content value if defined
40
+ content = content.delete("=") || content
34
41
 
35
- return [content, attribs]
36
- end#filter
42
+ return [content, attribs]
43
+ end#self.filter
37
44
 
38
- private
45
+ private
39
46
 
40
- # Provides a convenience function to iterate over the hash
41
- # Logic will filter out attribute and content keys from hash values
42
- def each_with_xml(hash, opts={})
43
- xml = XmlFu::Markup.new(opts)
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
- hash.each do |key,value|
46
- node_value = value
47
- node_attrs = {}
52
+ hash.each do |key,value|
53
+ node_value = value
54
+ node_attrs = {}
48
55
 
49
- # yank the attribute keys into their own hash
50
- if value.respond_to?(:keys)
51
- filtered = Hash.filter(value)
52
- node_value, node_attrs = filtered.first, filtered.last
53
- end
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
- # Use symbol conversion algorithm to set tag name
56
- node_name = ( Symbol === key ?
57
- XmlFu::Node.symbol_conversion_algorithm.call(key) :
58
- key.to_s )
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
- yield xml, node_name, node_value, node_attrs
61
- end
67
+ yield xml, node_name, node_value, node_attrs
68
+ end
62
69
 
63
- xml.target!
64
- end#each_with_xml
70
+ xml.target!
71
+ end#self.each_with_xml
65
72
 
66
- end#class << self
67
73
 
68
74
  end#Hash
69
75
 
@@ -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 = self.class.symbol_conversion_algorithm.call(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 then @value = val.to_s
123
- when ::Hash then @value = val
124
- when ::Array then @value = val
125
- when ::DateTime then @value = val.strftime XS_DATETIME_FORMAT
126
- when ::Time then @value = val.strftime XS_DATETIME_FORMAT
127
- when ::Date then @value = val.strftime XS_DATETIME_FORMAT
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
@@ -1,3 +1,3 @@
1
1
  module XmlFu
2
- VERSION = "0.1.9"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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
@@ -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
@@ -2,97 +2,180 @@ require 'spec_helper'
2
2
 
3
3
  describe XmlFu do
4
4
  describe ".xml" do
5
- it "should return nil for value that isn't a Hash or Array" do
6
- XmlFu.xml(1).should == nil
7
- XmlFu.xml(nil).should == nil
8
- XmlFu.xml(Object.new).should == nil
9
- end
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
- it "translates a given Hash to XML" do
12
- XmlFu.xml( :id => 1 ).should == "<id>1</id>"
13
- end
14
+ it "translates a given Hash to XML" do
15
+ XmlFu.xml({:id => 1}).should == "<id>1</id>"
16
+ end
14
17
 
15
- it "doesn't modify the input hash" do
16
- the_hash = {
17
- :person => {
18
- "@id" => "007",
19
- :first_name => "James",
20
- :last_name => "Bond"
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
- XmlFu.xml(the_hash)
26
- original_hash.should == the_hash
27
- end
28
+ XmlFu.xml(the_hash)
29
+ original_hash.should == the_hash
30
+ end
28
31
 
29
- it "should return correct value based on nested array of hashes" do
30
- hash = {
31
- "SecretAgents" => [
32
- {"agent/" => {"@name"=>"Alec Trevelyan"}},
33
- {"agent/" => {"@name"=>"James Bond"}}
34
- ]
35
- }
36
- expected = "<SecretAgents><agent name=\"Alec Trevelyan\"/><agent name=\"James Bond\"/></SecretAgents>"
37
- XmlFu.xml(hash).should == expected
38
- end
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
- it "should return correct value for nested collection of hashes" do
41
- hash = {
42
- "foo*" => [
43
- {"@bar" => "biz"},
44
- {"@biz" => "bang"}
45
- ]
46
- }
47
- XmlFu.xml(hash).should == "<foo bar=\"biz\"></foo><foo biz=\"bang\"></foo>"
48
- end
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
- it "should return list of self-closing nodes" do
51
- hash = {
52
- "foo/*" => [
53
- {"@bar" => "biz"},
54
- {"@biz" => "bang"}
55
- ]
56
- }
57
- XmlFu.xml(hash).should == "<foo bar=\"biz\"/><foo biz=\"bang\"/>"
58
- end
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
- it "should ignore nested values for content array" do
61
- output = XmlFu.xml("foo/" => [{:bar => "biz"}, {:bar => "biz"}])
62
- output.should == "<foo/>"
63
- end
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
- it "should ignore nested keys if they aren't attributes" do
66
- output = XmlFu.xml("foo/" => {"bar" => "biz"})
67
- output.should == "<foo/>"
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
- output = XmlFu.xml("foo/" => {"@id" => "0"})
70
- output.should == "<foo id=\"0\"/>"
71
- end
72
- end
72
+ output = XmlFu.xml("foo/" => {"@id" => "0"})
73
+ output.should == '<foo id="0"/>'
74
+ end
75
+ end#default configuration
73
76
 
74
- describe "configure" do
75
- it "yields the XmlFu module" do
76
- XmlFu.configure do |xf|
77
- xf.should respond_to(:infer_simple_value_nodes)
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
- it "should set XmlFu Module variable 'infer_simple_value_nodes'" do
82
- XmlFu.infer_simple_value_nodes.should == false
83
- XmlFu.infer_simple_value_nodes = true
84
- XmlFu.infer_simple_value_nodes.should == true
85
- XmlFu.infer_simple_value_nodes = false
86
- XmlFu.infer_simple_value_nodes.should == false
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 ".parse" do
91
- it "should always return an array" do
92
- ::Array === XmlFu.parse()
93
- ::Array === XmlFu.parse("foo")
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: 9
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 1
9
- - 9
10
- version: 0.1.9
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-07-22 00:00:00 Z
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.24
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