Ensuring a Trailing Slash in Rails W/Out Dependencies

For some reason this is more difficult in rails than it should be.

The semantic value of the concept of having a trailing slash in the URL is basically one of representing a folder, a virtual directory. In RESTful terms, this represents a collection route, or an :index action. Exhibit A:

GET /products/

This page will list many products as links. In restful terms, this will lead to the :show action. In this instance, we are representing a final destination, a member route. This dictates no trailing slash, semantically speaking:

GET /products/some-permalink
GET /products/some--ther-permalink

So how to deal with this in Rails? Rails will naturally favor slash-less versions of all URLs, unless specified otherwise. Step one is to generate all internal links the correct way, to do this, simply pass the :trailing_slash => true key-value pair to your named route. You ARE using named routes like you should be, right? Good.

member_path(object, :trailing_slash => true) or
collection_path(:trailing_slash => true)

This will generate the correct link. Excellent. However, when you write your routing or functional tests, like you should be, testing the extreme input, you will notice that the slash-less routes work just fine and Rails acts as if everything is normal.

I expected to be able to find a way to deal with this cleanly, by accessing a hash of sorts in the Rails’ request object, but nothing came up. the params hash omits this. As does request.request_uri, request.path and even request.fullpath. Well, shiet, what’s a bro to do?

Well, take a look at my solution in the form of a pair of private methods in ApplicationController:

class ApplicationController < ActionController::Base

private
  def ensure_trailing_slash
    redirect_to url_for(params.merge(:trailing_slash => true)), :status => 301 unless trailing_slash?
  end

  def trailing_slash?
    request.env['REQUEST_URI'].match(/[^\?]+/).to_s.last == '/'
  end
end

Then, when we need to use this in any actual controller, we simple declare the following:

class ObjectController < ApplicationController
  before_filter :ensure_trailing_slash, :only => :index
  [...]
end

Notice I am using 301 redirects here, as a sound SEO measure.

Hope this helped, until next time.