Rails and SOAP – A Dirth of Information

Scrubbing Bubble

I am writing this post to both vocalise the success of my first encounter with SOAP in Rails, as well as explain the process. For anyone working on a Rails application with SOAP for the first time, its probably become apparent that there is little documentation for the first time users out there.  Read on to learn more about Rails, SOAP, and hopefully gather enough to get your application up and running.

Introduction to SOAP

I will assume that if you are still with me, then you have some experience with Rails. I mean, we do have to start somewhere. I always viewed SOAP as one of the legacies that Java has left on this world. And by legacy, I mean like how Herpes never really goes away. SOAP used to stand for Simple Object Access Protocol, which in English means absolutely nothing more than being a clever acronym. SOAP in layman’s terms is a way that two bases of code can communicate with each other regardless of the Operating System, language, or anything else proprietary. SOAP also allows outside users to make changes to your application while enforcing a level of security.

In SOAP implementations, there are two parts – the client and the server. The client connects to the server to either query for information, or to request changes to that information. The methods that are available for a client to use are entirely up to the server’s definition. This definition is a very real file named the WSDL. Yeah thats right – another clever acronymn. The WSDL file is the Web Services Description Language. This is where methods, their documentation, and what parameters they take in, and spit out are recorded. This file is a plain-Jane XML file, so you can view it on your own if you so desire.

A little trick that I have seen several SOAP server implementations pull is securing this WSDL file from the outside world. Typically this is via Basic Authentication, which we will need to handle with Ruby when connecting. Regardless of the security implementation, we will need to have access to this WSDL file for our Rails application to consume. More on this in a minute.

Speaking of consumption, if you glanced at the WSDL file, you probably had a hard time swallowing it. Those of you familiar with Java will quickly recognize this as a signature feature of the language in general. Simply, the WSDL file was never really intended to be made sense of by mere humans. Most languages have a SOAP parser that will read this XML, and construct classes and methods in its native code to make life a bit easier. For example, Microsoft Visual Studio allows a user to “Add a web service” which will read the file, and generate C# or VB.NET code. Aren’t they smart cookies?

Rails and SOAP Together

Rails is no stranger to the SOAP game, and offers its own way to use web services. To start with, you will need to acquire a few files. Specifically, you will need to obtain the wsdl2ruby script which is part of soap4r.  This can be obtained from their website, but downloading stuff directly is a bit old school now-a-days. Instead, we will use the gem package manager to fetch this file and its dependencies. From a terminal, issue:

gem install soap4r

This will fetch the gem, and its dependencies. Now, you should have a utility named “wsdl2ruby” accessible from your terminal. An important note: soap4r comes bundled with Ruby, but it is a much older version. To you will need to be explicit in which version you want to use. This means in IRB, or in your Rails model, you need to place ‘gem soap4r‘ at the top of your files. This loads the new soap4r gem instead of using the bundled soap4r code.

There are two main ways in which to use wsdl2ruby in your Rails application. The first is calling in the library to parse SOAP files (via “require”), and generating your methods on-the-fly. This is a really good approach for keeping your code as terse as possible, however it makes development a little slower, since the methods you can call are not readily apparent. The second method calls wsdl2ruby manually from a terminal, and generates a driver file, and example code for how to use it in your application. I recommend doing both, the former for your actual application, and the latter for reference during development. After you are finished developing, you can simply delete the generated files without any side-effects.

Generating Client Files for Web Service

The usage syntax for wsdl2ruby is a little ugly, but here is the simplified version of what it can do:

%>wsdl2ruby
Usage: /ruby/bin/wsdl2ruby.rb --wsdl wsdl_location [options]
  wsdl_location: filename or URL
 
Example:
  For client side:
    /ruby/bin/wsdl2ruby.rb --wsdl myapp.wsdl --type client
 
Options:
  --wsdl wsdl_location
  --type server|client
  --force
  --quiet

You can see from the usage above, that we can run wsdl2ruby against a WSDL file location, and have it generate the classes and methods needed for our rails application. An example might be (substituting your own WSDL file of course):

wsdl2ruby --wsdl https://wemedy.clayton.edu/services/getmbxunread.asmx?WSDL --type client --force

The “–wsdl” argument indicates that we want to generate files based on a WSDL definition. The “–type client” argument indicates that we want to generate a “driver” file, as well as see a sample client library to show how one might use the driver. The “–force” argument makes the parsing of WSDL a little more robust when building the Ruby code. When you run this, it will create 4 files in your current working directory. Of these, the file we are most concerned with is the “xxxClient.rb” file. This is your sample code to use as a reference.

Take a gander at this file, and you will see something similar to the following:

#!/usr/bin/env ruby
require 'defaultDriver.rb'
 
endpoint_url = ARGV.shift
obj = GetMBXUnreadSoap.new(endpoint_url)
 
# run ruby with -d to see SOAP wiredumps.
obj.wiredump_dev = STDERR if $DEBUG
 
# SYNOPSIS
#   getUnread(parameters)
#
# ARGS
#   parameters      GetUnread - {http://tempuri.org/}getUnread
#
# RETURNS
#   parameters      GetUnreadResponse - {http://tempuri.org/}getUnreadResponse
#
parameters = nil
puts obj.getUnread(parameters)

The name of my Web Service is getMBXUnread. Each method available for use is shown by the synopsis section. For example, my first method shown here is the “getUnread” method. I can see that it takes a parameter (which parameter is mysteriously omitted, but we can find it easy enough), and returns a value in the property “getUnreadResult” when called. To find out the name of the parameter it is expecting, look at the default.rb file. In my example, I see the following class (named the same as my method in the Synopsis above:

require 'xsd/qname'
 
# {http://tempuri.org/}getUnread
#   strUsername - SOAP::SOAPString
class GetUnread
  attr_accessor :strUsername
 
  def initialize(strUsername = nil)
    @strUsername = strUsername
  end
end

I can now see that this method takes a value for “attr_accessor :strUsername” upon creation. This means that I can say “…getUnread(:strUsername => ‘username’)” in my Rails application. This is what we need to get started programming.

SOAP in your Rails Application

Open up one of your Rails models and get ready to roll up your sleeves. At the top, we will need to require a file to be loaded to make the SOAP functions accessible from the model class. Place the following at the very top of the model:

require 'soap/wsdlDriver'

Now, you can create a new method, and reference your WSDL resource we were playing with earlier. I will need to initialize my SOAP client like so:

soap_client = SOAP::WSDLDriverFactory.new("https://wemedy.clayton.edu/services/getmbxunread.asmx?WSDL").create_rpc_driver

The URL that I have as a parameter will need to reflect your WSDL location. The “create_rpc_driver” method will instruct Ruby to read this resource, and construct the classes we already saw earlier. The only difference is now it is doing it on-the-fly instead of creating files in the working directory. Now that our client is ready, we can query our SOAP server for information:

soap_client.getUnread(:strUsername => username).getUnreadResult

I called the “getUnread” method on my client and passed in the parameter it was expecting from earlier. This is still available to reference in your “xxxClient.rb” file if you want to refer back to it. Next, I pass in a username variable as the parameter in a hash format. At this point, I now have a SOAP object, and barring any errors connecting, I should be able to retrieve the “getUnreadResult” property of this request.

Basic Authentication

Earlier, I mentioned that some SOAP server implementation may make use of Basic Authentication (prompting for a username / password) before allowing access to the WSDL file. This is handled by soap4r (and thus wsdl2ruby) by using a property file on startup. You will need to create this property file inside a folder named “soap” somewhere on you application’s load path. For IRB, this load path is configured using the “-I” switch, and specifying a directory following. For example, if I am working out of “C:\”, then I could create the file “C:\soap\property”, and load this file in IRB by issuing “irb -I ‘C:\“. In Rails, you can place this soap/properties in a number of locations, but I would recommend sticking it inside the vendor folder.

Inside this property file, we can associate URLs with credentials. Here is an example property file listing:

client.protocol.http.basic_auth.1.url = http://example/path/to/wsdl
client.protocol.http.basic_auth.1.userid = username
client.protocol.http.basic_auth.1.password = PaSsWoRd

Note that this file does not have an “.rb” extension – that is because this is a property file and is not valid Ruby syntax! You can also create multiple basic authorization definitions by incrementing the group number. The first group shown above is “1″.

If you are working with SSL, then check out this article. You can use the same property file above, with some added settings.

Conclusion

SOAP isn’t easy. In fact, I read in the Enterprise Integration with Ruby book that SOAP now doesn’t stand for Simple Object Access Protocol because it is no longer simple. SOAP just means SOAP.  As the complexity of your WSDL file increases so do the odds of wsdl2ruby choking on it. A lot of this has to do with mapping classes to Ruby equivalent objects. wsdl2ruby has made remarkable ground, but it still isn’t perfect.

If you get stuck, try generating the client files, or viewing the “methods” of your soap_client inside of a Rails console, or IRB. The help out there isn’t great, but try checking out “http://dev.ctor.org/soap4r/wiki”, for the soap4r API upon which wsdl2ruby is based.

The book Enterprise Integration with Ruby has about five pages talking about wsdl2ruby in particular, and an entire chapter talking about RPC calls with Ruby, including how to create your own SOAP server.

Good luck!