friendly_id 3.3.3.0 → 4.0.0.beta7

Sign up to get free protection for your applications and to get access to all the features.
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