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
@@ -1,348 +0,0 @@
1
- # encoding: utf-8
2
- require File.dirname(__FILE__) + '/test_helper'
3
-
4
- class SluggedModelTest < Test::Unit::TestCase
5
-
6
- context "A slugged model with default FriendlyId options" do
7
-
8
- setup do
9
- Post.friendly_id_options = FriendlyId::DEFAULT_OPTIONS.merge(:method => :name, :use_slug => true)
10
- @post = Post.new :name => "Test post", :published => true
11
- @post.save!
12
- end
13
-
14
- teardown do
15
- Post.delete_all
16
- Person.delete_all
17
- Place.delete_all
18
- Slug.delete_all
19
- end
20
-
21
- should "have friendly_id options" do
22
- assert_not_nil Post.friendly_id_options
23
- end
24
-
25
- should "have a slug" do
26
- assert_not_nil @post.slug
27
- end
28
-
29
- should "be findable by its friendly_id" do
30
- assert Post.find(@post.friendly_id)
31
- end
32
-
33
- should "be findable by its regular id" do
34
- assert Post.find(@post.id)
35
- end
36
-
37
- should "be findable by its regular id as a string" do
38
- assert Post.find(@post.id.to_s)
39
- end
40
-
41
- should "be findable by its instance" do
42
- assert Post.find(@post)
43
- end
44
-
45
- should "not be findable by its id if looking for something else" do
46
- assert_raises ActiveRecord::RecordNotFound do
47
- Post.find("#{@post.id}-i-dont-exists")
48
- end
49
- end
50
-
51
- should "generate slug text" do
52
- post = Post.new :name => "Test post"
53
- assert_not_nil post.slug_text
54
- end
55
-
56
- should "respect finder conditions" do
57
- assert_raises ActiveRecord::RecordNotFound do
58
- Post.find(@post.friendly_id, :conditions => "1 = 2")
59
- end
60
- end
61
-
62
- should "raise an error if the friendly_id text is reserved" do
63
- assert_raises(FriendlyId::SlugGenerationError) do
64
- Post.create!(:name => "new")
65
- end
66
- end
67
-
68
- should "raise an error if the friendly_id text is an empty string" do
69
- assert_raises(FriendlyId::SlugGenerationError) do
70
- Post.create(:name => "")
71
- end
72
- end
73
-
74
- should "raise an error if the friendly_id text is nil" do
75
- assert_raises(FriendlyId::SlugGenerationError) do
76
- Post.create(:name => nil)
77
- end
78
- end
79
-
80
- should "raise an error if the normalized friendly id becomes blank" do
81
- assert_raises(FriendlyId::SlugGenerationError) do
82
- post = Post.create!(:name => "-.-")
83
- end
84
- end
85
-
86
- should "not make a new slug unless the friendly_id method value has changed" do
87
- @post.published = !@post.published
88
- @post.save!
89
- assert_equal 1, @post.slugs.size
90
- end
91
-
92
- should "make a new slug if the friendly_id method value has changed" do
93
- @post.name = "Changed title"
94
- @post.save!
95
- assert_equal 2, @post.slugs.size
96
- end
97
-
98
- should "have a slug sequence of 1 by default" do
99
- assert_equal 1, @post.slug.sequence
100
- end
101
-
102
- should "increment sequence for duplicate slug names" do
103
- @post2 = Post.create! :name => @post.name
104
- assert_equal 2, @post2.slug.sequence
105
- end
106
-
107
- should "have a friendly_id that terminates with -- and the slug sequence if the sequence is greater than 1" do
108
- @post2 = Post.create! :name => @post.name
109
- assert_match(/--2\z/, @post2.friendly_id)
110
- end
111
-
112
- should "allow datetime columns to be used as slugs" do
113
- assert Event.create(:name => "Test", :event_date => DateTime.now)
114
- end
115
-
116
- should "not strip diacritics" do
117
- post = Post.new(:name => "¡Feliz año!")
118
- assert_match(/#{'ñ'}/, post.slug_text)
119
- end
120
-
121
- should "not convert to ASCII" do
122
- post = Post.new(:name => "katakana: ゲコゴサザシジ")
123
- assert_equal "katakana-ゲコゴサザシジ", post.slug_text
124
- end
125
-
126
- should "allow the same friendly_id across models" do
127
- district = District.create!(:name => @post.name)
128
- assert_equal district.friendly_id, @post.friendly_id
129
- end
130
-
131
- should "truncate slug text longer than the max length" do
132
- post = Post.new(:name => "a" * (Post.friendly_id_options[:max_length] + 1))
133
- assert_equal post.slug_text.length, Post.friendly_id_options[:max_length]
134
- end
135
-
136
- should "truncate slug in 'right way' when slug is unicode" do
137
- post = Post.new(:name => "ё" * 100 + 'ю' *(Post.friendly_id_options[:max_length] - 100 + 1))
138
- assert_equal post.slug_text.mb_chars[-1], 'ю'
139
- end
140
-
141
- should "be able to reuse an old friendly_id without incrementing the sequence" do
142
- old_title = @post.name
143
- old_friendly_id = @post.friendly_id
144
- @post.name = "A changed title"
145
- @post.save!
146
- @post.name = old_title
147
- @post.save!
148
- assert_equal old_friendly_id, @post.friendly_id
149
- end
150
-
151
- should "allow eager loading of slugs" do
152
- assert_nothing_raised do
153
- Post.find(@post.friendly_id, :include => :slugs)
154
- end
155
- end
156
-
157
- # This emulates a fairly common issue where id's generated by fixtures are very high.
158
- should "continue to admit very large ids" do
159
- Person.connection.execute("INSERT INTO people (id, name) VALUES (2147483647, 'Joe Schmoe')")
160
- assert Person.find(2147483647)
161
- end
162
-
163
- context "and configured to strip diacritics" do
164
- setup do
165
- Post.friendly_id_options = Post.friendly_id_options.merge(:strip_diacritics => true)
166
- end
167
-
168
- should "strip diacritics from Roman alphabet based characters" do
169
- post = Post.new(:name => "¡Feliz año!")
170
- assert_no_match(/#{'ñ'}/, post.slug_text)
171
- end
172
-
173
- should "raise an error if the friendly_id text is an empty string" do
174
- assert_raises(FriendlyId::SlugGenerationError) do
175
- Post.create(:name => "")
176
- end
177
- end
178
-
179
- should "raise an error if the friendly_id text is nil" do
180
- assert_raises(FriendlyId::SlugGenerationError) do
181
- Post.create(:name => nil)
182
- end
183
- end
184
-
185
- end
186
-
187
- context "and configured to convert to ASCII" do
188
- setup do
189
- Post.friendly_id_options = Post.friendly_id_options.merge(:strip_non_ascii => true)
190
- end
191
-
192
- should "strip non-ascii characters" do
193
- post = Post.new(:name => "katakana: ゲコゴサザシジ")
194
- assert_equal "katakana", post.slug_text
195
- end
196
- end
197
-
198
- context "that uses a custom table name" do
199
- should "support normal CRUD operations" do
200
- assert thing = Place.create!(:name => "a name")
201
- thing.name = "a new name"
202
- assert thing.save!
203
- assert thing.destroy
204
- end
205
- end
206
-
207
- context "that doesn't have a slug" do
208
-
209
- setup do
210
- @post.slug.destroy
211
- @post = Post.find(@post.id)
212
- end
213
-
214
- should "have a to_param method that returns the id cast to a string" do
215
- assert_equal @post.id.to_s, @post.to_param
216
- end
217
-
218
- end
219
-
220
- context "when found using its friendly_id" do
221
- setup do
222
- @post = Post.find(@post.friendly_id)
223
- end
224
-
225
- should "indicate that it was found using the friendly_id" do
226
- assert @post.found_using_friendly_id?
227
- end
228
-
229
- should "not indicate that it has a better id" do
230
- assert !@post.has_better_id?
231
- end
232
-
233
- should "not indicate that it was found using its numeric id" do
234
- assert !@post.found_using_numeric_id?
235
- end
236
-
237
- should "have a finder slug" do
238
- assert_not_nil @post.finder_slug
239
- end
240
-
241
- end
242
-
243
- context "when found using its regular id" do
244
- setup do
245
- @post = Post.find(@post.id)
246
- end
247
-
248
- should "indicate that it was not found using the friendly id" do
249
- assert !@post.found_using_friendly_id?
250
- end
251
-
252
- should "indicate that it has a better id" do
253
- assert @post.has_better_id?
254
- end
255
-
256
- should "indicate that it was found using its numeric id" do
257
- assert @post.found_using_numeric_id?
258
- end
259
-
260
- should "not have a finder slug" do
261
- assert_nil @post.finder_slug
262
- end
263
-
264
- end
265
-
266
- context "when found using an outdated friendly id" do
267
- setup do
268
- old_id = @post.friendly_id
269
- @post.name = "Title changed"
270
- @post.save!
271
- @post = Post.find(old_id)
272
- end
273
-
274
- should "indicate that it was found using a friendly_id" do
275
- assert @post.found_using_friendly_id?
276
- end
277
-
278
- should "indicate that it has a better id" do
279
- assert @post.has_better_id?
280
- end
281
-
282
- should "not indicate that it was found using its numeric id" do
283
- assert !@post.found_using_numeric_id?
284
- end
285
-
286
- should "should have a finder slug different from its default slug" do
287
- assert_not_equal @post.slug, @post.finder_slug
288
- end
289
-
290
- end
291
-
292
- context "when table does not exist" do
293
- should "not raise an error when doing friendly_id setup" do
294
- assert_nothing_raised do
295
- Question.has_friendly_id :title, :use_slug => true
296
- end
297
- end
298
- end
299
-
300
- context "when using an array as the find argument" do
301
-
302
- setup do
303
- @post2 = Post.create!(:name => "another post", :published => true)
304
- end
305
-
306
- should "return results when passed an array of non-friendly ids" do
307
- assert_equal 2, Post.find([@post.id, @post2.id]).size
308
- end
309
-
310
- should "return results when passed an array of friendly ids" do
311
- assert_equal 2, Post.find([@post.friendly_id, @post2.friendly_id]).size
312
- end
313
-
314
- should "return results when searching using a named scope" do
315
- assert_equal 2, Post.published.find([@post.id, @post2.id]).size
316
- end
317
-
318
- should "return results when passed a mixed array of friendly and non-friendly ids" do
319
- assert_equal 2, Post.find([@post.friendly_id, @post2.id]).size
320
- end
321
-
322
- should "return results when passed an array of non-friendly ids, of which one represents a record with multiple slugs" do
323
- @post2.update_attributes(:name => 'another post [updated]')
324
- assert_equal 2, Post.find([@post.id, @post2.id]).size
325
- end
326
-
327
- should "indicate that the results were found using a friendly_id" do
328
- @posts = Post.find [@post.friendly_id, @post2.friendly_id]
329
- @posts.each { |p| assert p.found_using_friendly_id? }
330
- end
331
-
332
- should "raise an error when all records are not found" do
333
- assert_raises(ActiveRecord::RecordNotFound) do
334
- Post.find([@post.friendly_id, 'non-existant-slug-record'])
335
- end
336
- end
337
-
338
- should "allow eager loading of slugs" do
339
- assert_nothing_raised do
340
- Post.find([@post.friendly_id, @post2.friendly_id], :include => :slugs)
341
- end
342
- end
343
-
344
- end
345
-
346
- end
347
-
348
- end
data/test/sti_test.rb DELETED
@@ -1,49 +0,0 @@
1
- require File.dirname(__FILE__) + '/test_helper'
2
-
3
- class STIModelTest < Test::Unit::TestCase
4
-
5
- context "A slugged model using single table inheritance" do
6
-
7
- setup do
8
- Novel.friendly_id_options = FriendlyId::DEFAULT_OPTIONS.merge(:method => :name, :use_slug => true)
9
- @novel = Novel.new :name => "Test novel"
10
- @novel.save!
11
- end
12
-
13
- teardown do
14
- Novel.delete_all
15
- Slug.delete_all
16
- end
17
-
18
- should "have a slug" do
19
- assert_not_nil @novel.slug
20
- end
21
-
22
- context "found by its friendly id" do
23
-
24
- setup do
25
- @novel = Novel.find(@novel.friendly_id)
26
- end
27
-
28
- should "not indicate that it has a better id" do
29
- assert !@novel.has_better_id?
30
- end
31
-
32
- end
33
-
34
-
35
- context "found by its numeric id" do
36
-
37
- setup do
38
- @novel = Novel.find(@novel.id)
39
- end
40
-
41
- should "indicate that it has a better id" do
42
- assert @novel.has_better_id?
43
- end
44
-
45
- end
46
-
47
- end
48
-
49
- end
data/test/tasks_test.rb DELETED
@@ -1,105 +0,0 @@
1
- require File.dirname(__FILE__) + '/test_helper'
2
- require "friendly_id/tasks"
3
-
4
- class TasksTest < Test::Unit::TestCase
5
-
6
- context "FriendlyId tasks" do
7
-
8
- should "parse a top-level class name and return a class" do
9
- assert_equal String, FriendlyId::Tasks.parse_class_name("String")
10
- end
11
-
12
- should "parse a namespaced class name and return a class" do
13
- assert_equal Test::Unit, FriendlyId::Tasks.parse_class_name("Test::Unit")
14
- end
15
-
16
- end
17
-
18
- context "The 'make slugs' task" do
19
-
20
- setup do
21
- City.create! :name => "Buenos Aires"
22
- City.create! :name => "Rio de Janeiro"
23
- City.create! :name => "Tokyo"
24
- City.create! :name => "Nairobi"
25
- Slug.delete_all
26
- end
27
-
28
- teardown do
29
- City.delete_all
30
- Slug.delete_all
31
- end
32
-
33
- should "make one slug per model" do
34
- assert_equal 0, Slug.count
35
- FriendlyId::Tasks.make_slugs("City")
36
- assert_equal 4, Slug.count
37
- end
38
-
39
- end
40
-
41
- context "The 'delete_slugs_for' task" do
42
-
43
- setup do
44
- @post = Post.create! :name => "Slugs Considered Harmful"
45
- @city = City.create! :name => "Buenos Aires"
46
- end
47
-
48
- teardown do
49
- Post.delete_all
50
- City.delete_all
51
- Slug.delete_all
52
- end
53
-
54
- should "Delete only slugs for the specified model" do
55
- assert_equal 2, Slug.count
56
- FriendlyId::Tasks.delete_slugs_for("City")
57
- assert_equal 1, Slug.count
58
- end
59
-
60
- should "set the cached_slug column to NULL" do
61
- District.create! :name => "Garment"
62
- FriendlyId::Tasks.delete_slugs_for("District")
63
- assert_nil District.first.cached_slug
64
- end
65
-
66
- end
67
-
68
- context "The 'delete_old_slugs' task" do
69
-
70
- setup do
71
- @post = Post.create! :name => "Slugs Considered Harmful"
72
- @city = City.create! :name => "Buenos Aires"
73
- City.connection.execute "UPDATE slugs SET created_at = '%s' WHERE id = %d" % [
74
- 45.days.ago.strftime("%Y-%m-%d"), @city.slug.id]
75
- @city.name = "Ciudad de Buenos Aires"
76
- @city.save!
77
- end
78
-
79
- teardown do
80
- Post.delete_all
81
- City.delete_all
82
- Slug.delete_all
83
- end
84
-
85
- should "delete slugs older than 45 days by default" do
86
- assert_equal 3, Slug.count
87
- FriendlyId::Tasks.delete_old_slugs
88
- assert_equal 2, Slug.count
89
- end
90
-
91
- should "respect the days argument" do
92
- assert_equal 3, Slug.count
93
- FriendlyId::Tasks.delete_old_slugs(100)
94
- assert_equal 3, Slug.count
95
- end
96
-
97
- should "respect the class argument" do
98
- assert_equal 3, Slug.count
99
- FriendlyId::Tasks.delete_old_slugs(1, "Post")
100
- assert_equal 3, Slug.count
101
- end
102
-
103
- end
104
-
105
- end