Skip to content

dharmdip/friendly_id

 
 

Repository files navigation

= 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:

OLD AND BUSTED: http://www.example.com/members/12

to

NEW AND HOT: http://www.example.com/members/joe

== 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

http://www.example.com/posts/new-version-released

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
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.

==== 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?
	end

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

Friendly_id 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.

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

That works fine sometimes, but makes ugly urls:

- OLD AND BUSTED: http://www.example.org/profiles/12-joe
- NEW AND HOT: http://www.example.org/profiles/joe

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

- http://www.example.org/posts/12-i-love-joe
- http://www.example.org/posts/12-i-hate-joe
- http://www.example.org/posts/12-this-is-getting-kinda-ridiculous

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.

== Git repository:

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

== Setup:

- script/plugin install git://github.com/norman/friendly_id.git
- 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:

rake:friendly_id:remove_old_slugs

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

If you're using an older version of Rails that does not support installing
plugins via Git, you can {download a tarball of the
plugin}[http://github.com/norman/friendly_id/tarball/master] and unarchive it
in your vendor/plugins directory.

== Bugs:

Please report them on Lighthouse[http://randomba.lighthouseapp.com/projects/14675-friendly_id].

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: http://randomba.org.

== Credits:

Friendly_id was created by Norman Clarke
(norman@addthree.com[mailto:norman@addthree.com]) and Adrian Mugnolo
(adrian@giro54.com[mailto:adrian@giro54.com]).

== Similar Plugins:

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

http://agilewebdevelopment.com/plugins/friendly_identifier

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

http://agilewebdevelopment.com/plugins/acts_as_friendly_param

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

http://agilewebdevelopment.com/plugins/acts_as_slugable

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

http://agilewebdevelopment.com/plugins/acts_as_sluggable

== Legal:

Copyright (c) 2008 Norman Clarke and Adrian Mugnolo, released under the MIT
license.

About

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

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published