friendly_id 3.3.3.0 → 4.0.0.beta7

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 (79) hide show
  1. data/.gitignore +11 -0
  2. data/.travis.yml +24 -0
  3. data/.yardopts +4 -0
  4. data/Changelog.md +9 -10
  5. data/README.md +39 -48
  6. data/Rakefile +56 -58
  7. data/WhatsNew.md +95 -0
  8. data/bench.rb +63 -0
  9. data/friendly_id.gemspec +40 -0
  10. data/gemfiles/Gemfile.rails-3.0.rb +18 -0
  11. data/gemfiles/Gemfile.rails-3.0.rb.lock +52 -0
  12. data/gemfiles/Gemfile.rails-3.1.rb +18 -0
  13. data/gemfiles/Gemfile.rails-3.1.rb.lock +57 -0
  14. data/lib/friendly_id.rb +126 -80
  15. data/lib/friendly_id/active_record_adapter/relation.rb +10 -2
  16. data/lib/friendly_id/active_record_adapter/slugged_model.rb +3 -9
  17. data/lib/friendly_id/base.rb +132 -0
  18. data/lib/friendly_id/configuration.rb +65 -152
  19. data/lib/friendly_id/finder_methods.rb +20 -0
  20. data/lib/friendly_id/history.rb +88 -0
  21. data/lib/friendly_id/migration.rb +18 -0
  22. data/lib/friendly_id/model.rb +22 -0
  23. data/lib/friendly_id/object_utils.rb +40 -0
  24. data/lib/friendly_id/reserved.rb +46 -0
  25. data/lib/friendly_id/scoped.rb +131 -0
  26. data/lib/friendly_id/slug.rb +9 -0
  27. data/lib/friendly_id/slug_sequencer.rb +82 -0
  28. data/lib/friendly_id/slugged.rb +191 -76
  29. data/lib/friendly_id/version.rb +2 -2
  30. data/test/base_test.rb +54 -0
  31. data/test/configuration_test.rb +27 -0
  32. data/test/core_test.rb +30 -0
  33. data/test/databases.yml +19 -0
  34. data/test/helper.rb +88 -0
  35. data/test/history_test.rb +55 -0
  36. data/test/object_utils_test.rb +26 -0
  37. data/test/reserved_test.rb +26 -0
  38. data/test/schema.rb +59 -0
  39. data/test/scoped_test.rb +57 -0
  40. data/test/shared.rb +118 -0
  41. data/test/slugged_test.rb +83 -0
  42. data/test/sti_test.rb +48 -0
  43. metadata +110 -102
  44. data/Contributors.md +0 -46
  45. data/Guide.md +0 -626
  46. data/extras/README.txt +0 -3
  47. data/extras/bench.rb +0 -40
  48. data/extras/extras.rb +0 -38
  49. data/extras/prof.rb +0 -19
  50. data/extras/template-gem.rb +0 -26
  51. data/extras/template-plugin.rb +0 -28
  52. data/generators/friendly_id/friendly_id_generator.rb +0 -30
  53. data/generators/friendly_id/templates/create_slugs.rb +0 -18
  54. data/lib/tasks/friendly_id.rake +0 -19
  55. data/rails/init.rb +0 -2
  56. data/test/active_record_adapter/ar_test_helper.rb +0 -149
  57. data/test/active_record_adapter/basic_slugged_model_test.rb +0 -14
  58. data/test/active_record_adapter/cached_slug_test.rb +0 -76
  59. data/test/active_record_adapter/core.rb +0 -138
  60. data/test/active_record_adapter/custom_normalizer_test.rb +0 -20
  61. data/test/active_record_adapter/custom_table_name_test.rb +0 -22
  62. data/test/active_record_adapter/default_scope_test.rb +0 -30
  63. data/test/active_record_adapter/optimistic_locking_test.rb +0 -18
  64. data/test/active_record_adapter/scoped_model_test.rb +0 -129
  65. data/test/active_record_adapter/simple_test.rb +0 -76
  66. data/test/active_record_adapter/slug_test.rb +0 -34
  67. data/test/active_record_adapter/slugged.rb +0 -33
  68. data/test/active_record_adapter/slugged_status_test.rb +0 -28
  69. data/test/active_record_adapter/sti_test.rb +0 -22
  70. data/test/active_record_adapter/support/database.jdbcsqlite3.yml +0 -2
  71. data/test/active_record_adapter/support/database.mysql.yml +0 -4
  72. data/test/active_record_adapter/support/database.mysql2.yml +0 -4
  73. data/test/active_record_adapter/support/database.postgres.yml +0 -6
  74. data/test/active_record_adapter/support/database.sqlite3.yml +0 -2
  75. data/test/active_record_adapter/support/models.rb +0 -104
  76. data/test/active_record_adapter/tasks_test.rb +0 -82
  77. data/test/compatibility/ancestry/Gemfile.lock +0 -34
  78. data/test/friendly_id_test.rb +0 -96
  79. data/test/test_helper.rb +0 -13
@@ -1,138 +0,0 @@
1
- require File.expand_path("../ar_test_helper", __FILE__)
2
-
3
- module FriendlyId
4
-
5
- module Test
6
-
7
- module ActiveRecordAdapter
8
-
9
- module Core
10
-
11
- def teardown
12
- klass.delete_all
13
- other_class.delete_all
14
- Slug.delete_all
15
- end
16
-
17
- def find_method
18
- :find
19
- end
20
-
21
- def create_method
22
- :create!
23
- end
24
-
25
- def delete_all_method
26
- :delete_all
27
- end
28
-
29
- def save_method
30
- :save!
31
- end
32
-
33
- def unfriendly_class
34
- Unfriendly
35
- end
36
-
37
- def validation_exceptions
38
- [ActiveRecord::RecordInvalid, FriendlyId::ReservedError, FriendlyId::BlankError]
39
- end
40
-
41
- test "should return their friendly_id for #to_param" do
42
- assert_match(instance.friendly_id, instance.to_param)
43
- end
44
-
45
- test "instances should be findable by their own instance" do
46
- assert_equal instance, klass.find(instance)
47
- end
48
-
49
- test "instances should be findable by an array of friendly_ids" do
50
- second = klass.create!(:name => "second_instance")
51
- third = klass.create!(:name => "third_instance")
52
- assert_equal 2, klass.find([instance.friendly_id, second.friendly_id]).size
53
- end
54
-
55
- test "failing finds with array of unfriendly_id should raise errors normally" do
56
- assert_raise ActiveRecord::RecordNotFound do
57
- klass.find([0, -1])
58
- end
59
- end
60
-
61
- test "instances should be findable by an array of numeric ids" do
62
- second = klass.create!(:name => "second_instance")
63
- third = klass.create!(:name => "third_instance")
64
- assert_equal 2, klass.find([instance.id.to_i, second.id.to_i]).size
65
- end
66
-
67
- test "instances should be findable by an array of numeric ids as strings" do
68
- second = klass.create!(:name => "second_instance")
69
- third = klass.create!(:name => "third_instance")
70
- assert_equal 2, klass.find([instance.id.to_s, second.id.to_s]).size
71
- end
72
-
73
- test "instances should be findable by an array of instances" do
74
- second = klass.create!(:name => "second_instance")
75
- third = klass.create!(:name => "third_instance")
76
- assert_equal 2, klass.find([instance, second]).size
77
- end
78
-
79
- test "instances should be findable by an array of mixed types" do
80
- second = klass.create!(:name => "second_instance")
81
- third = klass.create!(:name => "third_instance")
82
- assert_equal 2, klass.find([instance.friendly_id, second]).size
83
- end
84
-
85
- test "should not raise error when finding with empty array" do
86
- assert_nothing_raised do
87
- klass.find []
88
- end
89
- end
90
-
91
- test "models should raise an error when not all records are found" do
92
- assert_raises(ActiveRecord::RecordNotFound) do
93
- klass.find([instance.friendly_id, 'bad-friendly-id'])
94
- end
95
- end
96
-
97
- test "models should respect finder conditions" do
98
- assert_raise ActiveRecord::RecordNotFound do
99
- klass.find(instance.friendly_id, :conditions => "1 = 2")
100
- end
101
- end
102
-
103
- # This emulates a fairly common issue where id's generated by fixtures are very high.
104
- test "should continue to admit very large ids" do
105
- klass.connection.execute("INSERT INTO #{klass.table_name} (id, name) VALUES (2047483647, 'An instance')")
106
- assert_nothing_raised do
107
- klass.base_class.find(2047483647)
108
- end
109
- end
110
-
111
- test "should not change failure behavior for models not using friendly_id" do
112
- assert_raise ActiveRecord::RecordNotFound do
113
- unfriendly_class.find(-1)
114
- end
115
- end
116
-
117
- test "failing finds with unfriendly_id should raise errors normally" do
118
- assert_raise ActiveRecord::RecordNotFound do
119
- klass.send(find_method, 0)
120
- end
121
- end
122
-
123
- test "instances found by a single id should not be read-only" do
124
- i = klass.find(instance.friendly_id)
125
- assert !i.readonly?, "expected instance not to be readonly"
126
- end
127
-
128
- test "instances found by an array of ids should not be read-only" do
129
- second = klass.create!(:name => "second_instance")
130
- third = klass.create!(:name => "third_instance")
131
- klass.find([instance.friendly_id, second.friendly_id]).each do |record|
132
- assert !record.readonly?, "expected instance not to be readonly"
133
- end
134
- end
135
- end
136
- end
137
- end
138
- end
@@ -1,20 +0,0 @@
1
- require File.expand_path("../ar_test_helper", __FILE__)
2
-
3
- module FriendlyId
4
- module Test
5
- module ActiveRecordAdapter
6
-
7
- class CustomNormalizerTest < ::Test::Unit::TestCase
8
-
9
- include FriendlyId::Test::ActiveRecordAdapter::Core
10
- include FriendlyId::Test::ActiveRecordAdapter::Slugged
11
- include FriendlyId::Test::CustomNormalizer
12
-
13
- def klass
14
- Person
15
- end
16
-
17
- end
18
- end
19
- end
20
- end
@@ -1,22 +0,0 @@
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
@@ -1,30 +0,0 @@
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
@@ -1,18 +0,0 @@
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
-
@@ -1,129 +0,0 @@
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 "should not use cached slug column with scopes" do
30
- @tourist = Tourist.create!(:name => "John Smith", :country => @usa)
31
- @tourist2 = Tourist.create!(:name => "John Smith", :country => @canada)
32
- assert_equal @canada, @canada.residents.find(@tourist2.friendly_id).country
33
- end
34
-
35
- test "a slugged model should auto-detect that it is being used as a parent scope" do
36
- assert_equal [Resident], Country.friendly_id_config.child_scopes
37
- end
38
-
39
- test "a slugged model should update its child model's scopes when its friendly_id changes" do
40
- @usa.update_attributes(:name => "United States")
41
- assert_equal "united-states", @usa.to_param
42
- assert_equal "united-states", @resident.slugs(true).first.scope
43
- end
44
-
45
- test "a non-slugged model should auto-detect that it is being used as a parent scope" do
46
- assert_equal [House], User.friendly_id_config.child_scopes
47
- end
48
-
49
- test "should update the slug when the scope changes" do
50
- @resident.update_attributes! :country => Country.create!(:name => "Argentina")
51
- assert_equal "argentina", @resident.slugs(true).first.scope
52
- end
53
-
54
- test "updating only the scope should not append sequence to friendly_id" do
55
- old_friendly_id = @resident.friendly_id
56
- @resident.update_attributes! :country => Country.create!(:name => "Argentina")
57
- assert_equal old_friendly_id, @resident.friendly_id
58
- end
59
-
60
- test "updating the scope should increment sequence to avoid conflicts" do
61
- old_friendly_id = @resident.friendly_id
62
- @resident.update_attributes! :country => @canada
63
- assert_equal "#{old_friendly_id}--2", @resident.friendly_id
64
- assert_equal "canada", @resident.slugs(true).first.scope
65
- end
66
-
67
- test "updating the scope should increment sequence correctly when there's more than one
68
- existing slug in scope" do
69
- resident4 = Resident.create!(:name => "John Smith", :country => @canada)
70
- old_friendly_id = @resident.friendly_id
71
- @resident.update_attributes! :country => @canada
72
- assert_equal "#{old_friendly_id}--3", @resident.friendly_id
73
- assert_equal "canada", @resident.slugs(true).first.scope
74
- end
75
-
76
- test "a non-slugged model should update its child model's scopes when its friendly_id changes" do
77
- @user.update_attributes(:name => "jack")
78
- assert_equal "jack", @user.to_param
79
- assert_equal "jack", @house.slugs(true).first.scope
80
- end
81
-
82
- test "should should not show the scope in the friendly_id" do
83
- assert_equal "john-smith", @resident.friendly_id
84
- assert_equal "john-smith", @resident2.friendly_id
85
- end
86
-
87
- test "should find all scoped records without scope" do
88
- name, sequence = @resident.friendly_id.parse_friendly_id
89
- assert_equal 2, Resident.find(:all, :joins => :slugs, :conditions => {
90
- :slugs => {:name => name, :sequence => sequence}}).size
91
- end
92
-
93
- test "should find a scoped record by friendly_id" do
94
- assert Resident.find(@resident.friendly_id)
95
- end
96
-
97
- test "should find a scope record as a relation member" do
98
- assert_equal @resident, @usa.residents.find("john-smith")
99
- assert_equal @resident2, @canada.residents.find("john-smith")
100
- end
101
-
102
- # test "scope records found as a relation member should be 'best'" do
103
- # assert_equal @resident, @usa.residents.find("john-smith")
104
- # assert @resident.friendly_id_status.best?
105
- # assert_equal @resident2, @canada.residents.find("john-smith")
106
- # assert @resident2.friendly_id_status.best?
107
- # end
108
-
109
- test "should find a single scoped record using slug conditions" do
110
- assert_equal @resident, Resident.find(@resident.friendly_id, :include => :slugs,
111
- :conditions => {:slugs => {:scope => @resident.country.to_param}})
112
- end
113
-
114
- test "should update the sluggable field when a polymorphic relationship exists" do
115
- @site.update_attributes(:name => "Uptown Venue")
116
- assert_equal "Uptown Venue", @site.name
117
- end
118
-
119
- test "should not assume that AR's reflect_on_all_associations with return AR classes" do
120
- reflections = Resident.reflect_on_all_associations
121
- reflections << Struct.new("Dummy", :options, :klass).new(:options => [], :klass => Struct)
122
- Resident.expects(:reflect_on_all_associations).returns(reflections)
123
- assert_nothing_raised do
124
- Resident.friendly_id_config.send(:associated_friendly_classes)
125
- end
126
- end
127
- end
128
- end
129
- end
@@ -1,76 +0,0 @@
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
-
@@ -1,34 +0,0 @@
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