The API comes for free
And are you going to turn away a free gift?
With Rails 1.2, you can easily respond to the same request with different content types. Say you have an action that displays an HTML page with all the books in the category ‘Ruby’. From the same action, you can also return the data as XML or JSON.
Once you start adding XML and JSON output to your actions, it becomes second nature and minimal effort you get an API for your Web app.
Documenting APIs takes some time, so here’s another tip to make your API better. Let people explore it using their browser.
Say you want to list all the books in a certain category. Use the browser to navigate to a page, now add .xml or .js to the end of the URL. You’ve just discovered the URL, and you can use the browser to see sample output.
And for updates, the URLs and request parameters are the same as used in the HTML form.
I can’t think of an easier way to build and learn an API.
Resource overloading and caching
But be careful when overloading different content types on the same URL.
Say the browser requests the full HTML page, and your app sets the expiration time to 30 minutes. The user refreshes the page, the browser asks again, but this time using the Is-Modified-Since header to request a new page only if the content changes. Dutifully, your app checks for any modifications, and either returns a new page, or tells the browser to grab it from cache.
So far it’s all good.
But say the browser is making an XHLHttpRequest to that same URL, so it only needs to update the content without updating the entire page. In Rails, you can tell the difference using request.xhr?. But, at least with Firefox, you’ll get the cached page instead, and the partial update will wreck havoc on your page, and using stale information to boot.
As a general rule, always use suffixes for different content. If it differs by type, use .xml or .js. Or use separate actions, e.g. ;feed for the Web feed, and ;partial for a partial update. (With RESTful resources, the semicolon is used to distinguish a different view of the same resource).
DOM IDs
Everyone wants to be unique.
If your JavaScript has to identify elements on the page, e.g. to highlight an updated element, or reorder items in a list, you need to use unique IDs. These quickly crowd your view with too much concatenations.
An easier method is to add a typed_id method to your models, and call it from your views. You can keep your models DRY by extending ActiveRecord::Base with this method:
def typed_id()
"#{self.class.name.demodulize.downcase}_#{self.id}"
end
If I have a model class Book and a book with id 45, I can use id="<%= book.typed_id %>" to generate id="book-45".
URL slugs
Friendly URLs make for happy users.
I like when I can tell what a URL is about just by looking at it. I have to admit, occasionally I will check the browser status bar for the URL before clicking a link, to decide if I should even bother. Friendly URLs get more clicks from me.
A URL like /book/ruby-in-practice is a good hint at what lies behind the link. Except when it breaks.
Periods — unlike hashes, slashes and question marks — are not escaped in the URL, and if they show up in the URL will cause Rails to confuse them for type suffixes.
Spaces are encoded as plus sign, not exactly the most friendly of separators.
And one gotcha to look for: URLs with text are not always unique. A blog might have two posts with the same title, or you might decide to truncate long text, and can no longer tell URLs apart.
I use the text part of the URL as a slug that follows the actual identifier. This also works well with routes and filters that expect the ID to be a number, as they parse the first part of the identifier and ignore the text that follows.
Here’s a method that returns a friendly URL by adding the book title:
def to_param()
"#{id}-#{title.gsub(/\./, '').gsub(/\s+/, '-')}"
end
You can then create the link with link_to book_url(book) and look it up with Book.find(params[:id]).
Be liberal with your URLs
Good domain names are hard to find, but URLs are plenty so use them liberally.
Once in a while I end up overloading too much stuff onto one URL. Usually in the form of query parameters, and it happens because I’m adding one feature on top of another. When I try to use it, it becomes a pain figuring which mixture of query parameters leads to what result.
So now I practice looking for smelly URLs that try to do too many things, and refactoring them into separate URLs. They’re easier to document, test and maintain. And I know I’m never going to run out of good URLs.
In-place that degrades
AJAX is no excuse to make browsers less useful.
If you use GMail, you know what I’m talking about. You click an e-mail and it opens in the same page, and you can reply or forward without ever leaving that page. There’s a desktop app feel to it, and usually that’s a Good Thing.
But occasionally, I’m working on a long e-mail, the kind that could take hours to complete, and I want to check my e-mails in a separate window. Or I’m writing one e-mail and copying text from another.
In Firefox, I can use the Shift and Ctrl keys to open links in separate windows and tabs, depending on what kind of work I’m doing. I like to use tabs for related actions, and windows for separate tasks that demand their own space.
Either way, when AJAX takes control of links, it breaks that feature. Now there’s a legacy desktop feel to it, and not one you want to replicate.
I work around that with ease. I write actions that return a partial if called from XHR, and return the same partial with layout if called to render a full page. That way I can use the same action for in-place editing and to open separate windows and tabs. (But be careful when caching, see above).
In the onclick event handler, I look for modifier keys. If the user pressed a modifier key, I let the browser handle the linking. If the user didn’t press a modifier key, I let AJAX do it’s in-place magic.
It’s the traditional technique for creating “degraded” UIs, but the end result is a more powerful and useful UI.
Update: Thanks to Scott Matthewman for the to_param tip.
Balance On Rails : Seis dicas para construir melhores aplicações Web com Rails
Jon Rowett’s Workblog » Links for 31 January 2007