lolsoap 0.1.0

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.
Files changed (47) hide show
  1. data/.document +5 -0
  2. data/.travis.yml +7 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +22 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +124 -0
  8. data/Rakefile +29 -0
  9. data/VERSION +1 -0
  10. data/lib/lolsoap.rb +11 -0
  11. data/lib/lolsoap/builder.rb +93 -0
  12. data/lib/lolsoap/client.rb +25 -0
  13. data/lib/lolsoap/envelope.rb +94 -0
  14. data/lib/lolsoap/errors.rb +15 -0
  15. data/lib/lolsoap/fault.rb +26 -0
  16. data/lib/lolsoap/hash_builder.rb +48 -0
  17. data/lib/lolsoap/request.rb +54 -0
  18. data/lib/lolsoap/response.rb +50 -0
  19. data/lib/lolsoap/wsdl.rb +98 -0
  20. data/lib/lolsoap/wsdl/element.rb +28 -0
  21. data/lib/lolsoap/wsdl/null_element.rb +15 -0
  22. data/lib/lolsoap/wsdl/null_type.rb +19 -0
  23. data/lib/lolsoap/wsdl/operation.rb +18 -0
  24. data/lib/lolsoap/wsdl/type.rb +38 -0
  25. data/lib/lolsoap/wsdl_parser.rb +121 -0
  26. data/lolsoap.gemspec +97 -0
  27. data/test/fixtures/stock_quote.wsdl +74 -0
  28. data/test/fixtures/stock_quote_fault.xml +16 -0
  29. data/test/fixtures/stock_quote_response.xml +8 -0
  30. data/test/helper.rb +14 -0
  31. data/test/integration/test_client.rb +20 -0
  32. data/test/integration/test_envelope.rb +45 -0
  33. data/test/integration/test_request.rb +19 -0
  34. data/test/integration/test_response.rb +15 -0
  35. data/test/integration/test_wsdl.rb +28 -0
  36. data/test/unit/test_builder.rb +95 -0
  37. data/test/unit/test_client.rb +12 -0
  38. data/test/unit/test_envelope.rb +112 -0
  39. data/test/unit/test_fault.rb +33 -0
  40. data/test/unit/test_hash_builder.rb +127 -0
  41. data/test/unit/test_request.rb +48 -0
  42. data/test/unit/test_response.rb +39 -0
  43. data/test/unit/test_wsdl.rb +143 -0
  44. data/test/unit/test_wsdl_parser.rb +105 -0
  45. data/test/unit/wsdl/test_element.rb +31 -0
  46. data/test/unit/wsdl/test_type.rb +44 -0
  47. metadata +152 -0
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ # - jruby
7
+ # - rbx-18mode
data/.yardopts ADDED
@@ -0,0 +1 @@
1
+ --no-private
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'nokogiri'
4
+
5
+ group :development do
6
+ gem "bundler", "~> 1.0.0"
7
+ gem "jeweler", "~> 1.6.4"
8
+ gem "minitest"
9
+ gem "yard"
10
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.6.4)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ minitest (2.10.0)
10
+ nokogiri (1.5.0)
11
+ rake (0.9.2.2)
12
+ yard (0.7.2)
13
+
14
+ PLATFORMS
15
+ ruby
16
+
17
+ DEPENDENCIES
18
+ bundler (~> 1.0.0)
19
+ jeweler (~> 1.6.4)
20
+ minitest
21
+ nokogiri
22
+ yard
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Loco2 Ltd.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,124 @@
1
+ # LolSoap #
2
+
3
+ A library for dealing with SOAP requests and responses. We tear our hair
4
+ out so you don't have to.
5
+
6
+ https://github.com/loco2/lolsoap
7
+
8
+ [![Build Status](https://secure.travis-ci.org/loco2/lolsoap.png)](http://travis-ci.org/loco2/lolsoap)
9
+
10
+ ## Aims ##
11
+
12
+ * A collection of classes to make dealing with SOAP requests and
13
+ responses, and WSDL documents, easier.
14
+ * The classes are intended to be loosely coupled and non-prescriptive
15
+ about how they are used.
16
+ * LolSoap does not know anything about what HTTP library you want to
17
+ use, and does not care whether you're doing the IO in a synchronous or
18
+ asynchronous fashion. This does mean you have to provide a little bit
19
+ of glue code, but the benefit is flexibility.
20
+ * No monkey-patching.
21
+ * Runs without warnings.
22
+
23
+ ## Synopsis ##
24
+
25
+ ``` ruby
26
+ # You will need your own HTTP client
27
+ http = MyHttpClient.new
28
+
29
+ # LolSoap::Client is just a thin wrapper object that handles creating
30
+ # other objects for you, with a given WSDL file
31
+ client = LolSoap::Client.new(File.read('lolapi.wsdl'))
32
+
33
+ # Create a request object
34
+ request = client.request('getLols')
35
+
36
+ # Populate the request with some data. Namespacing is taken care of
37
+ # using the type data from the WSDL.
38
+ request.body do |b|
39
+ b.lolFactor '11'
40
+ b.lolDuration 'lolever'
41
+ ...
42
+ end
43
+
44
+ # See the full request XML
45
+ puts request.content
46
+
47
+ # Send that request!
48
+ raw_response = http.post(request.url, request.headers, request.content)
49
+
50
+ # Create a response object
51
+ response = client.response(request, raw_response)
52
+
53
+ # Get access to the XML structure (a Nokogiri::XML::Document)
54
+ p response.doc
55
+
56
+ # Get access to the first node inside the Body
57
+ p response.body
58
+
59
+ # Turn the body into a hash. The WSDL schema is used to work out which
60
+ # elements are supposed to be collections and which are just singular.
61
+ p response.body_hash
62
+ ```
63
+
64
+ ## Bugs/Features ##
65
+
66
+ * SOAP 1.1 is not supported. Patches to add support will be considered
67
+ if they don't add too much extra complexity.
68
+ * WSSE is not supported.
69
+ * Assumes that you are able to supply a WSDL document for the service.
70
+
71
+ ## Overview ##
72
+
73
+ These are some of the key classes. If you want, you can require them
74
+ directly (e.g. `require 'lolsoap/request'` rather than having to
75
+ `require 'lolsoap'`).
76
+
77
+ The main ones:
78
+
79
+ * `LolSoap::Request` - A HTTP request to be sent
80
+ * `LolSoap::Envelope` - The SOAP envelope in the body of a request
81
+ * `LolSoap::Response` - The API's response
82
+ * `LolSoap::WSDL` - A WSDL document
83
+
84
+ The others:
85
+
86
+ * `LolSoap::WSDLParser` - Lower level representation of the WSDL
87
+ document
88
+ * `LolSoap::Builder` - XML builder object that knows about types, and
89
+ therefore how elements should be namespaced.
90
+ * `LolSoap::Fault` - A SOAP 'fault' (error)
91
+ * `LolSoap::HashBuilder` - Builds hashes from the API response, using
92
+ the WSDL type data to determine which elements are collection
93
+ elements.
94
+
95
+ ## Authors ##
96
+
97
+ * [Jon Leighton](http://jonathanleighton.com/)
98
+
99
+ Development sponsored by [Loco2](http://loco2.com/).
100
+
101
+ ## License ##
102
+
103
+ (The MIT License)
104
+
105
+ Copyright (c) 2012 Loco2 Ltd.
106
+
107
+ Permission is hereby granted, free of charge, to any person obtaining
108
+ a copy of this software and associated documentation files (the
109
+ 'Software'), to deal in the Software without restriction, including
110
+ without limitation the rights to use, copy, modify, merge, publish,
111
+ distribute, sublicense, and/or sell copies of the Software, and to
112
+ permit persons to whom the Software is furnished to do so, subject to
113
+ the following conditions:
114
+
115
+ The above copyright notice and this permission notice shall be
116
+ included in all copies or substantial portions of the Software.
117
+
118
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
119
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
120
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
121
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
122
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
123
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
124
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+ require 'rake'
6
+
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
10
+ gem.name = "lolsoap"
11
+ gem.homepage = "http://github.com/jonleighton/lolsoap"
12
+ gem.license = "MIT"
13
+ gem.summary = %Q{A library for dealing with SOAP requests and responses.}
14
+ gem.description = %Q{A library for dealing with SOAP requests and responses. We tear our hair out so you don't have to.}
15
+ gem.email = "j@jonathanleighton.com"
16
+ gem.authors = ["Jon Leighton"]
17
+ # dependencies defined in Gemfile
18
+ end
19
+ Jeweler::RubygemsDotOrgTasks.new
20
+
21
+ require 'rake/testtask'
22
+
23
+ Rake::TestTask.new do |t|
24
+ t.libs = ["test"]
25
+ t.pattern = "test/**/test_*.rb"
26
+ t.ruby_opts = ['-w']
27
+ end
28
+
29
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
data/lib/lolsoap.rb ADDED
@@ -0,0 +1,11 @@
1
+ module LolSoap
2
+ require 'lolsoap/builder'
3
+ require 'lolsoap/client'
4
+ require 'lolsoap/envelope'
5
+ require 'lolsoap/errors'
6
+ require 'lolsoap/fault'
7
+ require 'lolsoap/request'
8
+ require 'lolsoap/response'
9
+ require 'lolsoap/wsdl'
10
+ require 'lolsoap/wsdl_parser'
11
+ end
@@ -0,0 +1,93 @@
1
+ require 'lolsoap/wsdl'
2
+
3
+ module LolSoap
4
+ # Used to build XML, with namespaces automatically added.
5
+ #
6
+ # @example General
7
+ # builder = Builder.new(node, type)
8
+ # builder.someTag do |t|
9
+ # t.foo 'bar'
10
+ # end
11
+ # # => <ns1:someTag><ns1:foo>bar</ns1:foo></ns1:someTag>
12
+ #
13
+ # @example Explicitly specifying a namespace prefix
14
+ # builder = Builder.new(node, type)
15
+ # builder['ns2'].someTag
16
+ # # => <ns2:someTag/>
17
+ class Builder
18
+ RESERVED_METHODS = %w(object_id respond_to_missing? inspect === to_s)
19
+
20
+ alias :__class__ :class
21
+ instance_methods.each do |m|
22
+ undef_method m unless RESERVED_METHODS.include?(m.to_s) || m =~ /^__/
23
+ end
24
+
25
+ # @private
26
+ class Prefix
27
+ instance_methods.each do |m|
28
+ undef_method m unless RESERVED_METHODS.include?(m.to_s) || m =~ /^__/
29
+ end
30
+
31
+ def initialize(owner, prefix)
32
+ @owner = owner
33
+ @prefix = prefix
34
+ end
35
+
36
+ def respond_to?(name)
37
+ true
38
+ end
39
+
40
+ private
41
+
42
+ def method_missing(*args, &block)
43
+ @owner.__prefixed_tag__(@prefix, LolSoap::WSDL::NullType.new, *args, &block)
44
+ end
45
+ end
46
+
47
+ def initialize(node, type = WSDL::NullType.new)
48
+ @node = node
49
+ @type = type
50
+ end
51
+
52
+ # Add a tag manually, rather than through method_missing. This is so you can still
53
+ # add tags for the very small number of tags that are also existing methods.
54
+ def __tag__(name, *args, &block)
55
+ __prefixed_tag__(@type.prefix, @type.sub_type(name.to_s), name, *args, &block)
56
+ end
57
+
58
+ # @private
59
+ def __prefixed_tag__(prefix, sub_type, name, *args)
60
+ sub_node = @node.document.create_element(name.to_s, *args)
61
+ sub_node.namespace = @node.namespace_scopes.find { |n| n.prefix == prefix }
62
+
63
+ @node << sub_node
64
+
65
+ builder = __class__.new(sub_node, sub_type)
66
+ yield builder if block_given?
67
+ builder
68
+ end
69
+
70
+ # Node accessor. Named to prevent method_missing conflict.
71
+ def __node__
72
+ @node
73
+ end
74
+
75
+ # Type accessor. Named to prevent method_missing conflict.
76
+ def __type__
77
+ @type
78
+ end
79
+
80
+ # Specify a namespace prefix explicitly
81
+ def [](prefix)
82
+ Prefix.new(self, prefix)
83
+ end
84
+
85
+ def respond_to?(name)
86
+ true
87
+ end
88
+
89
+ private
90
+
91
+ alias method_missing __tag__
92
+ end
93
+ end
@@ -0,0 +1,25 @@
1
+ require 'lolsoap/wsdl'
2
+ require 'lolsoap/request'
3
+ require 'lolsoap/envelope'
4
+ require 'lolsoap/response'
5
+
6
+ module LolSoap
7
+ class Client
8
+ attr_reader :wsdl
9
+
10
+ # @param wsdl a WSDL object, or a string that will be parsed into one
11
+ def initialize(wsdl)
12
+ @wsdl = wsdl.respond_to?(:to_str) ? WSDL.parse(wsdl.to_str) : wsdl
13
+ end
14
+
15
+ # @return [LolSoap::Request] A request for the API action you want to perform
16
+ def request(name)
17
+ Request.new(Envelope.new(wsdl, wsdl.operation(name)))
18
+ end
19
+
20
+ # @return [LolSoap::Response] A response object for an API action that has been performed
21
+ def response(request, raw)
22
+ Response.parse(request, raw)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,94 @@
1
+ require 'nokogiri'
2
+ require 'lolsoap/builder'
3
+
4
+ module LolSoap
5
+ class Envelope
6
+ attr_reader :wsdl, :operation, :doc
7
+
8
+ # @private
9
+ SOAP_PREFIX = 'soap'
10
+
11
+ # @private
12
+ SOAP_NAMESPACE = 'http://www.w3.org/2003/05/soap-envelope'
13
+
14
+ def initialize(wsdl, operation, doc = Nokogiri::XML::Document.new)
15
+ @wsdl = wsdl
16
+ @operation = operation
17
+ @doc = doc
18
+
19
+ initialize_doc
20
+ end
21
+
22
+ # Build the body of the envelope
23
+ #
24
+ # @example
25
+ # env.body do |b|
26
+ # b.some 'data'
27
+ # end
28
+ def body(klass = Builder)
29
+ builder = klass.new(input, operation.input)
30
+ yield builder if block_given?
31
+ builder
32
+ end
33
+
34
+ # Build the header of the envelope
35
+ def header(klass = Builder)
36
+ builder = klass.new(@header)
37
+ yield builder if block_given?
38
+ builder
39
+ end
40
+
41
+ def endpoint
42
+ wsdl.endpoint
43
+ end
44
+
45
+ def action
46
+ operation.action
47
+ end
48
+
49
+ def input_type
50
+ operation.input
51
+ end
52
+
53
+ def output_type
54
+ operation.output
55
+ end
56
+
57
+ def to_xml
58
+ doc.to_xml
59
+ end
60
+
61
+ def soap_prefix
62
+ SOAP_PREFIX
63
+ end
64
+
65
+ def soap_namespace
66
+ SOAP_NAMESPACE
67
+ end
68
+
69
+ private
70
+
71
+ # @private
72
+ def input; @input; end
73
+
74
+ # @private
75
+ def initialize_doc
76
+ doc.root = root = doc.create_element('Envelope')
77
+
78
+ namespaces = Hash[wsdl.type_namespaces.map { |prefix, uri| [prefix, root.add_namespace(prefix, uri)] }]
79
+ namespaces[soap_prefix] = root.add_namespace(soap_prefix, soap_namespace)
80
+
81
+ @header = doc.create_element 'Header'
82
+
83
+ @body = doc.create_element 'Body'
84
+ @input = doc.create_element input_type.name
85
+
86
+ [root, @header, @body].each { |el| el.namespace = namespaces[soap_prefix] }
87
+ @input.namespace = namespaces[input_type.prefix]
88
+
89
+ @body << @input
90
+ root << @header
91
+ root << @body
92
+ end
93
+ end
94
+ end