savon 0.9.14 → 1.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.
@@ -1,3 +1,14 @@
1
+ ## 1.0.0 (2012-06-09)
2
+
3
+ * Fix: `Savon.client` didn't pass the optional block.
4
+
5
+ * Improvement: [#291](https://github.com/rubiii/savon/issues/291) changed the `:soap_request` hook to act
6
+ like an around filter. The hook now receives a callback block to execute the SOAP call and can return
7
+ the result of the callback to continue the request. It can also not call the callback block and return
8
+ some `HTTPI::Response` to mock the SOAP request.
9
+
10
+ As this change affects `savon_spec`, you need to update `savon_spec` to v1.3.0.
11
+
1
12
  ## 0.9.14 (2012-06-07)
2
13
 
3
14
  * Fix: [#292](https://github.com/rubiii/savon/issues/292) again
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- Savon [![Build Status](https://secure.travis-ci.org/rubiii/savon.png)](http://travis-ci.org/rubiii/savon)
1
+ Savon [![Build Status](https://secure.travis-ci.org/rubiii/savon.png?branch=master)](http://travis-ci.org/rubiii/savon)
2
2
  =====
3
3
 
4
4
  Heavy metal Ruby SOAP client
@@ -6,8 +6,8 @@ require "savon/model"
6
6
  module Savon
7
7
  extend self
8
8
 
9
- def client(*args)
10
- Client.new(*args)
9
+ def client(*args, &block)
10
+ Client.new(*args, &block)
11
11
  end
12
12
 
13
13
  def configure
@@ -4,17 +4,24 @@ module Savon
4
4
  module CoreExt
5
5
  module String
6
6
 
7
- # Returns the String in snake_case.
8
- def snakecase
9
- str = dup
10
- str.gsub! /::/, '/'
11
- str.gsub! /([A-Z]+)([A-Z][a-z])/, '\1_\2'
12
- str.gsub! /([a-z\d])([A-Z])/, '\1_\2'
13
- str.tr! ".", "_"
14
- str.tr! "-", "_"
15
- str.downcase!
16
- str
17
- end unless method_defined?(:snakecase)
7
+ def self.included(base)
8
+ unless "savon".respond_to?(:snakecase)
9
+ base.send(:include, Extension)
10
+ end
11
+ end
12
+
13
+ module Extension
14
+ def snakecase
15
+ str = dup
16
+ str.gsub! /::/, '/'
17
+ str.gsub! /([A-Z]+)([A-Z][a-z])/, '\1_\2'
18
+ str.gsub! /([a-z\d])([A-Z])/, '\1_\2'
19
+ str.tr! ".", "_"
20
+ str.tr! "-", "_"
21
+ str.downcase!
22
+ str
23
+ end
24
+ end
18
25
 
19
26
  end
20
27
  end
@@ -9,14 +9,18 @@ module Savon
9
9
  class Group
10
10
 
11
11
  # Accepts an Array of +hooks+ to start with.
12
- def initialize(hooks = nil)
13
- self.hooks = hooks
12
+ def initialize(hooks = [])
13
+ @hooks = hooks
14
14
  end
15
15
 
16
- attr_writer :hooks
16
+ # Returns whether this group contains hooks.
17
+ def empty?
18
+ hooks.empty?
19
+ end
17
20
 
18
- def hooks
19
- @hooks ||= []
21
+ # Returns the number of hooks in this group.
22
+ def count
23
+ hooks.count
20
24
  end
21
25
 
22
26
  # Adds a new hook.
@@ -25,14 +29,21 @@ module Savon
25
29
  end
26
30
 
27
31
  # Removes hooks matching the given +ids+.
28
- def reject!(*ids)
32
+ def reject(*ids)
29
33
  ids = ids.flatten
30
34
  hooks.reject! { |hook| ids.include? hook.id }
31
35
  end
32
36
 
33
- # Returns a new group for a given +hook+.
34
- def select(hook)
35
- Group.new hooks.select { |h| h.hook == hook }
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
36
47
  end
37
48
 
38
49
  # Calls the hooks with the given +args+ and returns the
@@ -41,6 +52,17 @@ module Savon
41
52
  hooks.inject(nil) { |memo, hook| hook.call(*args) }
42
53
  end
43
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
+
44
66
  end
45
67
  end
46
68
  end
@@ -8,17 +8,42 @@ module Savon
8
8
 
9
9
  HOOKS = [
10
10
 
11
- # Replaces the POST request executed to call a service.
11
+ # :soap_request
12
+ #
13
+ # Around filter wrapping the POST request executed to call a SOAP service.
12
14
  # See: Savon::SOAP::Request#response
13
15
  #
14
- # Receives the <tt>Savon::SOAP::Request</tt> and is expected to return an <tt>HTTPI::Response</tt>.
15
- # It can change the request and return something falsy to still execute the POST request.
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
16
37
  :soap_request
17
38
 
18
39
  ]
19
40
 
20
41
  # Expects an +id+, the name of the +hook+ to use and a +block+ to be called.
21
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
+
22
47
  self.id = id
23
48
  self.hook = hook
24
49
  self.block = block
@@ -30,8 +30,7 @@ module Savon
30
30
  def define_class_action(action)
31
31
  class_action_module.module_eval %{
32
32
  def #{action.to_s.snakecase}(body = nil, &block)
33
- response = client.request :wsdl, #{action.inspect}, :body => body, &block
34
- config.hooks.select(:model_soap_response).call(response) || response
33
+ client.request :wsdl, #{action.inspect}, :body => body, &block
35
34
  end
36
35
  }
37
36
  end
@@ -49,11 +48,6 @@ module Savon
49
48
  def class_action_module
50
49
  @class_action_module ||= Module.new do
51
50
 
52
- # Returns the memoized <tt>Savon::Config</tt>.
53
- def config
54
- @config ||= Savon.config.clone
55
- end
56
-
57
51
  # Returns the memoized <tt>Savon::Client</tt>.
58
52
  def client(&block)
59
53
  @client ||= Savon::Client.new(&block)
@@ -31,7 +31,7 @@ module Savon
31
31
  # Executes the request and returns the response.
32
32
  def response
33
33
  @response ||= begin
34
- response = config.hooks.select(:soap_request).call(self) || with_logging { HTTPI.post(http) }
34
+ response = config.hooks.fire(:soap_request, self) { with_logging { HTTPI.post(http) } }
35
35
  SOAP::Response.new(config, response)
36
36
  end
37
37
  end
@@ -1,5 +1,5 @@
1
1
  module Savon
2
2
 
3
- VERSION = "0.9.14"
3
+ VERSION = "1.0.0"
4
4
 
5
5
  end
@@ -0,0 +1,71 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::Hooks::Group do
4
+
5
+ let(:group) { subject }
6
+
7
+ describe "#empty?" do
8
+ it "returns true for an empty group" do
9
+ group.should be_empty
10
+ end
11
+
12
+ it "returns false if the group contains hooks" do
13
+ group = Savon::Hooks::Group.new [:some_hook]
14
+ group.should_not be_empty
15
+ end
16
+ end
17
+
18
+ describe "#define" do
19
+ it "lets you define a new hook" do
20
+ group.define(:test_hook, :soap_request)
21
+ group.should_not be_empty
22
+ end
23
+
24
+ it "raises if there is no such hook" do
25
+ expect { group.define(:supposed_to_fail, :no_such_hook) }.to raise_error(ArgumentError)
26
+ end
27
+ end
28
+
29
+ describe "#reject" do
30
+ it "rejects hooks matching any given id" do
31
+ group.define(:remove1, :soap_request)
32
+ group.define(:here_to_stay, :soap_request)
33
+ group.define(:remove2, :soap_request)
34
+ group.count.should == 3
35
+
36
+ group.reject(:remove1, :remove2)
37
+ group.count.should == 1
38
+ end
39
+ end
40
+
41
+ describe "#fire" do
42
+ let(:hook) { lambda {} }
43
+ let(:fallback) { lambda {} }
44
+
45
+ context "with hooks" do
46
+ before do
47
+ group.define(:some_hook, :soap_request, &hook)
48
+ end
49
+
50
+ it "calls the hooks passing any arguments" do
51
+ hook.expects(:call).with(:arg1, :arg2)
52
+ group.fire(:soap_request, :arg1, :arg2)
53
+ end
54
+
55
+ it "calls the hooks passing any arguments and the callback" do
56
+ hook.expects(:call).with(fallback, :arg)
57
+ group.fire(:soap_request, :arg, &fallback)
58
+ end
59
+ end
60
+
61
+ context "without hooks" do
62
+ it "executes the callback" do
63
+ report = :call
64
+ fallback = lambda { report = :back }
65
+ group.fire(:soap_request, &fallback)
66
+ report.should == :back
67
+ end
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,16 @@
1
+ require "spec_helper"
2
+
3
+ describe Savon::Hooks::Hook do
4
+
5
+ let(:hook) { lambda {} }
6
+
7
+ describe "#call" do
8
+ it "calls the hook" do
9
+ hook = Savon::Hooks::Hook.new(:my_hook, :soap_request, &hook)
10
+ hook.expects(:call).with(:arg1, :arg2)
11
+
12
+ hook.call(:arg1, :arg2)
13
+ end
14
+ end
15
+
16
+ end
@@ -26,7 +26,7 @@ describe Savon::Logger do
26
26
  it "logs a given message (pretty and filtered)" do
27
27
  logger.subject.expects(logger.level).with(filtered_message.to_xml(:indent => 2))
28
28
  logger.filter << :hello
29
- warn logger.log(message, :pretty => true, :filter => true)
29
+ logger.log(message, :pretty => true, :filter => true)
30
30
  end
31
31
 
32
32
  it "defaults to wrap the standard Logger" do
@@ -6,38 +6,12 @@ describe Savon::Model do
6
6
  Class.new { extend Savon::Model }
7
7
  end
8
8
 
9
- describe ":model_soap_response hook" do
10
- before(:all) do
11
- model.actions :get_user, "GetAllUsers"
12
- end
13
-
14
- after do
15
- Savon.config.hooks.reject! :test_hook
16
- end
17
-
18
- it "can be used for pre-processing SOAP responses" do
19
- Savon.config.hooks.define(:test_hook, :model_soap_response) do |response|
20
- "hello #{response}"
21
- end
22
-
23
- model.client.stubs(:request).returns("world") #
24
- model.get_user.should == "hello world"
25
- end
26
- end
27
-
28
9
  describe ".client" do
29
10
  it "memoizes the Savon::Client" do
30
11
  model.client.should equal(model.client)
31
12
  end
32
13
  end
33
14
 
34
- describe ".config" do
35
- it "memoizes a clone of the global config" do
36
- model.config.should be_a(Savon::Config)
37
- model.config.should_not equal(Savon.config)
38
- end
39
- end
40
-
41
15
  describe ".endpoint" do
42
16
  it "sets the SOAP endpoint" do
43
17
  model.endpoint "http://example.com"
@@ -2,9 +2,15 @@ require "spec_helper"
2
2
 
3
3
  describe Savon::SOAP::Request do
4
4
 
5
- let(:soap_request) { Savon::SOAP::Request.new(config, http_request, soap_xml) }
6
- let(:http_request) { HTTPI::Request.new }
7
- let(:config) { Savon::Config.default }
5
+ let(:soap_request) { Savon::SOAP::Request.new(config, http_request, soap_xml) }
6
+ let(:http_request) { HTTPI::Request.new }
7
+ let(:http_response) { HTTPI::Response.new 200, {}, Fixture.response(:authentication) }
8
+
9
+ let(:config) {
10
+ config = Savon::Config.default
11
+ config.log = false
12
+ config
13
+ }
8
14
 
9
15
  def soap_xml(*args)
10
16
  @soap_xml ||= soap_xml!(*args)
@@ -26,7 +32,7 @@ describe Savon::SOAP::Request do
26
32
 
27
33
  describe ".execute" do
28
34
  it "executes a SOAP request and returns the response" do
29
- HTTPI.expects(:post).returns(HTTPI::Response.new 200, {}, Fixture.response(:authentication))
35
+ HTTPI.expects(:post).returns(http_response)
30
36
  response = Savon::SOAP::Request.execute config, http_request, soap_xml
31
37
  response.should be_a(Savon::SOAP::Response)
32
38
  end
@@ -67,9 +73,40 @@ describe Savon::SOAP::Request do
67
73
 
68
74
  describe "#response" do
69
75
  it "executes an HTTP POST request and returns a Savon::SOAP::Response" do
70
- HTTPI.expects(:post).returns(HTTPI::Response.new 200, {}, Fixture.response(:authentication))
76
+ HTTPI.expects(:post).returns(http_response)
71
77
  soap_request.response.should be_a(Savon::SOAP::Response)
72
78
  end
79
+
80
+ context "with a :soap_request hook" do
81
+ it "lets you replace the HTTP request and return your own response" do
82
+ config.hooks.define(:test, :soap_request) do |_, request|
83
+ request.should be_a(Savon::SOAP::Request)
84
+ http_response
85
+ end
86
+
87
+ response = soap_request.response
88
+ response.http.should equal(http_response)
89
+ end
90
+
91
+ it "works as an around filter for the SOAP request" do
92
+ HTTPI.expects(:post).returns(http_response)
93
+ state = []
94
+
95
+ config.hooks.define(:test, :soap_request) do |callback, request|
96
+ state << :before
97
+ response = callback.call
98
+ state << response
99
+ state << :after
100
+ response
101
+ end
102
+
103
+ response = soap_request.response
104
+
105
+ state[0].should == :before
106
+ state[1].should be_a(HTTPI::Response)
107
+ state[2].should == :after
108
+ end
109
+ end
73
110
  end
74
111
 
75
112
  end
@@ -6,7 +6,7 @@ RSpec.configure do |config|
6
6
  end
7
7
 
8
8
  # Silence log output
9
- $stdout = StringIO.new
9
+ Savon.config.log = false
10
10
  HTTPI.log = false
11
11
 
12
12
  require "support/endpoint"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: savon
3
3
  version: !ruby/object:Gem::Version
4
- hash: 39
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 9
9
- - 14
10
- version: 0.9.14
9
+ - 0
10
+ version: 1.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Daniel Harrington
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2012-06-07 00:00:00 Z
18
+ date: 2012-06-09 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
21
  version_requirements: &id001 !ruby/object:Gem::Requirement
@@ -242,6 +242,8 @@ files:
242
242
  - spec/savon/client_spec.rb
243
243
  - spec/savon/config_spec.rb
244
244
  - spec/savon/core_ext/string_spec.rb
245
+ - spec/savon/hooks/group_spec.rb
246
+ - spec/savon/hooks/hook_spec.rb
245
247
  - spec/savon/http/error_spec.rb
246
248
  - spec/savon/logger_spec.rb
247
249
  - spec/savon/model_spec.rb