savon 0.9.9 → 0.9.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,5 @@
1
1
  # https://github.com/travis-ci/travis-ci/wiki/.travis.yml-options
2
+ language: "ruby"
2
3
  script: "bundle exec rake"
3
4
  rvm:
4
5
  - 1.8.7
data/.yardopts CHANGED
@@ -3,3 +3,4 @@
3
3
  -
4
4
  CHANGELOG.md
5
5
  LICENSE
6
+ lib/**/*.rb
@@ -1,3 +1,33 @@
1
+ ## 0.9.10 (2012-06-06)
2
+
3
+ * Feature: [#289](https://github.com/rubiii/savon/pull/289) - Allow the SOAP envelope header to be set as a String.
4
+
5
+ * Feature: In addition to the global configuration, there's now also one configuration per client.
6
+ The global config is cloned when a new client is initialized and gets used instead of the global one.
7
+ In addition, for `Savon::Model` classes, the config is cloned per class.
8
+
9
+ Closes [#84](https://github.com/rubiii/savon/issues/84) by allowing one logger per client and
10
+ [#270](https://github.com/rubiii/savon/issues/270) by allowing to specify error handling per client.
11
+
12
+ * Feature: Added an option to pretty print XML in log messages. Closes [#256](https://github.com/rubiii/savon/issues/256)
13
+ and [#280](https://github.com/rubiii/savon/issues/280).
14
+
15
+ ``` ruby
16
+ # global
17
+ Savon.configure do |config|
18
+ config.pretty_print_xml = true
19
+ end
20
+
21
+ # per client
22
+ client.config.pretty_print_xml = true
23
+ ```
24
+
25
+ * Refactoring:
26
+ * Added `Savon.client` as a shortcut for creating a new `Savon::Client`
27
+ * Changed `Savon::Config` from a module to a class.
28
+ * Moved logging to the new `Savon::Logger` object.
29
+ * Removed the `blank?` extension from `Object`.
30
+
1
31
  ## 0.9.9 (2012-02-17)
2
32
 
3
33
  * Improvement: [pull request 255](https://github.com/rubiii/savon/pull/255) - Raise an error if fetching
data/Rakefile CHANGED
@@ -1,7 +1,20 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
- RSpec::Core::RakeTask.new
4
+ RSpec::Core::RakeTask.new do |t|
5
+ t.pattern = "spec/savon/**/*_spec.rb"
6
+ end
7
+
8
+ desc "Run RSpec integration examples"
9
+ RSpec::Core::RakeTask.new "spec:integration" do |t|
10
+ t.pattern = "spec/integration/**/*_spec.rb"
11
+ end
12
+
13
+ # http://stackoverflow.com/q/5771758/279024
14
+ task :require_ruby_18 do
15
+ raise "This must be run on Ruby 1.8" unless RUBY_VERSION =~ /^1\.8/
16
+ end
17
+ task :release => [:require_ruby_18]
5
18
 
6
19
  task :default => :spec
7
20
  task :test => :spec
@@ -4,12 +4,20 @@ require "savon/client"
4
4
  require "savon/model"
5
5
 
6
6
  module Savon
7
- extend Config
7
+ extend self
8
8
 
9
- # Yields this module to a given +block+. Please refer to the
10
- # <tt>Savon::Config</tt> module for configuration options.
11
- def self.configure
12
- yield self if block_given?
9
+ def client(*args)
10
+ Client.new(*args)
13
11
  end
14
12
 
13
+ def configure
14
+ yield config
15
+ end
16
+
17
+ def config
18
+ @config ||= Config.default
19
+ end
20
+
21
+ attr_writer :config
22
+
15
23
  end
@@ -30,11 +30,16 @@ module Savon
30
30
  # wsdl.namespace = "http://users.example.com"
31
31
  # end
32
32
  def initialize(wsdl_document = nil, &block)
33
+ self.config = Savon.config.clone
33
34
  wsdl.document = wsdl_document if wsdl_document
35
+
34
36
  process 1, &block if block
35
37
  wsdl.request = http
36
38
  end
37
39
 
40
+ # Accessor for the <tt>Savon::Config</tt>.
41
+ attr_accessor :config
42
+
38
43
  # Returns the <tt>Savon::Wasabi::Document</tt>.
39
44
  def wsdl
40
45
  @wsdl ||= Wasabi::Document.new
@@ -71,12 +76,12 @@ module Savon
71
76
  def request(*args, &block)
72
77
  raise ArgumentError, "Savon::Client#request requires at least one argument" if args.empty?
73
78
 
74
- self.soap = SOAP::XML.new
79
+ self.soap = SOAP::XML.new(config)
75
80
  preconfigure extract_options(args)
76
81
  process &block if block
77
82
  soap.wsse = wsse
78
83
 
79
- response = SOAP::Request.new(http, soap).response
84
+ response = SOAP::Request.new(config, http, soap).response
80
85
  set_cookie response.http.headers
81
86
  response
82
87
  end
@@ -1,103 +1,27 @@
1
- require "logger"
2
- require "savon/soap"
1
+ require "savon/logger"
3
2
  require "savon/hooks/group"
3
+ require "savon/soap"
4
4
 
5
5
  module Savon
6
- module Config
7
-
8
- # Sets whether to log HTTP requests.
9
- attr_writer :log
10
-
11
- # Returns whether to log HTTP requests. Defaults to +true+.
12
- def log?
13
- @log != false
14
- end
15
-
16
- # Sets the logger to use.
17
- attr_writer :logger
18
-
19
- # Returns the logger. Defaults to an instance of +Logger+ writing to STDOUT.
20
- def logger
21
- @logger ||= ::Logger.new STDOUT
22
- end
23
-
24
- # Sets the log level.
25
- attr_writer :log_level
26
-
27
- # Returns the log level. Defaults to :debug.
28
- def log_level
29
- @log_level ||= :debug
30
- end
31
-
32
- # Logs a given +message+. Optionally filtered if +xml+ is truthy.
33
- def log(message, xml = false)
34
- return unless log?
35
- message = filter_xml(message) if xml && !log_filter.empty?
36
- logger.send log_level, message
37
- end
38
-
39
- # Returns the log filter. Defaults to an empty Array.
40
- def log_filter
41
- @log_filter ||= []
42
- end
43
-
44
- # Sets the log filter. Expects an Array.
45
- attr_writer :log_filter
46
-
47
- # Filters the given +xml+ based on log filter.
48
- def filter_xml(xml)
49
- doc = Nokogiri::XML(xml)
50
- return xml unless doc.errors.empty?
51
-
52
- log_filter.each do |filter|
53
- doc.xpath("//*[local-name()='#{filter}']").map { |node| node.content = "***FILTERED***" }
54
- end
55
-
56
- doc.root.to_s
57
- end
58
-
59
- # Sets whether to raise HTTP errors and SOAP faults.
60
- attr_writer :raise_errors
6
+ Config = Struct.new(:logger, :pretty_print_xml, :raise_errors, :soap_version, :env_namespace, :soap_header) do
61
7
 
62
- # Returns whether to raise errors. Defaults to +true+.
63
- def raise_errors?
64
- @raise_errors != false
8
+ def self.default
9
+ config = new
10
+ config.logger = Logger.new
11
+ config.raise_errors = true
12
+ config.soap_version = SOAP::DefaultVersion
13
+ config
65
14
  end
66
15
 
67
- # Sets the global SOAP version.
68
- def soap_version=(version)
69
- raise ArgumentError, "Invalid SOAP version: #{version}" if version && !SOAP::Versions.include?(version)
70
- @version = version
71
- end
72
-
73
- # Returns SOAP version. Defaults to +DefaultVersion+.
74
- def soap_version
75
- @version ||= SOAP::DefaultVersion
76
- end
77
-
78
- # Accessor for the global env_namespace.
79
- attr_accessor :env_namespace
80
-
81
- # Accessor for the global soap_header.
82
- attr_accessor :soap_header
83
-
84
- # Returns the hooks.
85
16
  def hooks
86
17
  @hooks ||= Hooks::Group.new
87
18
  end
88
19
 
89
- # Reset to default configuration.
90
- def reset_config!
91
- self.log = nil
92
- self.logger = nil
93
- self.log_level = nil
94
- self.log_filter = nil
95
- self.raise_errors = nil
96
- self.soap_version = nil
97
- self.env_namespace = nil
98
- self.soap_header = nil
20
+ def clone
21
+ config = super
22
+ config.logger = config.logger.clone
23
+ config
99
24
  end
100
25
 
101
26
  end
102
27
  end
103
-
@@ -0,0 +1,47 @@
1
+ module Savon
2
+ class LogMessage
3
+
4
+ def initialize(message, filter, options = {})
5
+ self.message = message
6
+ self.filter = filter
7
+ self.with_pretty = options[:pretty]
8
+ self.with_filter = options[:filter]
9
+ end
10
+
11
+ attr_accessor :message, :filter, :with_pretty, :with_filter
12
+
13
+ def filter?
14
+ with_filter && filter.any?
15
+ end
16
+
17
+ def pretty?
18
+ with_pretty
19
+ end
20
+
21
+ def to_s
22
+ return message unless pretty? || filter?
23
+
24
+ doc = Nokogiri::XML(message)
25
+ doc = apply_filter(doc) if filter?
26
+ doc.to_xml(pretty_options)
27
+ end
28
+
29
+ private
30
+
31
+ def apply_filter(doc)
32
+ return doc unless doc.errors.empty?
33
+
34
+ filter.each do |fi|
35
+ doc.xpath("//*[local-name()='#{fi}']").each { |node| node.content = "***FILTERED***" }
36
+ end
37
+
38
+ doc
39
+ end
40
+
41
+ def pretty_options
42
+ return {} unless pretty?
43
+ { :indent => 2 }
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,37 @@
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
+ def log_raw(message)
19
+ subject.send(level, message)
20
+ end
21
+
22
+ attr_writer :subject, :level, :filter
23
+
24
+ def subject
25
+ @subject ||= ::Logger.new(device)
26
+ end
27
+
28
+ def level
29
+ @level ||= :debug
30
+ end
31
+
32
+ def filter
33
+ @filter ||= []
34
+ end
35
+
36
+ end
37
+ end
@@ -31,7 +31,7 @@ module Savon
31
31
  class_action_module.module_eval %{
32
32
  def #{action.to_s.snakecase}(body = nil, &block)
33
33
  response = client.request :wsdl, #{action.inspect}, :body => body, &block
34
- Savon.hooks.select(:model_soap_response).call(response) || response
34
+ config.hooks.select(:model_soap_response).call(response) || response
35
35
  end
36
36
  }
37
37
  end
@@ -49,6 +49,11 @@ module Savon
49
49
  def class_action_module
50
50
  @class_action_module ||= Module.new do
51
51
 
52
+ # Returns the memoized <tt>Savon::Config</tt>.
53
+ def config
54
+ @config ||= Savon.config.clone
55
+ end
56
+
52
57
  # Returns the memoized <tt>Savon::Client</tt>.
53
58
  def client(&block)
54
59
  @client ||= Savon::Client.new(&block)
@@ -14,23 +14,26 @@ module Savon
14
14
 
15
15
  # Expects an <tt>HTTPI::Request</tt> and a <tt>Savon::SOAP::XML</tt> object
16
16
  # to execute a SOAP request and returns the response.
17
- def self.execute(http, soap)
18
- new(http, soap).response
17
+ def self.execute(config, http, soap)
18
+ new(config, http, soap).response
19
19
  end
20
20
 
21
- # Expects an <tt>HTTPI::Request</tt> and a <tt>Savon::SOAP::XML</tt> object.
22
- def initialize(http, soap)
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
23
25
  self.soap = soap
24
26
  self.http = configure(http)
25
27
  end
26
28
 
27
- attr_accessor :soap, :http
29
+ attr_accessor :soap, :http, :config
28
30
 
29
31
  # Executes the request and returns the response.
30
32
  def response
31
- @response ||= SOAP::Response.new(
32
- Savon.hooks.select(:soap_request).call(self) || with_logging { HTTPI.post(http) }
33
- )
33
+ @response ||= begin
34
+ response = config.hooks.select(:soap_request).call(self) || with_logging { HTTPI.post(http) }
35
+ SOAP::Response.new(config, response)
36
+ end
34
37
  end
35
38
 
36
39
  private
@@ -54,15 +57,15 @@ module Savon
54
57
 
55
58
  # Logs the SOAP request +url+, +headers+ and +body+.
56
59
  def log_request(url, headers, body)
57
- Savon.log "SOAP request: #{url}"
58
- Savon.log headers.map { |key, value| "#{key}: #{value}" }.join(", ")
59
- Savon.log body, :filter
60
+ config.logger.log "SOAP request: #{url}"
61
+ config.logger.log headers.map { |key, value| "#{key}: #{value}" }.join(", ")
62
+ config.logger.log body, :pretty => config.pretty_print_xml, :filter => true
60
63
  end
61
64
 
62
65
  # Logs the SOAP response +code+ and +body+.
63
66
  def log_response(code, body)
64
- Savon.log "SOAP response (status #{code}):"
65
- Savon.log body
67
+ config.logger.log "SOAP response (status #{code}):"
68
+ config.logger.log body, :pretty => config.pretty_print_xml
66
69
  end
67
70
 
68
71
  end
@@ -12,12 +12,13 @@ module Savon
12
12
  class Response
13
13
 
14
14
  # Expects an <tt>HTTPI::Response</tt> and handles errors.
15
- def initialize(response)
15
+ def initialize(config, response)
16
+ self.config = config
16
17
  self.http = response
17
- raise_errors if Savon.raise_errors?
18
+ raise_errors if config.raise_errors
18
19
  end
19
20
 
20
- attr_accessor :http
21
+ attr_accessor :http, :config
21
22
 
22
23
  # Returns whether the request was successful.
23
24
  def success?
@@ -25,12 +25,15 @@ module Savon
25
25
  }
26
26
 
27
27
  # Accepts an +endpoint+, an +input+ tag and a SOAP +body+.
28
- def initialize(endpoint = nil, input = nil, body = nil)
28
+ def initialize(config, endpoint = nil, input = nil, body = nil)
29
+ self.config = config
29
30
  self.endpoint = endpoint if endpoint
30
31
  self.input = input if input
31
32
  self.body = body if body
32
33
  end
33
34
 
35
+ attr_accessor :config
36
+
34
37
  # Accessor for the SOAP +input+ tag.
35
38
  attr_accessor :input
36
39
 
@@ -43,9 +46,9 @@ module Savon
43
46
  @version = version
44
47
  end
45
48
 
46
- # Returns the SOAP +version+. Defaults to <tt>Savon.soap_version</tt>.
49
+ # Returns the SOAP +version+. Defaults to <tt>Savon.config.soap_version</tt>.
47
50
  def version
48
- @version ||= Savon.soap_version
51
+ @version ||= config.soap_version
49
52
  end
50
53
 
51
54
  # Sets the SOAP +header+ Hash.
@@ -53,7 +56,7 @@ module Savon
53
56
 
54
57
  # Returns the SOAP +header+. Defaults to an empty Hash.
55
58
  def header
56
- @header ||= Savon.soap_header.nil? ? {} : Savon.soap_header
59
+ @header ||= config.soap_header.nil? ? {} : config.soap_header
57
60
  end
58
61
 
59
62
  # Sets the SOAP envelope namespace.
@@ -61,7 +64,7 @@ module Savon
61
64
 
62
65
  # Returns the SOAP envelope namespace. Uses the global namespace if set Defaults to :env.
63
66
  def env_namespace
64
- @env_namespace ||= Savon.env_namespace.nil? ? :env : Savon.env_namespace
67
+ @env_namespace ||= config.env_namespace.nil? ? :env : config.env_namespace
65
68
  end
66
69
 
67
70
  # Sets the +namespaces+ Hash.
@@ -70,8 +73,9 @@ module Savon
70
73
  # Returns the +namespaces+. Defaults to a Hash containing the SOAP envelope namespace.
71
74
  def namespaces
72
75
  @namespaces ||= begin
73
- key = env_namespace.blank? ? "xmlns" : "xmlns:#{env_namespace}"
74
- { key => SOAP::Namespace[version] }
76
+ key = ["xmlns"]
77
+ key << env_namespace if env_namespace && env_namespace != ""
78
+ { key.join(":") => SOAP::Namespace[version] }
75
79
  end
76
80
  end
77
81
 
@@ -172,8 +176,11 @@ module Savon
172
176
  # Expects a builder +xml+ instance, a tag +name+ and accepts optional +namespaces+
173
177
  # and a block to create an XML tag.
174
178
  def tag(xml, name, namespaces = {}, &block)
175
- return xml.tag! name, namespaces, &block if env_namespace.blank?
176
- xml.tag! env_namespace, name, namespaces, &block
179
+ if env_namespace && env_namespace != ""
180
+ xml.tag! env_namespace, name, namespaces, &block
181
+ else
182
+ xml.tag! name, namespaces, &block
183
+ end
177
184
  end
178
185
 
179
186
  # Returns the complete Hash of namespaces.
@@ -185,7 +192,7 @@ module Savon
185
192
 
186
193
  # Returns the SOAP header as an XML String.
187
194
  def header_for_xml
188
- @header_for_xml ||= Gyoku.xml(header) + wsse_header
195
+ @header_for_xml ||= (Hash === header ? Gyoku.xml(header) : header) + wsse_header
189
196
  end
190
197
 
191
198
  # Returns the WSSE header or an empty String in case WSSE was not set.