dougcole-friendly_id 2.0.5 → 2.0.6

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 (43) hide show
  1. data/History.txt +20 -0
  2. data/Manifest.txt +10 -18
  3. data/README.rdoc +34 -5
  4. data/Rakefile +8 -0
  5. data/VERSION.yml +1 -1
  6. data/lib/friendly_id.rb +33 -13
  7. data/lib/friendly_id/non_sluggable_class_methods.rb +3 -3
  8. data/lib/friendly_id/non_sluggable_instance_methods.rb +9 -1
  9. data/lib/friendly_id/slug.rb +14 -12
  10. data/lib/friendly_id/sluggable_class_methods.rb +14 -3
  11. data/lib/friendly_id/sluggable_instance_methods.rb +11 -4
  12. data/lib/friendly_id/version.rb +1 -1
  13. data/test/custom_slug_normalizer_test.rb +35 -0
  14. data/test/models/book.rb +2 -0
  15. data/test/{fixtures → models}/country.rb +0 -0
  16. data/test/models/novel.rb +3 -0
  17. data/test/{fixtures → models}/person.rb +0 -0
  18. data/test/models/post.rb +3 -0
  19. data/test/models/thing.rb +6 -0
  20. data/test/{fixtures → models}/user.rb +0 -0
  21. data/test/non_slugged_test.rb +71 -60
  22. data/test/schema.rb +29 -20
  23. data/test/scoped_model_test.rb +43 -13
  24. data/test/slug_test.rb +93 -74
  25. data/test/slugged_model_test.rb +263 -0
  26. data/test/sti_test.rb +48 -0
  27. data/test/test_helper.rb +30 -29
  28. metadata +15 -20
  29. data/lib/friendly_id/shoulda_macros.rb +0 -36
  30. data/test/database.yml +0 -3
  31. data/test/fixtures/countries.yml +0 -4
  32. data/test/fixtures/people.yml +0 -7
  33. data/test/fixtures/post.rb +0 -3
  34. data/test/fixtures/posts.yml +0 -23
  35. data/test/fixtures/slugs.yml +0 -53
  36. data/test/fixtures/users.yml +0 -7
  37. data/test/rails/2.x/app/controllers/application.rb +0 -0
  38. data/test/rails/2.x/config/boot.rb +0 -109
  39. data/test/rails/2.x/config/database.yml +0 -3
  40. data/test/rails/2.x/config/environment.rb +0 -7
  41. data/test/rails/2.x/config/environments/test.rb +0 -6
  42. data/test/rails/2.x/config/routes.rb +0 -0
  43. data/test/sluggable_test.rb +0 -185
@@ -0,0 +1,2 @@
1
+ class Book < ActiveRecord::Base
2
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ class Novel < Book
2
+ has_friendly_id :title, :use_slug => true
3
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ class Post < ActiveRecord::Base
2
+ has_friendly_id :title, :use_slug => true
3
+ end
@@ -0,0 +1,6 @@
1
+ require 'digest/sha1'
2
+ class Thing < ActiveRecord::Base
3
+ has_friendly_id :name, :use_slug => true do |text|
4
+ Digest::SHA1::hexdigest(text)
5
+ end
6
+ end
File without changes
@@ -2,84 +2,95 @@ require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
3
  class NonSluggedTest < Test::Unit::TestCase
4
4
 
5
- fixtures :users
5
+ context "A non-slugged model with default FriendlyId options" do
6
6
 
7
- def setup
8
- end
7
+ setup do
8
+ User.delete_all
9
+ @user = User.create!(:login => "joe", :email => "joe@example.org")
10
+ end
9
11
 
10
- def test_should_find_user_using_friendly_id
11
- assert User.find(users(:joe).friendly_id)
12
- end
12
+ should "have friendly_id options" do
13
+ assert_not_nil User.friendly_id_options
14
+ end
13
15
 
14
- def test_should_find_users_using_friendly_id
15
- assert User.find([users(:joe).friendly_id])
16
- end
16
+ should "not have a slug" do
17
+ assert !@user.respond_to?(:slug)
18
+ end
17
19
 
18
- def test_to_param_should_return_a_string
19
- assert_equal String, users(:joe).to_param.class
20
- end
20
+ should "be findable by its friendly_id" do
21
+ assert User.find(@user.friendly_id)
22
+ end
21
23
 
22
- def test_should_not_find_users_using_non_existent_friendly_ids
23
- assert_raises ActiveRecord::RecordNotFound do
24
- User.find(['bad', 'bad2'])
24
+ should "be findable by its regular id" do
25
+ assert User.find(@user.id)
25
26
  end
26
- end
27
-
28
- def test_finding_by_array_with_friendly_and_non_friendly_id_for_same_record_raises_error
29
- assert_raises ActiveRecord::RecordNotFound do
30
- User.find([users(:joe).id, "joe"]).size
27
+
28
+ should "respect finder conditions" do
29
+ assert_raises ActiveRecord::RecordNotFound do
30
+ User.find(@user.friendly_id, :conditions => "1 = 2")
31
+ end
31
32
  end
32
- end
33
33
 
34
- def test_finding_with_mixed_array_should_indicate_whether_found_by_numeric_or_friendly
35
- @users = User.find([users(:jane).id, "joe"], :order => "login ASC")
36
- assert @users[0].found_using_numeric_id?
37
- assert @users[1].found_using_friendly_id?
38
- end
34
+ should "indicate if it was found by its friendly id" do
35
+ @user = User.find(@user.friendly_id)
36
+ assert @user.found_using_friendly_id?
37
+ end
39
38
 
40
- def test_finder_options_are_not_ignored
41
- assert_raises ActiveRecord::RecordNotFound do
42
- User.find(users(:joe).friendly_id, :conditions => "1 = 2")
39
+ should "indicate if it was found by its numeric id" do
40
+ @user = User.find(@user.id)
41
+ assert @user.found_using_numeric_id?
43
42
  end
44
- assert_raises ActiveRecord::RecordNotFound do
45
- User.find([users(:joe).friendly_id], :conditions => "1 = 2")
43
+
44
+ should "indicate if it has a better id" do
45
+ @user = User.find(@user.id)
46
+ assert @user.has_better_id?
46
47
  end
47
- end
48
48
 
49
- def test_user_should_have_friendly_id_options
50
- assert_not_nil User.friendly_id_options
51
- end
49
+ should "not validate if the friendly_id text is reserved" do
50
+ @user = User.new(:login => "new", :email => "test@example.org")
51
+ assert !@user.valid?
52
+ end
52
53
 
53
- def test_user_should_not_be_found_using_friendly_id_unless_it_really_was
54
- assert !User.find(users(:joe).id).found_using_friendly_id?
55
- end
54
+ should "have always string for a friendly_id" do
55
+ assert_equal String, @user.to_param.class
56
+ end
56
57
 
57
- def test_users_should_not_be_found_using_friendly_id_unless_they_really_were
58
- @users = User.find([users(:jane).id])
59
- assert @users[0].found_using_numeric_id?
60
- end
58
+ should "return its id if the friendly_id is null" do
59
+ @user.login = nil
60
+ assert_equal @user.id.to_s, @user.to_param
61
+ end
61
62
 
62
- def test_user_should_be_considered_found_by_numeric_id_as_default
63
- @user = User.new
64
- assert @user.found_using_numeric_id?
65
- end
66
63
 
67
- def test_user_should_indicate_if_it_was_found_using_numeric_id
68
- @user = User.find(users(:joe).id)
69
- assert @user.found_using_numeric_id?
70
- assert !@user.found_using_friendly_id?
71
- end
64
+ context "when using an array as the find argument" do
72
65
 
73
- def test_user_should_indicate_if_it_was_found_using_friendly_id
74
- @user = User.find(users(:joe).friendly_id)
75
- assert !@user.found_using_numeric_id?
76
- assert @user.found_using_friendly_id?
77
- end
66
+ setup do
67
+ @user2 = User.create(:login => "jane", :email => "jane@example.org")
68
+ end
69
+
70
+ should "return results" do
71
+ assert_equal 2, User.find([@user.friendly_id, @user2.friendly_id]).size
72
+ end
73
+
74
+ should "not allow mixed friendly and non-friendly ids for the same record" do
75
+ assert_raises ActiveRecord::RecordNotFound do
76
+ User.find([@user.id, @user.friendly_id]).size
77
+ end
78
+ end
79
+
80
+ should "raise an error when all records are not found" do
81
+ assert_raises ActiveRecord::RecordNotFound do
82
+ User.find(['bad', 'bad2'])
83
+ end
84
+ end
85
+
86
+ should "indicate if the results were found using a friendly_id" do
87
+ @users = User.find([@user.id, @user2.friendly_id], :order => "login ASC")
88
+ assert @users[0].found_using_friendly_id?
89
+ assert @users[1].found_using_numeric_id?
90
+ end
91
+
92
+ end
78
93
 
79
- def test_should_indicate_there_is_a_better_id_if_found_by_numeric_id
80
- @user = User.find(users(:joe).id)
81
- assert @user.found_using_numeric_id?
82
- assert @user.has_better_id?
83
94
  end
84
95
 
85
96
  end
data/test/schema.rb CHANGED
@@ -1,35 +1,44 @@
1
- ActiveRecord::Schema.define(:version => 3) do
1
+ ActiveRecord::Schema.define(:version => 1) do
2
+
3
+ create_table "books", :force => true do |t|
4
+ t.column "title", "string"
5
+ t.column "type", "text"
6
+ end
7
+
8
+ create_table "things", :force => true do |t|
9
+ t.column "name", "string"
10
+ end
2
11
 
3
12
  create_table "posts", :force => true do |t|
4
- t.string "name"
5
- t.text "content"
6
- t.datetime "created_at"
7
- t.datetime "updated_at"
13
+ t.column "title", "string"
14
+ t.column "content", "text"
15
+ t.column "created_at", "datetime"
16
+ t.column "updated_at", "datetime"
8
17
  end
9
18
 
10
19
  create_table "users", :force => true do |t|
11
- t.string "login"
12
- t.string "email"
13
- t.datetime "created_at"
14
- t.datetime "updated_at"
20
+ t.column "login", "string"
21
+ t.column "email", "string"
22
+ t.column "created_at", "datetime"
23
+ t.column "updated_at", "datetime"
15
24
  end
16
-
25
+
17
26
  create_table "people", :force => true do |t|
18
- t.string "name"
19
- t.integer "country_id"
27
+ t.column "name", "string"
28
+ t.column "country_id", "integer"
20
29
  end
21
-
30
+
22
31
  create_table "countries", :force => true do |t|
23
- t.string "name"
32
+ t.column "name", "string"
24
33
  end
25
34
 
26
35
  create_table "slugs", :force => true do |t|
27
- t.string "name"
28
- t.integer "sluggable_id"
29
- t.integer "sequence", :null => false, :default => 1
30
- t.string "sluggable_type", :limit => 40
31
- t.string "scope", :limit => 40
32
- t.datetime "created_at"
36
+ t.column "name", "string"
37
+ t.column "sluggable_id", "integer"
38
+ t.column "sequence", "integer", :null => false, :default => 1
39
+ t.column "sluggable_type", "string", :limit => 40
40
+ t.column "scope", "string", :limit => 40
41
+ t.column "created_at", "datetime"
33
42
  end
34
43
 
35
44
  add_index "slugs", ["sluggable_id"], :name => "index_slugs_on_sluggable_id"
@@ -2,20 +2,50 @@ require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
3
  class ScopedModelTest < Test::Unit::TestCase
4
4
 
5
- fixtures :people, :countries, :slugs
6
-
7
- def test_should_find_scoped_records_without_scope
8
- assert_equal 2, Person.find(:all, "john-smith").size
9
- end
5
+ context "A slugged model that uses a scope" do
6
+
7
+ setup do
8
+ Person.delete_all
9
+ Country.delete_all
10
+ Slug.delete_all
11
+ @usa = Country.create!(:name => "USA")
12
+ @canada = Country.create!(:name => "Canada")
13
+ @person = Person.create!(:name => "John Smith", :country => @usa)
14
+ @person2 = Person.create!(:name => "John Smith", :country => @canada)
15
+ end
16
+
17
+ should "find all scoped records without scope" do
18
+ assert_equal 2, Person.find(:all, @person.friendly_id).size
19
+ end
20
+
21
+ should "find a single scoped records with a scope" do
22
+ assert Person.find(@person.friendly_id, :scope => @person.country.to_param)
23
+ end
24
+
25
+ should "raise an error when finding a single scoped record with no scope" do
26
+ assert_raises ActiveRecord::RecordNotFound do
27
+ Person.find(@person.friendly_id)
28
+ end
29
+ end
30
+
31
+ should "append scope error info when missing scope causes a find to fail" do
32
+ begin
33
+ Person.find(@person.friendly_id)
34
+ fail "The find should not have succeeded"
35
+ rescue ActiveRecord::RecordNotFound => e
36
+ assert_match /expected scope/, e.message
37
+ end
38
+ end
39
+
40
+ should "append scope error info when the scope value causes a find to fail" do
41
+ begin
42
+ Person.find(@person.friendly_id, :scope => "badscope")
43
+ fail "The find should not have succeeded"
44
+ rescue ActiveRecord::RecordNotFound => e
45
+ assert_match /scope=badscope/, e.message
46
+ end
47
+ end
10
48
 
11
- def test_should_find_scoped_records_with_scope
12
- assert_equal people(:john_smith), Person.find("john-smith", :scope => "argentina")
13
- assert_equal people(:john_smith2), Person.find("john-smith", :scope => "usa")
14
- end
15
-
16
- def test_should_create_scoped_records_with_scope
17
- person = Person.create!(:name => "Joe Schmoe", :country => countries(:usa))
18
- assert_equal "usa", person.slug.scope
19
49
  end
20
50
 
21
51
  end
data/test/slug_test.rb CHANGED
@@ -1,87 +1,106 @@
1
+ # encoding: utf-8
2
+
1
3
  require File.dirname(__FILE__) + '/test_helper'
2
4
 
3
5
  class SlugTest < Test::Unit::TestCase
4
6
 
5
- fixtures :posts, :slugs
6
-
7
- def test_should_indicate_if_it_is_the_most_recent
8
- assert slugs(:two_new).is_most_recent?
9
- assert !slugs(:two_old).is_most_recent?
10
- end
11
-
12
- def test_parse_should_return_slug_name_and_sequence
13
- assert_equal ["test", "2"], Slug::parse("test--2")
14
- end
15
-
16
- def test_parse_should_return_a_default_sequnce_of_1
17
- assert_equal ["test", "1"], Slug::parse("test")
18
- end
19
-
20
- def test_strip_diacritics_should_strip_diacritics
21
- assert_equal "acai", Slug::strip_diacritics("açaí")
22
- end
23
-
24
- def test_to_friendly_id_should_include_sequence_if_its_greater_than_1
25
- slug = Slug.new(:name => "test", :sequence => 2)
26
- assert_equal "test--2", slug.to_friendly_id
27
- end
28
-
29
- def test_to_friendly_id_should_include_sequence_if_its_than_1
30
- slug = Slug.new(:name => "test", :sequence => 1)
31
- assert_equal "test", slug.to_friendly_id
32
- end
33
-
34
- def test_normalize_should_lowercase_strings
35
- assert_match /abc/, Slug::normalize("ABC")
36
- end
37
-
38
- def test_normalize_should_replace_whitespace_with_dashes
39
- assert_match /a-b/, Slug::normalize("a b")
40
- end
41
-
42
- def test_normalize_should_replace_2spaces_with_1dash
43
- assert_match /a-b/, Slug::normalize("a b")
44
- end
45
-
46
- def test_normalize_should_remove_punctuation
47
- assert_match /abc/, Slug::normalize('abc!@#$%^&*•¶§∞¢££¡¿()><?"":;][]\.,/')
48
- end
49
-
50
- def test_normalize_should_strip_trailing_space
51
- assert_match /ab/, Slug::normalize("ab ")
52
- end
53
-
54
- def test_normalize_should_strip_leading_space
55
- assert_match /ab/, Slug::normalize(" ab")
56
- end
57
-
58
- def test_normalize_should_strip_trailing_slashes
59
- assert_match /ab/, Slug::normalize("ab-")
60
- end
7
+ context "a slug" do
8
+
9
+ setup do
10
+ Slug.delete_all
11
+ Post.delete_all
12
+ end
61
13
 
62
- def test_normalize_should_strip_leading_slashes
63
- assert_match /ab/, Slug::normalize("-ab")
64
- end
14
+ should "indicate if it is the most recent slug" do
15
+ @post = Post.create!(:title => "test title", :content => "test content")
16
+ @post.title = "a new title"
17
+ @post.save!
18
+ assert @post.slugs.last.is_most_recent?
19
+ assert !@post.slugs.first.is_most_recent?
20
+ end
65
21
 
66
- def test_normalize_should_not_modify_valid_name_strings
67
- assert_match /a-b-c-d/, Slug::normalize("a-b-c-d")
68
22
  end
23
+
24
+ context "the Slug class" do
25
+
26
+ should "parse the slug name and sequence" do
27
+ assert_equal ["test", "2"], Slug::parse("test--2")
28
+ end
29
+
30
+ should "parse with a default sequence of 1" do
31
+ assert_equal ["test", "1"], Slug::parse("test")
32
+ end
69
33
 
70
- # These strings are taken from various international Google homepages. I
71
- # would be most grateful if a fluent speaker of any language that uses a
72
- # writing system other than the Roman alphabet could help me make some
73
- # better tests to ensure this is working correctly.
74
- def test_normalize_works_with_non_roman_chars
75
- assert_equal "検-索", Slug::normalize("検 索")
34
+ should "should strip diacritics" do
35
+ assert_equal "acai", Slug::strip_diacritics("açaí")
36
+ end
37
+
38
+ should "strip diacritics correctly " do
39
+ input = "ÀÁÂÃÄÅÆÇÈÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ"
40
+ output = Slug::strip_diacritics(input).split(//)
41
+ expected = "AAAAAAAECEEEIIIIDNOOOOOOUUUUYThssaaaaaaaeceeeeiiiidnoooooouuuuythy".split(//)
42
+ output.split.each_index do |i|
43
+ assert_equal output[i], expected[i]
44
+ end
45
+ end
46
+
76
47
  end
77
-
78
- def test_strip_diactics_correctly_strips_diacritics
79
- input = "ÀÁÂÃÄÅÆÇÈÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿ"
80
- output = Slug::strip_diacritics(input).split(//)
81
- expected = "AAAAAAAECEEEIIIIDNOOOOOOUUUUYThssaaaaaaaeceeeeiiiidnoooooouuuuythy".split(//)
82
- output.split.each_index do |i|
83
- assert_equal output[i], expected[i]
48
+
49
+ context "the Slug class's to_friendly_id method" do
50
+
51
+ should "include the sequence if the sequence is greater than 1" do
52
+ slug = Slug.new(:name => "test", :sequence => 2)
53
+ assert_equal "test--2", slug.to_friendly_id
84
54
  end
55
+
56
+ should "not include the sequence if the sequence is 1" do
57
+ slug = Slug.new(:name => "test", :sequence => 1)
58
+ assert_equal "test", slug.to_friendly_id
59
+ end
60
+
85
61
  end
86
62
 
63
+ context "the Slug class's normalize method" do
64
+
65
+ should "should lowercase strings" do
66
+ assert_match /abc/, Slug::normalize("ABC")
67
+ end
68
+
69
+ should "should replace whitespace with dashes" do
70
+ assert_match /a-b/, Slug::normalize("a b")
71
+ end
72
+
73
+ should "should replace 2spaces with 1dash" do
74
+ assert_match /a-b/, Slug::normalize("a b")
75
+ end
76
+
77
+ should "should remove punctuation" do
78
+ assert_match /abc/, Slug::normalize('abc!@#$%^&*•¶§∞¢££¡¿()><?"":;][]\.,/')
79
+ end
80
+
81
+ should "should strip trailing space" do
82
+ assert_match /ab/, Slug::normalize("ab ")
83
+ end
84
+
85
+ should "should strip leading space" do
86
+ assert_match /ab/, Slug::normalize(" ab")
87
+ end
88
+
89
+ should "should strip trailing slashes" do
90
+ assert_match /ab/, Slug::normalize("ab-")
91
+ end
92
+
93
+ should "should strip leading slashes" do
94
+ assert_match /ab/, Slug::normalize("-ab")
95
+ end
96
+
97
+ should "should not modify valid name strings" do
98
+ assert_match /a-b-c-d/, Slug::normalize("a-b-c-d")
99
+ end
100
+
101
+ should "work with non roman chars" do
102
+ assert_equal "検-索", Slug::normalize("検 索")
103
+ end
104
+
105
+ end
87
106
  end