1. Jan 8th, 2007

    Six tips for building better Web apps with Rails

    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.

    1. Jan 8th, 2007

      Eldon Alameda

      Nice article – one minor comment on your typed_id method though.

      The Simply_helpful plugin in Rails 1.2 features a built-in set of methods for this. (dom_id and dom_class). I can’t recall if they were going to be included into core or not, but I’ve used them with some edge stuff and they’ve been especially nice for RJS.

      There was a good write up about it a while back at http://www.matthewman.net/articles/2006/09/04/new-rails-feature-simply_helpful

    2. Jan 8th, 2007

      Balance On Rails : Seis dicas para construir melhores aplicações Web com Rails

      [...] Fonte: Labnotes [...]

    3. Jan 8th, 2007

      Scott Matthewman

      Thanks for the link, Eldon!

      Re: your URL slugs tip, you can override to_param (i.e., name your method to_param instead of url_slug), and Rails’ built-in routing commands will use the combined id-and-name whenever it builds a URL, so you don’t need to manually specify it.

    4. Jan 8th, 2007

      Assaf

      Scott,

      Thanks for the tip. I didn’t realize that, I’m going to give it a try.

      Eldon,

      I came across Simply_Helpful recently when looking for something else, so I guess I’m not the only one to feel the pain. If I understand correctly, Simply_Helpful adds helper methods. I wanted to do the ID generation in the model itself. I feel it belongs there.

    5. Jan 8th, 2007

      Rob Sanheim

      This will be a little bit better to_param:


      "#{id}-#{name.gsub(/[^a-z1-9]+/i, '-')}"

      I can’t take credit, its from here: http://www.notsostupid.com/blog/2006/07/07/urls-on-rails/

    6. Jan 31st, 2007

      Jon Rowett’s Workblog » Links for 31 January 2007

      [...] Six tips for building better Web apps with RailsSome good hints around APIs and friendly URLs.[Tags: rails ruby workblog ] [...]