Handsoap is a new, fresh Ruby library for creating SOAP clients. Why am I excited about soap? I’m not, unless they’re beautiful. Yet, somewhere along the line you are going to be faced with writing a client for a SOAP service. Whether it is from your suppliers (4 different crap API’s in my case), or just for mashing up data from a rest-less source.
Ruby has very little to offer in terms of SOAP, afaik it only had soap4r until handsoap came along…
BIG DISCLAIMER: This is not a flame war against soap4r, it’s against SOAP.
The authors and contributors have spent a lot of time and effort over the years to build soap4r up to where it is currently. It is a remarkable piece of work, but the cracks are showing. I’ve spent countless hours digging through the code trying to figure out why my mappings and registeries aren’t working as expected. You find code working around bugs in Ruby 1.8.2 and 1.8.4, and we’re all trying to move to 1.9. That is a lot of skeletons to hide…
It’s not all doom for soap4r, I’ve used it to play around with some public API’s for weather and currency data, and if the API is well defined and leverages SOAP as a protocol, soap4r really rocks. The results of wsdl2ruby is bit tricky to navigate, but mostly you can leave that alone and focus on your code.
But, and there always has to be a but…
A lot of API’s are written by people who have no idea what it is like to be on the consuming side of their mess. This is where soap4r falls flat on its face. Take the case of Postini and the postini gem for instance. The gem was my first gem ever and as far as I’m considered it was my worst code ever (open source at least). It was an absolute mess, barely worked more than what our systems required, and was not expandable in any way. The root of the problem was not soap4r, but the worst combination of WSDL and PDF docs to land on any developers screen. The documentation and the WSDL don’t match, not by a long shot, and you have to debug minor issues with wiredumps. I’ll stop my rant now.
Enter handsoap
As development of our premier product comes closer to launch, our email systems came under scrutiny and I needed to update the way we interact with Postini through our gem. I couldn’t, I could barely figure out what I did in the first place. If there was any proper use of the gem outside our company, I would have probably been summoned to criminal court for the injustice I had done in the first release.
So I started over, and I learned about handsoap a couple of weeks ago already.
This was the result in 24 hours:
$ git log --stat 2f91d4d6eaa8bd76c188f20929a19e456d1bb52e..HEAD | grep changed
2 files changed, 15 insertions(+), 4 deletions(-)
1 files changed, 38 insertions(+), 0 deletions(-)
2 files changed, 282 insertions(+), 1 deletions(-)
1 files changed, 1 insertions(+), 1 deletions(-)
12 files changed, 9 insertions(+), 682 deletions(-)
1 files changed, 1 insertions(+), 0 deletions(-)
8 files changed, 184 insertions(+), 3883 deletions(-)
6 files changed, 154 insertions(+), 18 deletions(-)
That is over 4000 lines of code, gone! Talk about beauty soap…
At the moment the library is a super thin layer on top of the raw API, but it works surprisingly well and easier to comprehend.
Handsoap’s beauty comes that it leaves you to do all the parsing and prepping of the SOAP requests/responses. At first this sounds daunting, but as you start using it the benefits become very clear and you are in full control of your client. The author published a video tutorial which shows how to build a weather server client in Rails. It is a very fast paced video, but download a copy and step through it piece by piece.
Handsoap does an excellent job at bringing together curb as the HTTP client, and Nokogiri for the XML parsing. It delivers, blazingly fast.
Code Teaser
The EndpointResolverService in the postini gem is a nice example of a single method service that is used to return another endpoint for subsequent API calls.
At the very least you need a skeleton class that looks something like this:
EXAMPLE_SERVICE_ENDPOINT = {
:uri => 'http://example.org/ws/service',
:version => 2
}
class SomeService < Handsoap::Service
endpoint EXAMPLE_SERVICE_ENDPOINT
on_create_document do |doc|
doc.alias 'end', 'https://api-meta.postini.com/api2/endpointresolver'
end
end
After that, you’re pretty much on your own (which turns out to be great). Troels shows in the video tutorial how to use a clever Java application called soapUI to analyze the WSDL and prepare your request markup and response parsing accordingly. It works like I charm.
I have three other internal soap4r-based gems that work on the worst API’s you’ve seen in your life (can you say nusoap?). One of them even lacks a WSDL, and returns serialized PHP data. They were a nightmare to consume, and I’m looking forward to cleaning them up with handsoap as well.
I’m hoping this blog post serves as a motivator for you to clean up your (SOAP) act too. I’ve got two more posts lined up on handsoap, one about mocking your way through writing a SOAP client, and another on using soapUI’s mock service capabilities.
Until then, please share in the comments your worst Ruby SOAP stories (and go easy on soap4r, it’s not their fault).