smacks-apricoteatsgorilla 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,14 +1,15 @@
1
1
  = Apricot eats Gorilla
2
2
 
3
- Apricot eats Gorilla converts SOAP response messages to Ruby hashes.
4
- It's based on CobraVsMongoose but uses Hpricot instead of REXML and it
5
- also doesn't follow the BadgerFish convention.
3
+ Apricot eats Gorilla translates between XML documents and Ruby hashes.
4
+ It's based on CobraVsMongoose but uses Hpricot instead of REXML to parse
5
+ XML and it also doesn't follow the BadgerFish convention.
6
+
7
+ It's initial purpose was to convert SOAP response messages to Ruby hashes,
8
+ but it quickly evolved into a more general translation tool.
6
9
 
7
10
  == Install
8
11
 
9
- $ sudo gem sources -a http://gems.github.com
10
- $ sudo gem install smacks-apricoteatsgorilla
11
- $ sudo gem install hpricot
12
+ $ sudo gem install smacks-apricoteatsgorilla --source http://gems.github.com
12
13
 
13
14
  == Dependencies
14
15
 
@@ -16,7 +17,7 @@ also doesn't follow the BadgerFish convention.
16
17
 
17
18
  == An example
18
19
 
19
- Let's assume you receive the following SOAP response message:
20
+ Let's assume you receive the following SOAP response:
20
21
 
21
22
  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
22
23
  <soap:Body>
@@ -37,9 +38,9 @@ Let's assume you receive the following SOAP response message:
37
38
 
38
39
  Just pass in the raw XML string like this:
39
40
 
40
- require 'rubygems'
41
- require 'apricoteatsgorilla'
42
- hash = ApricotEatsGorilla.soap_response_to_hash(xml)
41
+ require "rubygems"
42
+ require "apricoteatsgorilla
43
+ hash = ApricotEatsGorilla.xml_to_hash(xml, "//return")
43
44
 
44
45
  And it gets converted into a nice little hash:
45
46
 
@@ -54,12 +55,13 @@ And it gets converted into a nice little hash:
54
55
 
55
56
  == The conclusion
56
57
 
57
- * It assumes a root node named "return", but you can pass in an XPath
58
- expession as the second parameter, to define a different root node.
59
- * All attributes are ignored and won't be included in the hash.
58
+ * The xml_to_hash method starts parsing the XML at the root node by default.
59
+ By calling the method with an XPath expression as second parameter, we define
60
+ a custom root node.
61
+ * Node attributes are ignored and won't be included in the hash.
60
62
  * The value of empty element nodes will be nil.
61
- * Values of "true" or "false" will be converted to boolean objects.
63
+ * Node values of "true" or "false" will be converted to boolean objects.
62
64
 
63
65
  == For more information
64
66
 
65
- Take a look at the tests to see some more examples.
67
+ Take a look at the tests for some more examples.
@@ -2,23 +2,42 @@
2
2
  require 'rubygems'
3
3
  require 'hpricot'
4
4
 
5
- # Apricot eats Gorilla converts SOAP response messages to Ruby hashes.
6
- # It's based on CobraVsMongoose but uses Hpricot instead of REXML and it
7
- # also doesn't follow the BadgerFish convention.
5
+ # Apricot eats Gorilla translates between XML documents and Ruby hashes.
6
+ # It's based on CobraVsMongoose but uses Hpricot instead of REXML to parse
7
+ # XML and it also doesn't follow the BadgerFish convention.
8
+ #
9
+ # It's initial purpose was to convert SOAP response messages to Ruby hashes,
10
+ # but it quickly evolved into a more general translation tool.
8
11
  class ApricotEatsGorilla
9
12
 
10
- # Converts a given SOAP response message into a Ruby Hash.
11
- def self.soap_response_to_hash(xml, root_node = nil)
12
- xml = prepare_xml(xml)
13
- root_node ||= "//return"
13
+ # Converts XML into a Ruby 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 is not 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)
14
23
  doc = Hpricot.XML(xml)
15
- xml_node_to_hash doc.at(root_node)
24
+ root = root_node ? doc.at(root_node) : doc.root
25
+ xml_node_to_hash(root)
26
+ end
27
+
28
+ # Converts a Ruby 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)
16
36
  end
17
37
 
18
38
  private
19
39
 
20
- # Converts XML nodes into a Ruby Hash. Recursively calls itself to grab
21
- # nested XML nodes and values.
40
+ # Converts XML nodes into a Ruby Hash.
22
41
  def self.xml_node_to_hash(node)
23
42
  this_node = {}
24
43
 
@@ -45,8 +64,43 @@ private
45
64
  this_node
46
65
  end
47
66
 
67
+ # Converts a Ruby Hash to XML.
68
+ def self.nested_data_to_xml(name, item)
69
+ children = {}
70
+ case item
71
+ when String
72
+ make_tag(name) { item }
73
+ when Array
74
+ item.map { |subitem| nested_data_to_xml(name, subitem) }.join
75
+ when Hash
76
+ make_tag(name) do
77
+ item.map { |tag, value|
78
+ case value
79
+ when String
80
+ make_tag(tag) { value }
81
+ when Array
82
+ value.map { |subitem| nested_data_to_xml(tag, subitem) }.join
83
+ when Hash
84
+ nested_data_to_xml(tag, value)
85
+ end
86
+ }.join
87
+ end
88
+ end
89
+ end
90
+
91
+ # Helper to create an XML tag. Expects a block for tag content.
92
+ # Defaults to an empty element tag in case no block was supplied.
93
+ def self.make_tag(name)
94
+ body = yield
95
+ if body && !body.empty?
96
+ "<#{name}>" << body << "</#{name}>"
97
+ else
98
+ "<#{name} />"
99
+ end
100
+ end
101
+
48
102
  # Helper to remove line breaks and whitespace between XML nodes.
49
- def self.prepare_xml(xml)
103
+ def self.clean_xml(xml)
50
104
  xml = xml.gsub(/\n+/, "")
51
105
  xml = xml.gsub(/(>)\s*(<)/, '\1\2')
52
106
  end
@@ -0,0 +1,39 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'test/unit'
3
+ require File.join(File.dirname(__FILE__), "..", "lib", "apricoteatsgorilla")
4
+
5
+ class HashToXmlTest < Test::Unit::TestCase
6
+
7
+ def test_dead_simple
8
+ hash = { "dude" => "likes beer" }
9
+ expected = "<dude>likes beer</dude>"
10
+
11
+ result = ApricotEatsGorilla.hash_to_xml(hash)
12
+ assert_equal expected, result
13
+ end
14
+
15
+ def test_nested_hash
16
+ hash = { "dude" => { "likes" => "beer", "hates" => "appletini" } }
17
+ expected = "<dude><hates>appletini</hates><likes>beer</likes></dude>"
18
+
19
+ result = ApricotEatsGorilla.hash_to_xml(hash)
20
+ assert_equal expected, result
21
+ end
22
+
23
+ def test_nested_hash_with_array
24
+ hash = { "dude" => { "likes" => [ "beer", "helicopters" ] } }
25
+ expected = "<dude><likes>beer</likes><likes>helicopters</likes></dude>"
26
+
27
+ result = ApricotEatsGorilla.hash_to_xml(hash)
28
+ assert_equal expected, result
29
+ end
30
+
31
+ def test_nested_hash_with_array_containing_a_hash
32
+ hash = { "dude" => { "likes" => [ { "beer" => "a lot" }, { "helicopters" => "a little more" } ] } }
33
+ expected = "<dude><likes><beer>a lot</beer></likes><likes><helicopters>a little more</helicopters></likes></dude>"
34
+
35
+ result = ApricotEatsGorilla.hash_to_xml(hash)
36
+ assert_equal expected, result
37
+ end
38
+
39
+ end
@@ -1,9 +1,8 @@
1
1
  # -*- coding: utf-8 -*-
2
- require 'rubygems'
3
2
  require 'test/unit'
4
3
  require File.join(File.dirname(__FILE__), "..", "lib", "apricoteatsgorilla")
5
4
 
6
- class ApricotEatsGorillaTest < Test::Unit::TestCase
5
+ class XmlToHashTest < Test::Unit::TestCase
7
6
 
8
7
  def test_soap_response_example
9
8
  xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
@@ -18,49 +17,49 @@ class ApricotEatsGorillaTest < Test::Unit::TestCase
18
17
  </ns2:authenticateResponse>
19
18
  </soap:Body>
20
19
  </soap:Envelope>'
21
- expected = {'authValue' => {'token' => 'secret', 'client' => 'example'}}
20
+ expected = { 'authValue' => { 'token' => 'secret', 'client' => 'example' } }
22
21
 
23
- result = ApricotEatsGorilla.soap_response_to_hash(xml)
22
+ result = ApricotEatsGorilla.xml_to_hash(xml, '//return')
24
23
  assert_equal expected, result
25
24
  end
26
25
 
27
26
  def test_that_boolean_string_values_are_converted_to_actual_boolean_objects
28
27
  xml = '<root><yes>true</yes><no>false</no><txt>something</txt></root>'
29
- expected = {'yes' => true, 'no' => false, 'txt' => 'something'}
28
+ expected = { 'yes' => true, 'no' => false, 'txt' => 'something' }
30
29
 
31
- result = ApricotEatsGorilla.soap_response_to_hash(xml, '//root')
30
+ result = ApricotEatsGorilla.xml_to_hash(xml)
32
31
  assert_equal expected, result
33
32
  end
34
33
 
35
34
  def test_that_empty_element_tags_are_converted_to_nil
36
35
  xml = '<contact><name>Jungle Julia</name><email /><phone/></contact>'
37
- expected = {'name' => 'Jungle Julia', 'email' => nil, 'phone' => nil}
36
+ expected = { 'name' => 'Jungle Julia', 'email' => nil, 'phone' => nil }
38
37
 
39
- result = ApricotEatsGorilla.soap_response_to_hash(xml, '//contact')
38
+ result = ApricotEatsGorilla.xml_to_hash(xml)
40
39
  assert_equal expected, result
41
40
  end
42
41
 
43
42
  def test_that_attributes_get_removed
44
43
  xml = '<todo><paint subject="chair" color="#000000">black</paint></todo>'
45
- expected = {'paint' => 'black'}
44
+ expected = { 'paint' => 'black' }
46
45
 
47
- result = ApricotEatsGorilla.soap_response_to_hash(xml, '//todo')
46
+ result = ApricotEatsGorilla.xml_to_hash(xml)
48
47
  assert_equal expected, result
49
48
  end
50
49
 
51
50
  def test_that_node_groups_with_text_values_get_converted_to_arrays
52
51
  xml = '<root><items>first</items><items>second</items></root>'
53
- expected = {'items' => ['first', 'second']}
52
+ expected = { 'items' => [ 'first', 'second' ] }
54
53
 
55
- result = ApricotEatsGorilla.soap_response_to_hash(xml, '//root')
54
+ result = ApricotEatsGorilla.xml_to_hash(xml)
56
55
  assert_equal expected, result
57
56
  end
58
57
 
59
58
  def test_that_node_groups_with_nesting_get_converted_to_arrays_with_hashes
60
59
  xml = '<root><items><name>first</name></items><items><name>second</name></items></root>'
61
- expected = {'items' => [{'name' => 'first'}, {'name' => 'second'}]}
60
+ expected = { 'items' => [ { 'name' => 'first' }, { 'name' => 'second' } ] }
62
61
 
63
- result = ApricotEatsGorilla.soap_response_to_hash(xml, '//root')
62
+ result = ApricotEatsGorilla.xml_to_hash(xml)
64
63
  assert_equal expected, result
65
64
  end
66
65
 
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.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Harrington
@@ -22,7 +22,7 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 0.6.164
24
24
  version:
25
- description: Apricot eats Gorilla converts SOAP response messages to Ruby hashes
25
+ description: Apricot eats Gorilla translates between XML documents and Ruby hashes.
26
26
  email:
27
27
  executables: []
28
28
 
@@ -59,6 +59,7 @@ rubyforge_project:
59
59
  rubygems_version: 1.2.0
60
60
  signing_key:
61
61
  specification_version: 2
62
- summary: Apricot eats Gorilla converts SOAP response messages to Ruby hashes
62
+ summary: Apricot eats Gorilla translates between XML documents and Ruby hashes.
63
63
  test_files:
64
- - tests/apricoteatsgorilla_test.rb
64
+ - tests/hash_to_xml_test.rb
65
+ - tests/xml_to_hash_test.rb