savon 2.1.0 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -8,4 +8,5 @@ tmp
8
8
  *~
9
9
  *.gem
10
10
  .bundle
11
+ .rspec
11
12
  Gemfile.lock
@@ -1,10 +1,11 @@
1
1
  # https://github.com/travis-ci/travis-ci/wiki/.travis.yml-options
2
2
  language: "ruby"
3
- script: "bundle exec rake"
3
+ script: "bundle exec rake --trace"
4
4
  rvm:
5
5
  - 1.8.7
6
6
  - 1.9.2
7
7
  - 1.9.3
8
+ - 2.0
8
9
  - jruby-18mode
9
10
  - jruby-19mode
10
11
  - rbx-18mode
@@ -1,3 +1,41 @@
1
+ ### 2.2.0 (2013-04-21)
2
+
3
+ * Feature: [#416](https://github.com/savonrb/savon/pull/416) The global `namespace_identifier`
4
+ option can now be set to `nil` to not add a namespace identifier to the message tag.
5
+
6
+ * Feature: [#408](https://github.com/savonrb/savon/pull/408) Added `Savon::Client#service_name`
7
+ to return the name of the SOAP service.
8
+
9
+ * Improvement: When mistyping an option name, Savon used to raise a simple `NoMethodError`.
10
+ This is because regardless of whether you're using the Hash or block syntax to pass global
11
+ or local options, both are just method calls on some options object.
12
+
13
+ ```
14
+ NoMethodError: undefined method 'wsdk' for #<Savon::GlobalOptions:0x007fed95a55228>
15
+ ```
16
+
17
+ As of this change, Savon now catches those errors and raise a `Savon::UnknownOptionError`
18
+ with a slightly more helpful error message instead.
19
+
20
+ ```
21
+ Savon::UnknownOptionError:
22
+ Unknown global option: :wsdk
23
+ ```
24
+
25
+ * Improvement: [#385](https://github.com/savonrb/savon/issues/385) Instead of raising an
26
+ `ArgumentError` when Wasabi can't find any operations in the WSDL. Savon now raises a
27
+ `Savon::UnknownOperationError`. This might happen when Wasabi fails to parse the WSDL
28
+ because of imports for example.
29
+
30
+ * Fix: [#430](https://github.com/savonrb/savon/pull/430) allows you to rescue and ignore
31
+ `Savon::Error` errors in production while still having mocks trigger test failures.
32
+
33
+ * Fix: [#393](https://github.com/savonrb/savon/pull/393) changed `Savon::SOAPFault` to work
34
+ with generic response Hash keys.
35
+
36
+ * Fix: [#423](https://github.com/savonrb/savon/issues/423) fixes a problem where Wasabi was
37
+ not able to find extension base elements defined in imports it didn't follow.
38
+
1
39
  ### 2.1.0 (2013-02-03)
2
40
 
3
41
  * Feature: [#372](https://github.com/savonrb/savon/pull/372) added global `ssl_cert_key_password` option.
@@ -0,0 +1,46 @@
1
+ # Contribution Guide
2
+
3
+ This page describes how to contribute changes to Savon.
4
+
5
+ Please do not create a pull request without reading this guide first.
6
+ Make sure to read the documentation for your version at [savonrb.com](http://savonrb.com/)
7
+ and post questions to the [mailing list](https://groups.google.com/forum/#!forum/savonrb).
8
+
9
+ **Bug fixes**
10
+
11
+ If you really think you found a bug, please make sure to add as many information as possible
12
+ to the ticket. You're a developer, we are developers and you know we need tests to reproduce
13
+ problems and make sure they don't come back.
14
+
15
+ So if you can reproduce your problem in a spec, that would be awesome! If you can't, please
16
+ let us know how we could make this easier for you. Also, provide code and the WSDL of the
17
+ service your working with so others can try to come up with a spec for your problem.
18
+
19
+ After we have a failing spec, it obviously needs to be fixed. Make sure your new spec is the
20
+ only failing one under the `spec` directory. Travis only runs the "unit tests" at `spec/savon`,
21
+ but Savon actually has with some additional "integration/example specs" at `spec/integration`,
22
+ which you need to run locally to make sure the integration with real world services still works.
23
+
24
+ Notice that these specs are not run by Travis, because the service's are not guaranteed to work
25
+ all the time and the specs will timeout after a few seconds when the service is currently down.
26
+
27
+ Please follow this basic workflow for pull requests:
28
+
29
+ * [Fork the project](https://help.github.com/articles/fork-a-repo)
30
+ * Create a feature branch and make your bug fix
31
+ * Add tests for it!
32
+ * Update the [Changelog](https://github.com/savonrb/savon/blob/master/CHANGELOG.md)
33
+ * [Send a pull request](https://help.github.com/articles/using-pull-requests)
34
+ * [Check that your pull request passes the build](https://travis-ci.org/savonrb/savon/pull_requests)
35
+
36
+
37
+ **Improvements and feature requests**
38
+
39
+ If you have an idea for an improvement or a new feature, please feel free to
40
+ [create a new issue](https://github.com/savonrb/savon/issues/new) and describe your idea
41
+ so that other people can give their insights and opinions. This is also important to avoid
42
+ duplicate work.
43
+
44
+ Pull requests and issues on GitHub are meant to be used to discuss problems and ideas,
45
+ so please make sure to participate and follow up on questions. In case noone comments
46
+ on your ticket, please keep updating the ticket with additional information.
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
4
  gem "httpclient", "~> 2.3.0"
data/README.md CHANGED
@@ -1,14 +1,16 @@
1
- Savon [![Build Status](https://secure.travis-ci.org/savonrb/savon.png)](http://travis-ci.org/savonrb/savon)
2
- =====
1
+ # Savon
3
2
 
4
3
  Heavy metal SOAP client
5
4
 
6
5
  [Documentation](http://savonrb.com) | [RDoc](http://rubydoc.info/gems/savon) |
7
6
  [Mailing list](https://groups.google.com/forum/#!forum/savonrb) | [Twitter](http://twitter.com/savonrb)
8
7
 
8
+ [![Build Status](https://secure.travis-ci.org/savonrb/savon.png)](http://travis-ci.org/savonrb/savon)
9
+ [![Gem Version](https://badge.fury.io/rb/savon.png)](http://badge.fury.io/rb/savon)
10
+ [![Code Climate](https://codeclimate.com/github/savonrb/savon.png)](https://codeclimate.com/github/savonrb/savon)
9
11
 
10
- Installation
11
- ------------
12
+
13
+ ## Installation
12
14
 
13
15
  Savon is available through [Rubygems](http://rubygems.org/gems/savon) and can be installed via:
14
16
 
@@ -16,30 +18,40 @@ Savon is available through [Rubygems](http://rubygems.org/gems/savon) and can be
16
18
  $ gem install savon
17
19
  ```
18
20
 
21
+ or add it to your Gemfile like this:
22
+
23
+ ```
24
+ gem 'savon', '~> 2.1.0'
25
+ ```
26
+
19
27
 
20
- Introduction
21
- ------------
28
+ ## Usage example
22
29
 
23
30
  ``` ruby
24
- require "savon"
31
+ require 'savon'
25
32
 
26
- # create a client for your SOAP service
27
- client = Savon.client(wsdl: "http://service.example.com?wsdl")
33
+ # create a client for the service
34
+ client = Savon.client(wsdl: 'http://service.example.com?wsdl')
28
35
 
29
36
  client.operations
30
- # => [:create_user, :get_user, :get_all_users]
37
+ # => [:find_user, :list_users]
31
38
 
32
- # execute a SOAP request to call the "getUser" action
33
- response = client.call(:get_user) do
34
- message(user_id: 1)
35
- end
39
+ # call the 'findUser' operation
40
+ response = client.call(:find_user, message: { id: 42 })
36
41
 
37
42
  response.body
38
- # => { :get_user_response => { :first_name => "The", :last_name => "Hoff" } }
43
+ # => { find_user_response: { id: 42, name: 'Hoff' } }
39
44
  ```
40
45
 
46
+ For more examples, you should check out the [integration tests](https://github.com/savonrb/savon/tree/master/spec/integration).
47
+
48
+
49
+ ## Documentation
50
+
51
+ Please make sure to read the documentation for your version:
41
52
 
42
- Documentation
43
- -------------
53
+ * [Version 2](http://savonrb.com/version2.html)
54
+ * [Version 1](http://savonrb.com)
44
55
 
45
- Continue reading at [savonrb.com](http://savonrb.com)
56
+ And if you find any problems with it or if you think something's missing,
57
+ feel free to [help out and improve the documentation](https://github.com/savonrb/savonrb.com).
@@ -1,8 +1,10 @@
1
1
  module Savon
2
2
 
3
- Error = Class.new(RuntimeError)
4
- InitializationError = Class.new(Error)
5
- InvalidResponseError = Class.new(Error)
3
+ Error = Class.new(RuntimeError)
4
+ InitializationError = Class.new(Error)
5
+ UnknownOptionError = Class.new(Error)
6
+ UnknownOperationError = Class.new(Error)
7
+ InvalidResponseError = Class.new(Error)
6
8
 
7
9
  def self.client(globals = {}, &block)
8
10
  Client.new(globals, &block)
@@ -1,5 +1,6 @@
1
1
  require "savon/header"
2
2
  require "savon/message"
3
+ require "nokogiri"
3
4
  require "builder"
4
5
  require "gyoku"
5
6
 
@@ -27,6 +28,10 @@ module Savon
27
28
  @used_namespaces = convert_type_namespaces_to_hash
28
29
  end
29
30
 
31
+ def pretty
32
+ Nokogiri.XML(to_s).to_xml(:indent => 2)
33
+ end
34
+
30
35
  def to_s
31
36
  return @locals[:xml] if @locals.include? :xml
32
37
 
@@ -72,7 +77,12 @@ module Savon
72
77
  def namespaces
73
78
  @namespaces ||= begin
74
79
  namespaces = SCHEMA_TYPES.dup
75
- namespaces["xmlns:#{namespace_identifier}"] = @globals[:namespace] || @wsdl.namespace
80
+
81
+ if namespace_identifier == nil
82
+ namespaces["xmlns"] = @globals[:namespace] || @wsdl.namespace
83
+ else
84
+ namespaces["xmlns:#{namespace_identifier}"] = @globals[:namespace] || @wsdl.namespace
85
+ end
76
86
 
77
87
  key = ["xmlns"]
78
88
  key << env_namespace if env_namespace && env_namespace != ""
@@ -91,7 +101,9 @@ module Savon
91
101
  end
92
102
 
93
103
  def namespaced_message_tag
94
- if @used_namespaces[[@operation_name.to_s]]
104
+ if namespace_identifier == nil
105
+ [message_tag, message_attributes]
106
+ elsif @used_namespaces[[@operation_name.to_s]]
95
107
  [@used_namespaces[[@operation_name.to_s]], message_tag, message_attributes]
96
108
  else
97
109
  [namespace_identifier, message_tag, message_attributes]
@@ -12,9 +12,7 @@ module Savon
12
12
  raise_version1_initialize_error! globals
13
13
  end
14
14
 
15
- @globals = GlobalOptions.new(globals)
16
-
17
- BlockInterface.new(@globals).evaluate(block) if block
15
+ set_globals(globals, block)
18
16
 
19
17
  unless wsdl_or_endpoint_and_namespace_specified?
20
18
  raise_initialization_error!
@@ -38,14 +36,27 @@ module Savon
38
36
  operation(operation_name).call(locals, &block)
39
37
  end
40
38
 
39
+ def service_name
40
+ raise_missing_wsdl_error! unless @wsdl.document?
41
+ @wsdl.service_name
42
+ end
43
+
41
44
  private
42
45
 
46
+ def set_globals(globals, block)
47
+ globals = GlobalOptions.new(globals)
48
+ BlockInterface.new(globals).evaluate(block) if block
49
+
50
+ @globals = globals
51
+ end
52
+
43
53
  def build_wsdl_document
44
54
  @wsdl = Wasabi::Document.new
45
55
 
46
- @wsdl.document = @globals[:wsdl] if @globals.include? :wsdl
47
- @wsdl.endpoint = @globals[:endpoint] if @globals.include? :endpoint
48
- @wsdl.namespace = @globals[:namespace] if @globals.include? :namespace
56
+ @wsdl.document = @globals[:wsdl] if @globals.include? :wsdl
57
+ @wsdl.endpoint = @globals[:endpoint] if @globals.include? :endpoint
58
+ @wsdl.namespace = @globals[:namespace] if @globals.include? :namespace
59
+ @wsdl.servicename = @globals[:servicename] if @globals.include? :servicename
49
60
 
50
61
  @wsdl.request = WSDLRequest.new(@globals).build
51
62
  end
@@ -20,6 +20,8 @@ module Savon
20
20
 
21
21
  if @element_form_default == :qualified
22
22
  translated_operation_name = Gyoku.xml_tag(@operation_name, :key_converter => @key_converter).to_s
23
+ # XXX: there is no `@request_key_converter` instance variable!
24
+ # the third argument is therefore always `nil`. [dh, 2013-03-09]
23
25
  @message = QualifiedMessage.new(@types, @used_namespaces, @request_key_converter).to_hash(@message, [translated_operation_name])
24
26
  end
25
27
 
@@ -1,5 +1,5 @@
1
1
  module Savon
2
- class ExpectationError < Error; end
2
+ class ExpectationError < StandardError; end
3
3
  end
4
4
 
5
5
  require "savon/mock/expectation"
@@ -19,8 +19,8 @@ module Savon
19
19
 
20
20
  def self.ensure_exists!(operation_name, wsdl)
21
21
  unless wsdl.soap_actions.include? operation_name
22
- raise ArgumentError, "Unable to find SOAP operation: #{operation_name.inspect}\n" \
23
- "Operations provided by your service: #{wsdl.soap_actions.inspect}"
22
+ raise UnknownOperationError, "Unable to find SOAP operation: #{operation_name.inspect}\n" \
23
+ "Operations provided by your service: #{wsdl.soap_actions.inspect}"
24
24
  end
25
25
  end
26
26
 
@@ -37,12 +37,13 @@ module Savon
37
37
  @globals = globals
38
38
  end
39
39
 
40
- def call(locals = {}, &block)
41
- @locals = LocalOptions.new(locals)
42
-
43
- BlockInterface.new(@locals).evaluate(block) if block
40
+ def build(locals = {}, &block)
41
+ set_locals(locals, block)
42
+ Builder.new(@name, @wsdl, @globals, @locals)
43
+ end
44
44
 
45
- builder = Builder.new(@name, @wsdl, @globals, @locals)
45
+ def call(locals = {}, &block)
46
+ builder = build(locals, &block)
46
47
 
47
48
  response = Savon.notify_observers(@name, builder, @globals, @locals)
48
49
  response ||= call! build_request(builder)
@@ -54,6 +55,13 @@ module Savon
54
55
 
55
56
  private
56
57
 
58
+ def set_locals(locals, block)
59
+ locals = LocalOptions.new(locals)
60
+ BlockInterface.new(locals).evaluate(block) if block
61
+
62
+ @locals = locals
63
+ end
64
+
57
65
  def call!(request)
58
66
  log_request(request) if log?
59
67
  response = HTTPI.post(request)
@@ -9,6 +9,8 @@ module Savon
9
9
  assign options
10
10
  end
11
11
 
12
+ attr_reader :option_type
13
+
12
14
  def [](option)
13
15
  @options[option]
14
16
  end
@@ -30,11 +32,17 @@ module Savon
30
32
  end
31
33
  end
32
34
 
35
+ def method_missing(option, _)
36
+ raise UnknownOptionError, "Unknown #{option_type} option: #{option.inspect}"
37
+ end
38
+
33
39
  end
34
40
 
35
41
  class GlobalOptions < Options
36
42
 
37
43
  def initialize(options = {})
44
+ @option_type = :global
45
+
38
46
  defaults = {
39
47
  :encoding => "UTF-8",
40
48
  :soap_version => 1,
@@ -243,6 +251,8 @@ module Savon
243
251
  class LocalOptions < Options
244
252
 
245
253
  def initialize(options = {})
254
+ @option_type = :local
255
+
246
256
  defaults = {
247
257
  :advanced_typecasting => true,
248
258
  :response_parser => :nokogiri
@@ -19,20 +19,28 @@ module Savon
19
19
  attr_reader :http, :nori
20
20
 
21
21
  def to_s
22
- message_by_version to_hash[:fault]
22
+ fault = nori.find(to_hash, 'Fault')
23
+ message_by_version(fault)
23
24
  end
24
25
 
25
26
  def to_hash
26
- nori.parse(@http.body)[:envelope][:body]
27
+ parsed = nori.parse(@http.body)
28
+ nori.find(parsed, 'Envelope', 'Body')
27
29
  end
28
30
 
29
31
  private
30
32
 
31
33
  def message_by_version(fault)
32
- if fault[:faultcode]
33
- "(#{fault[:faultcode]}) #{fault[:faultstring]}"
34
- elsif fault[:code]
35
- "(#{fault[:code][:value]}) #{fault[:reason][:text]}"
34
+ if nori.find(fault, 'faultcode')
35
+ code = nori.find(fault, 'faultcode')
36
+ text = nori.find(fault, 'faultstring')
37
+
38
+ "(#{code}) #{text}"
39
+ elsif nori.find(fault, 'Code')
40
+ code = nori.find(fault, 'Code', 'Value')
41
+ text = nori.find(fault, 'Reason', 'Text')
42
+
43
+ "(#{code}) #{text}"
36
44
  end
37
45
  end
38
46
 
@@ -1,5 +1,5 @@
1
1
  module Savon
2
2
 
3
- VERSION = "2.1.0"
3
+ VERSION = "2.2.0"
4
4
 
5
5
  end
@@ -11,13 +11,13 @@ Gem::Specification.new do |s|
11
11
  s.email = "me@rubiii.com"
12
12
  s.homepage = "http://savonrb.com"
13
13
  s.summary = "Heavy metal SOAP client"
14
- s.description = "Delicious SOAP for the Ruby community"
14
+ s.description = s.summary
15
15
 
16
16
  s.rubyforge_project = s.name
17
17
 
18
- s.add_dependency "nori", "~> 2.0.3"
18
+ s.add_dependency "nori", "~> 2.1.0"
19
19
  s.add_dependency "httpi", "~> 2.0.2"
20
- s.add_dependency "wasabi", "~> 3.0.0"
20
+ s.add_dependency "wasabi", "~> 3.1.0"
21
21
  s.add_dependency "akami", "~> 1.2.0"
22
22
  s.add_dependency "gyoku", "~> 1.0.0"
23
23
 
@@ -25,7 +25,7 @@ Gem::Specification.new do |s|
25
25
  s.add_dependency "nokogiri", ">= 1.4.0"
26
26
 
27
27
  s.add_development_dependency "rack"
28
- s.add_development_dependency "puma", ">= 2.0.0.b3"
28
+ s.add_development_dependency "puma", "2.0.0.b4"
29
29
 
30
30
  s.add_development_dependency "rake", "~> 0.9"
31
31
  s.add_development_dependency "rspec", "~> 2.10"
@@ -33,7 +33,7 @@ Gem::Specification.new do |s|
33
33
  s.add_development_dependency "json", "~> 1.7"
34
34
 
35
35
  ignores = File.readlines(".gitignore").grep(/\S+/).map(&:chomp)
36
- dotfiles = %w[.gitignore .rspec .travis.yml .yardopts]
36
+ dotfiles = %w[.gitignore .travis.yml .yardopts]
37
37
 
38
38
  all_files_without_ignores = Dir["**/*"].reject { |f|
39
39
  File.directory?(f) || ignores.any? { |i| File.fnmatch(i, f) }