savon 1.2.0 → 2.0.0

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