Savon vs. Handsoap: Calling a service

This documentation is deprecated, please have a look at “savonrb.com”:http://savonrb.com/!

p. The two libraries have different approaches on how to get things done. While Handsoap is using an oldschool inheritance style definition:

class HandsoapBankCode < Handsoap::Service
  
  endpoint :uri => "some_wsdl", :version => 2

  def on_create_document(doc)
    doc.alias "tns", "some_namespace"
  end

  def on_response_document(doc)
    doc.add_namespace "ns1", "some_namespace"
  end
  [...]
end

p. Savon clients are just a kind of wrapper or proxy around a WSDL:

client = Savon::Client.new "some_wsdl"

p. While inheritance is a base concept of object oriented programming, it’s usually better to use delegation instead. For not being stuck on the API of the Handsoap::Service class, one would wrap things up into some other class or module, creating more code than necessary.

p. The proxy style client of Savon is less code and provides a flexible API, especially looking at SOAP calls.

p. Using “rspec”:http://rspec.info/ to demonstrate the expected behavior of the clients results in two identical spec for getting a zip code of a concrete client implementation:

describe "Savon" do
  it "should return the corrent zip code for a given bank" do
    zip_code = Shootout::SavonBankCode.zip_code @bank_code
    zip_code.should eql @zip_code
  end
end

describe "Handsoap" do
  it "should return the corrent zip code for a given bank" do
    zip_code = Shootout::HandsoapBankCode.zip_code @bank_code
    zip_code.should eql @zip_code
  end
end

p. Compared to the spec, the code of the two implementations differs a great deal. The task at hand is to call the getBank method of the “SOAP endpoint”:http://www.thomas-bayer.com/axis2/services/BLZService?wsdl providing a blz (bank code) parameter and extracting the plz (zip code) value of the response.

p. Using the Handsoap client class defined above, sending the “invoke()” message to the Handsoap::Service will do the job:

def zip_code(bank_code)
  response = invoke("tns:getBank") do |message|
    message.add "tns:blz", bank_code
  end
  (response/"//ns1:details/ns1:plz").first.to_s
end

p. The bank code parameter is assigned in the block, which yields a “SOAP message object”:http://github.com/unwire/handsoap/blob/master/lib/handsoap/xml_mason.rb. The resulting XML document is wrapped and can be accessed using some predefined XML library. Handsoap enables you to choose between different types of XML parsers like “REXML”:http://www.ruby-doc.org/core/classes/REXML.html, “ruby-libxml”:http://libxml.rubyforge.org/ or “nokogiri”:http://github.com/tenderlove/nokogiri.

p. Savon’s proxy client on the other hand is dynamic and can be accessed directly with the name of the SOAP method and a block:

class SavonBankCode
  def self.zip_code(bank_code)
    client = Savon::Client.new Shootout.endpoints[:bank_code][:uri]
    response = client.get_bank { |soap| soap.body = { "wsdl:blz" => bank_code } }
    response.to_hash[:get_bank_response][:details][:plz]
  end
end

p. The block yields a “SOAP request object”:http://github.com/rubiii/savon/blob/master/lib/savon/soap.rb for setting the payload or tweaking defaults like the SOAP header. Converting the response to a hash is a convenient way to access the desired result. The conversion is done using “crack”:http://github.com/jnunemaker/crack/.

1 thought on “Savon vs. Handsoap: Calling a service

  1. Pingback: Savon Handsoap Shootout | #nofail

Comments are closed.