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.
Files changed (58) hide show
  1. data/Changelog.md +354 -0
  2. data/Contributors.md +43 -0
  3. data/Guide.md +686 -0
  4. data/MIT-LICENSE +19 -0
  5. data/README.md +99 -0
  6. data/Rakefile +75 -0
  7. data/extras/README.txt +3 -0
  8. data/extras/bench.rb +40 -0
  9. data/extras/extras.rb +38 -0
  10. data/extras/prof.rb +19 -0
  11. data/extras/template-gem.rb +26 -0
  12. data/extras/template-plugin.rb +28 -0
  13. data/generators/friendly_id/friendly_id_generator.rb +30 -0
  14. data/generators/friendly_id/templates/create_slugs.rb +18 -0
  15. data/lib/friendly_id.rb +93 -0
  16. data/lib/friendly_id/active_record.rb +74 -0
  17. data/lib/friendly_id/active_record_adapter/configuration.rb +68 -0
  18. data/lib/friendly_id/active_record_adapter/finders.rb +148 -0
  19. data/lib/friendly_id/active_record_adapter/relation.rb +165 -0
  20. data/lib/friendly_id/active_record_adapter/simple_model.rb +63 -0
  21. data/lib/friendly_id/active_record_adapter/slug.rb +77 -0
  22. data/lib/friendly_id/active_record_adapter/slugged_model.rb +122 -0
  23. data/lib/friendly_id/active_record_adapter/tasks.rb +72 -0
  24. data/lib/friendly_id/configuration.rb +178 -0
  25. data/lib/friendly_id/datamapper.rb +5 -0
  26. data/lib/friendly_id/railtie.rb +22 -0
  27. data/lib/friendly_id/sequel.rb +5 -0
  28. data/lib/friendly_id/slug_string.rb +25 -0
  29. data/lib/friendly_id/slugged.rb +105 -0
  30. data/lib/friendly_id/status.rb +35 -0
  31. data/lib/friendly_id/test.rb +350 -0
  32. data/lib/friendly_id/version.rb +9 -0
  33. data/lib/generators/friendly_id_generator.rb +25 -0
  34. data/lib/tasks/friendly_id.rake +19 -0
  35. data/rails/init.rb +2 -0
  36. data/test/active_record_adapter/ar_test_helper.rb +150 -0
  37. data/test/active_record_adapter/basic_slugged_model_test.rb +14 -0
  38. data/test/active_record_adapter/cached_slug_test.rb +76 -0
  39. data/test/active_record_adapter/core.rb +138 -0
  40. data/test/active_record_adapter/custom_normalizer_test.rb +20 -0
  41. data/test/active_record_adapter/custom_table_name_test.rb +22 -0
  42. data/test/active_record_adapter/default_scope_test.rb +30 -0
  43. data/test/active_record_adapter/optimistic_locking_test.rb +18 -0
  44. data/test/active_record_adapter/scoped_model_test.rb +119 -0
  45. data/test/active_record_adapter/simple_test.rb +76 -0
  46. data/test/active_record_adapter/slug_test.rb +34 -0
  47. data/test/active_record_adapter/slugged.rb +33 -0
  48. data/test/active_record_adapter/slugged_status_test.rb +28 -0
  49. data/test/active_record_adapter/sti_test.rb +22 -0
  50. data/test/active_record_adapter/support/database.jdbcsqlite3.yml +2 -0
  51. data/test/active_record_adapter/support/database.mysql.yml +4 -0
  52. data/test/active_record_adapter/support/database.postgres.yml +6 -0
  53. data/test/active_record_adapter/support/database.sqlite3.yml +2 -0
  54. data/test/active_record_adapter/support/models.rb +104 -0
  55. data/test/active_record_adapter/tasks_test.rb +82 -0
  56. data/test/friendly_id_test.rb +96 -0
  57. data/test/test_helper.rb +13 -0
  58. 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,2 @@
1
+ adapter: jdbcsqlite3
2
+ database: ":memory:"
@@ -0,0 +1,4 @@
1
+ adapter: mysql
2
+ database: friendly_id_test
3
+ username: root
4
+ hostname: 127.0.0.1
@@ -0,0 +1,6 @@
1
+ adapter: postgresql
2
+ host: localhost
3
+ port: 5432
4
+ username: postgres
5
+ database: friendly_id_test
6
+ encoding: utf8
@@ -0,0 +1,2 @@
1
+ adapter: sqlite3
2
+ database: ":memory:"
@@ -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