smacks-apricoteatsgorilla 0.2.8 → 0.3.2

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.
@@ -1,68 +1,37 @@
1
1
  = Apricot eats Gorilla
2
2
 
3
- Apricot eats Gorilla is a helper for working with XML and SOAP messages.
4
- It's based on CobraVsMongoose but without REXML and the BadgerFish convention.
5
- Also it offers some extras for working with SOAP messages.
3
+ Apricot eats Gorilla is a SOAP communication helper.
4
+
5
+ It translates between SOAP response messages (XML) and Ruby Hashes and may
6
+ be used to build a SOAP request envelope. It is based on CobraVsMongoose but
7
+ uses Hpricot instead of REXML and doesn't follow the BadgerFish convention.
6
8
 
7
9
  == Install
8
10
 
9
- $ sudo gem install smacks-apricoteatsgorilla --source http://gems.github.com
11
+ $ gem install smacks-apricoteatsgorilla -s http://gems.github.com
10
12
 
11
13
  == Dependencies
12
14
 
13
15
  Hpricot 0.6.164 (also available for JRuby)
14
16
 
15
- == An example
16
-
17
- Let's assume you receive the following SOAP response:
18
-
19
- xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
20
- <soap:Body>
21
- <ns2:findCustomerByIdResponse xmlns:ns2="http://v1_0.ws.example.com/">
22
- <return>
23
- <empty>false</empty>
24
- <customer>
25
- <id>449810</id>
26
- <title xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
27
- <name>Jungle Julia</name>
28
- <email>jj@example.com</email>
29
- <address />
30
- </customer>
31
- </return>
32
- </ns2:findCustomerByIdResponse>
33
- </soap:Body>
34
- </soap:Envelope>'
35
-
36
- Just pass in the raw XML string:
37
-
38
- require "rubygems"
39
- require "apricoteatsgorilla
40
- hash = ApricotEatsGorilla.xml_to_hash(xml, "//return")
41
-
42
- Or use the shortcut:
43
-
44
- hash = ApricotEatsGorilla(xml, "//return")
17
+ == Translating an XML String into a Ruby Hash
45
18
 
46
- And it gets converted into a nice little Hash:
19
+ xml = "<apricot><eats>Gorilla</eats></apricot>"
20
+ ApricotEatsGorilla[xml]
21
+ # => { :eats => "Gorilla" }
47
22
 
48
- "empty" => false,
49
- "customer" => {
50
- "address" => nil,
51
- "name" => "Jungle Julia",
52
- "title" => nil,
53
- "id" => "449810",
54
- "email" => "jj@example.com"
55
- }
23
+ == Translating a Ruby Hash into an XML String
56
24
 
57
- == Conclusions
25
+ hash = { :apricot => { :eats => "Gorilla" } }
26
+ ApricotEatsGorilla[hash]
27
+ # => "<apricot><eats>Gorilla</eats></apricot>"
58
28
 
59
- * The xml_to_hash method starts parsing the XML at the root node by default.
60
- By calling the method with an XPath expression as second parameter, we're
61
- able to define a custom root node.
62
- * Node attributes are ignored and won't be included in the hash.
63
- * The value of empty element nodes will be nil.
64
- * Node values of "true" or "false" will be converted to boolean objects.
29
+ == Creating a SOAP request envelope
65
30
 
66
- == More information
31
+ ApricotEatsGorilla.soap_envelope { "<authenticate>me</authenticate>" }
67
32
 
68
- Take a look at the tests for some more examples.
33
+ # => '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
34
+ # => <env:Body>
35
+ # => <authenticate>me</authenticate>
36
+ # => </env:Body>
37
+ # => </env:Envelope>'
@@ -1,24 +1,77 @@
1
- # -*- coding: utf-8 -*-
2
- require 'rubygems'
3
- require 'hpricot'
4
-
5
- # Apricot eats Gorilla is a helper for working with XML and SOAP messages.
6
- # It's based on CobraVsMongoose but without REXML and the BadgerFish convention.
7
- # Also it offers some extras for working with SOAP messages.
1
+ require "rubygems"
2
+ require "hpricot"
3
+
4
+ # Apricot eats Gorilla is a SOAP communication helper.
5
+ #
6
+ # It translates between SOAP response messages (XML) and Ruby Hashes and may
7
+ # be used to build a SOAP request envelope. It is based on CobraVsMongoose but
8
+ # uses Hpricot instead of REXML and doesn't follow the BadgerFish convention.
9
+ #
10
+ # == Translating an XML String into a Ruby Hash
11
+ #
12
+ # xml = "<apricot><eats>Gorilla</eats></apricot>"
13
+ # ApricotEatsGorilla[xml]
14
+ # # => { :eats => "Gorilla" }
15
+ #
16
+ # == Translating a Ruby Hash into an XML String
17
+ #
18
+ # hash = { :apricot => { :eats => "Gorilla" } }
19
+ # ApricotEatsGorilla[hash]
20
+ # # => "<apricot><eats>Gorilla</eats></apricot>"
21
+ #
22
+ # == Creating a SOAP request envelope
23
+ #
24
+ # ApricotEatsGorilla.soap_envelope { "<authenticate>me</authenticate>" }
25
+ #
26
+ # # => '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
27
+ # # => <env:Body>
28
+ # # => <authenticate>me</authenticate>
29
+ # # => </env:Body>
30
+ # # => </env:Envelope>'
8
31
  class ApricotEatsGorilla
9
- class << self
32
+ class << self # Class methods
10
33
 
11
- # Flag to enable optional sorting of Hash keys. Especially useful for
12
- # comparing expected values with actual results while testing.
34
+ # Flag to enable optional sorting of Hash keys.
13
35
  attr_accessor :sort_keys
14
36
 
15
- # Converts XML into a Hash. Starts parsing at root node by default.
16
- # Call with XPath expession (Hpricot search) as second parameter to define
17
- # a custom root node. The root node itself will not be included in the Hash.
37
+ # Shortcut method for translating between XML Strings and Ruby Hashes.
38
+ # Delegates to xml_to_hash in case +source+ is of type String or delegates
39
+ # to hash_to_xml in case +source+ is of type Hash. Returns nil otherwise.
40
+ #
41
+ # ==== Parameters
18
42
  #
19
- # xml = "<dude><likes>beer</likes><hates>appletini</hates></dude>"
20
- # ApricotEatsGorilla.xml_to_hash(xml)
21
- # # => { "hates" => "appletini", "likes" => "beer" }
43
+ # * +source+ - The XML String or Ruby Hash to translate.
44
+ # * +root_node+ - Optional. Custom root node to start parsing the given XML.
45
+ def [](source, root_node = nil)
46
+ case source
47
+ when String
48
+ xml_to_hash(source, root_node)
49
+ when Hash
50
+ hash_to_xml(source)
51
+ else
52
+ nil
53
+ end
54
+ end
55
+
56
+ # Converts a given +xml+ String into a Ruby Hash. Starts parsing at root
57
+ # node by default. The optional +root_node+ parameter can be used to specify
58
+ # a custom root node to start parsing at via XPath (Hpricot search).
59
+ # The root node itself won't be included in the Hash.
60
+ #
61
+ # ==== Parameters
62
+ #
63
+ # * +xml+ - The XML String to translate into a Ruby Hash.
64
+ # * +root_node+ - Optional. Custom root node to start parsing the given XML.
65
+ #
66
+ # ==== Examples
67
+ #
68
+ # xml = "<apricot><eats>Gorilla</eats></apricot>"
69
+ # ApricotEatsGorilla[xml]
70
+ # # => { :eats => "Gorilla" }
71
+ #
72
+ # xml = "<apricot><eats><lotsOf>Gorillas</lotsOf></eats></apricot>"
73
+ # ApricotEatsGorilla[xml, "//eats"]
74
+ # # => { :lots_of => "Gorillas" }
22
75
  def xml_to_hash(xml, root_node = nil)
23
76
  xml = clean_xml(xml)
24
77
  doc = Hpricot.XML(xml)
@@ -26,24 +79,54 @@ class ApricotEatsGorilla
26
79
  xml_node_to_hash(root)
27
80
  end
28
81
 
29
- # Converts a Hash to XML.
30
- #
31
- # hash = { "dude" => { "likes" => "beer", "hates" => "appletini" } }
32
- # ApricotEatsGorilla.hash_to_xml(hash)
33
- # # => "<dude><hates>appletini</hates><likes>beer</likes></dude>"
82
+ # Converts a given +hash+ into an XML String.
83
+ #
84
+ # ==== Parameters
85
+ #
86
+ # * +hash+ - The Ruby Hash to translate into an XML String.
87
+ #
88
+ # ==== Examples
89
+ #
90
+ # hash = { :apricot => { :eats => "Gorilla" } }
91
+ # ApricotEatsGorilla[hash]
92
+ # # => "<apricot><eats>Gorilla</eats></apricot>"
93
+ #
94
+ # hash = { :apricot => { :eats => [ "Gorillas", "Snakes" ] } }
95
+ # ApricotEatsGorilla[hash]
96
+ # # => "<apricot><eats>Gorillas</eats><eats>Snakes</eats></apricot>"
34
97
  def hash_to_xml(hash)
35
98
  nested_data_to_xml(hash.keys.first, hash.values.first)
36
99
  end
37
100
 
38
- # Builds a SOAP envelope and includes the content from an expected block
39
- # into the envelope body. Pass in a Hash of namespaces and their URI to
40
- # set custom namespaces.
101
+ # Builds a SOAP request envelope and includes the content from a given
102
+ # +block+ into the envelope body. Accepts a Hash of +namespaces+ and their
103
+ # corresponding URI for specifying custom namespaces.
104
+ #
105
+ # ==== Parameters
106
+ #
107
+ # * +namespaces+ - A Hash of namespaces and their corresponding URI.
108
+ #
109
+ # ==== Examples
41
110
  #
42
- # <env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
43
- # <env:Body>
44
- # yield
45
- # </env:Body>
46
- # </env:Envelope>
111
+ # ApricotEatsGorilla.soap_envelope { "<authenticate>me</authenticate>" }
112
+ #
113
+ # # => '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
114
+ # # => <env:Body>
115
+ # # => <authenticate>me</authenticate>
116
+ # # => </env:Body>
117
+ # # => </env:Envelope>'
118
+ #
119
+ # ApricotEatsGorilla.soap_envelope :wsdl => "http://example.com" do
120
+ # "<authenticate>me</authenticate>"
121
+ # end
122
+ #
123
+ # # => '<env:Envelope
124
+ # # => xmlns:wsdl="http://example.com"
125
+ # # => xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
126
+ # # => <env:Body>
127
+ # # => <authenticate>me</authenticate>
128
+ # # => </env:Body>
129
+ # # => </env:Envelope>'
47
130
  def soap_envelope(namespaces = {})
48
131
  namespaces["env"] = "http://schemas.xmlsoap.org/soap/envelope/"
49
132
  tag("env:Envelope", namespaces) do
@@ -53,33 +136,28 @@ class ApricotEatsGorilla
53
136
  end
54
137
  end
55
138
 
56
- # Creates an XML tag. Expects a block for tag content.
57
- # Defaults to an empty element tag in case no block was supplied.
58
- def tag(name, attributes = {})
59
- return "<#{name} />" unless block_given?
60
-
61
- attr = opt_order(attributes).map { |k, v| %Q( xmlns:#{k}="#{v}") }.to_s
62
- body = (yield && !yield.empty?) ? yield : ""
63
- "<#{name}#{attr}>" << body << "</#{name}>"
64
- end
65
-
66
139
  private
67
140
 
68
- # Converts XML into a Hash.
69
- def xml_node_to_hash(node)
141
+ # Actual implementation for xml_to_hash. Takes and iterates through a given
142
+ # Hpricot +element+ and returns a Ruby Hash equal to the given content.
143
+ #
144
+ # ==== Parameters
145
+ #
146
+ # * +element+ - The Hpricot element to translate into a Ruby Hash.
147
+ def xml_node_to_hash(element)
70
148
  this_node = {}
71
- node.each_child do |child|
149
+ element.each_child do |child|
72
150
  # hpricot 0.6.1 returns an empty array, while 0.8 returns nil
73
151
  if child.children.nil? || child.children.empty?
74
152
  key, value = child.name, nil
75
153
  elsif child.children.size == 1 && child.children.first.text?
76
- key, value = child.name, booleanize(child.children.first.inner_text)
154
+ key, value = child.name, booleanize(child.children.first.to_html)
77
155
  else
78
156
  key, value = child.name, xml_node_to_hash(child)
79
157
  end
80
158
 
81
159
  key = remove_namespace(key)
82
- key = to_snake_case(key)
160
+ key = to_snake_case(key).intern
83
161
  current = this_node[key]
84
162
  case current
85
163
  when Array
@@ -93,7 +171,13 @@ class ApricotEatsGorilla
93
171
  this_node
94
172
  end
95
173
 
96
- # Converts a Hash to XML.
174
+ # Actual implementation for hash_to_xml. Takes a Hash key +name+ and a
175
+ # value +item+ and returns an XML String equal to the given content.
176
+ #
177
+ # ==== Parameters
178
+ #
179
+ # * +name+ - A Hash key to translate into an XML String.
180
+ # * +item+ - A Hash value to translate into an XML String.
97
181
  def nested_data_to_xml(name, item)
98
182
  case item
99
183
  when String
@@ -116,52 +200,53 @@ class ApricotEatsGorilla
116
200
  end
117
201
  end
118
202
 
119
- # Returns a sorted version of a given Hash in case :sort_keys is enabled.
120
- def opt_order(hash)
121
- return hash unless sort_keys
122
- hash.sort_by { |kv| kv.first }
203
+ # Creates an XML tag. Expects a block for tag content. Defaults to an empty
204
+ # element tag in case no block was supplied.
205
+ #
206
+ # ==== Parameters
207
+ #
208
+ # * +name+ - The name of the XML tag.
209
+ # * +attributes+ - Optional. Hash of attributes for the XML tag.
210
+ def tag(name, attributes = {})
211
+ return "<#{name} />" unless block_given?
212
+
213
+ attr = opt_order(attributes).map { |k, v| %Q( xmlns:#{k}="#{v}") }.to_s
214
+ body = (yield && !yield.empty?) ? yield : ""
215
+ "<#{name}#{attr}>" << body << "</#{name}>"
123
216
  end
124
217
 
125
- # Removes line breaks and whitespace between XML tags.
218
+ # Removes line breaks and whitespace between tags from a given +xml+ String.
126
219
  def clean_xml(xml)
127
220
  xml = xml.gsub(/\n+/, "")
128
221
  xml.gsub(/(>)\s*(<)/, '\1\2')
129
222
  end
130
223
 
131
- # Removes namespaces from XML tags.
224
+ # Removes the namespace from a given XML +tag+.
132
225
  def remove_namespace(tag)
133
226
  tag.sub(/.+:(.+)/, '\1')
134
227
  end
135
228
 
136
- # Converts CamelCase and lowerCamelCase to snake_case.
229
+ # Converts a given +string+ from CamelCase/lowerCamelCase to snake_case.
137
230
  def to_snake_case(string)
138
231
  string = string.gsub(/[A-Z]+/, '\1_\0').downcase
139
232
  string = string[1, string.length-1] if string[0, 1] == "_"
140
233
  string
141
234
  end
142
235
 
143
- # Converts "true" and "false" strings to boolean values.
144
- # Returns the original string in case it doesn't match "true" or "false".
236
+ # Checks to see if a given +string+ matches "true" or "false" and converts
237
+ # these values to actual Boolean objects. Returns the original string in
238
+ # case it does not match "true" or "false".
145
239
  def booleanize(string)
146
240
  return true if string == "true"
147
241
  return false if string == "false"
148
242
  string
149
243
  end
150
244
 
151
- end
152
- end
153
-
154
- # Shortcut method for translating between XML documents and Hashes.
155
- # Calls xml_to_hash in case the first parameter is of type String.
156
- # Calls hash_to_xml in case the first parameter is of type Hash.
157
- # Returns nil otherwise.
158
- def ApricotEatsGorilla(source, root_node = nil)
159
- case source
160
- when String
161
- ApricotEatsGorilla.xml_to_hash(source, root_node)
162
- when Hash
163
- ApricotEatsGorilla.hash_to_xml(source)
164
- else
165
- nil
245
+ # Returns a sorted version of a given +hash+ if :sort_keys was enabled.
246
+ def opt_order(hash)
247
+ return hash unless sort_keys
248
+ hash.sort_by { |kv| kv.first }
249
+ end
250
+
166
251
  end
167
252
  end
@@ -1,125 +1,5 @@
1
- # -*- coding: utf-8 -*-
2
- require 'test/unit'
3
- require File.join(File.dirname(__FILE__), "..", "lib", "apricoteatsgorilla")
4
-
5
- class ApricotEatsGorillaTest < Test::Unit::TestCase
6
-
7
- def setup
8
- ApricotEatsGorilla.sort_keys = true
9
- end
10
-
11
- # xml_to_hash
12
- def test_xml_to_hash_with_soap_response_example
13
- xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
14
- <soap:Body>
15
- <ns2:authenticateResponse xmlns:ns2="http://v1_0.ws.example.com/">
16
- <return>
17
- <authValue>
18
- <token>secret</token>
19
- <client>example</client>
20
- </authValue>
21
- </return>
22
- </ns2:authenticateResponse>
23
- </soap:Body>
24
- </soap:Envelope>'
25
- expected = { 'auth_value' => { 'token' => 'secret', 'client' => 'example' } }
26
-
27
- result = ApricotEatsGorilla.xml_to_hash(xml, '//return')
28
- assert_equal expected, result
29
- end
30
-
31
- def test_xml_to_hash_make_sure_boolean_values_get_converted_to_boolean_objects
32
- xml = '<root><yes>true</yes><no>false</no><txt>something</txt></root>'
33
- expected = { 'yes' => true, 'no' => false, 'txt' => 'something' }
34
-
35
- result = ApricotEatsGorilla.xml_to_hash(xml)
36
- assert_equal expected, result
37
- end
38
-
39
- def test_xml_to_hash_make_sure_empty_element_tags_get_converted_to_nil
40
- xml = '<contact><name>Jungle Julia</name><email /><phone/></contact>'
41
- expected = { 'name' => 'Jungle Julia', 'email' => nil, 'phone' => nil }
42
-
43
- result = ApricotEatsGorilla.xml_to_hash(xml)
44
- assert_equal expected, result
45
- end
46
-
47
- def test_xml_to_hash_make_sure_node_attributes_get_removed
48
- xml = '<todo><paint subject="chair" color="#000000">black</paint></todo>'
49
- expected = { 'paint' => 'black' }
50
-
51
- result = ApricotEatsGorilla.xml_to_hash(xml)
52
- assert_equal expected, result
53
- end
54
-
55
- def test_xml_to_hash_make_sure_node_groups_with_text_values_get_converted_to_arrays
56
- xml = '<root><items>first</items><items>second</items></root>'
57
- expected = { 'items' => [ 'first', 'second' ] }
58
-
59
- result = ApricotEatsGorilla.xml_to_hash(xml)
60
- assert_equal expected, result
61
- end
62
-
63
- def test_xml_to_hash_make_sure_node_groups_with_nesting_get_converted_to_arrays_with_hashes
64
- xml = '<root><items><name>first</name></items><items><name>second</name></items></root>'
65
- expected = { 'items' => [ { 'name' => 'first' }, { 'name' => 'second' } ] }
66
-
67
- result = ApricotEatsGorilla.xml_to_hash(xml)
68
- assert_equal expected, result
69
- end
70
-
71
- # hash_to_xml
72
- def test_hash_to_xml_with_simple_hash
73
- hash = { "dude" => "likes beer" }
74
- expected = "<dude>likes beer</dude>"
75
-
76
- result = ApricotEatsGorilla.hash_to_xml(hash)
77
- assert_equal expected, result
78
- end
79
-
80
- def test_hash_to_xml_with_nested_hash
81
- hash = { "dude" => { "likes" => "beer", "hates" => "appletini" } }
82
- expected = "<dude><hates>appletini</hates><likes>beer</likes></dude>"
83
-
84
- result = ApricotEatsGorilla.hash_to_xml(hash)
85
- assert_equal expected, result
86
- end
87
-
88
- def test_hash_to_xml_with_nested_hash_and_array
89
- hash = { "dude" => { "likes" => [ "beer", "helicopters" ] } }
90
- expected = "<dude><likes>beer</likes><likes>helicopters</likes></dude>"
91
-
92
- result = ApricotEatsGorilla.hash_to_xml(hash)
93
- assert_equal expected, result
94
- end
95
-
96
- def test_hash_to_xml_with_nested_hash_and_array_containing_a_hash
97
- hash = { "dude" => { "likes" => [ { "beer" => "a lot" }, { "helicopters" => "a little more" } ] } }
98
- expected = "<dude><likes><beer>a lot</beer></likes><likes><helicopters>a little more</helicopters></likes></dude>"
99
-
100
- result = ApricotEatsGorilla.hash_to_xml(hash)
101
- assert_equal expected, result
102
- end
103
-
104
- # soap_envelope
105
- def test_soap_envelope_without_namespace_and_block_returns_pure_envelope
106
- expected = '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">' <<
107
- '<env:Body></env:Body></env:Envelope>'
108
-
109
- result = ApricotEatsGorilla.soap_envelope
110
- assert_equal expected, result
111
- end
112
-
113
- def test_soap_envelope_with_namespace_and_block_returns_envelope_with_namespace_and_content
114
- expected = '<env:Envelope ' <<
115
- 'xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" ' <<
116
- 'xmlns:wsdl="http://example.com">' <<
117
- '<env:Body><id>123</id></env:Body></env:Envelope>'
118
-
119
- result = ApricotEatsGorilla.soap_envelope("wsdl" => "http://example.com") do
120
- "<id>123</id>"
121
- end
122
- assert_equal expected, result
123
- end
124
-
125
- end
1
+ $:.unshift(File.join(File.dirname(__FILE__), "apricoteatsgorilla"))
2
+ require "shortcut_method_test"
3
+ require "xml_to_hash_test"
4
+ require "hash_to_xml_test"
5
+ require "soap_envelope_test"
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.8
4
+ version: 0.3.2
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 is a helper for working with XML and SOAP messages.
25
+ description: Apricot eats Gorilla is a SOAP communication helper.
26
26
  email:
27
27
  executables: []
28
28
 
@@ -59,6 +59,6 @@ rubyforge_project:
59
59
  rubygems_version: 1.2.0
60
60
  signing_key:
61
61
  specification_version: 2
62
- summary: Apricot eats Gorilla is a helper for working with XML and SOAP messages.
62
+ summary: Apricot eats Gorilla is a SOAP communication helper.
63
63
  test_files:
64
64
  - tests/apricoteatsgorilla_test.rb