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
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module ActiveRecord2
|
6
|
+
module Simple
|
7
|
+
|
8
|
+
module SimpleTest
|
9
|
+
def klass
|
10
|
+
@klass ||= User
|
11
|
+
end
|
12
|
+
|
13
|
+
def instance
|
14
|
+
@instance ||= User.create! :name => "hello world"
|
15
|
+
end
|
16
|
+
|
17
|
+
def other_class
|
18
|
+
Author
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class StatusTest < ::Test::Unit::TestCase
|
23
|
+
|
24
|
+
include SimpleTest
|
25
|
+
|
26
|
+
test "should default to not friendly" do
|
27
|
+
assert !status.friendly?
|
28
|
+
end
|
29
|
+
|
30
|
+
test "should default to numeric" do
|
31
|
+
assert status.numeric?
|
32
|
+
end
|
33
|
+
|
34
|
+
test "should be friendly if name is set" do
|
35
|
+
status.name = "name"
|
36
|
+
assert status.friendly?
|
37
|
+
end
|
38
|
+
|
39
|
+
test "should be best if it is numeric, but record has no friendly_id" do
|
40
|
+
instance.send("#{klass.friendly_id_config.column}=", nil)
|
41
|
+
assert status.best?
|
42
|
+
end
|
43
|
+
|
44
|
+
def status
|
45
|
+
@status ||= instance.friendly_id_status
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
class BasicTest < ::Test::Unit::TestCase
|
51
|
+
include Core
|
52
|
+
include SimpleTest
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.dirname(__FILE__) + '/test_helper'
|
3
|
+
|
4
|
+
module FriendlyId
|
5
|
+
module Test
|
6
|
+
|
7
|
+
class SlugTest < ::Test::Unit::TestCase
|
8
|
+
|
9
|
+
def teardown
|
10
|
+
Slug.delete_all
|
11
|
+
Post.delete_all
|
12
|
+
end
|
13
|
+
|
14
|
+
test "should indicate if it is the most recent slug" do
|
15
|
+
post = Post.create!(:name => "test title")
|
16
|
+
post.name = "a new title"
|
17
|
+
post.save!
|
18
|
+
assert post.slugs.first.current?
|
19
|
+
assert !post.slugs.last.current?
|
20
|
+
end
|
21
|
+
|
22
|
+
test "should include the sequence if the sequence is greater than 1" do
|
23
|
+
slug = Slug.new(:name => "test", :sluggable => Post.new, :sequence => 2)
|
24
|
+
assert_equal "test--2", slug.to_friendly_id
|
25
|
+
end
|
26
|
+
|
27
|
+
test "should not include the sequence if the sequence is 1" do
|
28
|
+
slug = Slug.new(:name => "test", :sluggable => Post.new, :sequence => 1)
|
29
|
+
assert_equal "test", slug.to_friendly_id
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module ActiveRecord2
|
6
|
+
module Slugged
|
7
|
+
|
8
|
+
test "should allow eager loading of slugs" do
|
9
|
+
assert_nothing_raised do
|
10
|
+
klass.find(instance.friendly_id, :include => :slugs)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def klass
|
15
|
+
Post
|
16
|
+
end
|
17
|
+
|
18
|
+
def other_class
|
19
|
+
District
|
20
|
+
end
|
21
|
+
|
22
|
+
def instance
|
23
|
+
@instance ||= klass.create! :name => "hello world"
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
|
4
|
+
module FriendlyId
|
5
|
+
module Test
|
6
|
+
module ActiveRecord2
|
7
|
+
|
8
|
+
class StatusTest < ::Test::Unit::TestCase
|
9
|
+
|
10
|
+
test "should default to not friendly" do
|
11
|
+
assert !status.friendly?
|
12
|
+
end
|
13
|
+
|
14
|
+
test "should default to numeric" do
|
15
|
+
assert status.numeric?
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should be friendly if slug is set" do
|
19
|
+
status.slug = Slug.new
|
20
|
+
assert status.friendly?
|
21
|
+
end
|
22
|
+
|
23
|
+
test "should be friendly if name is set" do
|
24
|
+
status.name = "name"
|
25
|
+
assert status.friendly?
|
26
|
+
end
|
27
|
+
|
28
|
+
test "should be current if current slug is set" do
|
29
|
+
status.slug = instance.slug
|
30
|
+
assert status.current?
|
31
|
+
end
|
32
|
+
|
33
|
+
test "should not be current if non-current slug is set" do
|
34
|
+
status.slug = Slug.new(:sluggable => instance)
|
35
|
+
assert !status.current?
|
36
|
+
end
|
37
|
+
|
38
|
+
test "should be best if it is current" do
|
39
|
+
status.slug = instance.slug
|
40
|
+
assert status.best?
|
41
|
+
end
|
42
|
+
|
43
|
+
test "should be best if it is numeric, but record has not slug" do
|
44
|
+
instance.slugs = []
|
45
|
+
instance.slug = nil
|
46
|
+
assert status.best?
|
47
|
+
end
|
48
|
+
|
49
|
+
def instance
|
50
|
+
@instance ||= Post.create! :name => "hello world"
|
51
|
+
end
|
52
|
+
|
53
|
+
def status
|
54
|
+
@status ||= instance.friendly_id_status
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module ActiveRecord2
|
6
|
+
|
7
|
+
class StiTest < ::Test::Unit::TestCase
|
8
|
+
|
9
|
+
include FriendlyId::Test::Generic
|
10
|
+
include FriendlyId::Test::Slugged
|
11
|
+
include FriendlyId::Test::ActiveRecord2::Slugged
|
12
|
+
include FriendlyId::Test::ActiveRecord2::Core
|
13
|
+
|
14
|
+
def klass
|
15
|
+
Novel
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
File without changes
|
File without changes
|
@@ -1,42 +1,70 @@
|
|
1
1
|
class CreateSupportModels < ActiveRecord::Migration
|
2
2
|
def self.up
|
3
|
+
|
4
|
+
create_table :authors do |t|
|
5
|
+
t.string :name
|
6
|
+
end
|
7
|
+
|
3
8
|
create_table :books do |t|
|
4
9
|
t.string :name
|
5
10
|
t.string :type
|
11
|
+
t.string :note
|
6
12
|
end
|
13
|
+
|
7
14
|
create_table :cities do |t|
|
8
15
|
t.string :name
|
9
16
|
t.string :my_slug
|
10
17
|
t.integer :population
|
18
|
+
t.index :my_slug, :unique => true
|
11
19
|
end
|
20
|
+
|
12
21
|
create_table :countries do |t|
|
13
22
|
t.string :name
|
14
23
|
end
|
24
|
+
|
15
25
|
create_table :districts do |t|
|
16
26
|
t.string :name
|
27
|
+
t.string :note
|
17
28
|
t.string :cached_slug
|
29
|
+
t.index :cached_slug, :unique => true
|
18
30
|
end
|
31
|
+
|
19
32
|
create_table :events do |t|
|
20
33
|
t.string :name
|
21
34
|
t.datetime :event_date
|
22
35
|
end
|
36
|
+
|
37
|
+
create_table :houses do |t|
|
38
|
+
t.string :name
|
39
|
+
t.integer :user_id
|
40
|
+
end
|
41
|
+
|
23
42
|
create_table :legacy_table do |t|
|
24
43
|
t.string :name
|
44
|
+
t.string :note
|
25
45
|
end
|
46
|
+
|
26
47
|
create_table :people do |t|
|
27
48
|
t.string :name
|
49
|
+
t.string :note
|
28
50
|
end
|
51
|
+
|
29
52
|
create_table :posts do |t|
|
30
53
|
t.string :name
|
31
54
|
t.boolean :published
|
55
|
+
t.string :note
|
32
56
|
end
|
57
|
+
|
33
58
|
create_table :residents do |t|
|
34
59
|
t.string :name
|
35
60
|
t.integer :country_id
|
36
61
|
end
|
62
|
+
|
37
63
|
create_table :users do |t|
|
38
64
|
t.string :name
|
65
|
+
t.index :name, :unique => true
|
39
66
|
end
|
67
|
+
|
40
68
|
end
|
41
69
|
|
42
70
|
def self.down
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
require File.dirname(__FILE__) + '/../../lib/friendly_id/active_record2/tasks'
|
3
|
+
|
4
|
+
class TasksTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def teardown
|
7
|
+
ENV["MODEL"] = nil
|
8
|
+
[City, District, Post, Slug].map(&:delete_all)
|
9
|
+
end
|
10
|
+
|
11
|
+
test "should make slugs" do
|
12
|
+
City.create! :name => "Nairobi"
|
13
|
+
City.create! :name => "Buenos Aires"
|
14
|
+
Slug.delete_all
|
15
|
+
ENV["MODEL"] = "City"
|
16
|
+
FriendlyId::TaskRunner.new.make_slugs
|
17
|
+
assert_equal 2, Slug.count
|
18
|
+
end
|
19
|
+
|
20
|
+
test "should admit lower case, plural model names" do
|
21
|
+
ENV["MODEL"] = "cities"
|
22
|
+
assert_equal City, FriendlyId::TaskRunner.new.klass
|
23
|
+
end
|
24
|
+
|
25
|
+
test "make_slugs should raise error if no model given" do
|
26
|
+
assert_raise(RuntimeError) { FriendlyId::TaskRunner.new.make_slugs }
|
27
|
+
end
|
28
|
+
|
29
|
+
test "make_slugs should raise error if class doesn't use FriendlyId" do
|
30
|
+
ENV["MODEL"] = "String"
|
31
|
+
assert_raise(RuntimeError) { FriendlyId::TaskRunner.new.make_slugs }
|
32
|
+
end
|
33
|
+
|
34
|
+
test"delete_slugs delete only slugs for the specified model" do
|
35
|
+
Post.create! :name => "Slugs Considered Harmful"
|
36
|
+
City.create! :name => "Buenos Aires"
|
37
|
+
ENV["MODEL"] = "city"
|
38
|
+
FriendlyId::TaskRunner.new.delete_slugs
|
39
|
+
assert_equal 1, Slug.count
|
40
|
+
end
|
41
|
+
|
42
|
+
test "delete_slugs should set the cached_slug column to NULL" do
|
43
|
+
ENV["MODEL"] = "district"
|
44
|
+
District.create! :name => "Garment"
|
45
|
+
FriendlyId::TaskRunner.new.delete_slugs
|
46
|
+
assert_nil District.first.cached_slug
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
test "delete_old_slugs should delete slugs older than 45 days by default" do
|
51
|
+
set_up_old_slugs
|
52
|
+
FriendlyId::TaskRunner.new.delete_old_slugs
|
53
|
+
assert_equal 2, Slug.count
|
54
|
+
end
|
55
|
+
|
56
|
+
test "delete_old_slugs should respect the days argument" do
|
57
|
+
set_up_old_slugs
|
58
|
+
ENV["DAYS"] = "100"
|
59
|
+
FriendlyId::TaskRunner.new.delete_old_slugs
|
60
|
+
assert_equal 3, Slug.count
|
61
|
+
end
|
62
|
+
|
63
|
+
test "delete_old_slugs should respect the class argument" do
|
64
|
+
set_up_old_slugs
|
65
|
+
ENV["MODEL"] = "post"
|
66
|
+
FriendlyId::TaskRunner.new.delete_old_slugs
|
67
|
+
assert_equal 3, Slug.count
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def set_up_old_slugs
|
73
|
+
Post.create! :name => "Slugs Considered Harmful"
|
74
|
+
city = City.create! :name => "Buenos Aires"
|
75
|
+
City.connection.execute "UPDATE slugs SET created_at = '%s' WHERE id = %d" % [
|
76
|
+
45.days.ago.strftime("%Y-%m-%d"), city.slug.id
|
77
|
+
]
|
78
|
+
city.update_attributes :name => "Ciudad de Buenos Aires"
|
79
|
+
assert_equal 3, Slug.count
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require File.dirname(__FILE__) + "/../test_helper"
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "active_support"
|
5
|
+
require File.dirname(__FILE__) + "/../../lib/friendly_id/active_record2.rb"
|
6
|
+
require File.dirname(__FILE__) + "/../../generators/friendly_id/templates/create_slugs"
|
7
|
+
require File.dirname(__FILE__) + "/support/models"
|
8
|
+
require File.dirname(__FILE__) + '/core'
|
9
|
+
require File.dirname(__FILE__) + '/slugged'
|
10
|
+
|
11
|
+
local_db_settings = File.dirname(__FILE__) + "/support/database.yml"
|
12
|
+
default_db_settings = File.dirname(__FILE__) + "/support/database.sqlite3.yml"
|
13
|
+
db_settings = File.exists?(local_db_settings) ? local_db_settings : default_db_settings
|
14
|
+
ActiveRecord::Base.establish_connection(YAML::load(File.open(db_settings)))
|
15
|
+
|
16
|
+
class ActiveRecord::Base
|
17
|
+
def log_protected_attribute_removal(*args) end
|
18
|
+
end
|
19
|
+
|
20
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
21
|
+
ActiveRecord::Base.connection.drop_table(table)
|
22
|
+
end
|
23
|
+
ActiveRecord::Migration.verbose = false
|
24
|
+
CreateSlugs.up
|
25
|
+
CreateSupportModels.up
|
26
|
+
|
27
|
+
# A model that uses the automagically configured "cached_slug" column
|
28
|
+
class District < ActiveRecord::Base
|
29
|
+
has_friendly_id :name, :use_slug => true
|
30
|
+
end
|
31
|
+
|
32
|
+
# A model that specifies a custom cached slug column
|
33
|
+
class City < ActiveRecord::Base
|
34
|
+
attr_accessible :name
|
35
|
+
has_friendly_id :name, :use_slug => true, :cache_column => "my_slug"
|
36
|
+
end
|
37
|
+
|
38
|
+
# A model with a custom slug text normalizer
|
39
|
+
class Person < ActiveRecord::Base
|
40
|
+
has_friendly_id :name, :use_slug => true
|
41
|
+
|
42
|
+
def normalize_friendly_id(string)
|
43
|
+
string.upcase
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
# A slugged model that uses a scope
|
49
|
+
class Resident < ActiveRecord::Base
|
50
|
+
belongs_to :country
|
51
|
+
has_friendly_id :name, :use_slug => true, :scope => :country
|
52
|
+
end
|
53
|
+
|
54
|
+
# A slugged model used as a scope
|
55
|
+
class Country < ActiveRecord::Base
|
56
|
+
has_many :people
|
57
|
+
has_many :residents
|
58
|
+
has_friendly_id :name, :use_slug => true
|
59
|
+
end
|
60
|
+
|
61
|
+
# A model that doesn"t use slugs
|
62
|
+
class User < ActiveRecord::Base
|
63
|
+
has_friendly_id :name
|
64
|
+
has_many :houses
|
65
|
+
end
|
66
|
+
|
67
|
+
# Another model that doesn"t use slugs
|
68
|
+
class Author < ActiveRecord::Base
|
69
|
+
has_friendly_id :name
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
# A model that uses a non-slugged model for its scope
|
74
|
+
class House < ActiveRecord::Base
|
75
|
+
belongs_to :user
|
76
|
+
has_friendly_id :name, :use_slug => true, :scope => :user
|
77
|
+
end
|
78
|
+
|
79
|
+
# A model that uses default slug settings and has a named scope
|
80
|
+
class Post < ActiveRecord::Base
|
81
|
+
has_friendly_id :name, :use_slug => true
|
82
|
+
named_scope :published, :conditions => { :published => true }
|
83
|
+
end
|
84
|
+
|
85
|
+
# Model that uses a custom table name
|
86
|
+
class Place < ActiveRecord::Base
|
87
|
+
self.table_name = "legacy_table"
|
88
|
+
has_friendly_id :name, :use_slug => true
|
89
|
+
end
|
90
|
+
|
91
|
+
# A model that uses a datetime field for its friendly_id
|
92
|
+
class Event < ActiveRecord::Base
|
93
|
+
has_friendly_id :event_date, :use_slug => true
|
94
|
+
end
|
95
|
+
|
96
|
+
# A base model for single table inheritence
|
97
|
+
class Book < ActiveRecord::Base ; end
|
98
|
+
|
99
|
+
# A model that uses STI
|
100
|
+
class Novel < ::Book
|
101
|
+
has_friendly_id :name, :use_slug => true
|
102
|
+
end
|
103
|
+
|
104
|
+
# A model with no table
|
105
|
+
class Question < ActiveRecord::Base
|
106
|
+
has_friendly_id :name, :use_slug => true
|
107
|
+
end
|