Web services in Ruby
The basic point of distributed programming is to let computers communicate between themselves. The simplest and most common form of distributed programming is the web service. Web services work on top of HTTP: they generally involve sending an HTTP request to a certain URL (possibly including an XML document), and getting a response in the form of another XML document. Rather than showing this document to an end user the way a web browser would, the web service parses the XML response document and does something with it. Web services work well when there's a server with some interesting data and many clients who want it.
From a high-level view, web service implementations can be broken down into two categories: servers and clients. Most web services are based on one of three architectures: Representational State Transfer (REST), Simple Object Access Protocol (SOAP), or Extensible Markup Language Remote Procedural Calls (XML-RPC). REST is HTTP; XML-RPC and SOAP are protocols that run on top of HTTP. XML-RPC isn't used much nowadays.
WSDL stands for Web Service Description Language. A WSDL file is an XML document that defines the interface to a SOAP service. WSDL files provide details about the methods that the service exposes, the methods' arguments and return values, and the encodings used for data travelling between the client and the server. Everything you would ever need to know about a SOAP service can be described in a WSDL file. WSDL files serve both as a form of documentation for SOAP services and as a key to automating many of the steps of building SOAP clients. Thus it's possible to read a WSDL file and find out everything you need to know about the API it describes. The primary use of WSDL is to automate the client code so that you don't have to explicitly write code to handle different data encodings, different mappings between object types, and so on. Most modern web service platforms automatically generate WSDL files and make them available for clients to download.
Writing a SOAP ClientThanks to Kevin Marshall for some help with the example p071soapclient.rb. Here we are going to call a remote method through a SOAP-based web service. The SOAP client sends an XML representation of a method call to a server, and gets back an XML representation of a return value. The whole process is complex, but Ruby's built-in SOAP library (implements both the client and server sides of the SOAP protocol, including support for WSDL) handles the low-level details for you, leaving you free to focus on using the results in your program. To write the SOAP service, we use Hiroshi Nakamura's soap4r library. It is part of the Ruby standard library and implements SOAP version 1.1. We use the SOAP RPC Driver in the soap4r library. The simple program prints a quote of the day. It uses the SOAP RPC Driver to connect to the SOAP web service at codingtheweb.com. Details of this web service are available here:
http://www.strikeiron.com/WebServiceDetail.aspx?WSID=1061
The web service's WSDL file is available here:
http://webservices.codingtheweb.com/bin/qotd.wsdl
We can use the following site to decipher a WSDL file. Enter the location of the web services' WSDL file and it will report back the various methods/options available.
http://www.strikeiron.com/analyzer/onlineanalyzer.aspx
In our case, it tells us that the method to be used is getQuote and the service is qotd.
Here's our program p071soapclient.rb
| require 'soap/rpc/driver' driver = SOAP::RPC::Driver.new( 'http://webservices.codingtheweb.com/bin/qotd', 'urn:xmethods-qotd') driver.add_method('getQuote') puts driver.getQuote |
Here, 'http://webservices.codingtheweb.com/bin/qotd' is the location of the web service (known as the endpoint URL). This is available in the tag <soap:address location> in the wsdl file. Also, 'urn:xmethods-qotd' is the namespace used by the service's documents.
Once the driver is set up, we define the web service method we want to call (getQuote). We can then call it like a normal Ruby method and display the result. Behind the scenes, the call to add_method actually defines a new method on the SOAP::RPC::Driver object. The SOAP library uses metaprogramming to create custom Ruby methods that act like SOAP methods.
Writing a PuneRuby SOAP server and client
Again thanks to Kevin Marshall for this too. Let's say that PuneRuby wants to host a SOAP-based web service (we are going to build a trivial one just to show how it is done) using a standalone server (that is, not as part of a Rails application).
Building our own SOAP server (program p072soapserver.rb) really only requires three simple steps:
Subclass the SOAP::StandaloneServer class. In the initialize, register the methods you want to expose and the arguments they should take. Here we expose a method sayhelloto that expects one parameter, username:
require 'soap/rpc/standaloneServer' class MyServer < SOAP::RPC::StandaloneServer def initialize(* args) super add_method(self, 'sayhelloto', 'username') end end
|
Define the methods you exposed in Step 1:
class MyServer < SOAP::RPC::StandaloneServer def sayhelloto(username) "Hello, #{username}." end end
|
Finally, set up and start your server. Our example runs on port 12321 on www.puneruby.com It's name is 'PuneRubyServer' and its namespace is 'urn:mySoapServer':
server = MyServer.new('PuneRubyServer','urn:mySoapServer', 'www.puneruby.com',12321) trap('INT') {server.shutdown} server.start
|
We've now built a complete SOAP server. It uses the SOAP StandaloneServer and hosts one simple sayhelloto method that can be accessed at http://217.160.200.122:12321/ with a namespace of "urn:mySoapServer".
To test your service, start your server in one Ruby session and then use the simple script (p073prclient.rb) below in another Ruby session to call the method it exposes:
require 'soap/rpc/driver' driver = SOAP::RPC::Driver.new('http://217.160.200.122:12321/', 'urn:mySoapServer') driver.add_method('sayhelloto', 'username') puts driver.sayhelloto('PuneRuby')
|