How to Put Rewrite Rules in Your Ruby Code, Not Your Web Server

Need to put some URL rewrite rules in your Rails app? Not too crazy about writing Apache mod_rewrite rules? Prefer writing Ruby code?

Refraction to the rescue.

Refraction is a new Rails plugin from Josh Susser and Pivotal Labs that helps you easily implement URL rewrites in Ruby, rather than writing the rules in web-server-specific lingo. Your code gets called by Rack, although you don’t need to know anything about Rack to write rewrite rules.

Here’s an example. Let’s say you want to duplicate Twitter’s /@username routes (did you know Twitter supports URLs like that?) but have found as I did that routes.rb doesn’t seem to work with a “@:username” rule. Instead, create a RESTful UsersController and then put this in your refraction_rules.rb file:

Refraction.configure do |req|
  # rewrite /@username as /users/username
  if req.path =~ %r{/@(.+)}
    req.rewrite! :path => "/users/#{$1}"
  end
end

The notes in the blog post (see link above) mention that there is already a Rack rewrite plugin called Rack::Rewrite but “Rack::Rewrite only really gives you access to the request URI, whereas Refraction appears to give the entire request object… that can be much more useful for determining where to send someone.”

3 thoughts on “How to Put Rewrite Rules in Your Ruby Code, Not Your Web Server”

  1. Hey Brian,

    Brian, Rack::Rewrite has a development branch that will allow you to do everything that Refraction does more succinctly.

    As far as your example above, it’s actually simpler with Rack::Rewrite (http://github.com/jtrupiano/rack-rewrite ):

        config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
          rewrite /@(.+)/, "/users/$1"
        end
    
  2. Hi John,

    That looks nice. You’re right, it is more succinct.

    And thanks for making me think more about it–I noticed a bug in my code when I read your post, and I think my bug made its way to your reply too. I’ve corrected it in the post above. I was originally using /@(.+)/ as the regular expression but that rewrites URLs that have an @ sign in the middle. I changed it to %r{/@(.+)} (inserted a slash at the front and used %r to avoid having to escape the slash).

    I’m guessing the same thing would work in Rack::Rewrite.

  3. Hey there, thanks for the mention. I think the biggest difference between Rack::Rewrite (well, the one you mentioned, there are two of them) and Refraction is how they scale. Rack::Rewrite has a very limiting API, but Refraction can scale up to dealing with very complicated domain logic. All the stuff you have to do with :if procs and whatnot seem quite complicated to me, and Refraction handles them much better in plain old Ruby syntax. Rack::Rewrite is probably a little bit simpler for simple cases, but Refraction will be a lot simpler for complicated ones.

Leave a Reply

Your email address will not be published. Required fields are marked *

Feel free to use <a>, <b>, <i>, <strong>, <em>, <strike>, <code>.

Code blocks:
[code language="ruby/javascript/html/css/sass/bash"]
[/code]