friendly_id_globalize3 3.2.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 +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
|