friendly_id 2.2.7 → 2.3.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.
- data/Changelog.md +225 -0
- data/Contributors.md +28 -0
- data/Guide.md +509 -0
- data/LICENSE +1 -1
- data/README.md +76 -0
- data/Rakefile +48 -15
- data/extras/bench.rb +59 -0
- data/extras/extras.rb +31 -0
- data/extras/prof.rb +14 -0
- data/extras/template-gem.rb +1 -1
- data/extras/template-plugin.rb +1 -1
- data/generators/friendly_id/friendly_id_generator.rb +1 -1
- data/generators/friendly_id/templates/create_slugs.rb +2 -2
- data/lib/friendly_id.rb +54 -63
- data/lib/friendly_id/active_record2.rb +47 -0
- data/lib/friendly_id/active_record2/configuration.rb +66 -0
- data/lib/friendly_id/active_record2/finders.rb +140 -0
- data/lib/friendly_id/active_record2/simple_model.rb +162 -0
- data/lib/friendly_id/active_record2/slug.rb +111 -0
- data/lib/friendly_id/active_record2/slugged_model.rb +317 -0
- data/lib/friendly_id/active_record2/tasks.rb +66 -0
- data/lib/friendly_id/active_record2/tasks/friendly_id.rake +19 -0
- data/lib/friendly_id/configuration.rb +132 -0
- data/lib/friendly_id/finders.rb +106 -0
- data/lib/friendly_id/slug_string.rb +292 -0
- data/lib/friendly_id/slugged.rb +91 -0
- data/lib/friendly_id/status.rb +35 -0
- data/lib/friendly_id/test.rb +168 -0
- data/lib/friendly_id/version.rb +5 -5
- data/rails/init.rb +2 -0
- data/test/active_record2/basic_slugged_model_test.rb +14 -0
- data/test/active_record2/cached_slug_test.rb +61 -0
- data/test/active_record2/core.rb +93 -0
- data/test/active_record2/custom_normalizer_test.rb +20 -0
- data/test/active_record2/custom_table_name_test.rb +22 -0
- data/test/active_record2/scoped_model_test.rb +111 -0
- data/test/active_record2/simple_test.rb +59 -0
- data/test/active_record2/slug_test.rb +34 -0
- data/test/active_record2/slugged.rb +30 -0
- data/test/active_record2/slugged_status_test.rb +61 -0
- data/test/active_record2/sti_test.rb +22 -0
- data/test/active_record2/support/database.mysql.yml +4 -0
- data/test/{support/database.yml.postgres → active_record2/support/database.postgres.yml} +0 -0
- data/test/{support/database.yml.sqlite3 → active_record2/support/database.sqlite3.yml} +0 -0
- data/test/{support → active_record2/support}/models.rb +28 -0
- data/test/active_record2/tasks_test.rb +82 -0
- data/test/active_record2/test_helper.rb +107 -0
- data/test/friendly_id_test.rb +23 -0
- data/test/slug_string_test.rb +74 -0
- data/test/test_helper.rb +7 -102
- metadata +64 -56
- data/History.txt +0 -194
- data/README.rdoc +0 -385
- data/generators/friendly_id_20_upgrade/friendly_id_20_upgrade_generator.rb +0 -12
- data/generators/friendly_id_20_upgrade/templates/upgrade_friendly_id_to_20.rb +0 -19
- data/init.rb +0 -1
- data/lib/friendly_id/helpers.rb +0 -12
- data/lib/friendly_id/non_sluggable_class_methods.rb +0 -34
- data/lib/friendly_id/non_sluggable_instance_methods.rb +0 -45
- data/lib/friendly_id/slug.rb +0 -98
- data/lib/friendly_id/sluggable_class_methods.rb +0 -110
- data/lib/friendly_id/sluggable_instance_methods.rb +0 -161
- data/lib/friendly_id/tasks.rb +0 -56
- data/lib/tasks/friendly_id.rake +0 -25
- data/lib/tasks/friendly_id.rb +0 -1
- data/test/cached_slug_test.rb +0 -109
- data/test/custom_slug_normalizer_test.rb +0 -36
- data/test/non_slugged_test.rb +0 -99
- data/test/scoped_model_test.rb +0 -64
- data/test/slug_test.rb +0 -105
- data/test/slugged_model_test.rb +0 -348
- data/test/sti_test.rb +0 -49
- data/test/tasks_test.rb +0 -105
data/lib/tasks/friendly_id.rake
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
namespace :friendly_id do
|
2
|
-
desc "Make slugs for a model."
|
3
|
-
task :make_slugs => :environment do
|
4
|
-
validate_model_given
|
5
|
-
FriendlyId::Tasks.make_slugs(ENV["MODEL"]) do |r|
|
6
|
-
puts "%s(%d) friendly_id set to '%s'" % [r.class.to_s, r.id, r.slug.name]
|
7
|
-
end
|
8
|
-
end
|
9
|
-
|
10
|
-
desc "Regenereate slugs for a model."
|
11
|
-
task :redo_slugs => :environment do
|
12
|
-
validate_model_given
|
13
|
-
FriendlyId::Tasks.delete_slugs_for(ENV["MODEL"])
|
14
|
-
Rake::Task["friendly_id:make_slugs"].invoke
|
15
|
-
end
|
16
|
-
|
17
|
-
desc "Kill obsolete slugs older than DAYS=45 days."
|
18
|
-
task :remove_old_slugs => :environment do
|
19
|
-
FriendlyId::Tasks.delete_old_slugs(ENV["DAYS"], ENV["MODEL"])
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def validate_model_given
|
24
|
-
raise 'USAGE: rake friendly_id:make_slugs MODEL=MyModelName' if ENV["MODEL"].nil?
|
25
|
-
end
|
data/lib/tasks/friendly_id.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
load 'tasks/friendly_id.rake'
|
data/test/cached_slug_test.rb
DELETED
@@ -1,109 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper'
|
2
|
-
|
3
|
-
class CachedSlugModelTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
context "A slugged model with a cached_slugs column" do
|
6
|
-
|
7
|
-
setup do
|
8
|
-
@paris = City.new(:name => "Paris")
|
9
|
-
@paris.save!
|
10
|
-
end
|
11
|
-
|
12
|
-
teardown do
|
13
|
-
City.delete_all
|
14
|
-
Slug.delete_all
|
15
|
-
end
|
16
|
-
|
17
|
-
should "have a slug" do
|
18
|
-
assert_not_nil @paris.slug
|
19
|
-
end
|
20
|
-
|
21
|
-
should "have a cached slug" do
|
22
|
-
assert_not_nil @paris.my_slug
|
23
|
-
end
|
24
|
-
|
25
|
-
should "have a to_param method that returns the cached slug" do
|
26
|
-
assert_equal "paris", @paris.to_param
|
27
|
-
end
|
28
|
-
|
29
|
-
should "protect the cached slug value" do
|
30
|
-
@paris.update_attributes(:my_slug => "Madrid")
|
31
|
-
@paris.reload
|
32
|
-
assert_equal "paris", @paris.my_slug
|
33
|
-
end
|
34
|
-
|
35
|
-
should "cache the incremented sequence for duplicate slug names" do
|
36
|
-
paris2 = City.create!(:name => "Paris")
|
37
|
-
assert_equal 2, paris2.slug.sequence
|
38
|
-
assert_equal "paris--2", paris2.my_slug
|
39
|
-
end
|
40
|
-
|
41
|
-
should "not update the cached slug column if it has not changed" do
|
42
|
-
@paris.population = 10_000_000
|
43
|
-
@paris.expects(:my_slug=).never
|
44
|
-
@paris.save
|
45
|
-
end
|
46
|
-
|
47
|
-
|
48
|
-
context "found by its friendly id" do
|
49
|
-
|
50
|
-
setup do
|
51
|
-
@paris = City.find(@paris.friendly_id)
|
52
|
-
end
|
53
|
-
|
54
|
-
should "not indicate that it has a better id" do
|
55
|
-
assert !@paris.has_better_id?
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
context "found by its numeric id" do
|
61
|
-
|
62
|
-
setup do
|
63
|
-
@paris = City.find(@paris.id)
|
64
|
-
end
|
65
|
-
|
66
|
-
should "indicate that it has a better id" do
|
67
|
-
assert @paris.has_better_id?
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
|
73
|
-
context "with a new slug" do
|
74
|
-
|
75
|
-
setup do
|
76
|
-
@paris.name = "Paris, France"
|
77
|
-
@paris.save!
|
78
|
-
@paris.reload
|
79
|
-
end
|
80
|
-
|
81
|
-
should "have its cached slug updated" do
|
82
|
-
assert_equal "paris-france", @paris.my_slug
|
83
|
-
end
|
84
|
-
|
85
|
-
should "have its cached slug synchronized with its friendly_id" do
|
86
|
-
assert_equal @paris.my_slug, @paris.friendly_id
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
90
|
-
|
91
|
-
|
92
|
-
context "with a cached_slug column" do
|
93
|
-
|
94
|
-
setup do
|
95
|
-
District.delete_all
|
96
|
-
@district = District.new(:name => "Latin Quarter")
|
97
|
-
@district.save!
|
98
|
-
end
|
99
|
-
|
100
|
-
should "have its cached_slug filled automatically" do
|
101
|
-
assert_equal @district.cached_slug, "latin-quarter"
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|
105
|
-
|
106
|
-
end
|
107
|
-
|
108
|
-
end
|
109
|
-
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper'
|
2
|
-
|
3
|
-
class CustomSlugNormalizerTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
context "A slugged model using a custom slug generator" do
|
6
|
-
|
7
|
-
setup do
|
8
|
-
Person.friendly_id_options = FriendlyId::DEFAULT_OPTIONS.merge(:method => :name, :use_slug => true)
|
9
|
-
end
|
10
|
-
|
11
|
-
teardown do
|
12
|
-
Person.delete_all
|
13
|
-
Slug.delete_all
|
14
|
-
end
|
15
|
-
|
16
|
-
should "invoke the block code" do
|
17
|
-
@person = Person.create!(:name => "Joe Schmoe")
|
18
|
-
assert_equal "JOE SCHMOE", @person.friendly_id
|
19
|
-
end
|
20
|
-
|
21
|
-
should "respect the max_length option" do
|
22
|
-
Person.friendly_id_options = Person.friendly_id_options.merge(:max_length => 3)
|
23
|
-
@person = Person.create!(:name => "Joe Schmoe")
|
24
|
-
assert_equal "JOE", @person.friendly_id
|
25
|
-
end
|
26
|
-
|
27
|
-
should "respect the reserved option" do
|
28
|
-
Person.friendly_id_options = Person.friendly_id_options.merge(:reserved => ["JOE"])
|
29
|
-
assert_raises FriendlyId::SlugGenerationError do
|
30
|
-
Person.create!(:name => "Joe")
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
data/test/non_slugged_test.rb
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper'
|
2
|
-
|
3
|
-
class NonSluggedTest < Test::Unit::TestCase
|
4
|
-
|
5
|
-
context "A non-slugged model with default FriendlyId options" do
|
6
|
-
|
7
|
-
setup do
|
8
|
-
@user = User.create!(:name => "joe")
|
9
|
-
end
|
10
|
-
|
11
|
-
teardown do
|
12
|
-
User.delete_all
|
13
|
-
end
|
14
|
-
|
15
|
-
should "have friendly_id options" do
|
16
|
-
assert_not_nil User.friendly_id_options
|
17
|
-
end
|
18
|
-
|
19
|
-
should "not have a slug" do
|
20
|
-
assert !@user.respond_to?(:slug)
|
21
|
-
end
|
22
|
-
|
23
|
-
should "be findable by its friendly_id" do
|
24
|
-
assert User.find(@user.friendly_id)
|
25
|
-
end
|
26
|
-
|
27
|
-
should "be findable by its regular id" do
|
28
|
-
assert User.find(@user.id)
|
29
|
-
end
|
30
|
-
|
31
|
-
should "respect finder conditions" do
|
32
|
-
assert_raises ActiveRecord::RecordNotFound do
|
33
|
-
User.find(@user.friendly_id, :conditions => "1 = 2")
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
should "indicate if it was found by its friendly id" do
|
38
|
-
user = User.find(@user.friendly_id)
|
39
|
-
assert user.found_using_friendly_id?
|
40
|
-
end
|
41
|
-
|
42
|
-
should "indicate if it was found by its numeric id" do
|
43
|
-
user = User.find(@user.id)
|
44
|
-
assert user.found_using_numeric_id?
|
45
|
-
end
|
46
|
-
|
47
|
-
should "indicate if it has a better id" do
|
48
|
-
user = User.find(@user.id)
|
49
|
-
assert user.has_better_id?
|
50
|
-
end
|
51
|
-
|
52
|
-
should "not validate if the friendly_id text is reserved" do
|
53
|
-
user = User.new(:name => "new")
|
54
|
-
assert !user.valid?
|
55
|
-
end
|
56
|
-
|
57
|
-
should "have always string for a friendly_id" do
|
58
|
-
assert_equal String, @user.to_param.class
|
59
|
-
end
|
60
|
-
|
61
|
-
should "return its id if the friendly_id is null" do
|
62
|
-
@user.name = nil
|
63
|
-
assert_equal @user.id.to_s, @user.to_param
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
context "when using an array as the find argument" do
|
68
|
-
|
69
|
-
setup do
|
70
|
-
@user2 = User.create(:name => "jane")
|
71
|
-
end
|
72
|
-
|
73
|
-
should "return results" do
|
74
|
-
assert_equal 2, User.find([@user.friendly_id, @user2.friendly_id]).size
|
75
|
-
end
|
76
|
-
|
77
|
-
should "not allow mixed friendly and non-friendly ids for the same record" do
|
78
|
-
assert_raises ActiveRecord::RecordNotFound do
|
79
|
-
User.find([@user.id, @user.friendly_id]).size
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
should "raise an error when all records are not found" do
|
84
|
-
assert_raises ActiveRecord::RecordNotFound do
|
85
|
-
User.find(['bad', 'bad2'])
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
should "indicate if the results were found using a friendly_id" do
|
90
|
-
users = User.find([@user.id, @user2.friendly_id], :order => "name ASC")
|
91
|
-
assert users[0].found_using_friendly_id?
|
92
|
-
assert users[1].found_using_numeric_id?
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
data/test/scoped_model_test.rb
DELETED
@@ -1,64 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + '/test_helper'
|
2
|
-
|
3
|
-
|
4
|
-
class ScopedModelTest < Test::Unit::TestCase
|
5
|
-
|
6
|
-
context "A slugged model that uses a scope" do
|
7
|
-
|
8
|
-
setup do
|
9
|
-
@usa = Country.create!(:name => "USA")
|
10
|
-
@canada = Country.create!(:name => "Canada")
|
11
|
-
@resident = Resident.create!(:name => "John Smith", :country => @usa)
|
12
|
-
@resident2 = Resident.create!(:name => "John Smith", :country => @canada)
|
13
|
-
end
|
14
|
-
|
15
|
-
teardown do
|
16
|
-
Resident.delete_all
|
17
|
-
Country.delete_all
|
18
|
-
Slug.delete_all
|
19
|
-
end
|
20
|
-
|
21
|
-
should "should not show the scope in the friendly_id" do
|
22
|
-
assert_equal "john-smith", @resident.friendly_id
|
23
|
-
assert_equal "john-smith", @resident2.friendly_id
|
24
|
-
end
|
25
|
-
|
26
|
-
should "find all scoped records without scope" do
|
27
|
-
assert_equal 2, Resident.find(:all, @resident.friendly_id).size
|
28
|
-
end
|
29
|
-
|
30
|
-
should "find a single scoped records with a scope as a string" do
|
31
|
-
assert Resident.find(@resident.friendly_id, :scope => @resident.country.to_param)
|
32
|
-
end
|
33
|
-
|
34
|
-
should "find a single scoped records with a scope" do
|
35
|
-
assert Resident.find(@resident.friendly_id, :scope => @resident.country)
|
36
|
-
end
|
37
|
-
|
38
|
-
should "raise an error when finding a single scoped record with no scope" do
|
39
|
-
assert_raises ActiveRecord::RecordNotFound do
|
40
|
-
Resident.find(@resident.friendly_id)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
should "append scope error info when missing scope causes a find to fail" do
|
45
|
-
begin
|
46
|
-
Resident.find(@resident.friendly_id)
|
47
|
-
fail "The find should not have succeeded"
|
48
|
-
rescue ActiveRecord::RecordNotFound => e
|
49
|
-
assert_match /expected scope/, e.message
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
should "append scope error info when the scope value causes a find to fail" do
|
54
|
-
begin
|
55
|
-
Resident.find(@resident.friendly_id, :scope => "badscope")
|
56
|
-
fail "The find should not have succeeded"
|
57
|
-
rescue ActiveRecord::RecordNotFound => e
|
58
|
-
assert_match /scope=badscope/, e.message
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
end
|
data/test/slug_test.rb
DELETED
@@ -1,105 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
require File.dirname(__FILE__) + '/test_helper'
|
3
|
-
|
4
|
-
class SlugTest < Test::Unit::TestCase
|
5
|
-
|
6
|
-
context "a slug" do
|
7
|
-
|
8
|
-
teardown do
|
9
|
-
Slug.delete_all
|
10
|
-
Post.delete_all
|
11
|
-
end
|
12
|
-
|
13
|
-
should "indicate if it is the most recent slug" do
|
14
|
-
post = Post.create!(:name => "test title")
|
15
|
-
post.name = "a new title"
|
16
|
-
post.save!
|
17
|
-
assert post.slugs.last.is_most_recent?
|
18
|
-
assert !post.slugs.first.is_most_recent?
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
|
23
|
-
context "the Slug class" do
|
24
|
-
|
25
|
-
should "parse the slug name and sequence" do
|
26
|
-
assert_equal ["test", "2"], Slug::parse("test--2")
|
27
|
-
end
|
28
|
-
|
29
|
-
should "parse with a default sequence of 1" do
|
30
|
-
assert_equal ["test", "1"], Slug::parse("test")
|
31
|
-
end
|
32
|
-
|
33
|
-
should "should strip diacritics" do
|
34
|
-
assert_equal "acai", Slug::strip_diacritics("açaí")
|
35
|
-
end
|
36
|
-
|
37
|
-
should "strip diacritics correctly " do
|
38
|
-
input = "ÀÁÂÃÄÅÆÇÈÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ"
|
39
|
-
output = Slug::strip_diacritics(input).split(//)
|
40
|
-
expected = "AAAAAAAECEEEIIIIDNOOOOOOUUUUYThssaaaaaaaeceeeeiiiidnoooooouuuuythy".split(//)
|
41
|
-
output.each_index do |i|
|
42
|
-
assert_equal expected[i], output[i]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
|
48
|
-
context "the Slug class's to_friendly_id method" do
|
49
|
-
|
50
|
-
should "include the sequence if the sequence is greater than 1" do
|
51
|
-
slug = Slug.new(:name => "test", :sequence => 2)
|
52
|
-
assert_equal "test--2", slug.to_friendly_id
|
53
|
-
end
|
54
|
-
|
55
|
-
should "not include the sequence if the sequence is 1" do
|
56
|
-
slug = Slug.new(:name => "test", :sequence => 1)
|
57
|
-
assert_equal "test", slug.to_friendly_id
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
context "the Slug class's normalize method" do
|
63
|
-
|
64
|
-
should "should lowercase strings" do
|
65
|
-
assert_match /abc/, Slug::normalize("ABC")
|
66
|
-
end
|
67
|
-
|
68
|
-
should "should replace whitespace with dashes" do
|
69
|
-
assert_match /a-b/, Slug::normalize("a b")
|
70
|
-
end
|
71
|
-
|
72
|
-
should "should replace 2spaces with 1dash" do
|
73
|
-
assert_match /a-b/, Slug::normalize("a b")
|
74
|
-
end
|
75
|
-
|
76
|
-
should "should remove punctuation" do
|
77
|
-
assert_match /abc/, Slug::normalize('abc!@#$%^&*•¶§∞¢££¡¿()><?"":;][]\.,/')
|
78
|
-
end
|
79
|
-
|
80
|
-
should "should strip trailing space" do
|
81
|
-
assert_match /ab/, Slug::normalize("ab ")
|
82
|
-
end
|
83
|
-
|
84
|
-
should "should strip leading space" do
|
85
|
-
assert_match /ab/, Slug::normalize(" ab")
|
86
|
-
end
|
87
|
-
|
88
|
-
should "should strip trailing slashes" do
|
89
|
-
assert_match /ab/, Slug::normalize("ab-")
|
90
|
-
end
|
91
|
-
|
92
|
-
should "should strip leading slashes" do
|
93
|
-
assert_match /ab/, Slug::normalize("-ab")
|
94
|
-
end
|
95
|
-
|
96
|
-
should "should not modify valid name strings" do
|
97
|
-
assert_match /a-b-c-d/, Slug::normalize("a-b-c-d")
|
98
|
-
end
|
99
|
-
|
100
|
-
should "work with non roman chars" do
|
101
|
-
assert_equal "検-索", Slug::normalize("検 索")
|
102
|
-
end
|
103
|
-
|
104
|
-
end
|
105
|
-
end
|