friendly_id 2.2.7 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (73) hide show
  1. data/Changelog.md +225 -0
  2. data/Contributors.md +28 -0
  3. data/Guide.md +509 -0
  4. data/LICENSE +1 -1
  5. data/README.md +76 -0
  6. data/Rakefile +48 -15
  7. data/extras/bench.rb +59 -0
  8. data/extras/extras.rb +31 -0
  9. data/extras/prof.rb +14 -0
  10. data/extras/template-gem.rb +1 -1
  11. data/extras/template-plugin.rb +1 -1
  12. data/generators/friendly_id/friendly_id_generator.rb +1 -1
  13. data/generators/friendly_id/templates/create_slugs.rb +2 -2
  14. data/lib/friendly_id.rb +54 -63
  15. data/lib/friendly_id/active_record2.rb +47 -0
  16. data/lib/friendly_id/active_record2/configuration.rb +66 -0
  17. data/lib/friendly_id/active_record2/finders.rb +140 -0
  18. data/lib/friendly_id/active_record2/simple_model.rb +162 -0
  19. data/lib/friendly_id/active_record2/slug.rb +111 -0
  20. data/lib/friendly_id/active_record2/slugged_model.rb +317 -0
  21. data/lib/friendly_id/active_record2/tasks.rb +66 -0
  22. data/lib/friendly_id/active_record2/tasks/friendly_id.rake +19 -0
  23. data/lib/friendly_id/configuration.rb +132 -0
  24. data/lib/friendly_id/finders.rb +106 -0
  25. data/lib/friendly_id/slug_string.rb +292 -0
  26. data/lib/friendly_id/slugged.rb +91 -0
  27. data/lib/friendly_id/status.rb +35 -0
  28. data/lib/friendly_id/test.rb +168 -0
  29. data/lib/friendly_id/version.rb +5 -5
  30. data/rails/init.rb +2 -0
  31. data/test/active_record2/basic_slugged_model_test.rb +14 -0
  32. data/test/active_record2/cached_slug_test.rb +61 -0
  33. data/test/active_record2/core.rb +93 -0
  34. data/test/active_record2/custom_normalizer_test.rb +20 -0
  35. data/test/active_record2/custom_table_name_test.rb +22 -0
  36. data/test/active_record2/scoped_model_test.rb +111 -0
  37. data/test/active_record2/simple_test.rb +59 -0
  38. data/test/active_record2/slug_test.rb +34 -0
  39. data/test/active_record2/slugged.rb +30 -0
  40. data/test/active_record2/slugged_status_test.rb +61 -0
  41. data/test/active_record2/sti_test.rb +22 -0
  42. data/test/active_record2/support/database.mysql.yml +4 -0
  43. data/test/{support/database.yml.postgres → active_record2/support/database.postgres.yml} +0 -0
  44. data/test/{support/database.yml.sqlite3 → active_record2/support/database.sqlite3.yml} +0 -0
  45. data/test/{support → active_record2/support}/models.rb +28 -0
  46. data/test/active_record2/tasks_test.rb +82 -0
  47. data/test/active_record2/test_helper.rb +107 -0
  48. data/test/friendly_id_test.rb +23 -0
  49. data/test/slug_string_test.rb +74 -0
  50. data/test/test_helper.rb +7 -102
  51. metadata +64 -56
  52. data/History.txt +0 -194
  53. data/README.rdoc +0 -385
  54. data/generators/friendly_id_20_upgrade/friendly_id_20_upgrade_generator.rb +0 -12
  55. data/generators/friendly_id_20_upgrade/templates/upgrade_friendly_id_to_20.rb +0 -19
  56. data/init.rb +0 -1
  57. data/lib/friendly_id/helpers.rb +0 -12
  58. data/lib/friendly_id/non_sluggable_class_methods.rb +0 -34
  59. data/lib/friendly_id/non_sluggable_instance_methods.rb +0 -45
  60. data/lib/friendly_id/slug.rb +0 -98
  61. data/lib/friendly_id/sluggable_class_methods.rb +0 -110
  62. data/lib/friendly_id/sluggable_instance_methods.rb +0 -161
  63. data/lib/friendly_id/tasks.rb +0 -56
  64. data/lib/tasks/friendly_id.rake +0 -25
  65. data/lib/tasks/friendly_id.rb +0 -1
  66. data/test/cached_slug_test.rb +0 -109
  67. data/test/custom_slug_normalizer_test.rb +0 -36
  68. data/test/non_slugged_test.rb +0 -99
  69. data/test/scoped_model_test.rb +0 -64
  70. data/test/slug_test.rb +0 -105
  71. data/test/slugged_model_test.rb +0 -348
  72. data/test/sti_test.rb +0 -49
  73. data/test/tasks_test.rb +0 -105
data/README.rdoc DELETED
@@ -1,385 +0,0 @@
1
- = FriendlyId
2
-
3
- FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
4
- Ruby on Rails. It allows you to create pretty URL's and work with
5
- human-friendly strings as if they were numeric ids for ActiveRecord models.
6
-
7
- Using FriendlyId, it's easy to make your application use URL's like:
8
-
9
- http://example.com/states/washington
10
-
11
- instead of:
12
-
13
- http://example.com/states/4323454
14
-
15
- You can always read the latest version of this document at the {friendly_id page on
16
- rdoc.info}[http://rdoc.info/projects/norman/friendly_id].
17
-
18
- === Why?
19
-
20
- * Text-based id's look better
21
- * They make URL's easier to remember.
22
- * They give no hint about the number of records in your database.
23
- * They are better for search engine optimization.
24
-
25
- === But...
26
-
27
- * They can change, breaking your URL's and your SEO.
28
- * It can be tricky to ensure they're always unique.
29
- * They can become a pain to manage in large Rails applications.
30
- * They can conflict with your application's namespace.
31
-
32
- FriendlyId tries to offer you the all the advantages, and avoid or soften the
33
- potential impact of the disadvantages.
34
-
35
- == Typical Uses
36
-
37
- === User names ("non-slugged" models)
38
-
39
- Usually users have unique user names stored in a column with a unique
40
- constraint or index. In this case, all you need to do is add this to your
41
- model:
42
-
43
- has_friendly_id :login
44
-
45
- and you can then write code like this:
46
-
47
- @member = Member.find("joe") # the old Member.find(1) still works, too.
48
- @member.to_param # returns "joe"
49
- redirect_to @member # The URL would be /members/joe
50
-
51
- === Blog posts ("slugged" models)
52
-
53
- Blog posts generally have titles which are distinctive but not necessarily
54
- unique. In this and similar cases, FriendlyId provides a Slug model separate
55
- from your Post model. The Slug model handles duplicate friendly_ids, as well
56
- as versioning.
57
-
58
- Your model code would look something like this:
59
-
60
- has_friendly_id :title, :use_slug => true
61
-
62
- and you can then write code like this:
63
-
64
- @post = Post.find("new-version-released") # Post.find(1) still works, too
65
- @post.to_param # returns "new-version-released"
66
- redirect_to @post # The URL would be /posts/new-version-released
67
-
68
- Now in your controllers, if you want to prevent people from accessing your
69
- models by numeric id, you can detect whether they were found by the
70
- friendly_id:
71
-
72
- @post = Post.find(params[:id])
73
- raise "some error" if !@post.found_using_friendly_id?
74
-
75
- or, you can 301 redirect if the model was found by the numeric id if you don't
76
- care about numeric access, but want the SEO value of the friendly_id:
77
-
78
- @post = Post.find(params[:id])
79
- redirect_to @post, :status => 301 if @post.has_better_id?
80
-
81
- The "has_better_id?" method returns true if the model was found with the
82
- numeric id, or with an outdated slug.
83
-
84
- == Extra Features
85
-
86
- === Slug Versioning
87
-
88
- FriendlyId will record changes to slugs so that you can tell when the model is
89
- found with an older slug, or by the numeric id. This can be useful if you want
90
- to do a 301 redirect to your updated URL.
91
-
92
- class PostsController < ApplicationController
93
-
94
- before_filter ensure_current_post_url, :only => :show
95
-
96
- ...
97
-
98
- def ensure_current_post_url
99
- redirect_to @post, :status => :moved_permanently if @post.has_better_id?
100
- end
101
-
102
- end
103
-
104
- This is particularly useful when implementing FrindlyId on an existing
105
- website that already has many URL's with the old numeric id listed on search
106
- engines. When the search engine spiders crawl your site, they will
107
- eventually pick up the new, more SEO-friendly URL's.
108
-
109
- === Non-unique Slug Names
110
-
111
- FriendlyId will append a arbitrary number to the end of the id to keep it
112
- unique if necessary:
113
-
114
- /posts/new-version-released
115
- /posts/new-version-released--2
116
- /posts/new-version-released--3
117
- ...
118
- etc.
119
-
120
- Note that the number is preceeded by two dashes to distinguish it from the
121
- rest of the slug. This is important to enable having slugs like:
122
-
123
- /cars/peugeot-206
124
- /cars/peugeot-206--2
125
-
126
- === Reserved Names
127
-
128
- You can mark off some strings as reserved so that, for example, you don't end
129
- up with this problem:
130
-
131
- /users/joe-schmoe # A user chose "joe schmoe" as his user name - no worries.
132
- /users/new # A user chose "new" as his user name, and now no one can sign up.
133
-
134
- Here's how to do it:
135
-
136
- class Restaurant < ActiveRecord::Base
137
- belongs_to :city
138
- has_friendly_id :name, :use_slug => true, :reserved => ["my", "values"]
139
- end
140
-
141
- As of FriendlyId version 2.0.2, "new" and "index" are reseved by default. When
142
- you attempt to store a reserved value, FriendlyId raises a
143
- FriendlyId::SlugGenerationError.
144
-
145
- === Cached slugs
146
-
147
- Checking the slugs table all the time has an impact on performance, so as of
148
- 2.3.0, FriendlyId offers slug caching.
149
-
150
- This feature can improve the performance of some views by about 25%, and reduce
151
- memory consumption by about 40% as compared to the same view without cached
152
- slugs. The biggest improvement will be for "index" type views with many links
153
- that depend on slugs to generate the URL.
154
-
155
- ==== Automagic setup:
156
-
157
- To enable slug caching, simply add a column named "cached_slug" to your model.
158
- FriendlyId will automagically use this column if it detects it:
159
-
160
- class AddCachedSlugToUsers < ActiveRecord::Migration
161
- def self.up
162
- add_column :users, :cached_slug, :string
163
- end
164
-
165
- def self.down
166
- remove_column :users, :cached_slug
167
- end
168
- end
169
-
170
- Then, redo the slugs:
171
-
172
- rake friendly_id:redo_slugs MODEL=User
173
-
174
- This feature exists largely to improve the performance of URL
175
- generation, the part of Rails where FriendlyId has the biggest
176
- performance impact. FriendlyId never queries against this column, so
177
- it's not necessary to add an index on it unless your application does.
178
-
179
- Two warnings when using this feature:
180
-
181
- *DO NOT* forget to redo the slugs, or else this feature will not work!
182
-
183
- Also, this feature uses +attr_protected+ to protect the cached_slug
184
- column, unless you have already invoked +attr_accessible+. So if you
185
- wish to use +attr_accessible+, you must invoke it BEFORE you invoke
186
- +has_friendly_id+ in your class.
187
-
188
- ==== Using a custom column name:
189
-
190
- You can also use a different name for the column if you choose, via the
191
- :cache_column config option:
192
-
193
- class User < ActiveRecord::Base
194
- has_friendly_id :name, :use_slug => true, :cache_column => 'my_cached_slug'
195
- end
196
-
197
- === Scoped Slugs
198
-
199
- FriendlyId can generate unique slugs within a given scope. For example:
200
-
201
- class Restaurant < ActiveRecord::Base
202
- belongs_to :city
203
- has_friendly_id :name, :use_slug => true, :scope => :city
204
- end
205
-
206
- class City < ActiveRecord::Base
207
- has_many :restaurants
208
- has_friendly_id :name, :use_slug => true
209
- end
210
-
211
- http://example.org/cities/seattle/restaurants/joes-diner
212
- http://example.org/cities/chicago/restaurants/joes-diner
213
-
214
- Restaurant.find("joes-diner", :scope => "seattle") # returns 1 record
215
- Restaurant.find("joes-diner", :scope => "chicago") # returns 1 record
216
- Restaurant.find("joes-diner") # returns both records
217
-
218
-
219
- The value for the :scope key in your model can be a custom method you define,
220
- or the name of a relation. If it's the name of a relation, then the scope's
221
- text value will be the result of calling <code>to_param</code> on the related
222
- model record. In the example above, the city model also uses FriendlyId and so
223
- its <code>to_param</code> method returns its friendly_id: chicago or seattle.
224
-
225
- This feature is new in FriendlyId 2 and should be considered of experimental
226
- quality. Please don't use this for code that needs to run on the Space
227
- Shuttle.
228
-
229
- === Text Normalization
230
-
231
- FriendlyId's slugging can strip diacritics from Western European characters,
232
- so that you can have ASCII-only URL's; for example, conveting "ñøîéçü" to
233
- "noiecu."
234
-
235
- has_friendly_id :title, :use_slug => true, :strip_diacritics => true
236
-
237
- If you are not using slugs, you'll have to do this manually for whatever value
238
- you're using as the friendly_id.
239
-
240
- === Diacritic-sensitive normalization
241
-
242
- FriendlyId can also normalize slug text while preserving accented characters, if
243
- you prefer to leave them in your URL's:
244
-
245
- has_friendly_id :title, :use_slug => true
246
- ...
247
- @post = Post.create(:title => "¡Feliz Año!")
248
- @post.friendly_id # "feliz-año"
249
-
250
- === Unicode URL's
251
-
252
- FriendlyId can generate slugs in any language that can be written with
253
- Unicode. It does its best to strip away punctuation regardless of the language
254
- being used. Since the authors only speak English, Spanish, Portuguese and
255
- German, this has not been extensively tested with anything like Chinese,
256
- Russian, Greek, etc, but it "should work." If you're a speaker of a language
257
- that uses a non-Roman writing system, your feedback would be most welcome.
258
-
259
- has_friendly_id :title, :use_slug => true
260
- ...
261
- @post = Post.create(:title => "友好编号在中国")
262
- @post.friendly_id # "友好编号在中国"
263
- @post2 = Post.create(:title => "友好编号在中国")
264
- @post2.friendly_id # "友好编号在中国--2"
265
-
266
- === Custom Slug Generation
267
-
268
- While FriendlyId's slug generation options work for most people, you may need
269
- something else. As of version 2.0.4 you can pass in your own custom slug
270
- generation block:
271
-
272
- class Post < ActiveRecord::Base
273
- has_friendly_id :title, :use_slug => true do |text|
274
- MySlugGeneratorClass::my_slug_method(text)
275
- end
276
- end
277
-
278
- FriendlyId will still respect your settings for max length and reserved words,
279
- but will use your block rather than the baked-in methods to normalize the
280
- friendly_id text.
281
-
282
- == Getting it
283
-
284
- FriendlyId is best installed as a Ruby Gem:
285
-
286
- gem install friendly_id
287
-
288
- Alternatively, you can install it as a Rails plugin, though this is
289
- discouraged:
290
-
291
- ./script/plugin install git://github.com/norman/friendly_id.git
292
-
293
- == Setting it up
294
-
295
- The current release works with Rails 2.2 and above, and is compatible with
296
- Ruby 1.8 and 1.9.
297
-
298
- 1) Install the Gem:
299
-
300
- sudo gem install friendly_id
301
- cd my_app
302
- script/generate friendly_id
303
- rake db:migrate
304
-
305
- 2) Add the gem to config/environment.rb:
306
-
307
- config.gem "friendly_id"
308
-
309
- 3) Add some code to your models:
310
-
311
- class Post < ActiveRecord::Base
312
- has_friendly_id :title, :use_slug => true
313
- end
314
-
315
- 4) If you are using slugs, you can use a Rake task to generate slugs for your
316
- existing records:
317
-
318
- rake friendly_id:make_slugs MODEL=MyModelName
319
-
320
- If you eventually want to expire old slugs every so often, or perhaps every
321
- day via cron, you can do:
322
-
323
- rake friendly_id:remove_old_slugs
324
-
325
- The default is to remove dead slugs older than 45 days, but is configurable:
326
-
327
- rake friendly_id:remove_old_slugs MODEL=MyModelName DAYS=60
328
-
329
- == Installing an older version
330
-
331
- You can download older versions of FriendlyId from Github that work with Rails <
332
- 2.2. These versions are, however, no longer maintained or supported.
333
-
334
- == Hacking FriendlyId:
335
-
336
- FriendlyId is {hosted on Github}[git://github.com/norman/friendly_id.git], and
337
- we love pull requests. :-)
338
-
339
- == Bugs:
340
-
341
- Please report them on the {Github issue tracker}[http://github.com/norman/friendly_id/issues]
342
- for this project.
343
-
344
- If you have a bug to report, please include the following information:
345
-
346
- * Stack trace and error message.
347
- * Version information for FriendlyId, Rails and Ruby.
348
- * Any snippets of relevant model, view or controller code that shows how your are using FriendlyId.
349
-
350
- If you are able to, it helps even more if you can fork FriendlyId on Github, and
351
- add a test that reproduces the error you are experiencing.
352
-
353
- == Credits:
354
-
355
- FriendlyId was created by {Norman Clarke}[mailto:norman@njclarke.com],
356
- {Adrian Mugnolo}[mailto:adrian@mugnolo.com], and {Emilio Tagua}[mailto:miloops@gmail.com].
357
-
358
- We are grateful for many contributions from the Ruby and Rails community, in
359
- particular from the following people:
360
-
361
- * Adam Cigánek
362
- * Alistair Holt
363
- * Andrew Loe III
364
- * Ben Woosley
365
- * Bence Nagy
366
- * Bruno Michel
367
- * Chris Nolan
368
- * David Ramalho
369
- * Diego Carrion
370
- * Diego R. V.
371
- * Florian Aßmann
372
- * Harry Love
373
- * Jesse Crouch
374
- * Joe Van Dyk
375
- * Josh Nichols
376
- * Mikhail Shirkov
377
- * Nathan Phelps
378
- * Rob Ingram
379
- * Sean Abrahams
380
- * Steve Luscher
381
- * Steven Noble
382
- * Tim Kadom
383
-
384
- Copyright (c) 2008 Norman Clarke, Adrian Mugnolo and Emilio Tagua, released
385
- under the MIT license.
@@ -1,12 +0,0 @@
1
- class FriendlyId20UpgradeGenerator < Rails::Generator::Base
2
- def manifest
3
- record do |m|
4
- unless options[:skip_migration]
5
- m.migration_template(
6
- 'upgrade_friendly_id_to_20.rb', 'db/migrate', :migration_file_name => 'upgrade_friendly_id_to_20'
7
- )
8
- m.file "/../../../lib/tasks/friendly_id.rake", "lib/tasks/friendly_id.rake"
9
- end
10
- end
11
- end
12
- end
@@ -1,19 +0,0 @@
1
- class UpgradeFriendlyIdTo20 < ActiveRecord::Migration
2
-
3
- def self.up
4
- remove_column :slugs, :updated_at
5
- remove_index :slugs, :column => [:name, :sluggable_type]
6
- add_column :slugs, :sequence, :integer, :null => false, :default => 1
7
- add_column :slugs, :scope, :string, :limit => 40
8
- add_index :slugs, [:name, :sluggable_type, :scope, :sequence], :unique => true, :name => "index_slugs_on_n_s_s_and_s"
9
- end
10
-
11
- def self.down
12
- remove_index :slugs, :name => "index_slugs_on_n_s_s_and_s"
13
- remove_column :slugs, :scope
14
- remove_column :slugs, :sequence
15
- add_column :slugs, :updated_at, :datetime
16
- add_index :slugs, [:name, :sluggable_type], :unique => true
17
- end
18
-
19
- end
data/init.rb DELETED
@@ -1 +0,0 @@
1
- require "friendly_id"
@@ -1,12 +0,0 @@
1
- module FriendlyId
2
-
3
- module Helpers
4
- # Calculate expected result size for find_some_with_friendly (taken from
5
- # active_record/base.rb)
6
- def expected_size(ids_and_names, options) #:nodoc:#
7
- size = options[:offset] ? ids_and_names.size - options[:offset] : ids_and_names.size
8
- size = options[:limit] if options[:limit] && size > options[:limit]
9
- size
10
- end
11
- end
12
- end
@@ -1,34 +0,0 @@
1
- module FriendlyId::NonSluggableClassMethods
2
-
3
- include FriendlyId::Helpers
4
-
5
- protected
6
-
7
- def find_one(id, options) #:nodoc:#
8
- if id.respond_to?(:to_str) && result = send("find_by_#{ friendly_id_options[:method] }", id.to_str, options)
9
- result.send(:found_using_friendly_id=, true)
10
- else
11
- result = super id, options
12
- end
13
- result
14
- end
15
-
16
- def find_some(ids_and_names, options) #:nodoc:#
17
-
18
- names, ids = ids_and_names.partition {|id_or_name| id_or_name.respond_to?(:to_str) && id_or_name.to_str }
19
- results = with_scope :find => options do
20
- find :all, :conditions => ["#{quoted_table_name}.#{primary_key} IN (?) OR #{friendly_id_options[:method]} IN (?)",
21
- ids, names]
22
- end
23
-
24
- expected = expected_size(ids_and_names, options)
25
- if results.size != expected
26
- raise ActiveRecord::RecordNotFound, "Couldn't find all #{ name.pluralize } with IDs (#{ ids_and_names * ', ' }) AND #{ sanitize_sql options[:conditions] } (found #{ results.size } results, but was looking for #{ expected })"
27
- end
28
-
29
- results.each {|r| r.send(:found_using_friendly_id=, true) if names.include?(r.friendly_id)}
30
-
31
- results
32
-
33
- end
34
- end