Response Order in Rails Controller Actions

Posted by david
on 27 Oct 2010 at 23:07

When checking test coverage on a Rails application, there was one code path that was missing, even though a test existed for it.

Here’s a stripped-down version of the controller spec:

1
2
3
4
5
6
7
8
9
10
11
12
13
describe BooksController do
  it "should get a book via an AJAX request" do
    xhr :get, :show, :id => 9
    response.should be_success
    response.should have_text(/\bBook 9\b/)
  end

  it "should get a book via a non-AJAX request" do
    get :show, :id => 9
    response.should be_success
    response.should have_text(/\bBook 9\b/)
  end
end

And here’s the relevant parts of the controller:

1
2
3
4
5
6
7
8
9
10
11
12
class BooksController < ApplicationController
  def show
    respond_to do |format|
      format.js do
        # Do stuff for AJAX requests
      end
      format.html do
        # Do stuff for non-AJAX requests
      end
    end
  end
end

But in both cases, only the first response block was being run. Of course, if the expected response text had taken into account the differences between the two responses, and not just one piece of text, the non-AJAX spec would have failed and the problem would have been found sooner. But it wasn’t.

The problem was with the HTTP Accept header or, in this case, the lack of one. The get call did not generate an Accept header so the first response block handled the request. The xhr call, on the other hand, generated an Accept header containing text/javascript, text/html, application/xml, text/xml, */* so the correct response block would handle the request regardless of the order.

The problem never showed up in browsers because an HTTP Accept header is always being passed; Firefox, for example, sends a HTTP Accept header with a value of text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8. Only command-line tools like curl and wget don’t include text/html as part of the Accept header unless told to do so (for example, by using the --header option). I don’t know what search engines and other robots specify for an Accept header, but it might be safe to assume that they act more like curl and wget than Firefox.

So the easy fix in this case was to reverse the order of the response blocks, and remember to make sure that response blocks are always ordered so that the default response is first.

ActiveScaffold problems when using acts_as_audited

Posted by david
on 29 Jun 2009 at 10:36

While beefing up the admin backend of a Rails app that uses ActiveScaffold for its admin UI, I encountered an error that only happened in production and the stack trace wasn’t much help in identifying the cause.

After failing to recreate the problem with a (relatively) small test case, I took the opposite approach and began ripping code out of the original application until the failure went away.

Rails functional tests and 406 errors

Posted by david
on 21 Mar 2009 at 22:57

So I have an controller method that responds to multiple formats, like so.

1
2
3
4
5
6
def index
  respond_to do |format|
    format.html
    format.pdf
  end
end

Simple enough. So what do the tests look like?

1
2
3
4
5
6
7
8
9
def test_default_index
  get :index
  assert_response :success
end

def test_pdf_index
  get :index, :format => :pdf
  assert_response :success
end

Again, nothing fancy. Let’s run the test and …


  1) Failure:
test_pdf_index(FoosControllerTest)

    [stacktrace omitted for brevity]

Expected response to be a <:success>, but was <406>

2 tests, 2 assertions, 1 failures, 0 errors

Ruh-roh. How could such a simple test fail, and what’s a 406 response? Well, it’s the numeric code for a Not Acceptable HTTP response, so it looks like the HTTP request is borked. Perhaps the log file will reveal something, and indeed it does.


Processing FoosController#index to pdf (for 0.0.0.0 at 2009-03-21 22:57:10) [GET]
  Parameters: {"format"=>:pdf, "action"=>"index", "controller"=>"foos"}

Looks like we can’t use a symbol for the format in the test. Sure enough, a quick change to the failing test,

1
2
3
4
def test_pdf_index
  get :index, :format => 'pdf'
  assert_response :success
end

and the results are as expected.


2 tests, 2 assertions, 0 failures, 0 errors

Update: Looks like the code formatting is fubar.

Update II: The code formatting is now fixed. See the explanation if you’re interested in the details.

Two milestones, two years apart

Posted by david
on 20 Mar 2009 at 17:36

Today finds me celebrating (in a rather low-key manner) two milestones. The first milestone is finally getting this blog off the ground, and the second is the second anniversary of the first commit of my first serious Rails project. The initial site went live two weeks later, replacing a crufty old PHP-based codebase that I had grown weary of maintaining. Naturally, several new features have been added since the original launch and both Ruby and Rails have made developing the site more enjoyable than any of the languages that had been used previously.

For much of the year the site doesn’t look like anything that anyone would bother using Rails, or any other application framework, to create. Why not just throw up a bunch of static HTML pages?

Well, the site exists to support the Dixie Cup homebrew competition that’s held every year in October. The public side of the site handles the online registration process while an administrative backend handles the running of the competition. For about 10 months of the year the site sits quietly with infrequent updates and then springs to life for a couple of months of serious activity.

Since I’ve had several inquiries about the code from other clubs who want to use it for their own competitions, I’ve begun working to extract the guts into a separate project. I found myself needing to look at the revision history of some files and, at one point, decided to look all the way back to the beginning of the revision history for the entire project and noticed the date of the first commit was March 20, 2007.