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