Building Mashup-Friendly Sites in Rails
by Jack Herrington12/04/2007
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. 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. 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 definitionclass 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
end
end
def self.down
drop_table :photos
end
end
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 Modelclass Photo < ActiveRecord::Base end
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 Controllerclass PhotosController < ApplicationController
scaffold :photo
def xml
render( :content_type => 'text/xml',
:text => Photo.find(:all).to_xml() )
end
def json
render( :content_type => 'text/javascript',
:text => Photo.find(:all).to_json() )
end
def jscallback
callback = 'photos_callback'
callback = params['cb'] if ( params['cb'] != nil )
render( :content_type => 'text/javascript',
:text => "#{callback}(#{Photo.find(:all).to_json()});" )
end
end
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. 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.



