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