savon 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 1.2.0 (2012-09-15)
2
+
3
+ * Fix: [#312](https://github.com/rubiii/savon/pull/312) recursively determines the proper namespaces
4
+ for SOAP body Hashes with nested Arrays of Hashes.
5
+
6
+ * Improvement: [#318](https://github.com/rubiii/savon/pull/318) isolates building the request to
7
+ improve threadsafety.
8
+
9
+ * Refactoring: Use the `Wasabi::Document` with resolver instead of the custom `Savon::Wasabi::Document`.
10
+
1
11
  ## 1.1.0 (2012-06-28)
2
12
 
3
13
  * Improvement: Changed Savon's core dependencies to be more strict and only allow bug fix changes.
@@ -5,7 +15,7 @@
5
15
  This should improve the stability of the library and make it easier to update, because changes
6
16
  to these core dependencies will be documented here as well.
7
17
 
8
- * Fix: The latest version of Wasabi should now correctly detect the names of you operations.
18
+ * Fix: The latest version of Wasabi should now correctly detect the names of your operations.
9
19
  So you should be able to just get the names of some operation:
10
20
 
11
21
  ``` ruby
@@ -13,19 +23,19 @@
13
23
  # => [:authenticate, :find_user]
14
24
  ```
15
25
 
16
- and pass the Symbol to execute a request:
26
+ and pass the Symbol to execute a request:
17
27
 
18
28
  ``` ruby
19
29
  client.request :authenticate, body: { token: "secret" }
20
30
  ```
21
31
 
22
- If you still pass anything other than a single Symbol to that method, please open an issue!
23
- You shouldn't need to specify a namespace or additional attributes for the tag.
32
+ If you still pass anything other than a single Symbol to that method, please open an issue!
33
+ You shouldn't need to specify a namespace or additional attributes for the tag.
24
34
 
25
35
  * Refactoring: Moved code that sets the cookies from the last response for the
26
36
  next request to `HTTPI::Request#set_cookies`.
27
37
 
28
- ## 1.0.0 (2012-06-09)
38
+ ### 1.0.0 (2012-06-09)
29
39
 
30
40
  * Fix: `Savon.client` didn't pass the optional block.
31
41
 
@@ -34,7 +44,7 @@
34
44
  the result of the callback to continue the request. It can also not call the callback block and return
35
45
  some `HTTPI::Response` to mock the SOAP request.
36
46
 
37
- As this change affects `savon_spec`, you need to update `savon_spec` to v1.3.0.
47
+ As this change affects `savon_spec`, you need to update `savon_spec` to v1.3.0.
38
48
 
39
49
  ### 0.9.14 (2012-06-07)
40
50
 
@@ -99,8 +109,8 @@
99
109
  The global config is cloned when a new client is initialized and gets used instead of the global one.
100
110
  In addition, for `Savon::Model` classes, the config is cloned per class.
101
111
 
102
- Closes [#84](https://github.com/rubiii/savon/issues/84) by allowing one logger per client and
103
- [#270](https://github.com/rubiii/savon/issues/270) by allowing to specify error handling per client.
112
+ Closes [#84](https://github.com/rubiii/savon/issues/84) by allowing one logger per client and
113
+ [#270](https://github.com/rubiii/savon/issues/270) by allowing to specify error handling per client.
104
114
 
105
115
  * Feature: Added an option to pretty print XML in log messages. Closes [#256](https://github.com/rubiii/savon/issues/256)
106
116
  and [#280](https://github.com/rubiii/savon/issues/280).
@@ -309,16 +319,16 @@
309
319
  Gyoku.xml(:first_name => "Mac") # => "<FirstName></Firstname>"
310
320
  ```
311
321
 
312
- You can even define your own conversion formular.
322
+ You can even define your own conversion formular.
313
323
 
314
324
  ``` ruby
315
325
  Gyoku.convert_symbols_to { |key| key.upcase }
316
326
  Gyoku.xml(:first_name => "Mac") # => "<FIRST_NAME></FIRST_NAME>"
317
327
  ```
318
328
 
319
- This should also work for the SOAP input tag and SOAPAction header. So if you had to use a String for
320
- the SOAP action to call because your services uses CamelCase instead of lowerCamelCase, you can now
321
- change the default and use Symbols instead.
329
+ This should also work for the SOAP input tag and SOAPAction header. So if you had to use a String for
330
+ the SOAP action to call because your services uses CamelCase instead of lowerCamelCase, you can now
331
+ change the default and use Symbols instead.
322
332
 
323
333
  ``` ruby
324
334
  Gyoku.convert_symbols_to(:camelcase)
@@ -382,25 +392,31 @@
382
392
 
383
393
  * Changed `Savon::WSSE` to be based on a Hash instead of relying on builder ([4cebc3](https://github.com/rubiii/savon/commit/4cebc3)).
384
394
 
385
- `Savon::WSSE` now supports wsse:Timestamp headers ([issue #122](https://github.com/rubiii/savon/issues/122)) by setting
386
- `Savon::WSSE#timestamp` to `true`:
395
+ `Savon::WSSE` now supports wsse:Timestamp headers ([issue #122](https://github.com/rubiii/savon/issues/122)) by setting
396
+ `Savon::WSSE#timestamp` to `true`:
387
397
 
388
- client.request :some_method do
389
- wsse.timestamp = true
390
- end
398
+ ``` ruby
399
+ client.request :some_method do
400
+ wsse.timestamp = true
401
+ end
402
+ ```
391
403
 
392
- or by setting `Savon::WSSE#created_at` or `Savon::WSSE#expires_at`:
404
+ or by setting `Savon::WSSE#created_at` or `Savon::WSSE#expires_at`:
393
405
 
394
- client.request :some_method do
395
- wsse.created_at = Time.now
396
- wsse.expires_at = Time.now + 60
397
- end
406
+ ``` ruby
407
+ client.request :some_method do
408
+ wsse.created_at = Time.now
409
+ wsse.expires_at = Time.now + 60
410
+ end
411
+ ```
398
412
 
399
- You can also add custom tags to the WSSE header ([issue #69](https://github.com/rubiii/savon/issues/69)):
413
+ You can also add custom tags to the WSSE header ([issue #69](https://github.com/rubiii/savon/issues/69)):
400
414
 
401
- client.request :some_method do
402
- wsse["wsse:Security"]["wsse:UsernameToken"] = { "Organization" => "ACME", "Domain" => "acme.com" }
403
- end
415
+ ``` ruby
416
+ client.request :some_method do
417
+ wsse["wsse:Security"]["wsse:UsernameToken"] = { "Organization" => "ACME", "Domain" => "acme.com" }
418
+ end
419
+ ```
404
420
 
405
421
  ### 0.8.1 (2010-12-22)
406
422
 
@@ -430,8 +446,10 @@
430
446
  ([6df6a6](https://github.com/rubiii/savon/commit/6df6a6)). The method now accepts multiple arguments representing the response
431
447
  Hash keys to traverse and returns the result as an Array or an empty Array in case the key is nil or does not exist.
432
448
 
433
- response.to_array :get_user_response, :return
434
- # => [{ :id => 1, :name => "foo"}, { :id => 2, :name => "bar"}]
449
+ ``` ruby
450
+ response.to_array :get_user_response, :return
451
+ # => [{ :id => 1, :name => "foo"}, { :id => 2, :name => "bar"}]
452
+ ```
435
453
 
436
454
  ### 0.8.0.beta.3 (2010-11-06)
437
455
 
@@ -444,6 +462,7 @@
444
462
  the SOAP response Hash when a pattern (specified as an Array of Regexps and Symbols) matches the response. If for example
445
463
  your response always looks like ".+Response/return" as in:
446
464
 
465
+ ``` xml
447
466
  <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
448
467
  <soap:Body>
449
468
  <ns2:authenticateResponse xmlns:ns2="http://v1_0.ws.user.example.com">
@@ -453,23 +472,30 @@
453
472
  </ns2:authenticateResponse>
454
473
  </soap:Body>
455
474
  </soap:Envelope>
475
+ ```
456
476
 
457
- you could set the response pattern to:
477
+ you could set the response pattern to:
458
478
 
459
- Savon.configure do |config|
460
- config.response_pattern = [/.+_response/, :return]
461
- end
479
+ ``` ruby
480
+ Savon.configure do |config|
481
+ config.response_pattern = [/.+_response/, :return]
482
+ end
483
+ ```
462
484
 
463
- then instead of calling:
485
+ then instead of calling:
464
486
 
465
- response.to_hash[:authenticate_response][:return] # :some => "thing"
487
+ ``` ruby
488
+ response.to_hash[:authenticate_response][:return] # :some => "thing"
489
+ ```
466
490
 
467
- to get the actual content, Savon::SOAP::Response#to_hash will try to apply given the pattern:
491
+ to get the actual content, Savon::SOAP::Response#to_hash will try to apply given the pattern:
468
492
 
469
- response.to_hash # :some => "thing"
493
+ ``` ruby
494
+ response.to_hash # :some => "thing"
495
+ ```
470
496
 
471
- Please notice, that if you don't specify a response pattern or if the pattern doesn't match the
472
- response, Savon will behave like it always did.
497
+ Please notice, that if you don't specify a response pattern or if the pattern doesn't match the
498
+ response, Savon will behave like it always did.
473
499
 
474
500
  * Added `Savon::SOAP::Response#to_array` (which also uses the response pattern).
475
501
 
@@ -478,42 +504,52 @@
478
504
  * Changed `Savon::Client.new` to accept a block instead of multiple Hash arguments. You can access the
479
505
  wsdl, http and wsse objects inside the block to configure your client for a particular service.
480
506
 
481
- # Instantiating a client to work with a WSDL document
482
- client = Savon::Client.new do
483
- wsdl.document = "http://example.com?wsdl"
484
- end
507
+ ``` ruby
508
+ # Instantiating a client to work with a WSDL document
509
+ client = Savon::Client.new do
510
+ wsdl.document = "http://example.com?wsdl"
511
+ end
512
+ ```
485
513
 
486
- # Directly accessing the SOAP endpoint
487
- client = Savon::Client.new do
488
- wsdl.endpoint = "http://example.com"
489
- wsdl.namespace = "http://v1.example.com"
490
- end
514
+ ``` ruby
515
+ # Directly accessing the SOAP endpoint
516
+ client = Savon::Client.new do
517
+ wsdl.endpoint = "http://example.com"
518
+ wsdl.namespace = "http://v1.example.com"
519
+ end
520
+ ```
491
521
 
492
522
  * Fix for [issue #77](https://github.com/rubiii/savon/issues/77), which means you can now use
493
523
  local WSDL documents:
494
524
 
495
- client = Savon::Client.new do
496
- wsdl.document = "../wsdl/service.xml"
497
- end
525
+ ``` ruby
526
+ client = Savon::Client.new do
527
+ wsdl.document = "../wsdl/service.xml"
528
+ end
529
+ ```
498
530
 
499
531
  * Changed the way SOAP requests are being dispatched. Instead of using method_missing, you now use
500
532
  the new `request` method, which also accepts a block for you to access the wsdl, http, wsse and
501
533
  soap object. Please notice, that a new soap object is created for every request. So you can only
502
534
  access it inside this block.
503
535
 
504
- # A simple request to an :authenticate method
505
- client.request :authenticate do
506
- soap.body = { :id => 1 }
507
- end
536
+ ``` ruby
537
+ # A simple request to an :authenticate method
538
+ client.request :authenticate do
539
+ soap.body = { :id => 1 }
540
+ end
541
+ ```
508
542
 
509
543
  * The new `Savon::Client#request` method fixes issues [#37](https://github.com/rubiii/savon/issues/37),
510
544
  [#61](https://github.com/rubiii/savon/issues/61) and [#64](https://github.com/rubiii/savon/issues/64),
511
545
  which report problems with namespacing the SOAP input tag and attaching attributes to it.
512
546
  Some usage examples:
513
547
 
514
- client.request :get_user # Input tag: <getUser>
515
- client.request :wsdl, "GetUser" # Input tag: <wsdl:GetUser>
516
- client.request :get_user :active => true # Input tag: <getUser active="true">
548
+ ``` ruby
549
+ client.request :get_user # Input tag: <getUser>
550
+ client.request :wsdl, "GetUser" # Input tag: <wsdl:GetUser>
551
+ client.request :get_user :active => true # Input tag: <getUser active="true">
552
+ ```
517
553
 
518
554
  * Savon's new `request` method respects the given namespace. If you don't give it a namespace,
519
555
  Savon will set the target namespace to "xmlns:wsdl". But if you do specify a namespace, it will
@@ -571,13 +607,17 @@
571
607
  * Added support for gzipped requests and responses (http://github.com/lucascs). While gzipped SOAP
572
608
  responses are decoded automatically, you have to manually instruct Savon to gzip SOAP requests:
573
609
 
574
- client = Savon::Client.new "http://example.com/UserService?wsdl", :gzip => true
610
+ ``` ruby
611
+ client = Savon::Client.new "http://example.com/UserService?wsdl", :gzip => true
612
+ ```
575
613
 
576
614
  * Fix for [issue #51](https://github.com/rubiii/savon/issues/51). Added the :soap_endpoint option to
577
615
  `Savon::Client.new` which lets you specify a SOAP endpoint per client instance:
578
616
 
579
- client = Savon::Client.new "http://example.com/UserService?wsdl",
580
- :soap_endpoint => "http://localhost/UserService"
617
+ ``` ruby
618
+ client = Savon::Client.new "http://example.com/UserService?wsdl",
619
+ :soap_endpoint => "http://localhost/UserService"
620
+ ```
581
621
 
582
622
  * Fix for [issue #50](https://github.com/rubiii/savon/issues/50). Savon still escapes special characters
583
623
  in SOAP request Hash values, but you can now append an exclamation mark to Hash keys specifying that
@@ -667,28 +707,38 @@ Pay attention to the following list and read the updated Wiki: http://wiki.githu
667
707
 
668
708
  * SSL client authentication needs to be defined directly on the `Net::HTTP` object:
669
709
 
710
+ ``` ruby
670
711
  client.request.http.client_cert = ...
712
+ ```
671
713
 
672
714
  I added a shortcut method for setting all options through a Hash similar to the previous implementation:
673
715
 
716
+ ``` ruby
674
717
  client.request.http.ssl_client_auth :client_cert => ...
718
+ ```
675
719
 
676
720
  * Open and read timeouts also need to be set on the `Net::HTTP` object:
677
-
721
+
722
+ ``` ruby
678
723
  client.request.http.open_timeout = 30
679
724
  client.request.http.read_timeout = 30
725
+ ```
680
726
 
681
727
  * Please refer to the `Net::HTTP` documentation for more details:
682
728
  http://www.ruby-doc.org/stdlib/libdoc/net/http/rdoc/index.html
683
729
 
684
730
  * Thanks to JulianMorrison, Savon now supports HTTP basic authentication:
685
731
 
732
+ ``` ruby
686
733
  client.request.http.basic_auth "username", "password"
734
+ ```
687
735
 
688
736
  * Julian also added a way to explicitly specify the order of Hash keys and values, so you should now be able
689
737
  to work with services requiring a specific order of input parameters while still using Hash input.
690
738
 
691
- client.find_user { |soap| soap.body = { :name => "Lucy", :id => 666, :@inorder => [:id, :name] } }
739
+ ``` ruby
740
+ client.find_user { |soap| soap.body = { :name => "Lucy", :id => 666, :@inorder => [:id, :name] } }
741
+ ```
692
742
 
693
743
  * `Savon::Response#to_hash` now returns the content inside of "soap:Body" instead of trying to go one
694
744
  level deeper and return it's content. The previous implementation only worked when the "soap:Body" element
@@ -696,7 +746,9 @@ Pay attention to the following list and read the updated Wiki: http://wiki.githu
696
746
 
697
747
  * Added `Savon::SOAP#namespace` as a shortcut for setting the "xmlns:wsdl" namespace.
698
748
 
749
+ ``` ruby
699
750
  soap.namespace = "http://example.com"
751
+ ```
700
752
 
701
753
  ### 0.6.8 (2010-01-01)
702
754
 
@@ -709,9 +761,11 @@ Pay attention to the following list and read the updated Wiki: http://wiki.githu
709
761
  please take a look at the `action` and `input` methods of the `Savon::SOAP` object.
710
762
  One specific problem I know of is working with the createsend WSDL and its namespaced actions.
711
763
 
712
- To make it work, call the SOAP action without namespace and specify the input manually:
764
+ To make it work, call the SOAP action without namespace and specify the input manually:
713
765
 
766
+ ``` ruby
714
767
  client.get_api_key { |soap| soap.input = "User.GetApiKey" }
768
+ ```
715
769
 
716
770
  ### 0.6.7 (2009-12-18)
717
771
 
@@ -743,11 +797,13 @@ Pay attention to the following list and read the updated Wiki: http://wiki.githu
743
797
 
744
798
  * Replaced WSDL document with stream parsing.
745
799
 
746
- Benchmarks (1000 SOAP calls):
800
+ ```
801
+ Benchmarks (1000 SOAP calls):
747
802
 
748
- user system total real
749
- 0.6.4 72.180000 8.280000 80.460000 (750.799011)
750
- 0.6.3 192.900000 19.630000 212.530000 (914.031865)
803
+ user system total real
804
+ 0.6.4 72.180000 8.280000 80.460000 (750.799011)
805
+ 0.6.3 192.900000 19.630000 212.530000 (914.031865)
806
+ ```
751
807
 
752
808
  ### 0.6.3 (2009-12-11)
753
809
 
@@ -755,19 +811,23 @@ Pay attention to the following list and read the updated Wiki: http://wiki.githu
755
811
 
756
812
  * Added global and per request options for disabling `Savon::WSDL`.
757
813
 
814
+ ```
758
815
  Benchmarks (1000 SOAP calls):
759
816
 
760
817
  user system total real
761
818
  WSDL 192.900000 19.630000 212.530000 (914.031865)
762
819
  disabled WSDL 5.680000 1.340000 7.020000 (298.265318)
820
+ ```
763
821
 
764
822
  * Improved XPath expressions for parsing the WSDL document.
765
823
 
824
+ ```
766
825
  Benchmarks (1000 SOAP calls):
767
826
 
768
827
  user system total real
769
828
  0.6.3 192.900000 19.630000 212.530000 (914.031865)
770
829
  0.6.2 574.720000 78.380000 653.100000 (1387.778539)
830
+ ```
771
831
 
772
832
  ### 0.6.2 (2009-12-06)
773
833
 
data/lib/savon/client.rb CHANGED
@@ -1,10 +1,11 @@
1
+ require "wasabi/document"
1
2
  require "httpi/request"
2
3
  require "akami"
3
4
 
4
- require "savon/wasabi/document"
5
5
  require "savon/soap/xml"
6
6
  require "savon/soap/request"
7
7
  require "savon/soap/response"
8
+ require "savon/soap/request_builder"
8
9
 
9
10
  module Savon
10
11
 
@@ -40,7 +41,7 @@ module Savon
40
41
  # Accessor for the <tt>Savon::Config</tt>.
41
42
  attr_accessor :config
42
43
 
43
- # Returns the <tt>Savon::Wasabi::Document</tt>.
44
+ # Returns the <tt>Wasabi::Document</tt>.
44
45
  def wsdl
45
46
  @wsdl ||= Wasabi::Document.new
46
47
  end
@@ -55,13 +56,9 @@ module Savon
55
56
  @wsse ||= Akami.wsse
56
57
  end
57
58
 
58
- # Returns the <tt>Savon::SOAP::XML</tt> object. Please notice, that this object is only available
59
- # in a block given to <tt>Savon::Client#request</tt>. A new instance of this object is created
60
- # per SOAP request.
61
- attr_reader :soap
62
-
63
59
  # Executes a SOAP request for a given SOAP action. Accepts a +block+ which is evaluated in the
64
- # context of this object to let you access the +soap+, +wsdl+, +http+ and +wsse+ methods.
60
+ # context of the <tt>SOAP::RequestBuilder</tt> object to let you access its +soap+, +wsdl+,
61
+ # +http+ and +wsse+ methods.
65
62
  #
66
63
  # == Examples
67
64
  #
@@ -76,12 +73,17 @@ module Savon
76
73
  def request(*args, &block)
77
74
  raise ArgumentError, "Savon::Client#request requires at least one argument" if args.empty?
78
75
 
79
- self.soap = SOAP::XML.new(config)
80
- preconfigure extract_options(args)
81
- process &block if block
82
- soap.wsse = wsse
76
+ options = extract_options(args)
77
+
78
+ request_builder = SOAP::RequestBuilder.new(options.delete(:input), options)
79
+ request_builder.wsdl = wsdl
80
+ request_builder.http = http.dup
81
+ request_builder.wsse = wsse.dup
82
+ request_builder.config = config.dup
83
83
 
84
- response = SOAP::Request.new(config, http, soap).response
84
+ post_configuration = lambda { process(0, request_builder, &block) if block }
85
+
86
+ response = request_builder.request(&post_configuration).response
85
87
  http.set_cookies(response.http)
86
88
 
87
89
  if wsse.verify_response
@@ -93,91 +95,68 @@ module Savon
93
95
 
94
96
  private
95
97
 
96
- # Writer for the <tt>Savon::SOAP::XML</tt> object.
97
- attr_writer :soap
98
-
99
- # Accessor for the original self of a given block.
100
- attr_accessor :original_self
101
-
102
- # Expects an Array of +args+ and returns an Array containing the namespace (might be +nil+),
103
- # the SOAP input and a Hash of attributes for the input tag (which might be empty).
98
+ # Expects an Array of +args+ and returns a Hash containing the SOAP input,
99
+ # the namespace (might be +nil+), the SOAP action (might be +nil+),
100
+ # the SOAP body (might be +nil+), and a Hash of attributes for the input
101
+ # tag (which might be empty).
104
102
  def extract_options(args)
105
103
  attributes = Hash === args.last ? args.pop : {}
106
- namespace = args.size > 1 ? args.shift.to_sym : nil
107
- input = args.first
104
+ body = attributes.delete(:body)
105
+ soap_action = attributes.delete(:soap_action)
108
106
 
109
- [namespace, input, attributes]
110
- end
111
-
112
- # Expects an Array of +args+ to preconfigure the system.
113
- def preconfigure(args)
114
- soap.endpoint = wsdl.endpoint
115
- soap.element_form_default = wsdl.element_form_default
116
-
117
- body = args[2].delete(:body)
118
- soap.body = body if body
119
-
120
- wsdl.type_namespaces.each do |path, uri|
121
- soap.use_namespace(path, uri)
122
- end
123
-
124
- wsdl.type_definitions.each do |path, type|
125
- soap.types[path] = type
126
- end
127
-
128
- soap_action = args[2].delete(:soap_action) || args[1]
129
- set_soap_action soap_action
130
-
131
- if wsdl.document? && (operation = wsdl.operations[args[1]]) && operation[:namespace_identifier]
132
- soap.namespace_identifier = operation[:namespace_identifier].to_sym
133
- soap.namespace = wsdl.parser.namespaces[soap.namespace_identifier.to_s]
134
-
135
- # Override nil namespace with one specified in WSDL
136
- args[0] = soap.namespace_identifier unless args[0]
137
- else
138
- soap.namespace_identifier = args[0]
139
- soap.namespace = wsdl.namespace
140
- end
141
-
142
- set_soap_input *args
143
- end
144
-
145
- # Expects an +input+ and sets the +SOAPAction+ HTTP headers.
146
- def set_soap_action(input_tag)
147
- soap_action = wsdl.soap_action(input_tag.to_sym) if wsdl.document?
148
- soap_action ||= Gyoku::XMLKey.create(input_tag).to_sym
149
- http.headers["SOAPAction"] = %{"#{soap_action}"}
150
- end
107
+ namespace_identifier = args.size > 1 ? args.shift.to_sym : nil
108
+ input = args.first
151
109
 
152
- # Expects a +namespace+, +input+ and +attributes+ and sets the SOAP input.
153
- def set_soap_input(namespace, input, attributes)
154
- new_input_tag = wsdl.soap_input(input.to_sym) if wsdl.document?
155
- new_input_tag ||= Gyoku::XMLKey.create(input)
156
- soap.input = [namespace, new_input_tag.to_sym, attributes]
110
+ remove_blank_values(
111
+ :namespace_identifier => namespace_identifier,
112
+ :input => input,
113
+ :attributes => attributes,
114
+ :body => body,
115
+ :soap_action => soap_action
116
+ )
157
117
  end
158
118
 
159
119
  # Processes a given +block+. Yields objects if the block expects any arguments.
160
- # Otherwise evaluates the block in the context of this object.
161
- def process(offset = 0, &block)
162
- block.arity > 0 ? yield_objects(offset, &block) : evaluate(&block)
120
+ # Otherwise evaluates the block in the context of +instance+.
121
+ def process(offset = 0, instance = self, &block)
122
+ block.arity > 0 ? yield_objects(offset, instance, &block) : evaluate(instance, &block)
163
123
  end
164
124
 
165
125
  # Yields a number of objects to a given +block+ depending on how many arguments
166
126
  # the block is expecting.
167
- def yield_objects(offset, &block)
168
- yield *[soap, wsdl, http, wsse][offset, block.arity]
127
+ def yield_objects(offset, instance, &block)
128
+ to_yield = [:soap, :wsdl, :http, :wsse]
129
+ yield *(to_yield[offset, block.arity].map { |obj_name| instance.send(obj_name) })
169
130
  end
170
131
 
171
- # Evaluates a given +block+ inside this object. Stores the original block binding.
172
- def evaluate(&block)
173
- self.original_self = eval "self", block.binding
174
- instance_eval &block
132
+ # Evaluates a given +block+ inside +instance+. Stores the original block binding.
133
+ def evaluate(instance, &block)
134
+ original_self = eval "self", block.binding
135
+
136
+ # A proxy that attemps to make method calls on +instance+. If a NoMethodError is
137
+ # raised, the call will be made on +original_self+.
138
+ proxy = Object.new
139
+ proxy.instance_eval do
140
+ class << self
141
+ attr_accessor :original_self, :instance
142
+ end
143
+
144
+ def method_missing(method, *args, &block)
145
+ instance.send(method, *args, &block)
146
+ rescue NoMethodError
147
+ original_self.send(method, *args, &block)
148
+ end
149
+ end
150
+
151
+ proxy.instance = instance
152
+ proxy.original_self = original_self
153
+
154
+ proxy.instance_eval &block
175
155
  end
176
156
 
177
- # Handles calls to undefined methods by delegating to the original block binding.
178
- def method_missing(method, *args, &block)
179
- super unless original_self
180
- original_self.send method, *args, &block
157
+ # Removes all blank values from a given +hash+.
158
+ def remove_blank_values(hash)
159
+ hash.delete_if { |_, value| value.respond_to?(:empty?) ? value.empty? : !value }
181
160
  end
182
161
 
183
162
  end