Category Archives: RubyOnRails

Rails 3: Improving the speed of your Ajax Application

List Kung Fu uses Ajax a lot and so far it works out pretty well. Over the past two weeks I’ve looked into improving the performance of the UI and there are a couple of things that helped a lot and which I want to share here.

Returning the correct HTTP status code and optimize browser caching

There are two status codes I mean specifically:

  • 200 – “Ok“. Tells the browser that the server was able to respond successfully to the request. The response contains the data from the server in the HTTP payload.
  • 304 – “Not modified“. Tells the browser that the data for the sent request did not change and that data should be loaded from the cache. In this case the response does not contain any payload.

Since a “Not Modified”-message contains much less data (no content, only header) that’s what you preferably want to return to the browser. Of cause you only want to that if you are sure that the browser already has the data.

For my application I found that the server was returning far more often 200-responses than 304-responses which caused

  • More data than necessary had to be transmitted.
  • More data had to be processed by the UI.

Both things were slowing down the application. Just a little, but enough to be noticed in the UI. Luckily there are only a few things you need to change in your Rails application to get the right behaviour.

1) Use stale? in your GET methods

  def show
    @list_item = @list.list_items.find( params[ :id ] )
    if stale?( :etag => @list_item, :last_modified => @list_item.updated_at.utc, :public => true )
      respond_with( @list_item )
    end
  end

Stale? sends back an etag and a last_modified date in the response. With the next request to the same URL the browser will send this etag and last_modified date to the server. The stale? method then analyses both parameters and returns 304 in case they are the same or 200 plus the new content if the newly calculated values are different.

More information on stale? you get in Rails’ API documentation and on Rails Guides.

2) Make sure the browser asks for new data on each request

After above modifications something interesting was happening. If the same Ajax action was triggered multiple times within a short period of time, the browser wouldn’t send a request at all to the server, but instead take the data from the cache. While obviously this made the UI really fast, it wasn’t exactly what I wanted. My goal was maximum performance while guaranteeing correct and up to date data on the screen.
The browser’s caching behaviour is influenced by three HTTP header flags: cache-controll, pragma and expires.

To “disable” caching in the browser all together you could send the following:

  def set_cache_buster
    response.headers["Cache-Control"] = "no-cache, no-store, max-age=0, must-revalidate"
    response.headers["Pragma"] = "no-cache"
    response.headers["Expires"] = "Fri, 01 Jan 1990 00:00:00 GMT"
  end

What I wanted however is this:

  def set_must_revalidate
    response.headers["Cache-Control"] = "must-revalidate"
  end

This advices the browser to check for new / updated data with each request. I added this method to my application_controller.rb and I’m calling it in a before_filter of controllers for which I want to have this flag set.

3) Using stale? for GET methods which return collections (e.g. index)

Above stale? example is taken from a controller’s show method. This is what you find in most examples on the web. To make this work in a method which returns a collection, like typically a controller’s index method you need to find some way to figure out whether the current collection is the same as it was for the last request.
List Kung Fu has a model List which has_many ListItems. Each ListItem belongs_to a List. To be able to determine in list_items_controller whether a collection of ListItems changed, I added code which updates the list.updated_at timestamp for each write operation on a list_item.

In ListItem.rb:

class ListItem < ActiveRecord::Base
  belongs_to :list
  after_save :update_list
  after_destroy :update_list
 
  # [...]
 
  def update_list
    self.list.updated_at = Time.now
    self.list.save
  end
end

Thus, in the list_items_controller the index method could look like this:

  def index
    @list_items = @list.list_items
 
    if stale?( :last_modified => @list.updated_at )
      respond_with( @list_items )
    end
  end

Instead of using the updated_at field I could have added a version field to the List model, but that seemed unnecessary. If the model doesn't have a model it belongs_to, you need to find another strategy for checking whether the collection has been modified. Maybe it's possible to calculate a checksum over all objects in the collection...

So much for this post. I also switched from using jQuery's getJSON to Wycats jquery-offline. More about that in my next post.

DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in RubyOnRails | 2 Comments

Running SQL on Heroku

If you need to run some SQL manually on Heroku, the Heroku SQL Console is exactly what you’re searching for.

It’s installed in 5 seconds:

heroku plugins:install git://github.com/ddollar/heroku-sql-console.git

DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in Heroku, RubyOnRails | Leave a comment

Rails 3, jQuery UJS and dynamically added forms in Internet Explorer

I guess much more specific a title for a post couldn’t be. Anyways… I’m using jQuery in my Rails projects using the jQuery UJS adapter. Moreover some of the forms are added dynamically to the page.
This should not really be a problem because jQuery UJS relies on jQuery’s live method. In fact, it really isn’t a problem in all browsers… except.. Internet Explorer.

In Internet Explorer for some reason (and maybe just on my specific page), the live method didn’t work with the submit event of the form. The form would just post straight to the Rails application, which is not quite what I wanted. I know there were problems with .live() and a couple of Javascript events in previous versions of jQuery, but according to the doc, that should be all good now.

What I did in the end is this. In jQuery UJS I changed

$('form[data-remote]').live('submit', function (e) {
  $(this).callRemote();
  e.preventDefault();
});

to

$('form[data-remote]').find("input[type=submit]").live('click', function (e) {
  $(this).parentsUntil('form[data-remote]').last().parent().callRemote();
  e.preventDefault();
});

So instead of binding to the submit event, I’m binding a function to the click event of the submit button. That’s not as flexible as the original code, because you need to have a submit button and it wouldn’t work if the submit is triggered from Javascript directly. For my purpose however, the trick does it.

This two line change took me a while to figure out, so I’m really wondering if my current approach of writing Javascript applications with a Rails backend is the best approach. The approach is a mixture of retrieving JSON on the one hand vs. Rails returning Javascript code which gets executed once the Ajax call is completed on the other hand.
Alternatives would be to use Javascript MVC, Sproutcore, Pure MVC or Cappuccino. All have their advantages and disadvantages and I haven’t decided yet which one I like the best…

DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in jQuery, RubyOnRails | Leave a comment

How to get Rails Flash Messages when using Ajax

Rails provides a very convenient way of transporting short messages from the server to the user: The Flash.

So you can do for example stuff like this:

class LoginsController < ApplicationController 
  def destroy 
    session[:current_user_id] = nil 
    flash[:notice] = "You have successfully logged out"  
    redirect_to root_url 
  end 
end

If you use Ajax however the user will never see this message. You could do another Ajax request in order to update the area in your layout which displays the flash messages, but that’s pretty heavy. Better would be if the Flash messages were returned directly with the Ajax request.

After thinking about that for a while, I thought the cleanest solution for transporting flash messages is, to add them to the response header. So I added this method to my application controller:

class ApplicationController < ActionController::Base
  after_filter :add_flash_to_header
 
  def add_flash_to_header
    # only run this in case it's an Ajax request.
    return unless request.xhr?
 
    # add different flashes to header
    response.headers['X-Flash-Error'] = flash[:error] unless flash[:error].blank?
    response.headers['X-Flash-Warning'] = flash[:warning] unless flash[:warning].blank?
    response.headers['X-Flash-Notice'] = flash[:notice] unless flash[:notice].blank?
    response.headers['X-Flash-Message'] = flash[:message] unless flash[:message].blank?
 
    # make sure flash does not appear on the next page
    flash.discard
  end
end

Now, when doing an Ajax call like this:

var test = $.ajax({
	url: "http://localhost:3000/lists/5/edit",
	dataType: "json",
	type: "GET",
	processData: false,
	contentType: "application/json"
});

you can get the JSON data by calling

var json = $.parseJSON( test.responseText );

And you get get your flash message (assuming you created one in the edit action) by calling:

var notice = test.getResponseHeader("X-Flash-Notice");
DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in jQuery, RubyOnRails | Leave a comment

RESTful API with JSON messages in Rails 3 and jQuery

Rails 3 makes it really easy to return JSON data in the HTTP response. Consider this fragment:

class ListsController < ApplicationController
 
  respond_to :json
 
  def index
    @lists = List.all
    respond_with(@lists)
  end
 
  # ...
end

index might return e.g.

If you look at jQuery-ujs when sending an Ajax request, the content type is set to ‘application/x-www-form-urlencoded’ by default: The data is serialized as form data and send through the wire.

While this is absolutely fine, I was wondering how to use ‘application/json’ as content type when sending an Ajax request with jQuery. IMHO that’s cleaner because you then send JSON in the request and get back JSON in the response. Content types like ‘application/json’ and ‘text/xml’ are also much better if 3rd party applications will consume your service.

Rails by default creates seven different routes for a single (not nested) resource:

          GET    /lists(.:format)               {:action=>"index", :controller=>"lists"}
    lists POST   /lists(.:format)               {:action=>"create", :controller=>"lists"}
 new_list GET    /lists/new(.:format)           {:action=>"new", :controller=>"lists"}
          GET    /lists/:id(.:format)           {:action=>"show", :controller=>"lists"}
          PUT    /lists/:id(.:format)           {:action=>"update", :controller=>"lists"}
     list DELETE /lists/:id(.:format)           {:action=>"destroy", :controller=>"lists"}
edit_list GET    /lists/:id/edit(.:format)      {:action=>"edit", :controller=>"lists"}

Below the jQuery Ajax code for each of these routes:

GET /lists(.:format) {:action=>"index", :controller=>"lists"}

$.ajax({
	url: "http://localhost:3000/lists",
	dataType: "json",
	type: "GET",
	processData: false,
	contentType: "application/json"
});

POST /lists(.:format) {:action=>"create", :controller=>"lists"}

$.ajax({
	url: "http://localhost:3000/lists",
	dataType: "json",
	type: "POST",
	processData: false,
	contentType: "application/json",
	data: "{\"list\":{\"title\":\"test\"}}"
});

GET /lists/new(.:format) {:action=>"new", :controller=>"lists"}

// .new
$.ajax({
	url: "http://localhost:3000/lists/new",
	dataType: "json",
	type: "GET",
	processData: false,
	contentType: "application/json"
});

GET /lists/:id(.:format) {:action=>"show", :controller=>"lists"}

$.ajax({
	url: "http://localhost:3000/lists/3",
	dataType: "json",
	type: "GET",
	processData: false,
	contentType: "application/json"
});

PUT /lists/:id(.:format) {:action=>"update", :controller=>"lists"}

$.ajax({
	url: "http://localhost:3000/lists/3",
	dataType: "json",
	type: "POST",
	processData: false,
	contentType: "application/json",
	data: "{\"list\":{\"title\":\"hallo welt\"}}",
	beforeSend: function(xhr)   
	{
		xhr.setRequestHeader("X-Http-Method-Override", "PUT");
	}
});

DELETE /lists/:id(.:format) {:action=>"destroy", :controller=>"lists"}

$.ajax({
	url: "http://localhost:3000/lists/3",
	dataType: "json",
	type: "POST",
	processData: false,
	contentType: "application/json",
	beforeSend: function(xhr)   
	{
		xhr.setRequestHeader("X-Http-Method-Override", "DELETE");
	}
});

GET /lists/:id/edit(.:format) {:action=>"edit", :controller=>"lists"}

// .edit
$.ajax({
	url: "http://localhost:3000/lists/3/edit",
	dataType: "json",
	type: "GET",
	processData: false,
	contentType: "application/json"
});

Note that you need to override the HTTP method for update and destroy. You could use also type: "PUT" and type: "DELETE", but not all browsers understand these verbs. Therefore it's probably better to use the 'X-Http-Method-Override' header instead.

For those routes which require to send data, the data is send as JSON. The format of the JSON string needs to match what Rails would return in the response. If you used a different JSON string, you would have to parse it server side by yourself.

processData is set to false. If it is 'true' (the default), jQuery will try to serialize it into a form post. In this case that's not desirable, that's why it needs to be turned off.

DeliciousTwitterFacebookLinkedInRedditSlashdotTechnorati FavoritesDiggShare
Posted in jQuery, RubyOnRails | 1 Comment