Peter Marklund

Peter Marklund's Home

Mon Oct 13 2008 04:56:59 GMT+0000 (Coordinated Universal Time)

Rails Tip: SEO Friendly URLs (Permalinks)

There are several plugins already out there that can turn the typical Rails show path of /articles/show/1 into something more search engine friendly. I decided to roll my own implementation and it turned out to be fairly easy. My solution relies on the Slugalizer library by Henrik Nyh. First, I make sure we can turn any string into something URL friendly by patching the String class:

class String
  # Convert String to something URL and filename friendly
  def to_uri(max_size = 150, separator = "-")
    no_slashes = self.gsub(%r{[/]+}, separator)
    Slugalizer.slugalize(no_slashes.swedish_sanitation, separator).truncate_to_last_word(max_size, separator)
  end

  # We need this for SEO/Google reasons sincen å should become aa and Slugalizer translates å to a.
  def swedish_sanitation
    dup = self.dup
    dup.gsub!('å', 'aa')
    dup.gsub!('Å', 'aa')
    dup.gsub!('ä', 'ae')
    dup.gsub!('Ä', 'ae')
    dup.gsub!('ö', 'oe')
    dup.gsub!('Ö', 'oe')
    dup.gsub!('é', 'e')
    dup.gsub!('É', 'e')
    dup
  end
  
  def truncate_to_last_word(length, separator = "-")
    dup = self.dup
    if dup.size > length
      truncated = dup[0..(length-1)]
      if truncated.include?(separator)
        truncated[/^(.+)#{separator}/, 1]
      else
        truncated
      end
    else
      dup
    end
  end
end

All I have to do in my ActiveRecord model then is to override the to_param method:

  def permalink
    if name.present?
      "#{id}-#{name.to_uri}"
    else
      id
    end
  end

  def to_param
    permalink
  end

ActiveRecord will automatically ignore any non-digit characters after the leading digits in an id that you pass to it, but just to be on the safe side I added a before_filter to my application controller that will convert permalinks to proper ids:

  def convert_permalink_to_id
    if params[:id].present? && params[:id] =~ /\D/
      params[:id] = params[:id][/^(\d+)/, 1]
    end
  end

Credit for parts of this code goes to my cool programmer colleagues over at Newsdesk.se.