Building Mashup-Friendly Sites in Rails

by Jack Herrington

Your web services interface, be it in whatever format—XML/RPC, SOAP, even JSON—is not mashup friendly. Let me ask you a question, what is the gold standard of mashupable widgets? For many that would have to be Google Maps, arguably the first truly mashupable widget that really took off. And why did it take off? Because I can mashup with Google Maps using just Notepad (or TextEdit, TextMate, whatever) and a browser. I get my developer key, copy in a little JavaScript code, and away I go.

So you are probably thinking to yourself, "Fine, I can use Ajax from my web page to get data from my web services interface and then put it into maps." Not true. Because of the security model in most of the browsers, a web page can only make an Ajax request to pages in the same domain. Have a look at Figure 1.

figure 1
Figure 1. Getting to the proxied service

Let's say you have a web page and a Rails service located somewhere else that you want to get access to. (Actually, it doesn't have to be Rails, it can be anything, but I'll use Rails as an example.) As you see in Figure 1, the page itself cannot directly make a call to get the pages from the Rails service because it's hosted in a different domain. To make the call the page will have to go back to a proxy service located on a server in the same domain and have it make the request, then return the data.

This is why the widget sites, like Microsoft Live or iGoogle, all have proxies built into them: so that people can access their SOAP, XML/RPC, REST, or JSON interfaces without having to build a mashup-friendly interface.

In this article I'm going to take a page from the Google Maps playbook and show you how to build a web services interface that is mashup friendly, where the only tools you need to get to the data on your Rails server is Notepad and a browser, just like you see in Figure 2.

figure 2
Figure 2. Building the easily mashupable interface

That way you can display data from your Rails service on blogs, and static HTML pages, and who knows whatever else from anywhere on the Web.

I've chosen Rails for this example for two reasons: first, it's cool and second, it's easy. What I'm doing here you could easily do in PHP, Java, .NET, or any web server technology. I'll start us out by creating the Rails site, and then I'll show you a progression of different transport mechanisms, starting with the familiar Ajax/XML pattern and ending with a mashup-friendly script tag transport mechanism.

Building the Rails Service

To demonstrate building a mashup-friendly web service I'll build a simple Rails application. The application will store a list of images along with description, title, and location in latitude and longitude. The Rails code to create the database is shown in Listing 1.

Listing 1. The database definition
class CreatePhotos <  ActiveRecord::Migration
   def self.up
     create_table :photos do |t|
       t.column  :title, :string, :null => false
       t.column  :description, :string, :null => false
       t.column  :url, :string, :null => false
       t.column  :latitude, :double, :null => false
       t.column  :longitude, :double, :null => false

  def self.down
   drop_table :photos

Once the database is set up, we have to have a model to represent the individual photos. That model definition is shown in Listing 2.

Listing 2. The Photo Model
class Photo <  ActiveRecord::Base

There isn't much to it. I could add some constraints, but the application itself isn't the point of this article. The interesting stuff starts in the controller, which is shown in Listing 3.

Listing 3. The Photos Controller
class PhotosController  < ApplicationController
 scaffold :photo
 def xml
   render( :content_type => 'text/xml',
   :text => Photo.find(:all).to_xml() )

 def json
   render( :content_type =>  'text/javascript',
   :text => Photo.find(:all).to_json() )

 def jscallback
   callback = 'photos_callback'
   callback = params['cb'] if ( params['cb']  != nil )
   render( :content_type =>  'text/javascript',
   :text =>  "#{callback}(#{Photo.find(:all).to_json()});" )

At the top is the usual Rails scaffolding, which I will use to add some images into the database. The list of images I've entered into the database is shown in Figure 3.

figure 3
Figure 3. The scaffolding with the list of images

Below the scaffolding invocation are the three key view methods; xml, json, and jscallback. The 'xml' method returns all of the records from the database encoded as XML. The 'json' method does the same thing, but returns the records encoded in JavaScript Object Notation (JSON) format by using the ever handy 'to_json' call.

The third method, 'jscallback', creates some JavaScript code that invokes a JavaScript function on the client and supplies as an argument the array of data from the database. By default, the name of the callback is 'photos_callback', but the caller can specify whatever function call name they like by adding the 'cb' argument to the URL.

It's the 'jscallback' method that makes the application mashup friendly. But in order to explain why, I need to start with the basic Ajax/XML pattern that we are familiar with.

Pages: 1, 2, 3

Next Pagearrow