friendly_id_globalize3 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. data/Changelog.md +354 -0
  2. data/Contributors.md +43 -0
  3. data/Guide.md +686 -0
  4. data/MIT-LICENSE +19 -0
  5. data/README.md +99 -0
  6. data/Rakefile +75 -0
  7. data/extras/README.txt +3 -0
  8. data/extras/bench.rb +40 -0
  9. data/extras/extras.rb +38 -0
  10. data/extras/prof.rb +19 -0
  11. data/extras/template-gem.rb +26 -0
  12. data/extras/template-plugin.rb +28 -0
  13. data/generators/friendly_id/friendly_id_generator.rb +30 -0
  14. data/generators/friendly_id/templates/create_slugs.rb +18 -0
  15. data/lib/friendly_id.rb +93 -0
  16. data/lib/friendly_id/active_record.rb +74 -0
  17. data/lib/friendly_id/active_record_adapter/configuration.rb +68 -0
  18. data/lib/friendly_id/active_record_adapter/finders.rb +148 -0
  19. data/lib/friendly_id/active_record_adapter/relation.rb +165 -0
  20. data/lib/friendly_id/active_record_adapter/simple_model.rb +63 -0
  21. data/lib/friendly_id/active_record_adapter/slug.rb +77 -0
  22. data/lib/friendly_id/active_record_adapter/slugged_model.rb +122 -0
  23. data/lib/friendly_id/active_record_adapter/tasks.rb +72 -0
  24. data/lib/friendly_id/configuration.rb +178 -0
  25. data/lib/friendly_id/datamapper.rb +5 -0
  26. data/lib/friendly_id/railtie.rb +22 -0
  27. data/lib/friendly_id/sequel.rb +5 -0
  28. data/lib/friendly_id/slug_string.rb +25 -0
  29. data/lib/friendly_id/slugged.rb +105 -0
  30. data/lib/friendly_id/status.rb +35 -0
  31. data/lib/friendly_id/test.rb +350 -0
  32. data/lib/friendly_id/version.rb +9 -0
  33. data/lib/generators/friendly_id_generator.rb +25 -0
  34. data/lib/tasks/friendly_id.rake +19 -0
  35. data/rails/init.rb +2 -0
  36. data/test/active_record_adapter/ar_test_helper.rb +150 -0
  37. data/test/active_record_adapter/basic_slugged_model_test.rb +14 -0
  38. data/test/active_record_adapter/cached_slug_test.rb +76 -0
  39. data/test/active_record_adapter/core.rb +138 -0
  40. data/test/active_record_adapter/custom_normalizer_test.rb +20 -0
  41. data/test/active_record_adapter/custom_table_name_test.rb +22 -0
  42. data/test/active_record_adapter/default_scope_test.rb +30 -0
  43. data/test/active_record_adapter/optimistic_locking_test.rb +18 -0
  44. data/test/active_record_adapter/scoped_model_test.rb +119 -0
  45. data/test/active_record_adapter/simple_test.rb +76 -0
  46. data/test/active_record_adapter/slug_test.rb +34 -0
  47. data/test/active_record_adapter/slugged.rb +33 -0
  48. data/test/active_record_adapter/slugged_status_test.rb +28 -0
  49. data/test/active_record_adapter/sti_test.rb +22 -0
  50. data/test/active_record_adapter/support/database.jdbcsqlite3.yml +2 -0
  51. data/test/active_record_adapter/support/database.mysql.yml +4 -0
  52. data/test/active_record_adapter/support/database.postgres.yml +6 -0
  53. data/test/active_record_adapter/support/database.sqlite3.yml +2 -0
  54. data/test/active_record_adapter/support/models.rb +104 -0
  55. data/test/active_record_adapter/tasks_test.rb +82 -0
  56. data/test/friendly_id_test.rb +96 -0
  57. data/test/test_helper.rb +13 -0
  58. metadata +193 -0
data/Changelog.md ADDED
@@ -0,0 +1,354 @@
1
+ # FriendlyId Changelog
2
+
3
+ We would like to think our many {file:Contributors contributors} for
4
+ suggestions, ideas and improvements to FriendlyId.
5
+
6
+ * Table of Contents
7
+ {:toc}
8
+
9
+ ## 3.2.0 (2011-01-17)
10
+
11
+ * Fixed deprecation on Rails edge. Thanks [slbug](http://github.com/slbug)
12
+ * Removes `:scope` as a find parameter, allowing more flexible finds with
13
+ scoped records.
14
+ * Improve logic of `friendly_id:redo_slugs` to support models with non-numeric ids. (thanks Oleksandr Petrov)
15
+
16
+ ## 3.1.8 (2010-11-22)
17
+
18
+ * Fix compatibility with Active Record 3.0.3.
19
+
20
+ ## 3.1.7 (2010-09-22)
21
+
22
+ * Reserved words can now be regular expressions.
23
+ * Fix broken SQL when finding with a nil scope on AR3 (Tony Primerano)
24
+ * Slug#sluggable now works around default scopes (Cyrille Stepanyk)
25
+ * Explicitly make slug attributes accessible (William Melody)
26
+ * Improve abstraction support for DataMapper and Sequel (Alex Coles).
27
+
28
+ ## 3.1.6 (2010-09-02)
29
+
30
+ * Fix missing sluggable type in AR3 slug query. This was a fairly major oversight, and if you
31
+ are using 3.1.4 or 3.1.5, you should update right away.
32
+ * Fix scoped queries when the model has a cached slug column. Bascially, the cached slug
33
+ is now completely ignored when a scope is configured.
34
+
35
+ ## 3.1.5 (2010-09-01)
36
+
37
+ * Fix invalid empty symbol with 1.8.x.
38
+
39
+ ## 3.1.4 (2010-09-01)
40
+
41
+ * Significantly improve performance of queries using slugs with no cache on AR3.
42
+ * Fix callbacks being invoked after setting cached slugs.
43
+ * Add validations to some configuration params.
44
+
45
+ ## 3.1.3 (2010-08-11)
46
+
47
+ * Reverted approach to read-only fix from previous release.
48
+ See [this commit](http://github.com/norman/friendly_id/commit/e263f74e446c1e4d95901e9e8d7d6713c078c38e)
49
+ for details.
50
+
51
+ ## 3.1.2 (2010-08-11)
52
+
53
+ * Fixed records being returned read-only. (Thanks Luis Lavena)
54
+ * Don't assume relations are subclasses of ActiveRecord::Base. This fixes using
55
+ FriendlyId with [Static Model](http://github.com/quirkey/static_model). (Thanks
56
+ Luis Lavena)
57
+ * Avoid checking for dependent scopes when no models are using the scopes feature.
58
+
59
+
60
+ ## 3.1.1 (2010-07-30)
61
+
62
+ * Fixed call to method on nil value for failing unfriendly finds (thanks [jlippiner](http://github.com/jlippiner))
63
+
64
+ ## 3.1.0 (2010-07-29)
65
+
66
+ * Refactored/simplified Active Record 2 and 3 query code.
67
+ * Better support for Active Record 3 finds and scopes.
68
+ * Extract slug handling code into separate gem, [Babosa](http://github.com/norman/babosa).
69
+ * `:max-length` option now uses bytes rather than characters.
70
+ * Fix quoting issue that prevented using a domain- or database-qualified column. (thanks James Cropcho)
71
+ * Support for Active Record 2.2.x dropped; 2.3 or above is now required.
72
+ * Fixed a few small errors on Postgres.
73
+ * Improved porability for Sequel and in-progress DataMapper adapter.
74
+
75
+ ## 3.0.6 (2010-06-10)
76
+
77
+ * Fix bad call to apply_mapping on 2.3.
78
+
79
+ ## 3.0.5 (2010-06-10)
80
+
81
+ * Fixed support for Rails 3.0 beta4 (Bruno Michel)
82
+ * Made rake tasks skip validations (Emilio Tagua).
83
+ * Fixed incorrect status of records found with a numeric friendly_id.
84
+ * Made slug an explicit has_one relation to enable eager-loading via :include => :slug
85
+
86
+ ## 3.0.4 (2010-04-27)
87
+
88
+ * Fixed backwards-compatiblity with ActiveSupport 2.3.4 (Thanks Juergen Fesslmeier).
89
+
90
+ ## 3.0.3 (2010-04-26)
91
+
92
+ * Fixed scope update when models use polymorphic relations.
93
+
94
+ ## 3.0.2 (2010-04-09)
95
+
96
+ * Fixed finding non-slugged models by an array of ids.
97
+ * Added backported `tidy_bytes` implementation from [utf8_utils](http://github.com/norman/utf8_utils)
98
+ * Removed dependency on Rubygems 1.3.6; this blocked deploy on Heroku (thanks Steven Noble)
99
+ * Replaced File.dirname calls with File.expand_path, which should allow compatibility with Ruby 1.9.2
100
+ * Cleanups and some improvements to tests.
101
+
102
+ ## 3.0.1 (2010-03-30)
103
+
104
+ * Fixed bad path in Rails 2.x generator.
105
+
106
+ ## 3.0.0 (2010-03-30)
107
+
108
+ * Rails 3 support.
109
+ * Removed features deprecated in FriendlyId 2.3.
110
+ * Fixed searching by numeric friendly_id in non-slugged models.
111
+ * Added `:allow_nil` config option (Andre Duffeck and Norman Clarke)
112
+
113
+ ## 2.3.4 (2010-03-22)
114
+
115
+ * Made slugged status use the slug sequence. This fixes problems with #best?
116
+ returning false when finding with a sequenced slug.
117
+ * Doc fixes. (Juan Schiwndt)
118
+ * Misc cleanups.
119
+
120
+ ## 2.3.3 (2010-03-10)
121
+
122
+ * Fixed sequence regexp to grab all trailing digits. (Nash Kabbara)
123
+ * Block param now warns, not raises. (Kamal Fariz Mahyuddin)
124
+ * Misc doc fixes. (Kamal Fariz Mahyuddin)
125
+
126
+ ## 2.3.2 (2010-02-14)
127
+
128
+ * Fixed finding by old slug when using cached slugs.
129
+ * Sequence separator parsing now correctly handles occurrences of the sequence
130
+ separator string inside the friendly_id text (Johan Kok).
131
+ * Fixed missing quotes on table names in a few places (Brian Collins).
132
+
133
+
134
+ ## 2.3.1 (2010-02-09)
135
+
136
+ * Fixed stack level too deep error on #strip_diacritics.
137
+
138
+
139
+ ## 2.3.0 (2010-02-04)
140
+
141
+ This is a major update à la "Snow Leopard" that adds no new major features,
142
+ but significantly improves the underlying code. Most users should be able to
143
+ upgrade with no issues other than new deprecation messages appearing in the
144
+ logs.
145
+
146
+ If, however, you have monkey-patched FriendlyId, or are maintaining your own
147
+ fork, then this upgrade may causes issues.
148
+
149
+ **Changes:**
150
+
151
+ * Sequence separator can now be configured to something other than "--".
152
+ * New option to pass arguments to `FriendlyId::SlugString#approximate_ascii!`,
153
+ allowing custom approximations specific to German or Spanish.
154
+ * FriendlyId now queries against the cached_slug column, which improves performance.
155
+ * {FriendlyId::SlugString} class added, allowing finer-grained control over
156
+ Unicode friendly_id strings.
157
+ * {FriendlyId::Configuration} class added, offering more flexible/hackable
158
+ options.
159
+ * FriendlyId now raises subclasses of {FriendlyId::SlugGenerationError}
160
+ depending on the error context.
161
+ * Simple models now correctly validate friendly_id length.
162
+ * Passing block into FriendlyId deprecated in favor of overriding
163
+ the model's `normalize_friendly_id` method.
164
+ * Updating only the model's scope now also updates the slug.
165
+ * Major refactorings, cleanups and deprecations en route to the 3.0 release.
166
+
167
+ ## 2.2.7 (2009-12-16)
168
+
169
+ * Fixed typo in Rake tasks which caused delete_old_slugs to fail. (Diego R.V.)
170
+
171
+ ## 2.2.6 (2009-12-10)
172
+
173
+ * Made cached_slug automagic configuration occur outside of has_friendly_id.
174
+ This was causing problems in code where the class is loaded before
175
+ ActiveRecord has established its connection.
176
+ * Fixes for scope feature with Postgres (Ben Woosley)
177
+ * Migrated away from Hoe/Newgem for gem management.
178
+ * Made tests database-agnostic (Ben Woosley)
179
+
180
+ ## 2.2.5 (2009-11-30)
181
+
182
+ * Fixed typo in config options (Steven Noble).
183
+
184
+ ## 2.2.4 (2009-11-12)
185
+
186
+ * Fixed typo in post-install message.
187
+
188
+ ## 2.2.3 (2009-11-12)
189
+
190
+ * Fixed some issues with gem load order under 1.8.x (closes GH Issue #20)
191
+ * Made sure friendly_id generator makes a lib/tasks directory (Josh Nichols)
192
+ * Finders now accept instances of ActiveRecord::Base, matching AR's behavior
193
+ (Josh Nichols)
194
+ * SlugGenerationError now raise when a blank value is passed to
195
+ strip_diacritics
196
+
197
+ ## 2.2.2 (2009-10-26)
198
+
199
+ * Fixed Rake tasks creating duplicate slugs and not properly clearing cached
200
+ slugs (closes GH issues #14 and #15)
201
+
202
+ ## 2.2.1 (2009-10-23)
203
+
204
+ * slug cache now properly caches the slug sequence (closes GH issue #10)
205
+ * attr_protected is now only invoked on the cached_slug column if
206
+ attr_accessible has not already been invoked. (closes GH issue #11)
207
+
208
+ ## 2.2.0 (2009-10-19)
209
+
210
+ * Added slug caching, offers huge performance boost (Bruno Michel)
211
+ * Handle Unicode string length correctly (Mikhail Shirkov)
212
+ * Remove alias_method_chain in favor of super (Diego Carrion)
213
+
214
+ ## 2.1.4 (2009-09-01)
215
+
216
+ * Fixed upgrade generator not installing rake tasks (Harry Love)
217
+ * Fixed handling of very large id's (Nathan Phelps)
218
+ * Fixed long index name on migration (Rob Ingram)
219
+
220
+ ## 2.1.3 (2009-06-03)
221
+
222
+ * Always call #to_s on slug_text to allow objects such as DateTimes to be used
223
+ for the friendly_id text. (reported by Jon Ng)
224
+
225
+ ## 2.1.2 (2009-05-21)
226
+
227
+ * Non-slugged models now validate the friendly_id on save as well as create
228
+ (Joe Van Dyk).
229
+ * Replaced Shoulda with Contest.
230
+
231
+ ## 2.1.1 (2009-03-25)
232
+
233
+ * Fixed bug with find_some; if a record has old slugs, find_some will no
234
+ longer return multiple copies of that record when finding by numerical ID.
235
+ (Steve Luscher)
236
+ * Fixed bug with find_some: you can now find_some with an array of numerical
237
+ IDs without an error being thrown. (Steve Luscher)
238
+
239
+ ## 2.1.0 (2009-03-25)
240
+
241
+ * Ruby 1.9 compatibility.
242
+ * Removed dependency on ancient Unicode gem.
243
+
244
+ ## 2.0.4 (2009-02-12)
245
+
246
+ * You can now pass in your own custom slug generation blocks while setting up
247
+ friendly_id.
248
+
249
+ ## 2.0.3 (2009-02-11)
250
+
251
+ * Fixed to_param returning an empty string for non-slugged models with a null
252
+ friendly_id.
253
+
254
+ ## 2.0.2 (2009-02-09)
255
+
256
+ * Made FriendlyId depend only on ActiveRecord. It should now be possible to
257
+ use FriendlyId with Camping or any other codebase that uses AR.
258
+ * Overhauled creaky testing setup and switched to Shoulda.
259
+ * Made reserved words work for non-slugged models.
260
+
261
+ ## 2.0.1 (2009-01-19)
262
+
263
+ * Fix infinite redirect bug when using .has_better_id? in your controllers
264
+ (Sean Abrahams)
265
+
266
+
267
+ ## 2.0.0 (2009-01-03)
268
+
269
+ * Support for scoped slugs (Norman Clarke)
270
+ * Support for UTF-8 friendly_ids (Norman Clarke)
271
+ * Can now be installed via Ruby Gems, or as a Rails plugin (Norman Clarke)
272
+ * Improved handling of non-unique slugs (Norman Clarke and Adrian Mugnolo)
273
+ * Shoulda macro (Josh Nichols)
274
+ * Various small bugfixes, cleanups and refactorings
275
+
276
+ ## 1.0 (2008-12-11)
277
+
278
+ * Fixed bug that may return invalid records having similar id/names and using
279
+ MySQL. (Emilio Tagua)
280
+ * Fixed slug generation to increment only numeric extension without modifying
281
+ the name on duplicated slugs. (Emilio Tagua)
282
+
283
+ ## 2008-10-31
284
+
285
+ * Fixed compatibility with Rails 2.0.x. (Norman Clarke)
286
+ * friendly_id::make_slugs update records in chunks of 1000 to avoid running
287
+ out of memory with large datasets. (Tim Kadom)
288
+ * Fixed logic error with slug name collisions. Thanks to Tim Kadom for
289
+ reporting this bug.
290
+
291
+ ## 2008-10-22
292
+
293
+ * Reverted use of UTF8Handler - was causing errors for some people (Bence Nagy)
294
+ * Corrected find in case if a friendly_id begins with number (Bence Nagy)
295
+ * Added ability to reserve words from slugs (Adam Cigánek)
296
+
297
+ ## 2008-10-09
298
+
299
+ * Moved "require"" for iconv to init.rb (Florian Aßmann)
300
+ * Removed "require" for Unicode, use Rails' handler instead (Florian Aßmann)
301
+ * Replaced some magic numbers with constants (Florian Aßmann)
302
+ * Don't overwrite find, alias_method_chain find_one and find_some instead
303
+ (Florian Aßmann)
304
+ * Slugs behave more like ids now (Florian Aßmann)
305
+ * Can find by mixture of ids and slugs (Florian Aßmann)
306
+ * Reformatted code and comments (Florian Aßmann)
307
+ * Added support for Edge Rails' Inflector::parameterize (Norman Clarke)
308
+
309
+ ## 0.5 (2008-08-25)
310
+
311
+ * Moved strip_diacritics into Slug for easier reuse/better organization.
312
+ * Put class methods inside class << self block. (Norman Clarke)
313
+
314
+ * Small change to allow friendly_id to work better with STI. (David Ramalho)
315
+
316
+ ## 2008-07-14
317
+
318
+ * Improved slug generation for friendly id's with apostrophes. (Alistair Holt)
319
+ * Added support for namespaced models in Rakefile. (David Ramalho)
320
+
321
+ ## 2008-06-23
322
+
323
+ * Cached most recent slug to improve performance (Emilio Tagua).
324
+
325
+ ## 2008-06-10
326
+
327
+ * Added ability to find friendly_ids by array (Emilio Tagua)
328
+
329
+ ## 2008-05-15
330
+
331
+ * Made friendly_id raise an error if slug method returns a blank value.
332
+
333
+ ## 2008-05-12
334
+
335
+ * Added experimental Github gemspec.
336
+
337
+ ## 2008-04-18
338
+
339
+ * Improved slug name collision avoidance.
340
+
341
+ ## 2008-03-13
342
+
343
+ * Added :dependent => :destroy to slug relation, as suggested by Emilio Tagua.
344
+ * Fixed error when renaming a slugged item back to a previously used name.
345
+ * Incorporated documentation changes suggested by Jesse Crouch and Chris Nolan.
346
+
347
+ ## 2008-02-07
348
+
349
+ * Applied patches from blog commenter "suntzu" to fix problem with model
350
+ values were being overwritten.
351
+ * Applied patch from Dan Blue to make friendly_id no longer ignore options on
352
+ ActiveRecordBase#find.
353
+ * Added call to options.assert_valid_keys in has_friendly_id. Thanks to W.
354
+ Andrew Loe III for pointing out that this was missing.
data/Contributors.md ADDED
@@ -0,0 +1,43 @@
1
+ We are grateful for many contributions from the Ruby and Rails
2
+ community, in particular from the following people:
3
+
4
+ * Adam Cigánek
5
+ * Alex Coles
6
+ * Alexander Gräfe
7
+ * Alistair Holt
8
+ * Andre Duffeck
9
+ * Andrew Loe III
10
+ * Ben Woosley
11
+ * Bence Nagy
12
+ * Brian Collins
13
+ * Bruno Michel
14
+ * Chris Nolan
15
+ * Cyrille Stepanyk
16
+ * David Ramalho
17
+ * Diego Carrion
18
+ * Diego R. V.
19
+ * Eric Lindvall
20
+ * Florian Aßmanqn
21
+ * Harry Love
22
+ * Ian Stewart
23
+ * James Cropcho
24
+ * Jesse Crouch
25
+ * Joe Van Dyk
26
+ * Johan Kok
27
+ * Josh Nichols
28
+ * Juan Schwindt
29
+ * Kamal Fariz Mahyuddin
30
+ * Laurence A. Lee
31
+ * Louis T.
32
+ * Mikhail Shirkov
33
+ * Nash Kabbara
34
+ * Nathan Phelps
35
+ * Ramon Soares
36
+ * Rdavila
37
+ * Rob Ingram
38
+ * Ryan Wood
39
+ * Sean Abrahams
40
+ * Steve Luscher
41
+ * Steven Noble
42
+ * Tim Kadom
43
+ * William Melody
data/Guide.md ADDED
@@ -0,0 +1,686 @@
1
+ # FriendlyId Guide
2
+
3
+ * Table of Contents
4
+ {:toc}
5
+
6
+ ## Overview
7
+
8
+ FriendlyId is an ORM-centric Ruby library that lets you work with human-friendly
9
+ strings as if they were numeric ids. Among other things, this facilitates
10
+ replacing "unfriendly" URL's like:
11
+
12
+ http://example.com/states/4323454
13
+
14
+ with "friendly" ones such as:
15
+
16
+ http://example.com/states/washington
17
+
18
+ FriendlyId is typically used with Rails and Active Record, but can also be used in
19
+ non-Rails applications, and with [Sequel](http://github.com/norman/friendly_id_sequel) and
20
+ [DataMapper](http://github.com/myabc/friendly_id_datamapper).
21
+
22
+ ## Simple Models
23
+
24
+ The simplest way to use FriendlyId is with a model that has a uniquely indexed
25
+ column with no spaces or special characters, and that is seldom or never
26
+ updated. The most common example of this is a user name or login column:
27
+
28
+ class User < ActiveRecord::Base
29
+ validates_format_of :login, :with => /\A[a-z0-9]+\z/i
30
+ has_friendly_id :login
31
+ end
32
+
33
+ @user = User.find "joe" # the old User.find(1) still works, too
34
+ @user.to_param # returns "joe"
35
+ redirect_to @user # the URL will be /users/joe
36
+
37
+ In this case, FriendlyId assumes you want to use the column as-is; it will never
38
+ modify the value of the column, and your application should ensure that the value
39
+ is admissible in a URL:
40
+
41
+ class City < ActiveRecord::Base
42
+ has_friendly_id :name
43
+ end
44
+
45
+ @city.find "Viña del Mar"
46
+ redirect_to @city # the URL will be /cities/Viña%20del%20Mar
47
+
48
+ For this reason, it is often more convenient to use Slugs rather than a single
49
+ column.
50
+
51
+ ## Slugged Models
52
+
53
+ FriendlyId uses a separate table to store slugs for models which require some
54
+ processing of the friendly_id text. The most common example is a blog post's
55
+ title, which may have spaces, uppercase characters, or other attributes you
56
+ wish to modify to make them more suitable for use in URL's.
57
+
58
+ class Post < ActiveRecord::Base
59
+ has_friendly_id :title, :use_slug => true
60
+ end
61
+
62
+ @post = Post.create(:title => "This is the first post!")
63
+ @post.friendly_id # returns "this-is-the-first-post"
64
+ redirect_to @post # the URL will be /posts/this-is-the-first-post
65
+
66
+ If you are unsure whether to use slugs, then your best bet is to use them,
67
+ because FriendlyId provides many useful features that only work with this
68
+ feature. These features are explained in detail {file:Guide.md#features below}.
69
+
70
+ ## Installation
71
+
72
+ FriendlyId can be installed as a gem, or as a Rails plugin. It is compatible
73
+ with Rails 2.3.x. and 3.0.
74
+
75
+ ### As a Gem
76
+
77
+ gem install friendly_id
78
+
79
+ #### Rails 2.3.x
80
+
81
+ After installing the gem, add an entry in environment.rb:
82
+
83
+ config.gem "friendly_id", :version => "~> 3.2"
84
+
85
+ ### Rails 3.0
86
+
87
+ After installing the gem, add an entry in the Gemfile:
88
+
89
+ gem "friendly_id", "~> 3.2"
90
+
91
+ ### As a Plugin
92
+
93
+ Plugin installation is simple for all supported versions of Rails:
94
+
95
+ ./script/plugin install git://github.com/norman/friendly_id.git
96
+
97
+ However, installing as a gem offers simpler version control than plugin
98
+ installation. Whenever possible, install as a gem instead. Plugin support may
99
+ eventually be removed in a future version.
100
+
101
+ ### Future Compatibility
102
+
103
+ FriendlyId will always remain compatible with the current release of Rails, and
104
+ at least one stable release behind. That means that support for 2.3.x will not be
105
+ dropped until a stable release of 3.1 is out, or possibly longer.
106
+
107
+ ### Setup
108
+
109
+ After installing either as a gem or plugin, run:
110
+
111
+
112
+ rails generate friendly_id
113
+ # or "./script generate friendly_id" on Rails 2.3
114
+ rake db:migrate
115
+
116
+ This will install the Rake tasks and slug migration for FriendlyId. If you are
117
+ not going to use slugs, you can use the `skip-migration` option:
118
+
119
+ rails generate friendly_id --skip-migration
120
+
121
+ FriendlyId is now set up and ready for you to use.
122
+
123
+ ## Configuration
124
+
125
+ FriendlyId is configured in your model using the `has_friendly_id` method:
126
+
127
+ has_friendly_id :a_column_or_method options_hash
128
+
129
+ class Post < ActiveRecord::Base
130
+ # use the "title" column as the basis of the friendly_id, and use slugs
131
+ has_friendly_id :title, :use_slug => true,
132
+ # remove accents and other diacritics from Latin characters
133
+ :approximate_ascii => true,
134
+ # don't use slugs larger than 50 bytes
135
+ :max_length => 50
136
+ end
137
+
138
+ Read on to learn about the various features that can be configured. For the
139
+ full list of valid configuration options, see the instance attribute summary
140
+ for {FriendlyId::Configuration}.
141
+
142
+ # Features
143
+
144
+ ## FriendlyId Strings
145
+
146
+ FriendlyId uses the [Babosa](http://github.com/norman/babosa) library for
147
+ generating slug strings. When using slugs, FriendlyId/Babosa will automatically
148
+ modify the slug text to make it more suitable for use in a URL:
149
+
150
+ class City < ActiveRecord::Base
151
+ has_friendly_id :name, :use_slug => true
152
+ end
153
+
154
+ @city.create :name => "Viña del Mar"
155
+ @city.friendly_id # will be "viña-del-mar"
156
+
157
+ By default, the string is downcased and stripped, spaces are replaced with
158
+ dashes, and non-word characters other than "-" are removed.
159
+
160
+ ### Replacing Accented Characters
161
+
162
+ If your strings use Latin characters, you can use the `:approximate_ascii` option to remove
163
+ accents and other diacritics:
164
+
165
+ class City < ActiveRecord::Base
166
+ has_friendly_id :name, :use_slug => true, :approximate_ascii => true
167
+ end
168
+
169
+ @city.create :name => "Łódź, Poland"
170
+ @city.friendly_id # will be "lodz-poland"
171
+
172
+ There are special options for some languages:
173
+
174
+ class Person < ActiveRecord::Base
175
+ has_friendly_id :name, :use_slug => true, :approximate_ascii => true,
176
+ :ascii_approximation_options => :german
177
+ end
178
+
179
+ @person.create :name => "Jürgen Müller"
180
+ @person.friendly_id # will be "juergen-mueller"
181
+
182
+ FriendlyId supports whatever languages are supported by
183
+ [Babosa](https://github.com/norman/babosa); at the time of writing, this
184
+ includes Danish, German, Serbian and Spanish.
185
+
186
+ ### Unicode Slugs
187
+
188
+ By default, any character outside the Unicode Latin character range will be
189
+ passed through untouched, allowing you to have slugs in Arabic, Japanese,
190
+ Greek, etc:
191
+
192
+ @post.create :title => "katakana: ゲコゴサザシジ!"
193
+ @post.friendly_id # will be: "katakana-ゲコゴサザシジ"
194
+
195
+ ### ASCII Slugs
196
+
197
+ You can also configure FriendlyId using `:strip_non_ascii` to simply delete
198
+ any non-ascii characters:
199
+
200
+ class Post < ActiveRecord::Base
201
+ has_friendly_id :title, :use_slug => true, :strip_non_ascii => true
202
+ end
203
+
204
+ @post.create :title => "katakana: ゲコゴサザシジ!"
205
+ @post.friendly_id # will be: "katakana"
206
+
207
+
208
+ ### Using a Custom Method to Generate the Slug Text
209
+
210
+ FriendlyId can use either a column or a method to generate the slug text for
211
+ your model:
212
+
213
+ class City < ActiveRecord::Base
214
+
215
+ belongs_to :country
216
+ has_friendly_id :name_and_country, :use_slug => true
217
+
218
+ def name_and_country
219
+ #{name} #{country.name}
220
+ end
221
+
222
+ end
223
+
224
+ @country = Country.create(:name => "Argentina")
225
+ @city = City.create(:name => "Buenos Aires", :country => @country)
226
+ @city.friendly_id # will be "buenos-aires-argentina"
227
+
228
+ One word of caution: in the example above, if the country's name were updated,
229
+ say, to "Argentine Republic", the city's friendly_id would not be
230
+ automatically updated. For this reason, it's a good idea to avoid using
231
+ frequently-updated relations as a part of the friendly_id.
232
+
233
+ ## Using a Custom Method to Process the Slug Text
234
+
235
+ If the built-in slug text handling options don't work for your application,
236
+ you can override the `normalize_friendly_id` method in your model class in
237
+ order to fine-tune the output:
238
+
239
+ class City < ActiveRecord::Base
240
+
241
+ def normalize_friendly_id(text)
242
+ my_text_modifier_method(text)
243
+ end
244
+
245
+ end
246
+
247
+ The normalize_friendly_id method takes a single argument and receives an
248
+ instance of {FriendlyId::SlugString}, a class which wraps a regular Ruby string
249
+ with additional formatting options.
250
+
251
+ ### Converting non-Latin characters to ASCII with Stringex
252
+
253
+ Stringex is a library which provides some interesting options for transliterating
254
+ non-Latin strings to ASCII:
255
+
256
+ "你好".to_url => "ni-hao"
257
+
258
+ Using Stringex with FriendlyId is a simple matter of installing and requiring
259
+ the `stringex` gem, and overriding the `normalize_friendly_id` method in your
260
+ model:
261
+
262
+ class City < ActiveRecord::Base
263
+ def normalize_friendly_id(text)
264
+ text.to_url
265
+ end
266
+ end
267
+
268
+ However, be aware of some limitations of Stringex - it just does a context-free
269
+ character-by-character approximation for Unicode strings without sensitivity to
270
+ the string's language. This means, for example, that the Han characters used by
271
+ Japanese, Mandarin, Cantonese, and other languages are all replaced with the
272
+ same ASCII text. For Han characters, Stringex uses Mandarin, which makes its
273
+ output on Japanese text useless. You can read more about the limitations of
274
+ Stringex in [the documentation for
275
+ Unidecoder](http://search.cpan.org/~sburke/Text-Unidecode-0.04/lib/Text/Unidecode.pm#DESIGN_GOALS_AND_CONSTRAINTS),
276
+ the Perl library upon which Stringex is based.
277
+
278
+ ## Redirecting to the Current Friendly URL
279
+
280
+ FriendlyId maintains a history of your record's older slugs, so if your
281
+ record's friendly_id changes, your URL's won't break. It offers several
282
+ methods to determine whether the model instance was found using the most
283
+ recent friendly_id. This helps you redirect to your "unfriendly" URL's to your
284
+ new "friendly" ones when adding FriendlyId to an existing application:
285
+
286
+ class PostsController < ApplicationController
287
+
288
+ before_filter ensure_current_post_url, :only => :show
289
+
290
+ ...
291
+
292
+ def ensure_current_post_url
293
+ redirect_to @post, :status => :moved_permanently unless @post.friendly_id_status.best?
294
+ end
295
+
296
+ end
297
+
298
+ For more information, take a look at the documentation for {FriendlyId::Status}.
299
+
300
+ ## Non-unique Slugs
301
+
302
+ FriendlyId will append a arbitrary number to the end of the id to keep it
303
+ unique if necessary:
304
+
305
+ /posts/new-version-released
306
+ /posts/new-version-released--2
307
+ /posts/new-version-released--3
308
+ ...
309
+ etc.
310
+
311
+ Note that the number is preceded by "--" rather than "-" to distinguish it from
312
+ the rest of the slug. This is important to enable having slugs like:
313
+
314
+ /cars/peugeot-206
315
+ /cars/peugeot-206--2
316
+
317
+ You can configure the separator string used by your model by setting the
318
+ `:sequence_separator` option in `has_friendly_id`:
319
+
320
+ has_friendly_id :title, :use_slug => true, :sequence_separator => ":"
321
+
322
+ You can also override the default used in
323
+ {FriendlyId::Configuration::DEFAULTS} to set the value for any model using
324
+ FriendlyId. If you change this value in an existing application, be sure to
325
+ {file:Guide.md#regenerating_slugs regenerate the slugs} afterwards.
326
+
327
+ For reasons I hope are obvious, you can't change this value to "-". If you try,
328
+ FriendlyId will raise an error.
329
+
330
+ ## Reserved Words
331
+
332
+ You can configure a list of strings as reserved so that, for example, you
333
+ don't end up with this problem:
334
+
335
+ /users/joe-schmoe # A user chose "joe schmoe" as his user name - no worries.
336
+ /users/new # A user chose "new" as his user name, and now no one can sign up.
337
+
338
+ Reserved words are configured using the `:reserved_words` option:
339
+
340
+ class Restaurant < ActiveRecord::Base
341
+ belongs_to :city
342
+ has_friendly_id :name, :use_slug => true, :reserved_words => ["my", "values"]
343
+ end
344
+
345
+ The reserved words can be specified as an array or (since 3.1.7) as a regular
346
+ expression.
347
+
348
+ The strings "new" and "index" are reserved by default. When you attempt to
349
+ store a reserved value, FriendlyId raises a
350
+ {FriendlyId::ReservedError}. You can also override the default
351
+ reserved words in {FriendlyId::Configuration::DEFAULTS} to set the value for any
352
+ model using FriendlyId.
353
+
354
+ ## Caching the FriendlyId Slug for Better Performance
355
+
356
+ Checking the slugs table all the time has an impact on performance, so as of
357
+ 2.2.0, FriendlyId offers slug caching.
358
+
359
+ ### Automatic setup
360
+
361
+ To enable slug caching, simply add a column named "cached_slug" to your model.
362
+ FriendlyId will automatically use this column if it detects it:
363
+
364
+ class AddCachedSlugToUsers < ActiveRecord::Migration
365
+ def self.up
366
+ add_column :users, :cached_slug, :string
367
+ add_index :users, :cached_slug, :unique => true
368
+ end
369
+
370
+ def self.down
371
+ remove_column :users, :cached_slug
372
+ end
373
+ end
374
+
375
+ Then, redo the slugs:
376
+
377
+ rake friendly_id:redo_slugs MODEL=User
378
+
379
+ FriendlyId will automatically query against the cache column if it's available,
380
+ which will <a href="#some_benchmarks">improve the performance</a> of many queries.
381
+
382
+ A few warnings when using this feature:
383
+
384
+ * *DO NOT* forget to redo the slugs, or else this feature will not work!
385
+ * This feature uses `attr_protected` to protect the `cached_slug` column,
386
+ unless you have already invoked `attr_accessible`. If you wish to use
387
+ `attr_accessible`, you must invoke it BEFORE you invoke `has_friendly_id` in
388
+ your class.
389
+ * Cached slugs [are incompatible with scopes](#scoped_models_and_cached_slugs) and
390
+ are ignored if your model uses the `:scope option`.
391
+
392
+ ### Using a custom column name
393
+
394
+ You can also use a different name for the column if you choose, via the
395
+ `:cache_column` config option:
396
+
397
+ class User < ActiveRecord::Base
398
+ has_friendly_id :name, :use_slug => true, :cache_column => 'my_cached_slug'
399
+ end
400
+
401
+ Don't use "slug" or "slugs" because FriendlyId needs those names for its own
402
+ purposes.
403
+
404
+ ## Translating the slug to another language
405
+
406
+ When translating your application to multiple languages you will sometimes need
407
+ to have objects with slugs in different languages.
408
+
409
+ ### Automatic setup
410
+
411
+ To enable support for locales, simply add a column named "locale" to the friendly_id model.
412
+ FriendlyId will automatically use this column if it detects it:
413
+
414
+ class AddLocaleToSlugs < ActiveRecord::Migration
415
+ def self.up
416
+ add_column :slugs, :locale, :string
417
+ add_index :slugs, :locale, :unique => false
418
+ end
419
+
420
+ def self.down
421
+ remove_column :slugs, :locale
422
+ end
423
+ end
424
+
425
+ Then, redo the slugs:
426
+
427
+ rake friendly_id:redo_slugs
428
+
429
+ A few warnings when using this feature:
430
+
431
+ * *DO NOT* forget to redo the slugs, or else this feature may not work!
432
+ * Slugs with locale [may be incompatible with cached_slugs](#scoped_models_and_cached_slugs) and
433
+ are might be ignored if your model uses the `:cached_slug option`.
434
+
435
+ ### Setting a default locale
436
+
437
+ You can also set a default locale to use via the `:default_locale` config option:
438
+
439
+ class User < ActiveRecord::Base
440
+ has_friendly_id :name, :default_locale => :en
441
+ end
442
+
443
+ ## Nil slugs and skipping validations
444
+
445
+ You can choose to allow `nil` friendly_ids via the `:allow_nil` config option:
446
+
447
+ class User < ActiveRecord::Base
448
+ has_friendly_id :name, :allow_nil => true
449
+ end
450
+
451
+ This works whether the model uses slugs or not.
452
+
453
+ For slugged models, if the friendly_id text is `nil`, no slug will be created.
454
+ This can be useful, for example, to only create slugs for published articles
455
+ and avoid creating many slugs with sequences.
456
+
457
+ For models that don't use slugs, this will make FriendlyId skip all its
458
+ validations when the friendly_id text is `nil`. This can be useful, for
459
+ example, if you wish to add the friendly_id value in an `:after_save` callback.
460
+
461
+ For non-slugged models, if you simply wish to skip friendly_ids's validations
462
+ for some reason, you can override the `skip_friendly_id_validations` method.
463
+ Note that this method is **not** used by slugged models.
464
+
465
+ ## Scoped Slugs
466
+
467
+ _Note that in FriendlyId prior to 3.2.0, you could specify a non-standard
468
+ `:scope` argument on finds. This feature has been removed in 3.2.0 in favor of
469
+ the query stategies described below._
470
+
471
+ FriendlyId can generate unique slugs within a given scope. For example, assume
472
+ you have an application that displays restaurants. Without scoped slugs, if two
473
+ restaurants are named "Joe's Diner," the second one will end up with
474
+ "joes-diner--2" as its friendly_id. Using scoped allows you to keep the slug
475
+ names unique for each city, so that the second "Joe's Diner" can also have the
476
+ slug "joes-diner", as long as it's located in a different city:
477
+
478
+ class Restaurant < ActiveRecord::Base
479
+ belongs_to :city
480
+ has_friendly_id :name, :use_slug => true, :scope => :city
481
+ end
482
+
483
+ class City < ActiveRecord::Base
484
+ has_many :restaurants
485
+ has_friendly_id :name, :use_slug => true
486
+ end
487
+
488
+ City.find("seattle").restaurants.find("joes-diner")
489
+ City.find("chicago").restaurants.find("joes-diner")
490
+
491
+
492
+ The value for the `:scope` key in your model can be a custom method you
493
+ define, or the name of a relation. If it's the name of a relation, then the
494
+ scope's text value will be the result of calling `to_param` on the related
495
+ model record. In the example above, the city model also uses FriendlyId and so
496
+ its `to_param` method returns its friendly_id: "chicago" or "seattle".
497
+
498
+ ### Complications with Scoped Slugs
499
+
500
+ #### Scoped Models and Cached Slugs
501
+
502
+ If you want to use cached slugs with scoped models, be sure not to create a unique index on the
503
+ `cached_slug` column.
504
+
505
+
506
+ #### Finding Records by friendly\_id
507
+
508
+ If you are using scopes your friendly ids may not be unique, so a simple find like
509
+
510
+ Restaurant.find("joes-diner")
511
+
512
+ may return the wrong record. In these cases when you want to use the friendly\_id for queries,
513
+ either query as a relation, or specify the scope in your query conditions:
514
+
515
+ # will only return restaurants named "Joe's Diner" in the given city
516
+ @city.restaurants.find("joes-diner")
517
+
518
+ # or
519
+
520
+ Restaurants.find("joes-diner", :include => :slugs, :conditions => {:slugs => {:scope => @city.to_param}})
521
+
522
+
523
+ #### Finding All Records That Match a Scoped ID
524
+
525
+ If you want to find all records with a particular friendly\_id regardless of scope,
526
+ the easiest way is to use cached slugs and query this column directly:
527
+
528
+ Restaurant.find_all_by_cached_slug("joes-diner")
529
+
530
+
531
+ If you're not using cached slugs, then this is slightly more complicated, but
532
+ still doable:
533
+
534
+ name, sequence = params[:id].parse_friendly_id
535
+ Restaurant.all(:include => :slugs, :conditions => {
536
+ :slugs => {:name => name, :sequence => sequence}
537
+ })
538
+
539
+
540
+ #### Updating a Relation's Scoped Slugs
541
+
542
+ When using a relation as the scope, updating the relation will update the slugs,
543
+ but only if both models have specified the relationship. In the above example,
544
+ updates to City will update the slugs for Restaurant because City specifies that
545
+ it `has_many :restaurants`.
546
+
547
+ ### Routes for Scoped Models
548
+
549
+ Note that FriendlyId does not set up any routes for scoped models; you must do
550
+ this yourself in your application. Here's an example of one way to set this up:
551
+
552
+ # in routes.rb
553
+ resources :cities do
554
+ resources :restaurants
555
+ end
556
+
557
+ # in views
558
+ <%= link_to 'Show', [@city, @restaurant] %>
559
+
560
+ # in controllers
561
+ @city = City.find(params[:city_id])
562
+ @restaurant = @city.restaurants.find(params[:id])
563
+
564
+ # URL's:
565
+ http://example.org/cities/seattle/restaurants/joes-diner
566
+ http://example.org/cities/chicago/restaurants/joes-diner
567
+
568
+
569
+ ## FriendlyId Rake Tasks
570
+
571
+ FriendlyId provides several tasks to help maintain your application.
572
+
573
+ ### Generating New Slugs For the First Time
574
+
575
+ friendly_id:make_slugs MODEL=<model name>
576
+
577
+ Use this task to generate slugs after installing FriendlyId in a new
578
+ application.
579
+
580
+ ### Regenerating Slugs
581
+
582
+ friendly_id:redo_slugs MODEL=<model name>
583
+
584
+ Use this task to regenerate slugs after making any changes to your model's
585
+ FriendlyId configuration options that affect slug generation. For example,
586
+ if you introduce a `cached_slug` column or change the `:seqence_separator`.
587
+
588
+ ### Deleting Old Slugs
589
+
590
+ rake friendly_id:remove_old_slugs MODEL=<model name> DAYS=<days>
591
+
592
+ Use this task if you wish to delete expired slugs; manually or perhaps via
593
+ cron. If you don't specify the days option, the default is to remove unused
594
+ slugs older than 45 days.
595
+
596
+ # Misc tips
597
+
598
+ ## Allowing Users to Override/Control Slugs
599
+
600
+ Would you like to mostly use default slugs, but allow the option of a
601
+ custom user-chosen slug in your application? If so, then you're not the first to
602
+ want this. Here's a [demo
603
+ application](http://github.com/norman/friendly_id_manual_slug_demo) showing how
604
+ it can be done.
605
+
606
+ ## Default Scopes
607
+
608
+ Whether you're using FriendlyId or not, a good rule of thumb for default scopes
609
+ is to always use your model's table name. Otherwise any time you do a join, you
610
+ risk having queries fail because of duplicate column names - particularly for a
611
+ default scope like this one:
612
+
613
+ default_scope :order => "created_at DESC"
614
+
615
+ Instead, do this:
616
+
617
+ default_scope :order => = "#{quoted_table_name}.created_at DESC"
618
+
619
+ Or even better, unless you're using a custom primary key:
620
+
621
+ default_scope :order => = "#{quoted_table_name}.id DESC"
622
+
623
+ because sorting by a unique integer column is faster than sorting by a date
624
+ column.
625
+
626
+ ## MySQL MyISAM tables
627
+
628
+ Currently, the default FriendlyId migration will not work with MyISAM tables
629
+ because it creates an index that's too large. The easiest way to work around
630
+ this is to change the generated migration to add limits on some column lengths.
631
+ Please see [this issue](http://github.com/norman/friendly_id/issues#issue/50) in
632
+ the FriendlyId issue tracker for more information.
633
+
634
+ # Hacking FriendlyId
635
+
636
+ A couple of notes for programmers intending to work on FriendlyId:
637
+
638
+ If you intend to send a pull request, in general it's best to make minor
639
+ changes in the master branch, and major changes in the edge branch.
640
+
641
+ Before removing any public or protected methods, FriendlyId will deprecate
642
+ them through one major release cycle. Private methods may, however, change at
643
+ any time.
644
+
645
+ ## Some Benchmarks
646
+
647
+ These benchmarks can give you an idea of FriendlyId's impact on the
648
+ performance of your application. Of course your results may vary.
649
+
650
+ Note that much of the performance difference can be attributed to finding an
651
+ SQL record by a text column. Finding a single record by numeric primary key is
652
+ always the fastest operation, and thus the best choice when possible. If you
653
+ decide not to use FriendlyId for performance reasons, keep in mind that your
654
+ own solution is unlikely to be any faster than FriendlyId with cached slugs
655
+ enabled. But if it is, then your patches would be very welcome!
656
+
657
+
658
+ activerecord (2.3.8)
659
+ ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]
660
+ friendly_id (3.1.4)
661
+ sqlite3-ruby (1.3.1)
662
+ sqlite3 3.6.12 in-memory database
663
+
664
+ | DEFAULT | NO_SLUG | SLUG | CACHED_SLUG |
665
+ ------------------------------------------------------------------------------------------------
666
+ find model by id x1000 | 0.370 | 0.503 | 0.940 | 0.562 |
667
+ find model using array of ids x1000 | 0.612 | 0.615 | 1.054 | 0.957 |
668
+ find model using id, then to_param x1000 | 0.374 | 0.535 | 1.396 | 0.567 |
669
+ ================================================================================================
670
+ Total | 1.356 | 1.653 | 3.390 | 2.086 |
671
+
672
+
673
+ activerecord (3.0.0)
674
+ ruby 1.9.2p0 (2010-08-18 revision 29036) [x86_64-darwin10.4.0]
675
+ friendly_id (3.1.4)
676
+ sqlite3-ruby (1.3.1)
677
+ sqlite3 3.6.12 in-memory database
678
+
679
+ | DEFAULT | NO_SLUG | SLUG | CACHED_SLUG |
680
+ ------------------------------------------------------------------------------------------------
681
+ find model by id x1000 | 0.286 | 0.365 | 0.518 | 0.393 |
682
+ find model using array of ids x1000 | 0.329 | 0.441 | 0.709 | 0.475 |
683
+ find model using id, then to_param x1000 | 0.321 | 0.332 | 0.976 | 0.399 |
684
+ ================================================================================================
685
+ Total | 0.936 | 1.138 | 2.203 | 1.266 |
686
+