lolsoap 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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