public
Description: monkey patched friendly_id that allows for a larger number of duplicate slugs
Homepage:
Clone URL: git://github.com/tkadom/friendly_id.git
friendly_id / README
100644 209 lines (131 sloc) 7.038 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
= 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.