friendly_id_globalize3 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +354 -0
- data/Contributors.md +43 -0
- data/Guide.md +686 -0
- data/MIT-LICENSE +19 -0
- data/README.md +99 -0
- data/Rakefile +75 -0
- data/extras/README.txt +3 -0
- data/extras/bench.rb +40 -0
- data/extras/extras.rb +38 -0
- data/extras/prof.rb +19 -0
- data/extras/template-gem.rb +26 -0
- data/extras/template-plugin.rb +28 -0
- data/generators/friendly_id/friendly_id_generator.rb +30 -0
- data/generators/friendly_id/templates/create_slugs.rb +18 -0
- data/lib/friendly_id.rb +93 -0
- data/lib/friendly_id/active_record.rb +74 -0
- data/lib/friendly_id/active_record_adapter/configuration.rb +68 -0
- data/lib/friendly_id/active_record_adapter/finders.rb +148 -0
- data/lib/friendly_id/active_record_adapter/relation.rb +165 -0
- data/lib/friendly_id/active_record_adapter/simple_model.rb +63 -0
- data/lib/friendly_id/active_record_adapter/slug.rb +77 -0
- data/lib/friendly_id/active_record_adapter/slugged_model.rb +122 -0
- data/lib/friendly_id/active_record_adapter/tasks.rb +72 -0
- data/lib/friendly_id/configuration.rb +178 -0
- data/lib/friendly_id/datamapper.rb +5 -0
- data/lib/friendly_id/railtie.rb +22 -0
- data/lib/friendly_id/sequel.rb +5 -0
- data/lib/friendly_id/slug_string.rb +25 -0
- data/lib/friendly_id/slugged.rb +105 -0
- data/lib/friendly_id/status.rb +35 -0
- data/lib/friendly_id/test.rb +350 -0
- data/lib/friendly_id/version.rb +9 -0
- data/lib/generators/friendly_id_generator.rb +25 -0
- data/lib/tasks/friendly_id.rake +19 -0
- data/rails/init.rb +2 -0
- data/test/active_record_adapter/ar_test_helper.rb +150 -0
- data/test/active_record_adapter/basic_slugged_model_test.rb +14 -0
- data/test/active_record_adapter/cached_slug_test.rb +76 -0
- data/test/active_record_adapter/core.rb +138 -0
- data/test/active_record_adapter/custom_normalizer_test.rb +20 -0
- data/test/active_record_adapter/custom_table_name_test.rb +22 -0
- data/test/active_record_adapter/default_scope_test.rb +30 -0
- data/test/active_record_adapter/optimistic_locking_test.rb +18 -0
- data/test/active_record_adapter/scoped_model_test.rb +119 -0
- data/test/active_record_adapter/simple_test.rb +76 -0
- data/test/active_record_adapter/slug_test.rb +34 -0
- data/test/active_record_adapter/slugged.rb +33 -0
- data/test/active_record_adapter/slugged_status_test.rb +28 -0
- data/test/active_record_adapter/sti_test.rb +22 -0
- data/test/active_record_adapter/support/database.jdbcsqlite3.yml +2 -0
- data/test/active_record_adapter/support/database.mysql.yml +4 -0
- data/test/active_record_adapter/support/database.postgres.yml +6 -0
- data/test/active_record_adapter/support/database.sqlite3.yml +2 -0
- data/test/active_record_adapter/support/models.rb +104 -0
- data/test/active_record_adapter/tasks_test.rb +82 -0
- data/test/friendly_id_test.rb +96 -0
- data/test/test_helper.rb +13 -0
- metadata +193 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path("../ar_test_helper", __FILE__)
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module ActiveRecordAdapter
|
6
|
+
|
7
|
+
class CustomTableNameTest < ::Test::Unit::TestCase
|
8
|
+
|
9
|
+
include FriendlyId::Test::Generic
|
10
|
+
include FriendlyId::Test::Slugged
|
11
|
+
include FriendlyId::Test::ActiveRecordAdapter::Slugged
|
12
|
+
include FriendlyId::Test::ActiveRecordAdapter::Core
|
13
|
+
|
14
|
+
def klass
|
15
|
+
Place
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.expand_path('../ar_test_helper', __FILE__)
|
2
|
+
|
3
|
+
ActiveRecord::Migration.create_table :articles do |t|
|
4
|
+
t.string :name
|
5
|
+
t.string :status
|
6
|
+
end
|
7
|
+
|
8
|
+
class Article < ActiveRecord::Base
|
9
|
+
has_friendly_id :name, :use_slug => true
|
10
|
+
default_scope :conditions => "articles.status = 'published'"
|
11
|
+
end
|
12
|
+
|
13
|
+
module FriendlyId
|
14
|
+
module Test
|
15
|
+
module ActiveRecordAdapter
|
16
|
+
class DefaultScopeTest < ::Test::Unit::TestCase
|
17
|
+
|
18
|
+
def setup
|
19
|
+
Article.delete_all
|
20
|
+
Slug.delete_all
|
21
|
+
end
|
22
|
+
|
23
|
+
test "slug should load sluggable without default scope" do
|
24
|
+
Article.create!(:name => "hello world", :status => "draft")
|
25
|
+
assert_not_nil Slug.first.sluggable
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path("../ar_test_helper", __FILE__)
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module ActiveRecordAdapter
|
6
|
+
class OptimisticLockingTest < ::Test::Unit::TestCase
|
7
|
+
test "should update the cached slug when updating the slug" do
|
8
|
+
region = Region.create! :name => 'some name'
|
9
|
+
assert_nothing_raised do
|
10
|
+
region.update_attributes(:name => "new name")
|
11
|
+
end
|
12
|
+
assert_equal region.slug.to_friendly_id, region.cached_slug
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require File.expand_path("../ar_test_helper", __FILE__)
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
|
6
|
+
class ScopedModelTest < ::Test::Unit::TestCase
|
7
|
+
|
8
|
+
def setup
|
9
|
+
@user = User.create!(:name => "john")
|
10
|
+
@house = House.create!(:name => "123 Main", :user => @user)
|
11
|
+
@usa = Country.create!(:name => "USA")
|
12
|
+
@canada = Country.create!(:name => "Canada")
|
13
|
+
@resident = Resident.create!(:name => "John Smith", :country => @usa)
|
14
|
+
@resident2 = Resident.create!(:name => "John Smith", :country => @canada)
|
15
|
+
@resident3 = Resident.create!(:name => "Jim Beam", :country => @canada)
|
16
|
+
@owner = Company.create!(:name => "Acme Events")
|
17
|
+
@site = Site.create!(:name => "Downtown Venue", :owner => @owner)
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
Resident.delete_all
|
22
|
+
Country.delete_all
|
23
|
+
User.delete_all
|
24
|
+
House.delete_all
|
25
|
+
Slug.delete_all
|
26
|
+
Tourist.delete_all
|
27
|
+
end
|
28
|
+
|
29
|
+
test "As of 3.2.0, should raise error if :scope option is passed" do
|
30
|
+
assert_raise(RuntimeError) do
|
31
|
+
Tourist.find("hello", :scope => "usa")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
test "should not use cached slug column with scopes" do
|
36
|
+
@tourist = Tourist.create!(:name => "John Smith", :country => @usa)
|
37
|
+
@tourist2 = Tourist.create!(:name => "John Smith", :country => @canada)
|
38
|
+
assert_equal @canada, @canada.residents.find(@tourist2.friendly_id).country
|
39
|
+
end
|
40
|
+
|
41
|
+
test "a slugged model should auto-detect that it is being used as a parent scope" do
|
42
|
+
assert_equal [Resident], Country.friendly_id_config.child_scopes
|
43
|
+
end
|
44
|
+
|
45
|
+
test "a slugged model should update its child model's scopes when its friendly_id changes" do
|
46
|
+
@usa.update_attributes(:name => "United States")
|
47
|
+
assert_equal "united-states", @usa.to_param
|
48
|
+
assert_equal "united-states", @resident.slugs(true).first.scope
|
49
|
+
end
|
50
|
+
|
51
|
+
test "a non-slugged model should auto-detect that it is being used as a parent scope" do
|
52
|
+
assert_equal [House], User.friendly_id_config.child_scopes
|
53
|
+
end
|
54
|
+
|
55
|
+
test "should update the slug when the scope changes" do
|
56
|
+
@resident.update_attributes! :country => Country.create!(:name => "Argentina")
|
57
|
+
assert_equal "argentina", @resident.slugs(true).first.scope
|
58
|
+
end
|
59
|
+
|
60
|
+
test "updating only the scope should not append sequence to friendly_id" do
|
61
|
+
old_friendly_id = @resident.friendly_id
|
62
|
+
@resident.update_attributes! :country => Country.create!(:name => "Argentina")
|
63
|
+
assert_equal old_friendly_id, @resident.friendly_id
|
64
|
+
end
|
65
|
+
|
66
|
+
test "updating the scope should increment sequence to avoid conflicts" do
|
67
|
+
old_friendly_id = @resident.friendly_id
|
68
|
+
@resident.update_attributes! :country => @canada
|
69
|
+
assert_equal "#{old_friendly_id}--2", @resident.friendly_id
|
70
|
+
assert_equal "canada", @resident.slugs(true).first.scope
|
71
|
+
end
|
72
|
+
|
73
|
+
test "a non-slugged model should update its child model's scopes when its friendly_id changes" do
|
74
|
+
@user.update_attributes(:name => "jack")
|
75
|
+
assert_equal "jack", @user.to_param
|
76
|
+
assert_equal "jack", @house.slugs(true).first.scope
|
77
|
+
end
|
78
|
+
|
79
|
+
test "should should not show the scope in the friendly_id" do
|
80
|
+
assert_equal "john-smith", @resident.friendly_id
|
81
|
+
assert_equal "john-smith", @resident2.friendly_id
|
82
|
+
end
|
83
|
+
|
84
|
+
test "should find all scoped records without scope" do
|
85
|
+
name, sequence = @resident.friendly_id.parse_friendly_id
|
86
|
+
assert_equal 2, Resident.find(:all, :joins => :slugs, :conditions => {
|
87
|
+
:slugs => {:name => name, :sequence => sequence}}).size
|
88
|
+
end
|
89
|
+
|
90
|
+
test "should find a scoped record by friendly_id" do
|
91
|
+
assert Resident.find(@resident.friendly_id)
|
92
|
+
end
|
93
|
+
|
94
|
+
test "should find a scope record as a relation member" do
|
95
|
+
assert_equal @resident, @usa.residents.find("john-smith")
|
96
|
+
assert_equal @resident2, @canada.residents.find("john-smith")
|
97
|
+
end
|
98
|
+
|
99
|
+
test "should find a single scoped record using slug conditions" do
|
100
|
+
assert_equal @resident, Resident.find(@resident.friendly_id, :include => :slugs,
|
101
|
+
:conditions => {:slugs => {:scope => @resident.country.to_param}})
|
102
|
+
end
|
103
|
+
|
104
|
+
test "should update the sluggable field when a polymorphic relationship exists" do
|
105
|
+
@site.update_attributes(:name => "Uptown Venue")
|
106
|
+
assert_equal "Uptown Venue", @site.name
|
107
|
+
end
|
108
|
+
|
109
|
+
test "should not assume that AR's reflect_on_all_associations with return AR classes" do
|
110
|
+
reflections = Resident.reflect_on_all_associations
|
111
|
+
reflections << Struct.new("Dummy", :options, :klass).new(:options => [], :klass => Struct)
|
112
|
+
Resident.expects(:reflect_on_all_associations).returns(reflections)
|
113
|
+
assert_nothing_raised do
|
114
|
+
Resident.friendly_id_config.send(:associated_friendly_classes)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.expand_path('../ar_test_helper', __FILE__)
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module ActiveRecordAdapter
|
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
|
+
def setup
|
27
|
+
User.delete_all
|
28
|
+
end
|
29
|
+
|
30
|
+
test "should default to not friendly" do
|
31
|
+
assert !status.friendly?
|
32
|
+
end
|
33
|
+
|
34
|
+
test "should default to numeric" do
|
35
|
+
assert status.numeric?
|
36
|
+
end
|
37
|
+
|
38
|
+
test "should be friendly if name is set" do
|
39
|
+
status.name = "name"
|
40
|
+
assert status.friendly?
|
41
|
+
end
|
42
|
+
|
43
|
+
test "should be best if it is numeric, but record has no friendly_id" do
|
44
|
+
instance.send("#{klass.friendly_id_config.column}=", nil)
|
45
|
+
assert status.best?
|
46
|
+
end
|
47
|
+
|
48
|
+
def status
|
49
|
+
@status ||= instance.friendly_id_status
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
class BasicTest < ::Test::Unit::TestCase
|
55
|
+
include FriendlyId::Test::Generic
|
56
|
+
include FriendlyId::Test::Simple
|
57
|
+
include FriendlyId::Test::ActiveRecordAdapter::Core
|
58
|
+
include SimpleTest
|
59
|
+
|
60
|
+
test "status should be friendly when found using friendly id" do
|
61
|
+
record = klass.send(find_method, instance.friendly_id)
|
62
|
+
assert record.friendly_id_status.friendly?
|
63
|
+
end
|
64
|
+
|
65
|
+
test "status should not be friendly when found using numeric id" do
|
66
|
+
record = klass.send(find_method, instance.id)
|
67
|
+
assert !record.friendly_id_status.friendly?
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path("../ar_test_helper", __FILE__)
|
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,33 @@
|
|
1
|
+
require File.expand_path("../ar_test_helper", __FILE__)
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module ActiveRecordAdapter
|
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
|
+
|
13
|
+
assert_nothing_raised do
|
14
|
+
klass.find(instance.friendly_id, :include => :slug)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def klass
|
19
|
+
Post
|
20
|
+
end
|
21
|
+
|
22
|
+
def other_class
|
23
|
+
District
|
24
|
+
end
|
25
|
+
|
26
|
+
def instance
|
27
|
+
@instance ||= klass.create! :name => "hello world"
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.expand_path("../ar_test_helper", __FILE__)
|
2
|
+
|
3
|
+
|
4
|
+
module FriendlyId
|
5
|
+
module Test
|
6
|
+
module ActiveRecordAdapter
|
7
|
+
|
8
|
+
class StatusTest < ::Test::Unit::TestCase
|
9
|
+
|
10
|
+
include FriendlyId::Test::Status
|
11
|
+
include FriendlyId::Test::SluggedStatus
|
12
|
+
|
13
|
+
def klass
|
14
|
+
Post
|
15
|
+
end
|
16
|
+
|
17
|
+
def instance
|
18
|
+
@instance ||= klass.create! :name => "hello world"
|
19
|
+
end
|
20
|
+
|
21
|
+
def find_method
|
22
|
+
:find
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require File.expand_path('../ar_test_helper', __FILE__)
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module ActiveRecordAdapter
|
6
|
+
|
7
|
+
class StiTest < ::Test::Unit::TestCase
|
8
|
+
|
9
|
+
include FriendlyId::Test::Generic
|
10
|
+
include FriendlyId::Test::Slugged
|
11
|
+
include FriendlyId::Test::ActiveRecordAdapter::Slugged
|
12
|
+
include FriendlyId::Test::ActiveRecordAdapter::Core
|
13
|
+
|
14
|
+
def klass
|
15
|
+
Novel
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
class CreateSupportModels < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
|
4
|
+
create_table :authors do |t|
|
5
|
+
t.string :name
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :blocks do |t|
|
9
|
+
t.string :name
|
10
|
+
t.string :note
|
11
|
+
end
|
12
|
+
|
13
|
+
create_table :books do |t|
|
14
|
+
t.string :name
|
15
|
+
t.string :type
|
16
|
+
t.string :note
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table :cities do |t|
|
20
|
+
t.string :name
|
21
|
+
t.string :my_slug
|
22
|
+
t.integer :population
|
23
|
+
end
|
24
|
+
add_index :cities, :my_slug, :unique => true
|
25
|
+
|
26
|
+
create_table :regions do |t|
|
27
|
+
t.string :name
|
28
|
+
t.string :cached_slug
|
29
|
+
t.string :note
|
30
|
+
t.integer :lock_version, :null => false, :default => 0
|
31
|
+
end
|
32
|
+
add_index :regions, :cached_slug, :unique => true
|
33
|
+
|
34
|
+
create_table :countries do |t|
|
35
|
+
t.string :name
|
36
|
+
end
|
37
|
+
|
38
|
+
create_table :districts do |t|
|
39
|
+
t.string :name
|
40
|
+
t.string :note
|
41
|
+
t.string :cached_slug
|
42
|
+
end
|
43
|
+
add_index :districts, :cached_slug, :unique => true
|
44
|
+
|
45
|
+
create_table :events do |t|
|
46
|
+
t.string :name
|
47
|
+
t.datetime :event_date
|
48
|
+
end
|
49
|
+
|
50
|
+
create_table :houses do |t|
|
51
|
+
t.string :name
|
52
|
+
t.integer :user_id
|
53
|
+
end
|
54
|
+
|
55
|
+
create_table :legacy_table do |t|
|
56
|
+
t.string :name
|
57
|
+
t.string :note
|
58
|
+
end
|
59
|
+
|
60
|
+
create_table :people do |t|
|
61
|
+
t.string :name
|
62
|
+
t.string :note
|
63
|
+
end
|
64
|
+
|
65
|
+
create_table :posts do |t|
|
66
|
+
t.string :name
|
67
|
+
t.boolean :published
|
68
|
+
t.string :note
|
69
|
+
end
|
70
|
+
|
71
|
+
create_table :residents do |t|
|
72
|
+
t.string :name
|
73
|
+
t.integer :country_id
|
74
|
+
end
|
75
|
+
|
76
|
+
create_table :tourists do |t|
|
77
|
+
t.string :name
|
78
|
+
t.integer :country_id
|
79
|
+
t.string :cached_slug
|
80
|
+
end
|
81
|
+
|
82
|
+
create_table :users do |t|
|
83
|
+
t.string :name
|
84
|
+
end
|
85
|
+
add_index :users, :name, :unique => true
|
86
|
+
|
87
|
+
create_table :sites do |t|
|
88
|
+
t.string :name
|
89
|
+
t.integer :owner_id
|
90
|
+
t.string :owner_type
|
91
|
+
end
|
92
|
+
|
93
|
+
create_table :companies do |t|
|
94
|
+
t.string :name
|
95
|
+
end
|
96
|
+
|
97
|
+
create_table :unfriendlies do |t|
|
98
|
+
t.string :name
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.down
|
103
|
+
end
|
104
|
+
end
|