savon 1.2.0 → 2.0.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 (66) hide show
  1. data/CHANGELOG.md +119 -104
  2. data/README.md +12 -11
  3. data/Rakefile +0 -6
  4. data/lib/savon.rb +16 -14
  5. data/lib/savon/block_interface.rb +26 -0
  6. data/lib/savon/builder.rb +142 -0
  7. data/lib/savon/client.rb +36 -135
  8. data/lib/savon/header.rb +42 -0
  9. data/lib/savon/http_error.rb +27 -0
  10. data/lib/savon/log_message.rb +23 -25
  11. data/lib/savon/message.rb +35 -0
  12. data/lib/savon/mock.rb +5 -0
  13. data/lib/savon/mock/expectation.rb +70 -0
  14. data/lib/savon/mock/spec_helper.rb +62 -0
  15. data/lib/savon/model.rb +39 -61
  16. data/lib/savon/operation.rb +62 -0
  17. data/lib/savon/options.rb +265 -0
  18. data/lib/savon/qualified_message.rb +49 -0
  19. data/lib/savon/request.rb +92 -0
  20. data/lib/savon/response.rb +97 -0
  21. data/lib/savon/soap_fault.rb +40 -0
  22. data/lib/savon/version.rb +1 -1
  23. data/savon.gemspec +10 -8
  24. data/spec/integration/options_spec.rb +536 -0
  25. data/spec/integration/request_spec.rb +31 -16
  26. data/spec/integration/support/application.rb +80 -0
  27. data/spec/integration/support/server.rb +84 -0
  28. data/spec/savon/builder_spec.rb +81 -0
  29. data/spec/savon/client_spec.rb +90 -488
  30. data/spec/savon/http_error_spec.rb +49 -0
  31. data/spec/savon/log_message_spec.rb +33 -0
  32. data/spec/savon/mock_spec.rb +127 -0
  33. data/spec/savon/model_spec.rb +110 -99
  34. data/spec/savon/observers_spec.rb +92 -0
  35. data/spec/savon/operation_spec.rb +49 -0
  36. data/spec/savon/request_spec.rb +145 -0
  37. data/spec/savon/{soap/response_spec.rb → response_spec.rb} +22 -59
  38. data/spec/savon/soap_fault_spec.rb +94 -0
  39. data/spec/spec_helper.rb +5 -3
  40. data/spec/support/fixture.rb +5 -1
  41. metadata +202 -197
  42. data/lib/savon/config.rb +0 -46
  43. data/lib/savon/error.rb +0 -6
  44. data/lib/savon/hooks/group.rb +0 -68
  45. data/lib/savon/hooks/hook.rb +0 -61
  46. data/lib/savon/http/error.rb +0 -42
  47. data/lib/savon/logger.rb +0 -39
  48. data/lib/savon/null_logger.rb +0 -10
  49. data/lib/savon/soap.rb +0 -21
  50. data/lib/savon/soap/fault.rb +0 -59
  51. data/lib/savon/soap/invalid_response_error.rb +0 -13
  52. data/lib/savon/soap/request.rb +0 -86
  53. data/lib/savon/soap/request_builder.rb +0 -205
  54. data/lib/savon/soap/response.rb +0 -117
  55. data/lib/savon/soap/xml.rb +0 -257
  56. data/spec/savon/config_spec.rb +0 -38
  57. data/spec/savon/hooks/group_spec.rb +0 -71
  58. data/spec/savon/hooks/hook_spec.rb +0 -16
  59. data/spec/savon/http/error_spec.rb +0 -52
  60. data/spec/savon/logger_spec.rb +0 -51
  61. data/spec/savon/savon_spec.rb +0 -33
  62. data/spec/savon/soap/fault_spec.rb +0 -89
  63. data/spec/savon/soap/request_builder_spec.rb +0 -207
  64. data/spec/savon/soap/request_spec.rb +0 -112
  65. data/spec/savon/soap/xml_spec.rb +0 -357
  66. data/spec/savon/soap_spec.rb +0 -16
@@ -1,46 +0,0 @@
1
- require "savon/logger"
2
- require "savon/null_logger"
3
- require "savon/hooks/group"
4
- require "savon/soap"
5
-
6
- module Savon
7
- Config = Struct.new(:_logger, :pretty_print_xml, :raise_errors, :soap_version, :env_namespace, :soap_header) do
8
-
9
- def self.default
10
- config = new
11
- config._logger = Logger.new
12
- config.raise_errors = true
13
- config.soap_version = SOAP::DEFAULT_VERSION
14
- config
15
- end
16
-
17
- alias_method :logger, :_logger
18
-
19
- def logger=(logger)
20
- _logger.subject = logger
21
- end
22
-
23
- def log_level=(level)
24
- _logger.level = level
25
- end
26
-
27
- def log=(log)
28
- if log == true
29
- self._logger = Logger.new
30
- else
31
- self._logger = NullLogger.new
32
- end
33
- end
34
-
35
- def hooks
36
- @hooks ||= Hooks::Group.new
37
- end
38
-
39
- def clone
40
- config = super
41
- config._logger = config._logger.clone
42
- config
43
- end
44
-
45
- end
46
- end
@@ -1,6 +0,0 @@
1
- module Savon
2
-
3
- # Base class for Savon errors.
4
- class Error < RuntimeError; end
5
-
6
- end
@@ -1,68 +0,0 @@
1
- require "savon/hooks/hook"
2
-
3
- module Savon
4
- module Hooks
5
-
6
- # = Savon::Hooks::Group
7
- #
8
- # Manages a list of hooks.
9
- class Group
10
-
11
- # Accepts an Array of +hooks+ to start with.
12
- def initialize(hooks = [])
13
- @hooks = hooks
14
- end
15
-
16
- # Returns whether this group contains hooks.
17
- def empty?
18
- hooks.empty?
19
- end
20
-
21
- # Returns the number of hooks in this group.
22
- def count
23
- hooks.count
24
- end
25
-
26
- # Adds a new hook.
27
- def define(id, hook, &block)
28
- hooks << Hook.new(id, hook, &block)
29
- end
30
-
31
- # Removes hooks matching the given +ids+.
32
- def reject(*ids)
33
- ids = ids.flatten
34
- hooks.reject! { |hook| ids.include? hook.id }
35
- end
36
-
37
- # Fire a given +hook+ with any given +args+.
38
- def fire(hook, *args, &callback)
39
- callable = select(hook)
40
-
41
- if callable.empty?
42
- callback.call
43
- else
44
- args.unshift(callback) if callback
45
- callable.call(*args)
46
- end
47
- end
48
-
49
- # Calls the hooks with the given +args+ and returns the
50
- # value of the last hooks.
51
- def call(*args)
52
- hooks.inject(nil) { |memo, hook| hook.call(*args) }
53
- end
54
-
55
- private
56
-
57
- def hooks
58
- @hooks ||= []
59
- end
60
-
61
- # Returns a new group for a given +hook+.
62
- def select(hook)
63
- Group.new hooks.select { |h| h.hook == hook }
64
- end
65
-
66
- end
67
- end
68
- end
@@ -1,61 +0,0 @@
1
- module Savon
2
- module Hooks
3
-
4
- # = Savon::Hooks::Hook
5
- #
6
- # A hook used somewhere in the system.
7
- class Hook
8
-
9
- HOOKS = [
10
-
11
- # :soap_request
12
- #
13
- # Around filter wrapping the POST request executed to call a SOAP service.
14
- # See: Savon::SOAP::Request#response
15
- #
16
- # Arguments
17
- #
18
- # [callback] A block to execute the SOAP request
19
- # [request] The current <tt>Savon::SOAP::Request</tt>
20
- #
21
- # Examples
22
- #
23
- # Log the time before and after the SOAP call:
24
- #
25
- # Savon.config.hooks.define(:my_hook, :soap_request) do |callback, request|
26
- # Timer.log(:start, Time.now)
27
- # response = callback.call
28
- # Timer.log(:end, Time.now)
29
- # response
30
- # end
31
- #
32
- # Replace the SOAP call and return a custom response:
33
- #
34
- # Savon.config.hooks.define(:mock_hook, :soap_request) do |_, request|
35
- # HTTPI::Response.new(200, {}, "")
36
- # end
37
- :soap_request
38
-
39
- ]
40
-
41
- # Expects an +id+, the name of the +hook+ to use and a +block+ to be called.
42
- def initialize(id, hook, &block)
43
- unless HOOKS.include?(hook)
44
- raise ArgumentError, "No such hook: #{hook}. Expected one of: #{HOOKS.join(', ')}"
45
- end
46
-
47
- self.id = id
48
- self.hook = hook
49
- self.block = block
50
- end
51
-
52
- attr_accessor :id, :hook, :block
53
-
54
- # Calls the +block+ with the given +args+.
55
- def call(*args)
56
- block.call(*args)
57
- end
58
-
59
- end
60
- end
61
- end
@@ -1,42 +0,0 @@
1
- require "savon/error"
2
- require "savon/soap/xml"
3
-
4
- module Savon
5
- module HTTP
6
-
7
- # = Savon::HTTP::Error
8
- #
9
- # Represents an HTTP error. Contains the original <tt>HTTPI::Response</tt>.
10
- class Error < Error
11
-
12
- # Expects an <tt>HTTPI::Response</tt>.
13
- def initialize(http)
14
- self.http = http
15
- end
16
-
17
- # Accessor for the <tt>HTTPI::Response</tt>.
18
- attr_accessor :http
19
-
20
- # Returns whether an HTTP error is present.
21
- def present?
22
- http.error?
23
- end
24
-
25
- # Returns the HTTP error message.
26
- def to_s
27
- return "" unless present?
28
-
29
- @message ||= begin
30
- message = "HTTP error (#{http.code})"
31
- message << ": #{http.body}" unless http.body.empty?
32
- end
33
- end
34
-
35
- # Returns the HTTP response as a Hash.
36
- def to_hash
37
- @hash = { :code => http.code, :headers => http.headers, :body => http.body }
38
- end
39
-
40
- end
41
- end
42
- end
@@ -1,39 +0,0 @@
1
- require "logger"
2
- require "nokogiri"
3
- require "savon/log_message"
4
-
5
- module Savon
6
- class Logger
7
-
8
- def initialize(device = $stdout)
9
- self.device = device
10
- end
11
-
12
- attr_accessor :device
13
-
14
- def log(message, options = {})
15
- log_raw LogMessage.new(message, filter, options).to_s
16
- end
17
-
18
- attr_writer :subject, :level, :filter
19
-
20
- def subject
21
- @subject ||= ::Logger.new(device)
22
- end
23
-
24
- def level
25
- @level ||= :debug
26
- end
27
-
28
- def filter
29
- @filter ||= []
30
- end
31
-
32
- private
33
-
34
- def log_raw(message)
35
- subject.send(level, message)
36
- end
37
-
38
- end
39
- end
@@ -1,10 +0,0 @@
1
- require "savon/logger"
2
-
3
- module Savon
4
- class NullLogger < Logger
5
-
6
- def log(*)
7
- end
8
-
9
- end
10
- end
@@ -1,21 +0,0 @@
1
- module Savon
2
-
3
- # = Savon::SOAP
4
- #
5
- # Contains various SOAP details.
6
- module SOAP
7
-
8
- # Default SOAP version.
9
- DEFAULT_VERSION = 1
10
-
11
- # Supported SOAP versions.
12
- VERSIONS = 1..2
13
-
14
- # SOAP namespaces by SOAP version.
15
- NAMESPACE = {
16
- 1 => "http://schemas.xmlsoap.org/soap/envelope/",
17
- 2 => "http://www.w3.org/2003/05/soap-envelope"
18
- }
19
-
20
- end
21
- end
@@ -1,59 +0,0 @@
1
- require "savon/error"
2
- require "savon/soap/xml"
3
-
4
- module Savon
5
- module SOAP
6
-
7
- # = Savon::SOAP::Fault
8
- #
9
- # Represents a SOAP fault. Contains the original <tt>HTTPI::Response</tt>.
10
- class Fault < Error
11
-
12
- # Expects an <tt>HTTPI::Response</tt>.
13
- def initialize(http)
14
- self.http = http
15
- end
16
-
17
- # Accessor for the <tt>HTTPI::Response</tt>.
18
- attr_accessor :http
19
-
20
- # Returns whether a SOAP fault is present.
21
- def present?
22
- @present ||= http.body.include?("Fault>") && (soap1_fault? || soap2_fault?)
23
- end
24
-
25
- # Returns the SOAP fault message.
26
- def to_s
27
- return "" unless present?
28
- @message ||= message_by_version to_hash[:fault]
29
- end
30
-
31
- # Returns the SOAP response body as a Hash.
32
- def to_hash
33
- @hash ||= Nori.parse(http.body)[:envelope][:body]
34
- end
35
-
36
- private
37
-
38
- # Returns whether the response contains a SOAP 1.1 fault.
39
- def soap1_fault?
40
- http.body.include?("faultcode>") && http.body.include?("faultstring>")
41
- end
42
-
43
- # Returns whether the response contains a SOAP 1.2 fault.
44
- def soap2_fault?
45
- http.body.include?("Code>") && http.body.include?("Reason>")
46
- end
47
-
48
- # Returns the SOAP fault message by version.
49
- def message_by_version(fault)
50
- if fault[:faultcode]
51
- "(#{fault[:faultcode]}) #{fault[:faultstring]}"
52
- elsif fault[:code]
53
- "(#{fault[:code][:value]}) #{fault[:reason][:text]}"
54
- end
55
- end
56
-
57
- end
58
- end
59
- end
@@ -1,13 +0,0 @@
1
- require "savon/error"
2
-
3
- module Savon
4
- module SOAP
5
-
6
- # = Savon::SOAP::InvalidResponseError
7
- #
8
- # Represents an error when the response was not a valid SOAP envelope.
9
- class InvalidResponseError < Error
10
- end
11
-
12
- end
13
- end
@@ -1,86 +0,0 @@
1
- require "httpi"
2
- require "savon/soap/response"
3
-
4
- module Savon
5
- module SOAP
6
-
7
- # = Savon::SOAP::Request
8
- #
9
- # Executes SOAP requests.
10
- class Request
11
-
12
- # Content-Types by SOAP version.
13
- CONTENT_TYPE = { 1 => "text/xml;charset=UTF-8", 2 => "application/soap+xml;charset=UTF-8" }
14
-
15
- # Expects an <tt>HTTPI::Request</tt> and a <tt>Savon::SOAP::XML</tt> object
16
- # to execute a SOAP request and returns the response.
17
- def self.execute(config, http, soap)
18
- new(config, http, soap).response
19
- end
20
-
21
- # Expects an <tt>HTTPI::Request</tt>, a <tt>Savon::SOAP::XML</tt> object
22
- # and a <tt>Savon::Config</tt>.
23
- def initialize(config, http, soap)
24
- self.config = config
25
- self.soap = soap
26
- self.http = configure(http)
27
- end
28
-
29
- attr_accessor :soap, :http, :config
30
-
31
- # Executes the request and returns the response.
32
- def response
33
- @response ||= begin
34
- response = config.hooks.fire(:soap_request, self) { with_logging { HTTPI.post(http) } }
35
- SOAP::Response.new(config, response)
36
- end
37
- end
38
-
39
- private
40
-
41
- # Configures a given +http+ from the +soap+ object.
42
- def configure(http)
43
- http.url = soap.endpoint
44
-
45
- if soap.signature?
46
- # First generate the document so that Signature can digest sections
47
- soap.wsse.signature.document = soap.to_xml(true)
48
-
49
- # Then re-generate the document so that Signature can sign the digest
50
- soap.wsse.signature.document = soap.to_xml(true)
51
-
52
- # The third time we generate the document, we should have a signature
53
- http.body = soap.to_xml(true)
54
- else
55
- http.body = soap.to_xml
56
- end
57
-
58
- http.headers["Content-Type"] = CONTENT_TYPE[soap.version]
59
- http.headers["Content-Length"] = soap.to_xml.bytesize.to_s
60
- http
61
- end
62
-
63
- # Logs the HTTP request, yields to a given +block+ and returns a <tt>Savon::SOAP::Response</tt>.
64
- def with_logging
65
- log_request http.url, http.headers, http.body
66
- response = yield
67
- log_response response.code, response.body
68
- response
69
- end
70
-
71
- # Logs the SOAP request +url+, +headers+ and +body+.
72
- def log_request(url, headers, body)
73
- config.logger.log "SOAP request: #{url}"
74
- config.logger.log headers.map { |key, value| "#{key}: #{value}" }.join(", ")
75
- config.logger.log body, :pretty => config.pretty_print_xml, :filter => true
76
- end
77
-
78
- # Logs the SOAP response +code+ and +body+.
79
- def log_response(code, body)
80
- config.logger.log "SOAP response (status #{code}):"
81
- config.logger.log body, :pretty => config.pretty_print_xml
82
- end
83
-
84
- end
85
- end
86
- end