Skip to content
FriendlyId is a plugin for Ruby on Rails which allows you to work with human-friendly strings as well as numeric ids for ActiveRecords.
Pull request Compare This branch is 1434 commits behind norman:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


= FriendlyId

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

== The Problem

Numeric id's for your database records are perfect most of the time, but
present some problems when used in URL's:

- They're ugly and impersonal.
- They make URL's harder to remember.
- They can give a window into your database - people can fiddle with your urls by incrementing id's.
- They can give a hint about the number of records in your database.
- They are bad for search engine optimization.

However, text-based ids in URL's present some problems too:

- 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 lets you use textual id's for your active records while mostly
avoiding these problems so that you can go from:




== How it works

=== Scenario 1: You already have a unique string column in your model.

This is the usual case if you have a member/user model with a unique
login/nickname/username column. 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

=== Scenario 2: You want id's based on a not-necessarily unique string column in your model.

This would be the typical case if you have a Posts/Articles model with titles,
and you want the titles turned into slugs so you can have URL's like

Here you would include this in your model:

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

  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:

  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.

==== What if I change the title?

One of the problems with using text-based id's in URL's is that if you change
the id, your URL's using the old id all break. This could break people's
bookmarks, and your search engine listings. Sometimes this doesn't matter, but
often it does.

Friendly_id will record changes to slugs so that you can do a 301 redirect to
the new URL:

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

==== What if the title isn't unique?

Friendly_id will append a number to the end of the id to keep it unique if

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

==== Why not just override to_param with the id followed by a dasherized string?

That works fine sometimes, but makes ugly urls:


Also, these URL's will all point to the same content:


Search engines, especially Google, will penalize you for having duplicate
content if you have more than one URL pointing to the same content.

Also, any person can still simply increment the id part of the url, and gain a
level of access to your data that you may not wish them to have.

==== What about non-ascii characters?

Just do this:

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.

Also note, for this to work, you must install the "unicode" and "iconv" gems.

== Subversion repository:

A month of so after Rails 2.1 is released with support for plugins tracked via
git, the subversion repository will be taken offline, and the git repository
will be the way to go. For now, development is done using git but all changes
are backported to the svn repository to support current users.

== Git repository:

== Setup:

- script/plugin install
- script/generate friendly_id_migration
- rake db:migrate
- now add some code to your models: e.g., <tt>has_friendly_id :title, :use_slug => true</tt>
- if you are using slugs, then do rake friendly_id:make_slugs MODEL=MyModelName

Then every so often, or perhaps every day via cron, you can do:


This will remove any old slugs for friendly ids that have changed. The default
is to remove dead slugs older than 45 days, but you can change that by doing:

rake:friendly_id:remove_old_slugs DAYS=60

== Bugs:

At the moment, Model.exists? fails when using the slug (reported by Chris Nolan).

Finder options are ignored when using slugs; this code returns results when it should not:

  Post.find_using_friendly_id(slugs(:one).name, :conditions => "1 = 2")

This bug was reported by Dan Blue.

These bugs part of a general pattern of not acting enough like
ActiveRecord::Base, something I will address when I have a little more time.

There probably are more bugs. If you find any, please contact the developers,
or leave a comment at our site at:

== Credits:

Friendly_id was created by Norman Clarke
([]) and Adrian Mugnolo

== Similar Plugins:

friendly_identifier - Provides fully textual slugs, but no slug versioning.

acts_as_friendly_param - Versions slugs, but uses a to_param override, leaving
ids in the url.

acts_as_slugable - Provides fully textual slugs, but no slug versioning.

acts_as_sluggable - Uses a to_param override, leaving ids in the url. Doesn't
do versioning.

== Legal:

Copyright (c) 2008 Norman Clarke and Adrian Mugnolo, released under the MIT
Something went wrong with that request. Please try again.