apricoteatsgorilla 0.5.9

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.
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ nbproject
3
+ pkg
4
+ rdoc
5
+ .autotest
@@ -0,0 +1,54 @@
1
+ = Apricot eats Gorilla
2
+
3
+ Apricot eats Gorilla is a SOAP communication helper. It translates between
4
+ SOAP messages (XML) and Ruby Hashes and comes with some additional helpers
5
+ for working with SOAP services.
6
+
7
+ == Install
8
+
9
+ $ gem install rubiii-apricoteatsgorilla -s http://gems.github.com
10
+
11
+ == Dependencies
12
+
13
+ hpricot 0.8.241 (the latest JRuby-compatible version)
14
+
15
+ Also available at: {GitHub Downloads}[http://github.com/rubiii/apricoteatsgorilla/downloads]
16
+
17
+ == Translate an XML String into a Ruby Hash
18
+
19
+ xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
20
+ <soap:Body>
21
+ <ns2:authenticateResponse xmlns:ns2="http://v1_0.ws.example.com/">
22
+ <return>
23
+ <apricot>
24
+ <eats>Gorilla</eats>
25
+ </apricot>
26
+ </return>
27
+ </ns2:authenticateResponse>
28
+ </soap:Body>
29
+ </soap:Envelope>'
30
+
31
+ ApricotEatsGorilla[xml, "//return"]
32
+ # => { :apricot => { :eats => "Gorilla" } }
33
+
34
+ == Translate a Ruby Hash into an XML String
35
+
36
+ hash = { :apricot => { :eats => "Gorilla" } }
37
+
38
+ ApricotEatsGorilla[hash]
39
+ # => "<apricot><eats>Gorilla</eats></apricot>"
40
+
41
+ == Build a SOAP request envelope
42
+
43
+ ApricotEatsGorilla.soap_envelope { "<apricot><eats>Gorilla</eats></apricot>" }
44
+
45
+ # => '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
46
+ # => <env:Body>
47
+ # => <apricot><eats>Gorilla</eats></apricot>
48
+ # => </env:Body>
49
+ # => </env:Envelope>'
50
+
51
+ == Read more
52
+
53
+ For more detailed information, please take a look at the
54
+ {GitHub Wiki}[http://wiki.github.com/rubiii/apricoteatsgorilla].
@@ -0,0 +1,43 @@
1
+ require "rubygems"
2
+ require "rake"
3
+ require "spec/rake/spectask"
4
+ require "rake/rdoctask"
5
+
6
+ task :default => :spec
7
+
8
+ Spec::Rake::SpecTask.new do |spec|
9
+ spec.spec_files = FileList["spec/**/*_spec.rb"]
10
+ spec.spec_opts << "--color"
11
+ end
12
+
13
+ Rake::RDocTask.new do |rdoc|
14
+ rdoc.title = "Apricot eats Gorilla"
15
+ rdoc.rdoc_dir = "rdoc"
16
+ rdoc.main = "README.rdoc"
17
+ rdoc.rdoc_files.include("README.rdoc", "lib/**/*.rb")
18
+ rdoc.options = ["--line-numbers", "--inline-source"]
19
+ end
20
+
21
+ begin
22
+ require "jeweler"
23
+ Jeweler::Tasks.new do |spec|
24
+ spec.name = "apricoteatsgorilla"
25
+ spec.author = "Daniel Harrington"
26
+ spec.email = "me@rubiii.com"
27
+ spec.homepage = "http://github.com/rubiii/apricoteatsgorilla"
28
+ spec.summary = "SOAP communication helper"
29
+ spec.description = spec.summary
30
+
31
+ spec.rdoc_options += [
32
+ "--title", "Apricot eats Gorilla",
33
+ "--main", "README.rdoc",
34
+ "--line-numbers",
35
+ "--inline-source"
36
+ ]
37
+
38
+ spec.add_runtime_dependency("hpricot", "0.8.241")
39
+ spec.add_development_dependency("rspec", ">= 1.2.8")
40
+ end
41
+ rescue LoadError
42
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
43
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.5.9
@@ -0,0 +1,64 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{apricoteatsgorilla}
8
+ s.version = "0.5.9"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Daniel Harrington"]
12
+ s.date = %q{2009-10-06}
13
+ s.description = %q{SOAP communication helper}
14
+ s.email = %q{me@rubiii.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "apricoteatsgorilla.gemspec",
24
+ "lib/apricoteatsgorilla.rb",
25
+ "lib/apricoteatsgorilla/apricoteatsgorilla.rb",
26
+ "lib/apricoteatsgorilla/xml_node.rb",
27
+ "spec/apricoteatsgorilla/apricoteatsgorilla_spec.rb",
28
+ "spec/apricoteatsgorilla/hash_to_xml_spec.rb",
29
+ "spec/apricoteatsgorilla/soap_envelope_spec.rb",
30
+ "spec/apricoteatsgorilla/xml_node_spec.rb",
31
+ "spec/apricoteatsgorilla/xml_to_hash_spec.rb",
32
+ "spec/spec_helper.rb"
33
+ ]
34
+ s.has_rdoc = true
35
+ s.homepage = %q{http://github.com/rubiii/apricoteatsgorilla}
36
+ s.rdoc_options = ["--charset=UTF-8", "--title", "Apricot eats Gorilla", "--main", "README.rdoc", "--line-numbers", "--inline-source"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.1}
39
+ s.summary = %q{SOAP communication helper}
40
+ s.test_files = [
41
+ "spec/apricoteatsgorilla/apricoteatsgorilla_spec.rb",
42
+ "spec/apricoteatsgorilla/hash_to_xml_spec.rb",
43
+ "spec/apricoteatsgorilla/soap_envelope_spec.rb",
44
+ "spec/apricoteatsgorilla/xml_node_spec.rb",
45
+ "spec/apricoteatsgorilla/xml_to_hash_spec.rb",
46
+ "spec/spec_helper.rb"
47
+ ]
48
+
49
+ if s.respond_to? :specification_version then
50
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
51
+ s.specification_version = 2
52
+
53
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
54
+ s.add_runtime_dependency(%q<hpricot>, ["= 0.8.241"])
55
+ s.add_development_dependency(%q<rspec>, [">= 1.2.8"])
56
+ else
57
+ s.add_dependency(%q<hpricot>, ["= 0.8.241"])
58
+ s.add_dependency(%q<rspec>, [">= 1.2.8"])
59
+ end
60
+ else
61
+ s.add_dependency(%q<hpricot>, ["= 0.8.241"])
62
+ s.add_dependency(%q<rspec>, [">= 1.2.8"])
63
+ end
64
+ end
@@ -0,0 +1,3 @@
1
+ %w(apricoteatsgorilla xml_node).each do |file|
2
+ require File.join(File.dirname(__FILE__), "apricoteatsgorilla", file)
3
+ end
@@ -0,0 +1,324 @@
1
+ %w(digest/sha1 rubygems hpricot).each do |gem|
2
+ require gem
3
+ end
4
+
5
+ # == ApricotEatsGorilla
6
+ #
7
+ # Apricot eats Gorilla is a SOAP communication helper. It translates between
8
+ # SOAP messages (XML) and Ruby Hashes and comes with some additional helpers
9
+ # for working with SOAP services.
10
+ class ApricotEatsGorilla
11
+ class << self
12
+
13
+ # SOAP dateTime format.
14
+ SOAPDateTimeFormat = "%Y-%m-%dT%H:%M:%S"
15
+
16
+ # SOAP dateTime Regexp.
17
+ SOAPDateTimeRegexp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/
18
+
19
+ # SOAP namespaces by SOAP version.
20
+ SOAPNamespace = {
21
+ 1 => "http://schemas.xmlsoap.org/soap/envelope/",
22
+ 2 => "http://www.w3.org/2003/05/soap-envelope"
23
+ }
24
+
25
+ # Namespace for WS Security Secext
26
+ WSENamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
27
+
28
+ # Namespace for WS Security Utility
29
+ WSUNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
30
+
31
+ # Flag to enable sorting of Hash keys.
32
+ attr_accessor :sort_keys
33
+
34
+ # Flag to disable conversion of XML tags to lowerCamelCase.
35
+ attr_accessor :disable_tag_names_to_lower_camel_case
36
+
37
+ # Flag to disable conversion of Hash keys to snake_case.
38
+ attr_accessor :disable_hash_keys_to_snake_case
39
+
40
+ # Flag to disable conversion of Hash keys to Symbols.
41
+ attr_accessor :disable_hash_keys_to_symbols
42
+
43
+ # Hash of namespaces (keys) and XML nodes (values) to apply these
44
+ # namespaces to.
45
+ attr_accessor :nodes_to_namespace
46
+
47
+ # Shortcut method for translating between XML Strings and Ruby Hashes.
48
+ # Delegates to +xml_to_hash+ in case +source+ is a String or delegates
49
+ # to +hash_to_xml+ in case +source+ is a Hash. Returns nil otherwise.
50
+ def [](source, root_node = nil)
51
+ case source
52
+ when String
53
+ xml_to_hash(source, root_node)
54
+ when Hash
55
+ hash_to_xml(source)
56
+ else
57
+ nil
58
+ end
59
+ end
60
+
61
+ # Yields this class to a given +block+ for wrapping the setup of multiple
62
+ # flags at once.
63
+ def setup
64
+ yield self if block_given?
65
+ end
66
+
67
+ # Translates a given +xml+ String into a Ruby Hash.
68
+ #
69
+ # Starts parsing at the XML root node by default. Accepts an optional
70
+ # +root_node+ parameter for defining a custom root node to start parsing
71
+ # at using an XPath-Expression (Hpricot search).
72
+ #
73
+ # Notice that both namespaces and attributes get removed and the root node
74
+ # itself won't be included in the Hash.
75
+ #
76
+ # ==== Examples
77
+ #
78
+ # xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
79
+ # <soap:Body>
80
+ # <ns2:authenticateResponse xmlns:ns2="http://v1_0.ws.example.com/">
81
+ # <return>
82
+ # <apricot>
83
+ # <eats>Gorilla</eats>
84
+ # </apricot>
85
+ # </return>
86
+ # </ns2:authenticateResponse>
87
+ # </soap:Body>
88
+ # </soap:Envelope>'
89
+ #
90
+ # ApricotEatsGorilla.xml_to_hash(xml)
91
+ # # => { :body => { :authenticate_response => { :return => { :apricot => { :eats => "Gorilla" } } } } }
92
+ #
93
+ # ApricotEatsGorilla.xml_to_hash(xml, "//return")
94
+ # # => { :apricot => { :eats => "Gorilla" } }
95
+ def xml_to_hash(xml, root_node = nil)
96
+ doc = Hpricot.XML remove_whitespace(xml)
97
+ root = root_node ? doc.search(root_node) : doc.root
98
+
99
+ return nil if root.nil? || (root.respond_to?(:size) && root.size == 0)
100
+ if !root.respond_to? :each
101
+ xml_node_to_hash(root)
102
+ elsif root.size == 1
103
+ single_xml_root_node_to_hash(root)
104
+ else
105
+ multiple_xml_root_nodes_to_hash(root)
106
+ end
107
+ end
108
+
109
+ # Translates a given Ruby +hash+ into an XML String.
110
+ #
111
+ # ==== Examples
112
+ #
113
+ # hash = { :apricot => { :eats => "Gorilla" } }
114
+ # ApricotEatsGorilla.hash_to_xml(hash)
115
+ #
116
+ # # => "<apricot><eats>Gorilla</eats></apricot>"
117
+ #
118
+ # hash = { :apricot => { :eats => ["Gorillas", "Snakes"] } }
119
+ # ApricotEatsGorilla.hash_to_xml(hash)
120
+ #
121
+ # # => "<apricot><eats>Gorillas</eats><eats>Snakes</eats></apricot>"
122
+ def hash_to_xml(hash)
123
+ nested_data_to_xml(hash.keys.first, hash.values.first)
124
+ end
125
+
126
+ # Builds a SOAP request envelope and includes the content of a given
127
+ # +block+ into the envelope body. Accepts a Hash of additional +namespaces+
128
+ # to set. Also accepts an optional +version+ to specify the SOAP envelope
129
+ # namespace to use by SOAP version.
130
+ #
131
+ # ==== Examples
132
+ #
133
+ # ApricotEatsGorilla.soap_envelope do
134
+ # "<apricot><eats>Gorilla</eats></apricot>"
135
+ # end
136
+ #
137
+ # # => '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
138
+ # # => <env:Body>
139
+ # # => <apricot><eats>Gorilla</eats></apricot>
140
+ # # => </env:Body>
141
+ # # => </env:Envelope>'
142
+ #
143
+ # ApricotEatsGorilla.soap_envelope(:wsdl => "http://example.com") { "pureText" }
144
+ #
145
+ # # => '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsdl="http://example.com">
146
+ # # => <env:Body>
147
+ # # => pureText
148
+ # # => </env:Body>
149
+ # # => </env:Envelope>'
150
+ def soap_envelope(namespaces = {}, wsse = {}, version = 1)
151
+ namespaces[:env] ||= SOAPNamespace[version] if SOAPNamespace[version]
152
+
153
+ header = xml_node("env:Header") { soap_header(wsse) }
154
+ body = xml_node("env:Body") { (yield if block_given?) || nil }
155
+
156
+ xml_node("env:Envelope", namespaces) { header + body }
157
+ end
158
+
159
+ private
160
+
161
+ def soap_header(wsse)
162
+ return nil if !wsse || wsse.empty?
163
+
164
+ now = Time.now
165
+ xml_node("wsse:Security", :wsse => WSENamespace) do
166
+ xml_node("wsse:UsernameToken", :wsu => WSUNamespace) do
167
+ xml_node("wsse:Username") { wsse[:username] } <<
168
+ xml_node("wsse:Password") { wsse[:password] } <<
169
+ xml_node("wsse:Nonce") { wsse_nonce(now) } <<
170
+ xml_node("wsu:Created") { now.strftime(SOAPDateTimeFormat) }
171
+ end
172
+ end
173
+ end
174
+
175
+ # Returns a random WSSE nonce.
176
+ def wsse_nonce(now = nil)
177
+ now ||= Time.now
178
+ Digest::SHA1.hexdigest(random_string + now.to_i.to_s)
179
+ end
180
+
181
+ # Returns a random string of a certain +length+.
182
+ def random_string(length = 20)
183
+ (0...length).map { ("a".."z").to_a[rand(26)] }.join
184
+ end
185
+
186
+ # Translates a single XML root node to a Ruby Hash.
187
+ def single_xml_root_node_to_hash(root)
188
+ if root.first.children.nil?
189
+ {}
190
+ elsif root.first.children.first.kind_of? Hpricot::Text
191
+ map_xml_value(root.first.children.to_s)
192
+ else
193
+ xml_node_to_hash(root.first)
194
+ end
195
+ end
196
+
197
+ # Translates multiple XML root nodes to a Ruby Hash.
198
+ def multiple_xml_root_nodes_to_hash(root)
199
+ root.map do |node|
200
+ if node.children.first.kind_of? Hpricot::Text
201
+ map_xml_value(node.children.to_s)
202
+ else
203
+ xml_node_to_hash(node)
204
+ end
205
+ end
206
+ end
207
+
208
+ # Iterates through a given Hpricot +element+ and returns a Ruby Hash
209
+ # equal to the XML content of the given element.
210
+ def xml_node_to_hash(element)
211
+ hash = {}
212
+
213
+ element.each_child do |child|
214
+ key = create_hash_key(child.name)
215
+ value = create_hash_value(child)
216
+
217
+ case hash[key]
218
+ when Array
219
+ hash[key] << value
220
+ when nil
221
+ hash[key] = value
222
+ else
223
+ hash[key] = [ hash[key].dup, value ]
224
+ end
225
+ end
226
+
227
+ hash
228
+ end
229
+
230
+ # Returns a Hash key for +xml_node_to_hash+ by a given +name+.
231
+ def create_hash_key(name)
232
+ key = XMLNode.new(name)
233
+ key.strip_namespace!
234
+ key.to_snake_case! unless disable_hash_keys_to_snake_case
235
+ key = disable_hash_keys_to_symbols ? key.to_s : key.to_sym
236
+ end
237
+
238
+ # Returns a Hash value for +xml_node_to_hash+ by a given +value+.
239
+ def create_hash_value(value)
240
+ if value.children.nil? || value.children.empty?
241
+ nil
242
+ elsif value.children.size == 1 && value.children.first.text?
243
+ map_xml_value(value.children.first.to_html)
244
+ else
245
+ xml_node_to_hash(value)
246
+ end
247
+ end
248
+
249
+ # Expects a Hash +key+ and a Hash +value+. Iterates through the given Hash
250
+ # +value+ and returns an XML String of the given Hash structure.
251
+ def nested_data_to_xml(key, value)
252
+ case value
253
+ when Array
254
+ value.map { |subitem| nested_data_to_xml(key, subitem) }.join
255
+ when Hash
256
+ xml_node(key) do
257
+ sort_hash_keys(value).map do |subkey, subvalue|
258
+ case subvalue
259
+ when Array
260
+ subvalue.map { |subitem| nested_data_to_xml(subkey, subitem) }.join
261
+ when Hash
262
+ nested_data_to_xml(subkey, subvalue)
263
+ else
264
+ xml_node(subkey) { map_hash_value(subvalue) } if map_hash_value(subvalue)
265
+ end
266
+ end.join
267
+ end
268
+ else
269
+ xml_node(key) { map_hash_value(value) } if map_hash_value(value)
270
+ end
271
+ end
272
+
273
+ # Returns an XML tag with a given +name+. Accepts a +block+ for tag content.
274
+ # Defaults to returning an empty-element tag in case no block was given.
275
+ # Also accepts a Hash of +attributes+ to be added to the XML tag.
276
+ def xml_node(name, attributes = {})
277
+ node = XMLNode.new(name.to_s)
278
+ node.to_lower_camel_case! unless disable_tag_names_to_lower_camel_case
279
+ node.namespace_from_hash!(nodes_to_namespace)
280
+ node.attributes = sort_hash_keys(attributes)
281
+ node.body = yield if block_given?
282
+ node.to_tag
283
+ end
284
+
285
+ # Removes whitespace between tags of a given +xml+ String.
286
+ def remove_whitespace(xml)
287
+ xml.gsub(/(>)\s*(<)/, '\1\2')
288
+ end
289
+
290
+ # Converts XML values to more natural Ruby objects.
291
+ def map_xml_value(value)
292
+ case value
293
+ when SOAPDateTimeRegexp
294
+ DateTime.parse(value)
295
+ when "true"
296
+ true
297
+ when "false"
298
+ false
299
+ else
300
+ value
301
+ end
302
+ end
303
+
304
+ # Converts Hash values into valid XML values.
305
+ def map_hash_value(value)
306
+ if value.kind_of? DateTime
307
+ value.strftime(SOAPDateTimeFormat)
308
+ elsif !value.kind_of?(String) && value.respond_to?(:to_datetime)
309
+ value.to_datetime.strftime(SOAPDateTimeFormat)
310
+ elsif value.respond_to? :to_s
311
+ value.to_s
312
+ else
313
+ nil
314
+ end
315
+ end
316
+
317
+ # Returns a sorted version of a given +hash+ if +sort_keys+ is enabled.
318
+ def sort_hash_keys(hash)
319
+ return hash unless sort_keys
320
+ hash.keys.sort_by { |key| key.to_s }.map { |key| [ key, hash[key] ] }
321
+ end
322
+
323
+ end
324
+ end
@@ -0,0 +1,78 @@
1
+ # == XMLNode
2
+ #
3
+ # Representation of an XML node. Inherits from String and includes some
4
+ # useful XML-specific methods for namespaces, attributes, node content etc.
5
+ class XMLNode < String
6
+
7
+ # Hash of attributes.
8
+ attr_writer :attributes
9
+
10
+ # Node body content.
11
+ attr_writer :body
12
+
13
+ # Strips the namespace from this node.
14
+ def strip_namespace!
15
+ sub!(/.+:(.+)/, '\1')
16
+ end
17
+
18
+ # Converts this node to snake_case.
19
+ def to_snake_case!
20
+ gsub!(/[A-Z]/, '_\0')
21
+ gsub!(/^_/, '')
22
+ downcase!
23
+ end
24
+
25
+ # Converts this node to lowerCamelCase.
26
+ def to_lower_camel_case!
27
+ gsub!(/_(.)/) { $1.upcase }
28
+ end
29
+
30
+ # Checks if this node is included in a given Hash of +namespaces+ and
31
+ # sets the namespace for this node in case it was found in the Hash.
32
+ def namespace_from_hash!(namespaces)
33
+ if namespaces && !namespaces.empty?
34
+ namespaces.each { |ns, nodes| @namespace = ns if self_included? nodes }
35
+ end
36
+ end
37
+
38
+ # Returns this node as an XML tag including a namespace, attributes
39
+ # and a body in case these values were supplied.
40
+ def to_tag
41
+ if @body
42
+ "<#{namespace}#{self}#{attributes}>#{body}</#{namespace}#{self}>"
43
+ else
44
+ "<#{namespace}#{self}#{attributes} />"
45
+ end
46
+ end
47
+
48
+ private
49
+
50
+ # Returns +true+ if self as a String or a Symbol is included in a
51
+ # given +array+. Returns +false+ otherwise.
52
+ def self_included?(array)
53
+ array.include?(self.to_s) || array.include?(self.to_sym)
54
+ end
55
+
56
+ # Returns the namespace of this node. Defaults to an empty String
57
+ # in case no namespace was defined.
58
+ def namespace
59
+ @namespace ? "#{@namespace}:" : ""
60
+ end
61
+
62
+ # Returns the attributes of this node. Defaults to an empty String
63
+ # in case no attributes were defined.
64
+ def attributes
65
+ if @attributes
66
+ @attributes.map { |key, value| %Q( xmlns:#{key}="#{value}") }
67
+ else
68
+ ""
69
+ end
70
+ end
71
+
72
+ # Returns the body of this node. Defaults to an empty String in case
73
+ # no body was defined.
74
+ def body
75
+ @body ? @body : ""
76
+ end
77
+
78
+ end
@@ -0,0 +1,35 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
+
3
+ describe ApricotEatsGorilla do
4
+ include SpecHelper
5
+
6
+ # setup
7
+ describe "setup" do
8
+ it "yields self to a given block" do
9
+ ApricotEatsGorilla.setup do |yielded|
10
+ yielded.should == ApricotEatsGorilla
11
+ end
12
+ end
13
+ end
14
+
15
+ # []
16
+ describe "[]" do
17
+ before { reset_library_options }
18
+
19
+ it "converts a given XML into a Hash" do
20
+ xml = "<root><name>Jungle Julia</name></root>"
21
+ ApricotEatsGorilla[xml].should == { :name => "Jungle Julia" }
22
+ end
23
+
24
+ it "converts a given XML with a custom root node into a Hash" do
25
+ xml = "<root><something><name>Jungle Julia</name></something></root>"
26
+ ApricotEatsGorilla[xml, "//something"].should == { :name => "Jungle Julia" }
27
+ end
28
+
29
+ it "converts a given Hash into XML" do
30
+ hash = { "apricot" => "eats gorilla" }
31
+ ApricotEatsGorilla[hash] == "<apricot>eats gorilla</apricot>"
32
+ end
33
+ end
34
+
35
+ end
@@ -0,0 +1,78 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
+
3
+ describe ApricotEatsGorilla do
4
+ include SpecHelper
5
+
6
+ describe "hash_to_xml" do
7
+ before { reset_library_options }
8
+
9
+ it "converts Hash key Symbols into Strings" do
10
+ hash = { :apricot => { :eats => [ :gorilla, "snake" ] } }
11
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
12
+ "<apricot><eats>gorilla</eats><eats>snake</eats></apricot>"
13
+ end
14
+
15
+ it "converts lowerCamelCase Hash keys to snake_case" do
16
+ hash = { :apricot => { :eats => { :lots_of => "gorillas" } } }
17
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
18
+ "<apricot><eats><lotsOf>gorillas</lotsOf></eats></apricot>"
19
+ end
20
+
21
+ it "does not convert lowerCamelCase Hash keys to snake_case if this was disabled" do
22
+ ApricotEatsGorilla.disable_tag_names_to_lower_camel_case = true
23
+ hash = { :apricot => { :eats => { :lots_of => "gorillas" } } }
24
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
25
+ "<apricot><eats><lots_of>gorillas</lots_of></eats></apricot>"
26
+ end
27
+
28
+ it "converts values responding to to_s into Strings" do
29
+ hash = { :apricot => { :with => 100.01, :when => nil, :what => :gorillas } }
30
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
31
+ "<apricot><what>gorillas</what>" <<
32
+ "<when></when><with>100.01</with></apricot>"
33
+ end
34
+
35
+ it "converts DateTime objects into xsd:dateTime Strings" do
36
+ date = DateTime.new(y=2009, m=9, d=1, h=12, min=0, s=8)
37
+ hash = { :apricot => { :eats => { :at => date } } }
38
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
39
+ "<apricot><eats><at>2009-09-01T12:00:08</at></eats></apricot>"
40
+ end
41
+
42
+ it "converts objects responding to 'to_datetime' into xsd:dateTime Strings" do
43
+ TestObject = Struct.new(:whatever) do
44
+ def to_datetime
45
+ DateTime.new(y=2009, m=9, d=1, h=12, min=0, s=8)
46
+ end
47
+ end
48
+ hash = { :apricot => { :eats => { :at => TestObject.new } } }
49
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
50
+ "<apricot><eats><at>2009-09-01T12:00:08</at></eats></apricot>"
51
+ end
52
+
53
+ it "applies namespaces nodes defined via nodes_to_namespace" do
54
+ ApricotEatsGorilla.nodes_to_namespace = { :wsdl => [ :apricot ] }
55
+ hash = { :apricot => { :eats => "Gorilla" } }
56
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
57
+ "<wsdl:apricot><eats>Gorilla</eats></wsdl:apricot>"
58
+ end
59
+
60
+ it "converts a Hash containing only one key and a value into one node with content" do
61
+ hash = { "apricot" => "eats Gorilla" }
62
+ ApricotEatsGorilla.hash_to_xml(hash).should == "<apricot>eats Gorilla</apricot>"
63
+ end
64
+
65
+ it "converts a nested Hash into nested nodes" do
66
+ hash = { "apricot" => { "eats" => "gorilla", "drinks" => "beer" } }
67
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
68
+ "<apricot><drinks>beer</drinks><eats>gorilla</eats></apricot>"
69
+ end
70
+
71
+ it "converts a Hash containing an Array into multiple nodes" do
72
+ hash = { "apricot" => { "eats" => [ "gorilla", "snake" ] } }
73
+ ApricotEatsGorilla.hash_to_xml(hash).should ==
74
+ "<apricot><eats>gorilla</eats><eats>snake</eats></apricot>"
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,44 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
+
3
+ describe ApricotEatsGorilla do
4
+
5
+ describe "soap_envelope" do
6
+ before { ApricotEatsGorilla.sort_keys = true }
7
+
8
+ it "returns an empty SOAP envelope with a SOAP 1.1 envelope namespace" do
9
+ ApricotEatsGorilla.soap_envelope.should ==
10
+ '<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">' <<
11
+ '<env:Header /><env:Body /></env:Envelope>'
12
+ end
13
+
14
+ it "returns a SOAP envelope with a custom namespace and body content" do
15
+ ApricotEatsGorilla.soap_envelope(:wsdl => "http://example.com") do
16
+ "<id>123</id>"
17
+ end.should == '<env:Envelope' <<
18
+ ' xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"' <<
19
+ ' xmlns:wsdl="http://example.com">' <<
20
+ '<env:Header />' <<
21
+ '<env:Body><id>123</id></env:Body></env:Envelope>'
22
+ end
23
+
24
+ it "does not change an already defined SOAP envelope namespace" do
25
+ ApricotEatsGorilla.soap_envelope(:env => "http://example.com").should ==
26
+ '<env:Envelope xmlns:env="http://example.com">' <<
27
+ '<env:Header />' <<
28
+ '<env:Body /></env:Envelope>'
29
+ end
30
+
31
+ it "sets the SOAP envelope namespace for SOAP version 1.2 if requested" do
32
+ ApricotEatsGorilla.soap_envelope({}, {}, 2).should ==
33
+ '<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope">' <<
34
+ '<env:Header />' <<
35
+ '<env:Body /></env:Envelope>'
36
+ end
37
+
38
+ it "does not set a SOAP envelope namespace in case of an invalid SOAP version" do
39
+ ApricotEatsGorilla.soap_envelope({}, {}, 123).should ==
40
+ '<env:Envelope><env:Header /><env:Body /></env:Envelope>'
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,80 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
+
3
+ describe XMLNode do
4
+
5
+ # strip_namespace!
6
+ describe "strip_namespace!" do
7
+ it "strips the namespace from the node" do
8
+ node = XMLNode.new("wsdl:apricot")
9
+ node.strip_namespace!
10
+
11
+ node.should == "apricot"
12
+ end
13
+ end
14
+
15
+ # to_snake_case!
16
+ describe "to_snake_case!" do
17
+ it "converts the node from CamelCase to snake_case" do
18
+ node = XMLNode.new("ApricotEatsGorilla")
19
+ node.to_snake_case!
20
+
21
+ node.should == "apricot_eats_gorilla"
22
+ end
23
+
24
+ it "converts the node from lowerCamelCase to snake_case" do
25
+ node = XMLNode.new("apricotEatsGorilla")
26
+ node.to_snake_case!
27
+
28
+ node.should == "apricot_eats_gorilla"
29
+ end
30
+ end
31
+
32
+ # namespace_from_hash!
33
+ describe "namespace_from_hash!" do
34
+ it "namespaces the node if it's included in the given Hash" do
35
+ node = XMLNode.new("apricot")
36
+ node.namespace_from_hash!(:wsdl => [ :apricot ])
37
+
38
+ node.should == "apricot"
39
+ node.to_tag.should == "<wsdl:apricot />"
40
+ end
41
+
42
+ it "does nothing if the node is not included in the given Hash" do
43
+ node = XMLNode.new("apricot")
44
+ node.namespace_from_hash!(:wsdl => [ :some_key ])
45
+
46
+ node.should == "apricot"
47
+ node.to_tag.should == "<apricot />"
48
+ end
49
+ end
50
+
51
+ # to_tag
52
+ describe "to_tag" do
53
+ it "returns an empty-element tag for a bare node" do
54
+ node = XMLNode.new("apricot")
55
+ node.to_tag.should == "<apricot />"
56
+ end
57
+
58
+ it "returns a namespaced empty-element tag for a namespaced node" do
59
+ node = XMLNode.new("apricot")
60
+ node.namespace_from_hash!(:wsdl => [ :apricot ])
61
+
62
+ node.to_tag.should == "<wsdl:apricot />"
63
+ end
64
+
65
+ it "returns an empty-element tag with an attribute for an attributed node" do
66
+ node = XMLNode.new("apricot")
67
+ node.attributes = { :wsdl => "http://example.com" }
68
+
69
+ node.to_tag.should == '<apricot xmlns:wsdl="http://example.com" />'
70
+ end
71
+
72
+ it "returns a node with body content for a node with content" do
73
+ node = XMLNode.new("apricot")
74
+ node.body = "eats gorilla"
75
+
76
+ node.to_tag.should == "<apricot>eats gorilla</apricot>"
77
+ end
78
+ end
79
+
80
+ end
@@ -0,0 +1,90 @@
1
+ require File.join(File.dirname(__FILE__), "..", "spec_helper")
2
+
3
+ describe ApricotEatsGorilla do
4
+ include SpecHelper
5
+
6
+ describe "xml_to_hash" do
7
+ before { reset_library_options }
8
+
9
+ it "converts Hash keys to Symbols" do
10
+ xml = "<root><name>Jungle Julia</name></root>"
11
+ ApricotEatsGorilla.xml_to_hash(xml).should == { :name => "Jungle Julia" }
12
+ end
13
+
14
+ it "does not convert Hash keys to Symbols if this was disabled" do
15
+ ApricotEatsGorilla.disable_hash_keys_to_symbols = true
16
+ xml = "<root><name>Jungle Julia</name></root>"
17
+ ApricotEatsGorilla.xml_to_hash(xml).should == { "name" => "Jungle Julia" }
18
+ end
19
+
20
+ it "converts String values of 'true' and 'false' into TrueClass and FalseClass" do
21
+ xml = "<root><yes>true</yes><no>false</no></root>"
22
+ ApricotEatsGorilla.xml_to_hash(xml).should == { :yes => true, :no => false }
23
+ end
24
+
25
+ it "converts String values matching the SOAP dateTime format to DateTime objects" do
26
+ date = "2009-08-05T11:22:08"
27
+ date_with_offset = "2009-08-05T11:22:08+02:00"
28
+ xml = "<root><date>#{date}</date><dateWithOffset>#{date_with_offset}</dateWithOffset></root>"
29
+ ApricotEatsGorilla.xml_to_hash(xml).should ==
30
+ { :date => DateTime.parse(date), :date_with_offset => DateTime.parse(date_with_offset) }
31
+ end
32
+
33
+ it "converts empty-element tags to 'nil'" do
34
+ xml = "<contact><name>Jungle Julia</name><email /></contact>"
35
+ ApricotEatsGorilla.xml_to_hash(xml).should == { :name => "Jungle Julia", :email => nil }
36
+ end
37
+
38
+ it "converts lowerCamelCase nodes to snake_case" do
39
+ xml = "<root><firstName>Jungle</firstName><lastName>Julia</lastName></root>"
40
+ ApricotEatsGorilla.xml_to_hash(xml).should == { :first_name => "Jungle", :last_name => "Julia" }
41
+ end
42
+
43
+ it "does not convert lowerCamelCase nodes to snake_case if this was disabled" do
44
+ ApricotEatsGorilla.disable_hash_keys_to_snake_case = true
45
+ xml = "<root><firstName>Jungle</firstName><lastName>Julia</lastName></root>"
46
+ ApricotEatsGorilla.xml_to_hash(xml).should == { :firstName => "Jungle", :lastName => "Julia" }
47
+ end
48
+
49
+ it "removes namespaces from nodes" do
50
+ xml = "<root><wsdl:apricot><eats>Gorilla</eats></wsdl:apricot></root>"
51
+ ApricotEatsGorilla.xml_to_hash(xml).should == { :apricot => { :eats => "Gorilla" } }
52
+ end
53
+
54
+ it "removes attributes from nodes" do
55
+ xml = '<root><contact id="123">Jungle Julia</contact></root>'
56
+ ApricotEatsGorilla.xml_to_hash(xml).should == { :contact => "Jungle Julia" }
57
+ end
58
+
59
+ it "starts translating XML at a given custom root node" do
60
+ xml = '<root><contact><name>Jungle Julia</name></contact></root>'
61
+ ApricotEatsGorilla.xml_to_hash(xml, "//contact").should == { :name => "Jungle Julia" }
62
+ end
63
+
64
+ it "returns an Array of nodes and values in case of multiple root nodes" do
65
+ xml = '<root><return><id>1</id></return><return><id>2</id></return></root>'
66
+ ApricotEatsGorilla.xml_to_hash(xml, "//return").should == [{ :id => "1" }, { :id => "2" }]
67
+ end
68
+
69
+ it "returns the value of a node if it does not contain any subnodes" do
70
+ xml = '<root><return>123</return></root>'
71
+ ApricotEatsGorilla.xml_to_hash(xml, "//return").should == "123"
72
+ end
73
+
74
+ it "returns an empty Hash in case the root node is an empty-element tag" do
75
+ xml = '<root><return /></root>'
76
+ ApricotEatsGorilla.xml_to_hash(xml, "//return").should == {}
77
+ end
78
+
79
+ it "returns an Array of values in case of multiple root nodes without subnodes" do
80
+ xml = '<root><return>123</return><return>456</return></root>'
81
+ ApricotEatsGorilla.xml_to_hash(xml, "//return").should == ["123", "456"]
82
+ end
83
+
84
+ it "returns a Hash containing an Array of content for multiple subnodes" do
85
+ xml = '<root><return><items>first</items><items>second</items></return></root>'
86
+ ApricotEatsGorilla.xml_to_hash(xml, "//return").should == { :items => ["first", "second"] }
87
+ end
88
+ end
89
+
90
+ end
@@ -0,0 +1,20 @@
1
+ require "rubygems"
2
+ gem "rspec", ">= 1.2.8"
3
+ require "spec"
4
+ require File.join(File.dirname(__FILE__), "..", "lib", "apricoteatsgorilla")
5
+
6
+ Spec::Runner.configure do |config|
7
+ config.mock_with :rr
8
+ end
9
+
10
+ module SpecHelper
11
+ def reset_library_options
12
+ ApricotEatsGorilla.setup do |setup|
13
+ setup.sort_keys = true
14
+ setup.disable_tag_names_to_lower_camel_case = false
15
+ setup.disable_hash_keys_to_snake_case = false
16
+ setup.disable_hash_keys_to_symbols = false
17
+ setup.nodes_to_namespace = nil
18
+ end
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apricoteatsgorilla
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.9
5
+ platform: ruby
6
+ authors:
7
+ - Daniel Harrington
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-06 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hpricot
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.8.241
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.2.8
34
+ version:
35
+ description: SOAP communication helper
36
+ email: me@rubiii.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.rdoc
43
+ files:
44
+ - .gitignore
45
+ - README.rdoc
46
+ - Rakefile
47
+ - VERSION
48
+ - apricoteatsgorilla.gemspec
49
+ - lib/apricoteatsgorilla.rb
50
+ - lib/apricoteatsgorilla/apricoteatsgorilla.rb
51
+ - lib/apricoteatsgorilla/xml_node.rb
52
+ - spec/apricoteatsgorilla/apricoteatsgorilla_spec.rb
53
+ - spec/apricoteatsgorilla/hash_to_xml_spec.rb
54
+ - spec/apricoteatsgorilla/soap_envelope_spec.rb
55
+ - spec/apricoteatsgorilla/xml_node_spec.rb
56
+ - spec/apricoteatsgorilla/xml_to_hash_spec.rb
57
+ - spec/spec_helper.rb
58
+ has_rdoc: true
59
+ homepage: http://github.com/rubiii/apricoteatsgorilla
60
+ licenses: []
61
+
62
+ post_install_message:
63
+ rdoc_options:
64
+ - --charset=UTF-8
65
+ - --title
66
+ - Apricot eats Gorilla
67
+ - --main
68
+ - README.rdoc
69
+ - --line-numbers
70
+ - --inline-source
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: "0"
84
+ version:
85
+ requirements: []
86
+
87
+ rubyforge_project:
88
+ rubygems_version: 1.3.5
89
+ signing_key:
90
+ specification_version: 2
91
+ summary: SOAP communication helper
92
+ test_files:
93
+ - spec/apricoteatsgorilla/apricoteatsgorilla_spec.rb
94
+ - spec/apricoteatsgorilla/hash_to_xml_spec.rb
95
+ - spec/apricoteatsgorilla/soap_envelope_spec.rb
96
+ - spec/apricoteatsgorilla/xml_node_spec.rb
97
+ - spec/apricoteatsgorilla/xml_to_hash_spec.rb
98
+ - spec/spec_helper.rb