smacks-apricoteatsgorilla 0.2.4 → 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,108 +10,120 @@ require 'hpricot'
10
10
  # but it quickly evolved into a more general translation tool.
11
11
  class ApricotEatsGorilla
12
12
 
13
- # Converts XML into a Hash. Starts parsing at root node by default.
14
- # Call with XPath expession (Hpricot search) as second parameter to define
15
- # a custom root node. The root node itself will not be included in the Hash.
16
- #
17
- # E.g.
18
- # xml = "<dude><likes>beer</likes><hates>appletini</hates></dude>"
19
- # ApricotEatsGorilla.xml_to_hash(xml)
20
- # # => { "hates" => "appletini", "likes" => "beer" }
21
- def self.xml_to_hash(xml, root_node = nil)
22
- xml = clean_xml(xml)
23
- doc = Hpricot.XML(xml)
24
- root = root_node ? doc.at(root_node) : doc.root
25
- xml_node_to_hash(root)
26
- end
13
+ class << self
14
+ attr_accessor :sort_keys
27
15
 
28
- # Converts a Hash to XML.
29
- #
30
- # E.g.
31
- # hash = { "dude" => { "likes" => "beer", "hates" => "appletini" } }
32
- # ApricotEatsGorilla.hash_to_xml(hash)
33
- # # => "<dude><hates>appletini</hates><likes>beer</likes></dude>"
34
- def self.hash_to_xml(hash)
35
- nested_data_to_xml(hash.keys.first, hash.values.first)
36
- end
16
+ # Converts XML into a Hash. Starts parsing at root node by default.
17
+ # Call with XPath expession (Hpricot search) as second parameter to define
18
+ # a custom root node. The root node itself will not be included in the Hash.
19
+ #
20
+ # E.g.
21
+ # xml = "<dude><likes>beer</likes><hates>appletini</hates></dude>"
22
+ # ApricotEatsGorilla.xml_to_hash(xml)
23
+ # # => { "hates" => "appletini", "likes" => "beer" }
24
+ def xml_to_hash(xml, root_node = nil)
25
+ xml = clean_xml(xml)
26
+ doc = Hpricot.XML(xml)
27
+ root = root_node ? doc.at(root_node) : doc.root
28
+ xml_node_to_hash(root)
29
+ end
37
30
 
38
- private
31
+ # Converts a Hash to XML.
32
+ #
33
+ # E.g.
34
+ # hash = { "dude" => { "likes" => "beer", "hates" => "appletini" } }
35
+ # ApricotEatsGorilla.hash_to_xml(hash)
36
+ # # => "<dude><hates>appletini</hates><likes>beer</likes></dude>"
37
+ def hash_to_xml(hash)
38
+ nested_data_to_xml(hash.keys.first, hash.values.first)
39
+ end
39
40
 
40
- # Converts XML into a Hash.
41
- def self.xml_node_to_hash(node)
42
- this_node = {}
41
+ private
43
42
 
44
- node.each_child do |child|
45
- if child.children.nil?
46
- key, value = child.name, nil
47
- elsif child.children.size == 1 && child.children.first.text?
48
- key, value = child.name, string_to_bool?(child.children.first.raw_string)
49
- else
50
- key, value = child.name, xml_node_to_hash(child)
51
- end
43
+ # Converts XML into a Hash.
44
+ def xml_node_to_hash(node)
45
+ this_node = {}
52
46
 
53
- current = this_node[key]
54
- case current
55
- when Array:
56
- this_node[key] << value
57
- when nil
58
- this_node[key] = value
47
+ node.each_child do |child|
48
+ if child.children.nil? || child.children.empty?
49
+ key, value = child.name, nil
50
+ elsif child.children.size == 1 && child.children.first.text?
51
+ key, value = child.name, string_to_bool?(child.children.first.inner_text)
59
52
  else
60
- this_node[key] = [current.dup, value]
53
+ key, value = child.name, xml_node_to_hash(child)
54
+ end
55
+
56
+ current = this_node[key]
57
+ case current
58
+ when Array:
59
+ this_node[key] << value
60
+ when nil
61
+ this_node[key] = value
62
+ else
63
+ this_node[key] = [current.dup, value]
64
+ end
61
65
  end
66
+
67
+ this_node
62
68
  end
63
69
 
64
- this_node
65
- end
70
+ # Converts a Hash to XML.
71
+ def nested_data_to_xml(name, item)
72
+ case item
73
+ when String
74
+ make_tag(name) { item }
75
+ when Array
76
+ item.map { |subitem| nested_data_to_xml(name, subitem) }.join
77
+ when Hash
78
+ make_tag(name) do
79
+ opt_order(item).map { |tag, value|
80
+ case value
81
+ when String
82
+ make_tag(tag) { value }
83
+ when Array
84
+ value.map { |subitem| nested_data_to_xml(tag, subitem) }.join
85
+ when Hash
86
+ nested_data_to_xml(tag, value)
87
+ end
88
+ }.join
89
+ end
90
+ end
91
+ end
66
92
 
67
- # Converts a Hash to XML.
68
- def self.nested_data_to_xml(name, item)
69
- case item
70
- when String
71
- make_tag(name) { item }
72
- when Array
73
- item.map { |subitem| nested_data_to_xml(name, subitem) }.join
74
- when Hash
75
- make_tag(name) do
76
- item.map { |tag, value|
77
- case value
78
- when String
79
- make_tag(tag) { value }
80
- when Array
81
- value.map { |subitem| nested_data_to_xml(tag, subitem) }.join
82
- when Hash
83
- nested_data_to_xml(tag, value)
84
- end
85
- }.join
93
+ # Helper to create an XML tag. Expects a block for tag content.
94
+ # Defaults to an empty element tag in case no block was supplied.
95
+ def make_tag(name)
96
+ body = yield
97
+ if body && !body.empty?
98
+ "<#{name}>" << body << "</#{name}>"
99
+ else
100
+ "<#{name} />"
86
101
  end
87
102
  end
88
- end
89
103
 
90
- # Helper to create an XML tag. Expects a block for tag content.
91
- # Defaults to an empty element tag in case no block was supplied.
92
- def self.make_tag(name)
93
- body = yield
94
- if body && !body.empty?
95
- "<#{name}>" << body << "</#{name}>"
96
- else
97
- "<#{name} />"
104
+ def opt_order(hash)
105
+ if sort_keys
106
+ hash.sort_by{ |kv| kv.first }
107
+ else
108
+ hash
109
+ end
98
110
  end
99
- end
100
-
101
- # Helper to remove line breaks and whitespace between XML tags.
102
- def self.clean_xml(xml)
103
- xml = xml.gsub(/\n+/, "")
104
- xml = xml.gsub(/(>)\s*(<)/, '\1\2')
105
- end
106
111
 
107
- # Helper to convert "true" and "false" strings to boolean values.
108
- # Returns the original string in case it doesn't match "true" or "false".
109
- def self.string_to_bool?(string)
110
- return true if string == "true"
111
- return false if string == "false"
112
- string
113
- end
112
+ # Helper to remove line breaks and whitespace between XML tags.
113
+ def clean_xml(xml)
114
+ xml = xml.gsub(/\n+/, "")
115
+ xml = xml.gsub(/(>)\s*(<)/, '\1\2')
116
+ end
114
117
 
118
+ # Helper to convert "true" and "false" strings to boolean values.
119
+ # Returns the original string in case it doesn't match "true" or "false".
120
+ def string_to_bool?(string)
121
+ return true if string == "true"
122
+ return false if string == "false"
123
+ string
124
+ end
125
+
126
+ end
115
127
  end
116
128
 
117
129
  # Shortcut method for translating between XML documents and Hashes.
@@ -4,6 +4,10 @@ require File.join(File.dirname(__FILE__), "..", "lib", "apricoteatsgorilla")
4
4
 
5
5
  class HashToXmlTest < Test::Unit::TestCase
6
6
 
7
+ def setup
8
+ ApricotEatsGorilla.sort_keys = true
9
+ end
10
+
7
11
  def test_dead_simple
8
12
  hash = { "dude" => "likes beer" }
9
13
  expected = "<dude>likes beer</dude>"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smacks-apricoteatsgorilla
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington