Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
FriendlyId is the “Swiss Army bulldozer” of slugging and permalink plugins for ActiveRecord. It allows you to create pretty URL’s and work with human-friendly strings as if they were numeric ids for ActiveRecord models.

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
generators/friendly_id_migration
lib
tasks
test
.gitignore
CHANGES
MIT-LICENSE
README.rdoc
Rakefile
init.rb
install.rb
uninstall.rb

README.rdoc

FriendlyId

FriendlyId is a plugin for Ruby on Rails which allows you to work with human-friendly strings as well as numeric ids for ActiveRecord models.

There are a number of reasons you might want to use textual id's in your models. The main use we intend friendly_id for is in URL's, so your application can have URL's like:

http://example.com/states/washington

rather than

http://example.com/states/4323454

You can find a FriendlyId tutorial here.

Why?

  • Text-based id's look better

  • They make URL's easier to remember.

  • They give no hint about the number of records in your database.

  • They are better for search engine optimization.

But…

  • They can change, breaking your URL's.

  • They can have invalid URL characters, breaking your links in some browsers.

  • It can be tricky to ensure they're always unique.

FriendlyId tries to offer you the all the advantages, and avoid or soften the potential impact of the disadvantages.

Typical Uses

User names (“non-slugged” models)

Usually users have unique user names stored in a column with a unique constraint or index. In this case, all you need to do is add this to your model:

has_friendly_id :login

and you can then write code like this:

@member = Member.find("joe")   # the old Member.find(1) still works, too.
@member.to_param               # returns "joe"
redirect_to @member            # The URL would be /members/joe

Blog posts (“slugged” models)

Blog posts generally have titles which are distinctive but not necessarily unique. In this and similar cases, FriendlyId provides a Slug model separate from your Post model. The Slug model handles duplicate friendly_ids, as well as versioning.

Your model code would look something like this:

has_friendly_id :title, :use_slug => true

and you can then write code like this:

@post = Post.find("new-version-released")  # Post.find(1) still works, too
@post.to_param                             # returns "new-version-released"
redirect_to @post                          # The URL would be /posts/new-version-released

Now in your controllers, if you want to prevent people from accessing your models by numeric id, you can detect whether they were found by the friendly_id:

@post.find(params[:id])
raise "some error" if !@post.found_using_friendly_id?

or, you can 301 redirect if the model was found by the numeric id if you don't care about numeric access, but want the SEO value of the friendly_id:

@post.find(params[:id])
redirect_to @post, :status => 301 if @post.has_better_id?

The “has_better_id?” method returns true if the model was found with the numeric id, or with an outdated slug.

Extra Features

Slug Versioning

FriendlyId will record changes to slugs so that you can tell when the model is found with an older slug. This can be useful if you want to do a 301 redirect to your updated URL.

def find_record_using_friendly_id
  @post = Post.find(params[:id])
  redirect_to @post, :status => :moved_permanently if @post.has_better_id?
end

Unique Slug Names

FriendlyId will append a number to the end of the id to keep it unique if necessary:

/posts/new-version-released
/posts/new-version-released-2
/posts/new-version-released-3
etc.

Text Normalization

FriendlyId's slugging can strip diacritics from Western European characters, for example conveting “ñøîéçü” to “noiecu.”

has_friendly_id :title, :use_slug => true, :strip_diacritics => true

If you are not using slugs, you'll have to do this manually for whatever value you're using as the friendly_id.

Note that this ovbiously won't work for Chinese, Arabic, Russian, Hebrew, Thai, etc. because it will pretty much blow away all the chracters… it's only for languages that use some variant of the Roman alphabet, like English or Polish.

You must install the “unicode” and “iconv” gems to use this feature.

This is all too complicated. Why not just override to_param to return the id followed by a dasherized string?

This creates URL's like the following:

http://www.example.org/profiles/12-joe

Which works great. But at times you may prefer not having the id in the URL.

http://www.example.org/profiles/joe

Note that if you are using the to_param trick for URL's, you should check to make sure your URL's are being accessed with the most up-to-date slug, because the following two URL's will point to the same content:

http://www.example.org/posts/12-i-love-friend-id
http://www.example.org/profiles/12-friendly-id-sucks

In these cases it's convenient to have a before_filter in your controller that does something like this:

def ensure_current_url
  if request.headers['REQUEST_URI'] != post_path(@post)
    redirect_to @post, :status => :moved_permanently
  end  
end

Getting it

Git repository: git://github.com/norman/friendly_id.git

Setting it up

FriendlyId is supported with Rails 2.0.0 and higher.

If you're using Rails 2.1 or higher, you can do the following:

cd my_app
script/plugin install git://github.com/norman/friendly_id.git
script/generate friendly_id_migration
rake db:migrate

If you're using Rails 2.0.x, you should either download the tarball from Github, or use git clone to check out the repo in your vendor/plugins directory, because Rails 2.0 does not support installing plugins with git.

Now add some code to your models:

class Post < ActiveRecord::Base
  has_friendly_id :title, :use_slug => true
end

If you are using slugs, you can use a Rake task to generate slugs for your existing records:

friendly_id:make_slugs MODEL=MyModelName

If you eventually want to expire old slugs every so often, or perhaps every day via cron, you can do:

rake:friendly_id:remove_old_slugs

The default is to remove dead slugs older than 45 days, but is configurable:

rake:friendly_id:remove_old_slugs MODEL=MyModelName DAYS=60

Bugs:

Please report them on Lighthouse.

Credits:

FriendlyId was created by Norman Clarke and Adrian Mugnolo, and Emilio Tagua.

Copyright © 2008 Norman Clarke, Adrian Mugnolo and Emilio Tagua, released under the MIT license.

Something went wrong with that request. Please try again.