WSDL, Rails, & Complex Data Types
Posted by benrobb on February 5th, 2007
This could also be considered part three of my HowTo: Put WSDL on Rails series, but it’s not really a HowTo in the strictest sense of the word. Therefore, this article is more of a monologue than a set of instructions.
If you’ve stuck with me this far you’ve probably figured out that I’m discovering things on my own shortly before I spew them all out here. In Howto: Put WSDL on Rails we built a simple web service that would accept a string parameter, modify it, and send the modified version back to the user. In Part 2, we subscribed to (and used) that same web service using only the WSDL. At the time I thought I was done, but as I’ve played around a bit more, I realized that there was another major part of web services. So now we’ll cover dealing with complex data types.
I began by wondering what kinds of things besides [:string] you could pass as parameters to and from a web service. I finally happened upon the Ruby on Rails manual that somehow I had not discovered up to this point. Why don’t these pages come up higher in the Google search results? Rails uses ActionWebService to interact with web services of all kinds and you can find the beginning of the manual here. You can find the specific answers to my questions here and here.
After reading this and mulling it over a bit, everything was pretty clear except for the so-called Structured Type parameters. I set out to gain a better understanding of this, and this is what I accomplished. First off, you’ll want to read up an something called a Struct. In essence an ActionWebService::Struct is a special kind of object that can be used for web services, but doesn’t have any ties to a database like an ActiveRecord::Base object.
I first defined a new struct in app/models/weather_report.rb of my WebServiceProvider.
class WeatherReport < ActionWebService::Struct
member :zip, :int
member :temp, :int
member :wind_speed, :int
member :wind_direction, :string
end
I then created the proper method in my API at app/apis/weather_api.rb:
api_method :get_weather, :expects => [:string], :returns => [WeatherReport]
And since I was really just curious to see how this works, I defined a very dynamic method in app/controllers/weather_controller.rb:
def get_weather(zip_code)
return WeatherReport.new(:zip => 84097, :temp => 55, :wind_speed => 5, :wind_direction => 'SW')
end
And that’s really all there is to it. If you’ve got a database driven application you could simply pass in one of the models that corresponds to a table on your database. So instead of an ActionWebService::Struct you could just pass in an ActiveRecord::Base object with the exact same syntax.
Now to see what comes out on the other end. In another application, I subscribed to my web service and in my view put <%= debug @result %> with @result being the result of my GetWeather call to the web service.
The debugged results came out like this.
#<soap::mapping::object:0x14868c6 {}zip="84097" {}wind_direction="SW" {}wind_speed="5" {}temp="55">
Carrying this logical line one step further, if a web service required a complex data type as input, we could build an ActionWebService::Struct object or use an appropriate ActiveRecord::Base object to pass into a third party web service. I see the struct option being much more useful ona day to day basis, but in a B2B environment, you can start to see a place for some real hard-core service interactions passing full-blown database-drive object types around between systems.
As I began to play around with web services, I thought they were pretty neat, but I’m beginning to see the how powerful they could be given the right set of circumstances.