savon 0.9.9 → 0.9.10

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,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.