There is just something nice about those cute URIs that we have all grown to love, I'm sure you've seen them around: http://pork.ham/lists/32-grocery-list. Guess what, these are fun and easy to implement, here’s how!

First we need a method that can convert a normal title or name into something safe for use in a URI, here’s my answer:


class ::String

  # Returns a version of self that is safe for inclusion in a URI.
  def urilize
    strip.
    gsub(' ', '-').
    gsub(/[-]+/, '-').
    gsub('&', 'and').
    downcase.
    scan(/[a-z0-9\-]*/).to_s
  end

end

Users enter the darnedest data, so your urilize implementation will probably be tweaked over time. Anyways, the above String extension let’s us do stuff like:


'My Name Is Mud!'.urilize # => "my-name-is-mud"
'Awesome  &  Cool Stuff!!!'.urilize # => "awesome-and-cool-stuff"

Now we need a seamless way to get this functionality into our models so that they start throwing around all of these cool URIs for free. My answer to this is a sweet and simple mixin:


module Findable

  def to_param
    to_s.blank? ? id.to_s : "#{id}-#{to_s.urilize}"
  end

end

In order for our mixin to work we need our ActiveRecord models to have an overloaded to_s method that returns the title or name of the object. This is good practice and you should be doing it in any model where it makes sense to do so.

Anyways we can use our mixin as follows:


class Ticket < ActiveRecord::Base

  include Findable

  ...

  def to_s
    title
  end

  ...

end

Now imagine we have the following ticket in our database:


id: 6
created_by: Captain Obvious
assigned_to: Developer Dude
title: Feature Improvements
body: This massive feature that you implemented in far too little time is imperfect!

Assuming resourceful routes for Tickets we can do something like:


ticket = Ticket.find(6)
ticket_path(ticket) # => "/tickets/6-feature-improvements"

Neat.