diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2236102d..f596e08e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 1.0.0 (UPCOMING)
+
+* Feature: Added [hooks](http://savonrb.com/#hook_into_the_system).
+
## 0.9.7 (2011-08-25)
* Feature: Merged [pull request 210](https://github.com/rubiii/savon/pull/210) by
diff --git a/lib/savon/global.rb b/lib/savon/global.rb
index 320d66b3..22ccf21d 100644
--- a/lib/savon/global.rb
+++ b/lib/savon/global.rb
@@ -1,5 +1,6 @@
require "logger"
require "savon/soap"
+require "savon/hooks/group"
module Savon
module Global
@@ -92,6 +93,11 @@ def deprecate?
@deprecate != false
end
+ # Returns the hooks.
+ def hooks
+ @hooks ||= Hooks::Group.new
+ end
+
# Reset to default configuration.
def reset_config!
self.log = true
diff --git a/lib/savon/hooks/group.rb b/lib/savon/hooks/group.rb
new file mode 100644
index 00000000..26eb000b
--- /dev/null
+++ b/lib/savon/hooks/group.rb
@@ -0,0 +1,46 @@
+require "savon/hooks/hook"
+
+module Savon
+ module Hooks
+
+ # = Savon::Hooks::Group
+ #
+ # Manages a list of hooks.
+ class Group
+
+ # Accepts an Array of +hooks+ to start with.
+ def initialize(hooks = nil)
+ self.hooks = hooks
+ end
+
+ attr_writer :hooks
+
+ def hooks
+ @hooks ||= []
+ end
+
+ # Adds a new hook.
+ def define(id, hook, &block)
+ hooks << Hook.new(id, hook, &block)
+ end
+
+ # Removes hooks matching the given +ids+.
+ def reject!(*ids)
+ ids = ids.flatten
+ hooks.reject! { |hook| ids.include? hook.id }
+ end
+
+ # Returns a new group for a given +hook+.
+ def select(hook)
+ Group.new hooks.select { |h| h.hook == hook }
+ end
+
+ # Calls the hooks with the given +args+ and returns the
+ # value of the last hooks.
+ def call(*args)
+ hooks.inject(nil) { |memo, hook| hook.call(*args) }
+ end
+
+ end
+ end
+end
diff --git a/lib/savon/hooks/hook.rb b/lib/savon/hooks/hook.rb
new file mode 100644
index 00000000..b31b8d4e
--- /dev/null
+++ b/lib/savon/hooks/hook.rb
@@ -0,0 +1,36 @@
+module Savon
+ module Hooks
+
+ # = Savon::Hooks::Hook
+ #
+ # A hook used somewhere in the system.
+ class Hook
+
+ HOOKS = [
+
+ # Replaces the POST request executed to call a service.
+ # See: Savon::SOAP::Request#response
+ #
+ # Receives the Savon::SOAP::Request and is expected to return an HTTPI::Response.
+ # It can change the request and return something falsy to still execute the POST request.
+ :soap_request
+
+ ]
+
+ # Expects an +id+, the name of the +hook+ to use and a +block+ to be called.
+ def initialize(id, hook, &block)
+ self.id = id
+ self.hook = hook
+ self.block = block
+ end
+
+ attr_accessor :id, :hook, :block
+
+ # Calls the +block+ with the given +args+.
+ def call(*args)
+ block.call(*args)
+ end
+
+ end
+ end
+end
diff --git a/lib/savon/soap/request.rb b/lib/savon/soap/request.rb
index 6ce5465e..d7c383d4 100644
--- a/lib/savon/soap/request.rb
+++ b/lib/savon/soap/request.rb
@@ -14,43 +14,43 @@ class Request
# Expects an HTTPI::Request and a Savon::SOAP::XML object
# to execute a SOAP request and returns the response.
- def self.execute(request, soap)
- new(request, soap).response
+ def self.execute(http, soap)
+ new(http, soap).response
end
# Expects an HTTPI::Request and a Savon::SOAP::XML object.
- def initialize(request, soap)
- self.request = setup(request, soap)
+ def initialize(http, soap)
+ self.soap = soap
+ self.http = configure(http)
end
- # Accessor for the HTTPI::Request.
- attr_accessor :request
+ attr_accessor :soap, :http
# Executes the request and returns the response.
def response
- @response ||= with_logging { HTTPI.post request }
+ @response ||= SOAP::Response.new(
+ Savon.hooks.select(:soap_request).call(self) || with_logging { HTTPI.post(http) }
+ )
end
private
- # Sets up the +request+ using a given +soap+ object.
- def setup(request, soap)
- url, body = soap.endpoint, soap.to_xml
+ # Configures a given +http+ from the +soap+ object.
+ def configure(http)
+ http.url = soap.endpoint
+ http.body = soap.to_xml
+ http.headers["Content-Type"] ||= ContentType[soap.version]
+ http.headers["Content-Length"] ||= soap.to_xml.length.to_s
- request.url = url
- request.body = body
- request.headers["Content-Type"] ||= ContentType[soap.version]
- request.headers["Content-Length"] ||= body.length.to_s
-
- request
+ http
end
# Logs the HTTP request, yields to a given +block+ and returns a Savon::SOAP::Response.
def with_logging
- log_request request.url, request.headers, request.body
+ log_request http.url, http.headers, http.body
response = yield
log_response response.code, response.body
- SOAP::Response.new response
+ response
end
# Logs the SOAP request +url+, +headers+ and +body+.
diff --git a/lib/savon/version.rb b/lib/savon/version.rb
index 3b97ae27..df113078 100644
--- a/lib/savon/version.rb
+++ b/lib/savon/version.rb
@@ -1,5 +1,5 @@
module Savon
- Version = "0.9.7"
+ Version = "1.0.0"
end
diff --git a/spec/savon/soap/request_spec.rb b/spec/savon/soap/request_spec.rb
index e122b6c6..18414b62 100644
--- a/spec/savon/soap/request_spec.rb
+++ b/spec/savon/soap/request_spec.rb
@@ -20,30 +20,30 @@
describe ".new" do
it "uses the SOAP endpoint for the request" do
- soap_request.request.url.should == URI(soap.endpoint)
+ soap_request.http.url.should == URI(soap.endpoint)
end
it "sets the SOAP body for the request" do
- soap_request.request.body.should == soap.to_xml
+ soap_request.http.body.should == soap.to_xml
end
it "sets the Content-Type header for SOAP 1.1" do
- soap_request.request.headers["Content-Type"].should == Savon::SOAP::Request::ContentType[1]
+ soap_request.http.headers["Content-Type"].should == Savon::SOAP::Request::ContentType[1]
end
it "sets the Content-Type header for SOAP 1.2" do
soap.version = 2
- soap_request.request.headers["Content-Type"].should == Savon::SOAP::Request::ContentType[2]
+ soap_request.http.headers["Content-Type"].should == Savon::SOAP::Request::ContentType[2]
end
it "does not set the Content-Type header if it's already specified" do
headers = { "Content-Type" => "text/plain" }
soap_request = Savon::SOAP::Request.new HTTPI::Request.new(:headers => headers), soap
- soap_request.request.headers["Content-Type"].should == headers["Content-Type"]
+ soap_request.http.headers["Content-Type"].should == headers["Content-Type"]
end
it "sets the Content-Length header" do
- soap_request.request.headers["Content-Length"].should == soap.to_xml.length.to_s
+ soap_request.http.headers["Content-Length"].should == soap.to_xml.length.to_s
end
end