cmassimo-friendly_id 3.0.4.2

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 (56) hide show
  1. data/Changelog.md +277 -0
  2. data/Contributors.md +39 -0
  3. data/Guide.md +561 -0
  4. data/LICENSE +19 -0
  5. data/README.md +83 -0
  6. data/Rakefile +66 -0
  7. data/extras/README.txt +3 -0
  8. data/extras/bench.rb +36 -0
  9. data/extras/extras.rb +38 -0
  10. data/extras/prof.rb +14 -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 +73 -0
  16. data/lib/friendly_id/active_record.rb +52 -0
  17. data/lib/friendly_id/active_record_adapter/configuration.rb +67 -0
  18. data/lib/friendly_id/active_record_adapter/finders.rb +156 -0
  19. data/lib/friendly_id/active_record_adapter/simple_model.rb +123 -0
  20. data/lib/friendly_id/active_record_adapter/slug.rb +66 -0
  21. data/lib/friendly_id/active_record_adapter/slugged_model.rb +238 -0
  22. data/lib/friendly_id/active_record_adapter/tasks.rb +69 -0
  23. data/lib/friendly_id/configuration.rb +113 -0
  24. data/lib/friendly_id/finders.rb +109 -0
  25. data/lib/friendly_id/railtie.rb +20 -0
  26. data/lib/friendly_id/sequel.rb +5 -0
  27. data/lib/friendly_id/slug_string.rb +391 -0
  28. data/lib/friendly_id/slugged.rb +102 -0
  29. data/lib/friendly_id/status.rb +35 -0
  30. data/lib/friendly_id/test.rb +291 -0
  31. data/lib/friendly_id/version.rb +9 -0
  32. data/lib/generators/friendly_id_generator.rb +25 -0
  33. data/lib/tasks/friendly_id.rake +19 -0
  34. data/rails/init.rb +2 -0
  35. data/test/active_record_adapter/ar_test_helper.rb +119 -0
  36. data/test/active_record_adapter/basic_slugged_model_test.rb +14 -0
  37. data/test/active_record_adapter/cached_slug_test.rb +61 -0
  38. data/test/active_record_adapter/core.rb +98 -0
  39. data/test/active_record_adapter/custom_normalizer_test.rb +20 -0
  40. data/test/active_record_adapter/custom_table_name_test.rb +22 -0
  41. data/test/active_record_adapter/scoped_model_test.rb +118 -0
  42. data/test/active_record_adapter/simple_test.rb +76 -0
  43. data/test/active_record_adapter/slug_test.rb +34 -0
  44. data/test/active_record_adapter/slugged.rb +30 -0
  45. data/test/active_record_adapter/slugged_status_test.rb +25 -0
  46. data/test/active_record_adapter/sti_test.rb +22 -0
  47. data/test/active_record_adapter/support/database.jdbcsqlite3.yml +2 -0
  48. data/test/active_record_adapter/support/database.mysql.yml +4 -0
  49. data/test/active_record_adapter/support/database.postgres.yml +6 -0
  50. data/test/active_record_adapter/support/database.sqlite3.yml +2 -0
  51. data/test/active_record_adapter/support/models.rb +87 -0
  52. data/test/active_record_adapter/tasks_test.rb +82 -0
  53. data/test/friendly_id_test.rb +55 -0
  54. data/test/slug_string_test.rb +88 -0
  55. data/test/test_helper.rb +15 -0
  56. metadata +168 -0
data/Changelog.md ADDED
@@ -0,0 +1,277 @@
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.0.4 (2010-04-27)
10
+
11
+ * Fixed backwards-compatiblity with ActiveSupport 2.3.4 (Thanks Juergen Fesslmeier).
12
+
13
+ ## 3.0.3 (2010-04-26)
14
+
15
+ * Fixed scope update when models use polymorphic relations.
16
+
17
+ ## 3.0.2 (2010-04-09)
18
+
19
+ * Fixed finding non-slugged models by an array of ids.
20
+ * Added backported `tidy_bytes` implementation from [utf8_utils](http://github.com/norman/utf8_utils)
21
+ * Removed dependency on Rubygems 1.3.6; this blocked deploy on Heroku (thanks Steven Noble)
22
+ * Replaced File.dirname calls with File.expand_path, which should allow compatibility with Ruby 1.9.2
23
+ * Cleanups and some improvements to tests.
24
+
25
+ ## 3.0.1 (2010-03-30)
26
+
27
+ * Fixed bad path in Rails 2.x generator.
28
+
29
+ ## 3.0.0 (2010-03-30)
30
+
31
+ * Rails 3 support.
32
+ * Removed features deprecated in FriendlyId 2.3.
33
+ * Fixed searching by numeric friendly_id in non-slugged models.
34
+ * Added `:allow_nil` config option (Andre Duffeck and Norman Clarke)
35
+
36
+ ## 2.3.4 (2010-03-22)
37
+
38
+ * Made slugged status use the slug sequence. This fixes problems with #best?
39
+ returning false when finding with a sequenced slug.
40
+ * Doc fixes. (Juan Schiwndt)
41
+ * Misc cleanups.
42
+
43
+ ## 2.3.3 (2010-03-10)
44
+
45
+ * Fixed sequence regexp to grab all trailing digits. (Nash Kabbara)
46
+ * Block param now warns, not raises. (Kamal Fariz Mahyuddin)
47
+ * Misc doc fixes. (Kamal Fariz Mahyuddin)
48
+
49
+ ## 2.3.2 (2010-02-14)
50
+
51
+ * Fixed finding by old slug when using cached slugs.
52
+ * Sequence separator parsing now correctly handles occurrences of the sequence
53
+ separator string inside the friendly_id text (Johan Kok).
54
+ * Fixed missing quotes on table names in a few places (Brian Collins).
55
+
56
+
57
+ ## 2.3.1 (2010-02-09)
58
+
59
+ * Fixed stack level too deep error on #strip_diacritics.
60
+
61
+
62
+ ## 2.3.0 (2010-02-04)
63
+
64
+ This is a major update à la "Snow Leopard" that adds no new major features,
65
+ but significantly improves the underlying code. Most users should be able to
66
+ upgrade with no issues other than new deprecation messages appearing in the
67
+ logs.
68
+
69
+ If, however, you have monkey-patched FriendlyId, or are maintaining your own
70
+ fork, then this upgrade may causes issues.
71
+
72
+ **Changes:**
73
+
74
+ * Sequence separator can now be configured to something other than "--".
75
+ * New option to pass arguments to {FriendlyId::SlugString#approximate_ascii!},
76
+ allowing custom approximations specific to German or Spanish.
77
+ * FriendlyId now queries against the cached_slug column, which improves performance.
78
+ * {FriendlyId::SlugString} class added, allowing finer-grained control over
79
+ Unicode friendly_id strings.
80
+ * {FriendlyId::Configuration} class added, offering more flexible/hackable
81
+ options.
82
+ * FriendlyId now raises subclasses of {FriendlyId::SlugGenerationError}
83
+ depending on the error context.
84
+ * Simple models now correctly validate friendly_id length.
85
+ * Passing block into FriendlyId deprecated in favor of overriding
86
+ the model's `normalize_friendly_id` method.
87
+ * Updating only the model's scope now also updates the slug.
88
+ * Major refactorings, cleanups and deprecations en route to the 3.0 release.
89
+
90
+ ## 2.2.7 (2009-12-16)
91
+
92
+ * Fixed typo in Rake tasks which caused delete_old_slugs to fail. (Diego R.V.)
93
+
94
+ ## 2.2.6 (2009-12-10)
95
+
96
+ * Made cached_slug automagic configuration occur outside of has_friendly_id.
97
+ This was causing problems in code where the class is loaded before
98
+ ActiveRecord has established its connection.
99
+ * Fixes for scope feature with Postgres (Ben Woosley)
100
+ * Migrated away from Hoe/Newgem for gem management.
101
+ * Made tests database-agnostic (Ben Woosley)
102
+
103
+ ## 2.2.5 (2009-11-30)
104
+
105
+ * Fixed typo in config options (Steven Noble).
106
+
107
+ ## 2.2.4 (2009-11-12)
108
+
109
+ * Fixed typo in post-install message.
110
+
111
+ ## 2.2.3 (2009-11-12)
112
+
113
+ * Fixed some issues with gem load order under 1.8.x (closes GH Issue #20)
114
+ * Made sure friendly_id generator makes a lib/tasks directory (Josh Nichols)
115
+ * Finders now accept instances of ActiveRecord::Base, matching AR's behavior
116
+ (Josh Nichols)
117
+ * SlugGenerationError now raise when a blank value is passed to
118
+ strip_diacritics
119
+
120
+ ## 2.2.2 (2009-10-26)
121
+
122
+ * Fixed Rake tasks creating duplicate slugs and not properly clearing cached
123
+ slugs (closes GH issues #14 and #15)
124
+
125
+ ## 2.2.1 (2009-10-23)
126
+
127
+ * slug cache now properly caches the slug sequence (closes GH issue #10)
128
+ * attr_protected is now only invoked on the cached_slug column if
129
+ attr_accessible has not already been invoked. (closes GH issue #11)
130
+
131
+ ## 2.2.0 (2009-10-19)
132
+
133
+ * Added slug caching, offers huge performance boost (Bruno Michel)
134
+ * Handle Unicode string length correctly (Mikhail Shirkov)
135
+ * Remove alias_method_chain in favor of super (Diego Carrion)
136
+
137
+ ## 2.1.4 (2009-09-01)
138
+
139
+ * Fixed upgrade generator not installing rake tasks (Harry Love)
140
+ * Fixed handling of very large id's (Nathan Phelps)
141
+ * Fixed long index name on migration (Rob Ingram)
142
+
143
+ ## 2.1.3 (2009-06-03)
144
+
145
+ * Always call #to_s on slug_text to allow objects such as DateTimes to be used
146
+ for the friendly_id text. (reported by Jon Ng)
147
+
148
+ ## 2.1.2 (2009-05-21)
149
+
150
+ * Non-slugged models now validate the friendly_id on save as well as create
151
+ (Joe Van Dyk).
152
+ * Replaced Shoulda with Contest.
153
+
154
+ ## 2.1.1 (2009-03-25)
155
+
156
+ * Fixed bug with find_some; if a record has old slugs, find_some will no
157
+ longer return multiple copies of that record when finding by numerical ID.
158
+ (Steve Luscher)
159
+ * Fixed bug with find_some: you can now find_some with an array of numerical
160
+ IDs without an error being thrown. (Steve Luscher)
161
+
162
+ ## 2.1.0 (2009-03-25)
163
+
164
+ * Ruby 1.9 compatibility.
165
+ * Removed dependency on ancient Unicode gem.
166
+
167
+ ## 2.0.4 (2009-02-12)
168
+
169
+ * You can now pass in your own custom slug generation blocks while setting up
170
+ friendly_id.
171
+
172
+ ## 2.0.3 (2009-02-11)
173
+
174
+ * Fixed to_param returning an empty string for non-slugged models with a null
175
+ friendly_id.
176
+
177
+ ## 2.0.2 (2009-02-09)
178
+
179
+ * Made FriendlyId depend only on ActiveRecord. It should now be possible to
180
+ use FriendlyId with Camping or any other codebase that uses AR.
181
+ * Overhauled creaky testing setup and switched to Shoulda.
182
+ * Made reserved words work for non-slugged models.
183
+
184
+ ## 2.0.1 (2009-01-19)
185
+
186
+ * Fix infinite redirect bug when using .has_better_id? in your controllers
187
+ (Sean Abrahams)
188
+
189
+
190
+ ## 2.0.0 (2009-01-03)
191
+
192
+ * Support for scoped slugs (Norman Clarke)
193
+ * Support for UTF-8 friendly_ids (Norman Clarke)
194
+ * Can now be installed via Ruby Gems, or as a Rails plugin (Norman Clarke)
195
+ * Improved handling of non-unique slugs (Norman Clarke and Adrian Mugnolo)
196
+ * Shoulda macro (Josh Nichols)
197
+ * Various small bugfixes, cleanups and refactorings
198
+
199
+ ## 1.0 (2008-12-11)
200
+
201
+ * Fixed bug that may return invalid records having similar id/names and using
202
+ MySQL. (Emilio Tagua)
203
+ * Fixed slug generation to increment only numeric extension without modifying
204
+ the name on duplicated slugs. (Emilio Tagua)
205
+
206
+ ## 2008-10-31
207
+
208
+ * Fixed compatibility with Rails 2.0.x. (Norman Clarke)
209
+ * friendly_id::make_slugs update records in chunks of 1000 to avoid running
210
+ out of memory with large datasets. (Tim Kadom)
211
+ * Fixed logic error with slug name collisions. Thanks to Tim Kadom for
212
+ reporting this bug.
213
+
214
+ ## 2008-10-22
215
+
216
+ * Reverted use of UTF8Handler - was causing errors for some people (Bence Nagy)
217
+ * Corrected find in case if a friendly_id begins with number (Bence Nagy)
218
+ * Added ability to reserve words from slugs (Adam Cigánek)
219
+
220
+ ## 2008-10-09
221
+
222
+ * Moved "require"" for iconv to init.rb (Florian Aßmann)
223
+ * Removed "require" for Unicode, use Rails' handler instead (Florian Aßmann)
224
+ * Replaced some magic numbers with constants (Florian Aßmann)
225
+ * Don't overwrite find, alias_method_chain find_one and find_some instead
226
+ (Florian Aßmann)
227
+ * Slugs behave more like ids now (Florian Aßmann)
228
+ * Can find by mixture of ids and slugs (Florian Aßmann)
229
+ * Reformatted code and comments (Florian Aßmann)
230
+ * Added support for Edge Rails' Inflector::parameterize (Norman Clarke)
231
+
232
+ ## 0.5 (2008-08-25)
233
+
234
+ * Moved strip_diacritics into Slug for easier reuse/better organization.
235
+ * Put class methods inside class << self block. (Norman Clarke)
236
+
237
+ * Small change to allow friendly_id to work better with STI. (David Ramalho)
238
+
239
+ ## 2008-07-14
240
+
241
+ * Improved slug generation for friendly id's with apostrophes. (Alistair Holt)
242
+ * Added support for namespaced models in Rakefile. (David Ramalho)
243
+
244
+ ## 2008-06-23
245
+
246
+ * Cached most recent slug to improve performance (Emilio Tagua).
247
+
248
+ ## 2008-06-10
249
+
250
+ * Added ability to find friendly_ids by array (Emilio Tagua)
251
+
252
+ ## 2008-05-15
253
+
254
+ * Made friendly_id raise an error if slug method returns a blank value.
255
+
256
+ ## 2008-05-12
257
+
258
+ * Added experimental Github gemspec.
259
+
260
+ ## 2008-04-18
261
+
262
+ * Improved slug name collision avoidance.
263
+
264
+ ## 2008-03-13
265
+
266
+ * Added :dependent => :destroy to slug relation, as suggested by Emilio Tagua.
267
+ * Fixed error when renaming a slugged item back to a previously used name.
268
+ * Incorporated documentation changes suggested by Jesse Crouch and Chris Nolan.
269
+
270
+ ## 2008-02-07
271
+
272
+ * Applied patches from blog commenter "suntzu" to fix problem with model
273
+ values were being overwritten.
274
+ * Applied patch from Dan Blue to make friendly_id no longer ignore options on
275
+ ActiveRecordBase#find.
276
+ * Added call to options.assert_valid_keys in has_friendly_id. Thanks to W.
277
+ Andrew Loe III for pointing out that this was missing.
data/Contributors.md ADDED
@@ -0,0 +1,39 @@
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
+ * Alexander Gräfe
6
+ * Alistair Holt
7
+ * Andre Duffeck
8
+ * Andrew Loe III
9
+ * Ben Woosley
10
+ * Bence Nagy
11
+ * Brian Collins
12
+ * Bruno Michel
13
+ * Chris Nolan
14
+ * David Ramalho
15
+ * Diego Carrion
16
+ * Diego R. V.
17
+ * Eric Lindvall
18
+ * Florian Aßmanqn
19
+ * Harry Love
20
+ * Ian Stewart
21
+ * Jesse Crouch
22
+ * Joe Van Dyk
23
+ * Johan Kok
24
+ * Josh Nichols
25
+ * Juan Schwindt
26
+ * Kamal Fariz Mahyuddin
27
+ * Laurence A. Lee
28
+ * Louis T.
29
+ * Mikhail Shirkov
30
+ * Nash Kabbara
31
+ * Nathan Phelps
32
+ * Ramon Soares
33
+ * Rdavila
34
+ * Rob Ingram
35
+ * Ryan Wood
36
+ * Sean Abrahams
37
+ * Steve Luscher
38
+ * Steven Noble
39
+ * Tim Kadom
data/Guide.md ADDED
@@ -0,0 +1,561 @@
1
+ # FriendlyId Guide
2
+
3
+ * Table of Contents
4
+ {:toc}
5
+
6
+ ## Overview
7
+
8
+ FriendlyId is a Ruby gem which allows you to work with human-friendly strings
9
+ as if they were numeric ids for ActiveRecord models. This facilitates replacing
10
+ "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
+ ## Simple Models
19
+
20
+ The simplest way to use FriendlyId is with a model that has a uniquely indexed
21
+ column with no spaces or special characters, and that is seldom or never
22
+ updated. The most common example of this is a user name or login column:
23
+
24
+ class User < ActiveRecord::Base
25
+ validates_format_of :login, :with => /\A[a-z0-9]+\z/i
26
+ has_friendly_id :login
27
+ end
28
+
29
+ @user = User.find "joe" # the old User.find(1) still works, too
30
+ @user.to_param # returns "joe"
31
+ redirect_to @user # the URL will be /users/joe
32
+
33
+ In this case, FriendlyId assumes you want to use the column as-is; FriendlyId
34
+ will never modify the value of the column, and your application must ensure
35
+ that the value is admissible in a URL:
36
+
37
+ class City < ActiveRecord::Base
38
+ has_friendly_id :name
39
+ end
40
+
41
+ @city.find "Viña del Mar"
42
+ redirect_to @city # the URL will be /cities/Viña%20del%20Mar
43
+
44
+ For this reason, it is often more convenient to use Slugs rather than a single
45
+ column.
46
+
47
+ ## Slugged Models
48
+
49
+ FriendlyId uses a separate table to store slugs for models which require some
50
+ processing of the friendly_id text. The most common example is a blog post's
51
+ title, which may have spaces, uppercase characters, or other attributes you
52
+ wish to modify to make them more suitable for use in URL's.
53
+
54
+ class Post < ActiveRecord::Base
55
+ has_friendly_id :title, :use_slug => true
56
+ end
57
+
58
+ @post = Post.create(:title => "This is the first post!")
59
+ @post.friendly_id # returns "this-is-the-first-post"
60
+ redirect_to @post # the URL will be /posts/this-is-the-first-post
61
+
62
+ If you are unsure whether to use slugs, then your best bet is to use them,
63
+ because FriendlyId provides many useful features that only work with slugs.
64
+ These features are explained in detail {file:Guide.md#features below}.
65
+
66
+ ## Installation
67
+
68
+ FriendlyId can be installed as a gem, or as a Rails plugin. It is compatible
69
+ with Rails 2.2.x, 2.3.x. and 3.0.
70
+
71
+ ### As a Gem
72
+
73
+ gem install friendly_id
74
+
75
+ #### Rails 2.2.x - 2.3.x
76
+
77
+ After installing the gem, add an entry in environment.rb:
78
+
79
+ config.gem "friendly_id", :version => "~> 2.3"
80
+
81
+ ### Rails 3.0
82
+
83
+ After installing the gem, add an entry in the Gemfile:
84
+
85
+ gem "friendly_id", "~> 3.0"
86
+
87
+ ### As a Plugin
88
+
89
+ Plugin installation is simple for all supported versions of Rails:
90
+
91
+ ./script/plugin install git://github.com/norman/friendly_id.git
92
+
93
+ However, installing as a gem offers simpler version control than plugin
94
+ installation. Whenever possible, install as a gem instead. Plugin support may
95
+ eventually be removed in a future version.
96
+
97
+ ### Setup
98
+
99
+ After installing either as a gem or plugin, run:
100
+
101
+ ./script/generate friendly_id
102
+ rake db:migrate
103
+
104
+ This will install the Rake tasks and slug migration for FriendlyId. If you are
105
+ not going to use slugs, you can do:
106
+
107
+ ./script/generate friendly_id --skip-migration
108
+
109
+ FriendlyId is now set up and ready for you to use.
110
+
111
+ ## Configuration
112
+
113
+ FriendlyId is configured in your model using the `has_friendly_id` method:
114
+
115
+ has_friendly_id :a_column_or_method options_hash
116
+
117
+ class Post < ActiveRecord::Base
118
+ # use the "title" column as the basis of the friendly_id, and use slugs
119
+ has_friendly_id :title, :use_slug => true,
120
+ # remove accents and other diacritics from Western characters
121
+ :approximate_ascii => true,
122
+ # don't use slugs longer than 50 chars
123
+ :max_length => 50
124
+ end
125
+
126
+ Read on to learn about the various features that can be configured. For the
127
+ full list of valid configuration options, see the instance attribute summary
128
+ for {FriendlyId::Configuration}.
129
+
130
+ # Features
131
+
132
+ ## FriendlyId Strings
133
+
134
+ FriendlyId comes with {FriendlyId::SlugString excellent support for Unicode
135
+ strings}. When using slugs, FriendlyId will automatically modify the slug text
136
+ to make it more suitable for use in a URL:
137
+
138
+ class City < ActiveRecord::Base
139
+ has_friendly_id :name, :use_slug => true
140
+ end
141
+
142
+ @city.create :name => "Viña del Mar"
143
+ @city.friendly_id # will be "viña-del-mar"
144
+
145
+ By default, the string is {FriendlyId::SlugString#downcase! downcased} and
146
+ {FriendlyId::SlugString#clean! stripped}, {FriendlyId::SlugString#with_dashes! spaces are replaced with dashes},
147
+ and {FriendlyId::SlugString#word_chars! non-word characters are removed}.
148
+
149
+ ### Replacing Accented Characters
150
+
151
+ If your strings use Western characters, you can use the `:approximate_ascii` option to remove
152
+ accents and other diacritics:
153
+
154
+ class City < ActiveRecord::Base
155
+ has_friendly_id :name, :use_slug => true, :approximate_ascii => true
156
+ end
157
+
158
+ @city.create :name => "Łódź, Poland"
159
+ @city.friendly_id # will be "lodz-poland"
160
+
161
+ There are special options for some languages:
162
+
163
+ ### German Approximations
164
+
165
+ class Person < ActiveRecord::Base
166
+ has_friendly_id :name, :use_slug => true, :approximate_ascii => true,
167
+ :ascii_approximation_options => :german
168
+ end
169
+
170
+ @person.create :name => "Jürgen Müller"
171
+ @person.friendly_id # will be "juergen-mueller"
172
+
173
+ ### Spanish Approximations
174
+
175
+ class Post < ActiveRecord::Base
176
+ has_friendly_id :title, :use_slug => true, :approximate_ascii => true,
177
+ :ascii_approximation_options => :spanish
178
+ end
179
+
180
+ @post.create(:title => "¡Feliz año!")
181
+ @post.title # will be "feliz-anno"
182
+
183
+ ### Approximations for Other Languages
184
+
185
+ You can add custom approximations for your language by adding Hash of
186
+ approximations to {FriendlyId::SlugString::APPROXIMATIONS}. The approximations
187
+ must be listed as Unicode decimal numbers, and arrays of numbers.
188
+
189
+ ### Unicode Slugs
190
+
191
+ By default, any character outside the Unicode Western character sets will be
192
+ passed through untouched, allowing you to have slugs in Arabic, Japanese,
193
+ Greek, etc:
194
+
195
+ @post.create :title => "katakana: ゲコゴサザシジ!"
196
+ @post.friendly_id # will be: "katakana-ゲコゴサザシジ"
197
+
198
+ ### ASCII Slugs
199
+
200
+ You can also configure FriendlyId using `:strip_non_ascii` to completely delete
201
+ any non-ascii characters:
202
+
203
+ class Post < ActiveRecord::Base
204
+ has_friendly_id :title, :use_slug => true, :strip_non_ascii => true
205
+ end
206
+
207
+ @post.create :title => "katakana: ゲコゴサザシジ!"
208
+ @post.friendly_id # will be: "katakana"
209
+
210
+
211
+ ### Using a Custom Method to Generate the Slug Text
212
+
213
+ FriendlyId can use either a column or a method to generate the slug text for
214
+ your model:
215
+
216
+ class City < ActiveRecord::Base
217
+
218
+ belongs_to :country
219
+ has_friendly_id :name_and_country, :use_slug => true
220
+
221
+ def name_and_country
222
+ #{name} #{country.name}
223
+ end
224
+
225
+ end
226
+
227
+ @country = Country.create(:name => "Argentina")
228
+ @city = City.create(:name => "Buenos Aires", :country => @country)
229
+ @city.friendly_id # will be "buenos-aires-argentina"
230
+
231
+ One word of caution: in the example above, if the country's name were updated,
232
+ say, to "Argentine Republic", the city's friendly_id would not be
233
+ automatically updated. For this reason, it's a good idea to avoid using
234
+ frequently-updated relations as a part of the friendly_id.
235
+
236
+ ## Using a Custom Method to Process the Slug Text
237
+
238
+ If the built-in slug text handling options don't work for your application,
239
+ you can override the `normalize_friendly_id` method in your model class in
240
+ order to fine-tune the output:
241
+
242
+ class City < ActiveRecord::Base
243
+
244
+ def normalize_friendly_id(text)
245
+ my_text_modifier_method(text)
246
+ end
247
+
248
+ end
249
+
250
+ The normalize_friendly_id method takes a single argument and receives an
251
+ instance of {FriendlyId::SlugString}, a class which wraps a regular Ruby
252
+ string with some additional formatting options inherits Multibyte support from
253
+ ActiveSupport::Multibyte::Chars.
254
+
255
+ ### Converting non-Western characters to ASCII with Stringex
256
+
257
+ Stringex is a library which provides some interesting options for transliterating
258
+ non-Western strings to ASCII:
259
+
260
+ "你好".to_url => "ni-hao"
261
+
262
+ Using Stringex with FriendlyId is a simple matter of installing and requiring
263
+ the `stringex` gem, and overriding the `normalize_friendly_id` method in your
264
+ model:
265
+
266
+ class City < ActiveRecord::Base
267
+
268
+ def normalize_friendly_id(text)
269
+ text.to_url
270
+ end
271
+
272
+ end
273
+
274
+ ## Redirecting to the Current Friendly URL
275
+
276
+ FriendlyId maintains a history of your record's older slugs, so if your
277
+ record's friendly_id changes, your URL's won't break. It offers several
278
+ methods to determine whether the model instance was found using the most
279
+ recent friendly_id. This helps you redirect to your "unfriendly" URL's to your
280
+ new "friendly" ones when adding FriendlyId to an existing application:
281
+
282
+ class PostsController < ApplicationController
283
+
284
+ before_filter ensure_current_post_url, :only => :show
285
+
286
+ ...
287
+
288
+ def ensure_current_post_url
289
+ redirect_to @post, :status => :moved_permanently unless @post.friendly_id_status.best?
290
+ end
291
+
292
+ end
293
+
294
+ For more information, take a look at the documentation for {FriendlyId::Status}.
295
+
296
+ ## Non-unique Slugs
297
+
298
+ FriendlyId will append a arbitrary number to the end of the id to keep it
299
+ unique if necessary:
300
+
301
+ /posts/new-version-released
302
+ /posts/new-version-released--2
303
+ /posts/new-version-released--3
304
+ ...
305
+ etc.
306
+
307
+ Note that the number is preceded by "--" to distinguish it from the
308
+ rest of the slug. This is important to enable having slugs like:
309
+
310
+ /cars/peugeot-206
311
+ /cars/peugeot-206--2
312
+
313
+ You can configure the separator string used by your model by setting the
314
+ `:sequence_separator` option in `has_friendly_id`:
315
+
316
+ has_friendly_id :title, :use_slug => true, :sequence_separator => ";"
317
+
318
+ You can also override the default used in
319
+ {FriendlyId::Configuration::DEFAULTS} to set the value for any model using
320
+ FriendlyId. If you change this value in an existing application, be sure to
321
+ {file:Guide.md#regenerating_slugs regenerate the slugs} afterwards.
322
+
323
+ ## Reserved Words
324
+
325
+ You can configure a list of strings as reserved so that, for example, you
326
+ don't end up with this problem:
327
+
328
+ /users/joe-schmoe # A user chose "joe schmoe" as his user name - no worries.
329
+ /users/new # A user chose "new" as his user name, and now no one can sign up.
330
+
331
+ Reserved words are configured using the `:reserved_words` option:
332
+
333
+ class Restaurant < ActiveRecord::Base
334
+ belongs_to :city
335
+ has_friendly_id :name, :use_slug => true, :reserved_words => ["my", "values"]
336
+ end
337
+
338
+ The strings "new" and "index" are reserved by default. When you attempt to
339
+ store a reserved value, FriendlyId raises a
340
+ {FriendlyId::ReservedError}. You can also override the default
341
+ reserved words in {FriendlyId::Configuration::DEFAULTS} to set the value for any
342
+ model using FriendlyId.
343
+
344
+ ## Caching the FriendlyId Slug for Better Performance
345
+
346
+ Checking the slugs table all the time has an impact on performance, so as of
347
+ 2.2.0, FriendlyId offers slug caching.
348
+
349
+ ### Automatic setup
350
+
351
+ To enable slug caching, simply add a column named "cached_slug" to your model.
352
+ Is also advised to index this column for performance reason.
353
+ FriendlyId will automatically use this column if it detects it:
354
+
355
+ class AddCachedSlugToUsers < ActiveRecord::Migration
356
+ def self.up
357
+ add_column :users, :cached_slug, :string
358
+ add_index :users, :cached_slug
359
+ end
360
+
361
+ def self.down
362
+ remove_column :users, :cached_slug
363
+ end
364
+ end
365
+
366
+ Then, redo the slugs:
367
+
368
+ rake friendly_id:redo_slugs MODEL=User
369
+
370
+ FriendlyId will also query against the cache column if it's available, which
371
+ can significantly improve the performance of many queries, particularly when
372
+ passing an array of friendly ids to #find.
373
+
374
+ A few warnings when using this feature:
375
+
376
+ * *DO NOT* forget to redo the slugs, or else this feature will not work!
377
+ * This feature uses `attr_protected` to protect the `cached_slug` column,
378
+ unless you have already invoked `attr_accessible`. If you wish to use
379
+ `attr_accessible`, you must invoke it BEFORE you invoke `has_friendly_id` in
380
+ your class.
381
+ * FriendlyId can not query against the slug cache when you pass a :scope
382
+ argument to #find. Try to avoid passing an array of friendly id's and a
383
+ scope to #find, as this will result in weak performance.
384
+
385
+ ### Using a custom column name
386
+
387
+ You can also use a different name for the column if you choose, via the
388
+ `:cache_column` config option:
389
+
390
+ class User < ActiveRecord::Base
391
+ has_friendly_id :name, :use_slug => true, :cache_column => 'my_cached_slug'
392
+ end
393
+
394
+
395
+ ## Nil slugs and skipping validations
396
+
397
+ You can choose to allow `nil` friendly_ids via the `:allow_nil` config option:
398
+
399
+ class User < ActiveRecord::Base
400
+ has_friendly_id :name, :allow_nil => true
401
+ end
402
+
403
+ This works whether the model uses slugs or not.
404
+
405
+ For slugged models, if the friendly_id text is `nil`, no slug will be created. This can be useful, for example, to only create slugs for published articles and avoid creating many slugs with sequences.
406
+
407
+ For models that don't use slugs, this will make FriendlyId skip all its validations when the friendly_id text is `nil`. This can be useful, for example, if you wish to add the friendly_id value in an `:after_save` callback.
408
+
409
+ For non-slugged models, if you simply wish to skip friendly_ids's validations
410
+ for some reason, you can override the `skip_friendly_id_validations` method.
411
+ Note that this method is **not** used by slugged models.
412
+
413
+ ## Scoped Slugs
414
+
415
+ FriendlyId can generate unique slugs within a given scope. For example, assume
416
+ you have an application that displays restaurants. Without scoped slugs, if
417
+ two restaurants are named "Joe's Diner," the second one will end up with
418
+ "joes-diner--2" as its friendly_id. Using scoped allows you to keep the
419
+ slug names unique for each city, so that the second "Joe's Diner" could have
420
+ the slug "joes-diner" if it's located in a different city:
421
+
422
+ class Restaurant < ActiveRecord::Base
423
+ belongs_to :city
424
+ has_friendly_id :name, :use_slug => true, :scope => :city
425
+ end
426
+
427
+ class City < ActiveRecord::Base
428
+ has_many :restaurants
429
+ has_friendly_id :name, :use_slug => true
430
+ end
431
+
432
+ http://example.org/cities/seattle/restaurants/joes-diner
433
+ http://example.org/cities/chicago/restaurants/joes-diner
434
+
435
+ Restaurant.find("joes-diner", :scope => "seattle") # returns 1 record
436
+ Restaurant.find("joes-diner", :scope => "chicago") # returns 1 record
437
+ Restaurant.find("joes-diner") # returns both records
438
+
439
+ The value for the `:scope` key in your model can be a custom method you
440
+ define, or the name of a relation. If it's the name of a relation, then the
441
+ scope's text value will be the result of calling `to_param` on the related
442
+ model record. In the example above, the city model also uses FriendlyId and so
443
+ its `to_param` method returns its friendly_id: "chicago" or "seattle".
444
+
445
+ ### Updating a Relation's Scoped Slugs
446
+
447
+ When using a relation as the scope, updating the relation will update the
448
+ slugs, but only if both models have specified the relationship. In the above
449
+ example, updates to City will update the slugs for Restaurant because City
450
+ specifies that it `has_many :restaurants`.
451
+
452
+ ### Routes for Scoped Models
453
+
454
+ Note that FriendlyId does not set up any routes for scoped models; you must
455
+ do this yourself in your application. Here's an example of one way to set
456
+ this up:
457
+
458
+ # in routes.rb
459
+ map.resources :restaurants
460
+ map.restaurant "/restaurants/:scope/:id", :controller => "restaurants"
461
+
462
+ # in views
463
+ link_to 'Show', restaurant_path(restaurant.city, restaurant)
464
+
465
+ # in controllers
466
+ @restaurant = Restaurant.find(params[:id], :scope => params[:scope])
467
+
468
+
469
+ ## FriendlyId Rake Tasks
470
+
471
+ FriendlyId provides several tasks to help maintain your application.
472
+
473
+ ### Generating New Slugs For the First Time
474
+
475
+ friendly_id:make_slugs MODEL=<model name>
476
+
477
+ Use this task to generate slugs after installing FriendlyId in a new
478
+ application.
479
+
480
+ ### Regenerating Slugs
481
+
482
+ friendly_id:redo_slugs MODEL=<model name>
483
+
484
+ Use this task to regenerate slugs after making any changes to your model's
485
+ FriendlyId configuration options that affect slug generation. For example,
486
+ if you introduce a `cached_slug` column or change the `:seqence_separator`.
487
+
488
+ ### Deleting Old Slugs
489
+
490
+ rake friendly_id:remove_old_slugs MODEL=<model name> DAYS=<days>
491
+
492
+ Use this task if you wish to delete expired slugs; manually or perhaps via
493
+ cron. If you don't specify the days option, the default is to remove unused
494
+ slugs older than 45 days.
495
+
496
+ # Misc tips
497
+
498
+ ## MySQL 5.0 or less
499
+
500
+ Currently, the default FriendlyId migration will not work with MySQL 5.0 or less
501
+ because it creates and index that's too large. The easiest way to work around
502
+ this is to change the generated migration to add limits on some column lengths.
503
+ Please see [this issue](http://github.com/norman/friendly_id/issues#issue/50) in
504
+ the FriendlyId issue tracker for more information.
505
+
506
+ # Hacking FriendlyId
507
+
508
+ A couple of notes for programmers intending to work on FriendlyId:
509
+
510
+ If you intend to send a pull request, in general it's best to make minor
511
+ changes in the master branch, and major changes in the edge branch.
512
+
513
+ Before removing any public or protected methods, FriendlyId will deprecate
514
+ them through one major release cycle. Private methods may, however, change at
515
+ any time.
516
+
517
+ ## Running the Tests
518
+
519
+ FriendlyId uses [Bundler](http://github.com/carlhuda/bundler) to manage its gem
520
+ dependencies. To run the tests, first make sure you have bundler installed.
521
+ Then, copy Gemfile.default to Gemfile. If you wish to test against different gem
522
+ versions than the ones specified in the Gemfile (for example, to test with
523
+ Postgres or MySQL rather than SQLite3, or to test against different versions of
524
+ ActiveRecord), then simply modify the Gemfile to suit your dependencies.
525
+
526
+ ## Some Benchmarks
527
+
528
+ These benchmarks can give you an idea of FriendlyId's impact on the
529
+ performance of your application. Of course your results may vary.
530
+
531
+ Note that much of the performance difference can be attributed to finding an
532
+ SQL record by a text column. Finding a single record by numeric primary key is
533
+ always the fastest operation, and thus the best choice when possible. If you
534
+ decide not to use FriendlyId for performance reasons, keep in mind that your
535
+ own solution is unlikely to be any faster than FriendlyId with cached slugs
536
+ enabled. But if it is, then your patches would be very welcome!
537
+
538
+ ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-darwin10.2.0]
539
+ activerecord (2.3.5)
540
+ friendly_id (2.3.2)
541
+ sqlite3 3.6.19 in-memory database
542
+ | DEFAULT | NO_SLUG | SLUG | CACHED_SLUG |
543
+ ------------------------------------------------------------------------------------------------
544
+ find model by id x1000 | 0.274 | 0.426 | 0.780 | 0.489 |
545
+ find model using array of ids x1000 | 0.517 | 0.525 | 1.692 | 0.623 |
546
+ find model using id, then to_param x1000 | 0.279 | 0.431 | 1.253 | 0.498 |
547
+ ================================================================================================
548
+ Total | 1.071 | 1.382 | 3.725 | 1.610 |
549
+
550
+ ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-darwin10.2.0]
551
+ activerecord (3.0.0.beta1)
552
+ friendly_id (2.3.2)
553
+ sqlite3 3.6.19 in-memory database
554
+
555
+ | DEFAULT | NO_SLUG | SLUG | CACHED_SLUG |
556
+ ------------------------------------------------------------------------------------------------
557
+ find model by id x1000 | 0.557 | 1.135 | 6.491 | 1.398 |
558
+ find model using array of ids x1000 | 0.862 | 0.882 | 6.152 | 1.919 |
559
+ find model using id, then to_param x1000 | 0.658 | 2.200 | 8.398 | 1.539 |
560
+ ================================================================================================
561
+ Total | 2.077 | 4.217 | 21.041 | 4.856 |