mil_friendly_id 4.0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +12 -0
  4. data/.travis.yml +20 -0
  5. data/.yardopts +4 -0
  6. data/Changelog.md +86 -0
  7. data/Gemfile +15 -0
  8. data/Guide.rdoc +553 -0
  9. data/MIT-LICENSE +19 -0
  10. data/README.md +150 -0
  11. data/Rakefile +108 -0
  12. data/WhatsNew.md +95 -0
  13. data/bench.rb +63 -0
  14. data/friendly_id.gemspec +43 -0
  15. data/gemfiles/Gemfile.rails-3.0.rb +21 -0
  16. data/gemfiles/Gemfile.rails-3.1.rb +22 -0
  17. data/gemfiles/Gemfile.rails-3.2.rb +22 -0
  18. data/geothird_friendly_id.gemspec +45 -0
  19. data/lib/friendly_id.rb +114 -0
  20. data/lib/friendly_id/base.rb +291 -0
  21. data/lib/friendly_id/configuration.rb +80 -0
  22. data/lib/friendly_id/finder_methods.rb +35 -0
  23. data/lib/friendly_id/globalize.rb +115 -0
  24. data/lib/friendly_id/history.rb +134 -0
  25. data/lib/friendly_id/migration.rb +19 -0
  26. data/lib/friendly_id/object_utils.rb +50 -0
  27. data/lib/friendly_id/reserved.rb +68 -0
  28. data/lib/friendly_id/scoped.rb +149 -0
  29. data/lib/friendly_id/simple_i18n.rb +95 -0
  30. data/lib/friendly_id/slug.rb +14 -0
  31. data/lib/friendly_id/slug_generator.rb +80 -0
  32. data/lib/friendly_id/slugged.rb +329 -0
  33. data/lib/generators/friendly_id_generator.rb +17 -0
  34. data/mil_friendly_id.gemspec +45 -0
  35. data/test/base_test.rb +72 -0
  36. data/test/compatibility/ancestry/Gemfile +8 -0
  37. data/test/compatibility/ancestry/ancestry_test.rb +34 -0
  38. data/test/compatibility/threading/Gemfile +8 -0
  39. data/test/compatibility/threading/threading.rb +45 -0
  40. data/test/configuration_test.rb +48 -0
  41. data/test/core_test.rb +48 -0
  42. data/test/databases.yml +19 -0
  43. data/test/generator_test.rb +20 -0
  44. data/test/globalize_test.rb +57 -0
  45. data/test/helper.rb +87 -0
  46. data/test/history_test.rb +149 -0
  47. data/test/object_utils_test.rb +28 -0
  48. data/test/reserved_test.rb +40 -0
  49. data/test/schema.rb +79 -0
  50. data/test/scoped_test.rb +83 -0
  51. data/test/shared.rb +156 -0
  52. data/test/simple_i18n_test.rb +133 -0
  53. data/test/slugged_test.rb +280 -0
  54. data/test/sti_test.rb +77 -0
  55. metadata +262 -0
@@ -0,0 +1,149 @@
1
+ require "helper"
2
+
3
+ class Manual < ActiveRecord::Base
4
+ extend FriendlyId
5
+ friendly_id :name, :use => :history
6
+ end
7
+
8
+ class HistoryTest < MiniTest::Unit::TestCase
9
+
10
+ include FriendlyId::Test
11
+ include FriendlyId::Test::Shared::Core
12
+
13
+ def model_class
14
+ Manual
15
+ end
16
+
17
+ test "should insert record in slugs table on create" do
18
+ with_instance_of(model_class) {|record| assert record.slugs.any?}
19
+ end
20
+
21
+ test "should not create new slug record if friendly_id is not changed" do
22
+ with_instance_of(model_class) do |record|
23
+ record.active = true
24
+ record.save!
25
+ assert_equal 1, FriendlyId::Slug.count
26
+ end
27
+ end
28
+
29
+ test "should create new slug record when friendly_id changes" do
30
+ with_instance_of(model_class) do |record|
31
+ record.name = record.name + "b"
32
+ record.save!
33
+ assert_equal 2, FriendlyId::Slug.count
34
+ end
35
+ end
36
+
37
+ test "should be findable by old slugs" do
38
+ with_instance_of(model_class) do |record|
39
+ old_friendly_id = record.friendly_id
40
+ record.name = record.name + "b"
41
+ record.save!
42
+ begin
43
+ assert model_class.find(old_friendly_id)
44
+ assert model_class.exists?(old_friendly_id), "should exist? by old id"
45
+ rescue ActiveRecord::RecordNotFound
46
+ flunk "Could not find record by old id"
47
+ end
48
+ end
49
+ end
50
+
51
+ test "should create slug records on each change" do
52
+ transaction do
53
+ record = model_class.create! :name => "hello"
54
+ assert_equal 1, FriendlyId::Slug.count
55
+ record = model_class.find("hello")
56
+ record.name = "hello again"
57
+ record.save!
58
+ assert_equal 2, FriendlyId::Slug.count
59
+ end
60
+ end
61
+
62
+ test "should not be read only when found by old slug" do
63
+ with_instance_of(model_class) do |record|
64
+ old_friendly_id = record.friendly_id
65
+ record.name = record.name + "b"
66
+ record.save!
67
+ assert !model_class.find(old_friendly_id).readonly?
68
+ end
69
+ end
70
+
71
+ test "should create correct sequence numbers even when some conflicted slugs have changed" do
72
+ transaction do
73
+ record1 = model_class.create! :name => 'hello'
74
+ record2 = model_class.create! :name => 'hello!'
75
+ record2.update_attributes :name => 'goodbye'
76
+ record3 = model_class.create! :name => 'hello!'
77
+ assert_equal 'hello--3', record3.slug
78
+ end
79
+ end
80
+
81
+
82
+ test "should raise error if used with scoped" do
83
+ model_class = Class.new(ActiveRecord::Base) do
84
+ self.abstract_class = true
85
+ extend FriendlyId
86
+ end
87
+ assert_raises RuntimeError do
88
+ model_class.friendly_id :name, :use => [:history, :scoped]
89
+ end
90
+ end
91
+
92
+ test "should handle renames" do
93
+ with_instance_of(model_class) do |record|
94
+ record.name = 'x'
95
+ assert record.save
96
+ record.name = 'y'
97
+ assert record.save
98
+ record.name = 'x'
99
+ assert record.save
100
+ end
101
+ end
102
+
103
+ test "should not create new slugs that match old slugs" do
104
+ transaction do
105
+ first_record = model_class.create! :name => "foo"
106
+ first_record.name = "bar"
107
+ first_record.save!
108
+ second_record = model_class.create! :name => "foo"
109
+ assert second_record.slug != "foo"
110
+ assert second_record.slug == "foo--2"
111
+ end
112
+ end
113
+
114
+ test 'should increment the sequence by one for each historic slug' do
115
+ transaction do
116
+ previous_record = model_class.create! :name => "foo"
117
+ first_record = model_class.create! :name => 'another'
118
+ second_record = model_class.create! :name => 'another'
119
+ assert second_record.slug == "another--2"
120
+ end
121
+ end
122
+
123
+ test 'should not fail when updating historic slugs' do
124
+ transaction do
125
+ first_record = model_class.create! :name => "foo"
126
+ second_record = model_class.create! :name => 'another'
127
+
128
+ second_record.update_attributes :name => 'foo'
129
+ assert second_record.slug == "foo--2"
130
+ first_record.update_attributes :name => 'another'
131
+ assert first_record.slug == "another--2"
132
+ end
133
+ end
134
+
135
+ end
136
+
137
+ class HistoryTestWithSti < HistoryTest
138
+ class Journalist < ActiveRecord::Base
139
+ extend FriendlyId
140
+ friendly_id :name, :use => [:slugged, :history]
141
+ end
142
+
143
+ class Editorialist < Journalist
144
+ end
145
+
146
+ def model_class
147
+ Editorialist
148
+ end
149
+ end
@@ -0,0 +1,28 @@
1
+ require "helper"
2
+
3
+
4
+ class ObjectUtilsTest < MiniTest::Unit::TestCase
5
+
6
+ include FriendlyId::Test
7
+
8
+ test "strings with letters are friendly_ids" do
9
+ assert "a".friendly_id?
10
+ end
11
+
12
+ test "integers should be unfriendly ids" do
13
+ assert 1.unfriendly_id?
14
+ end
15
+
16
+ test "numeric strings are neither friendly nor unfriendly" do
17
+ assert_equal nil, "1".friendly_id?
18
+ assert_equal nil, "1".unfriendly_id?
19
+ end
20
+
21
+ test "ActiveRecord::Base instances should be unfriendly_ids" do
22
+ model_class = Class.new(ActiveRecord::Base) do
23
+ self.abstract_class = true
24
+ self.table_name = "authors"
25
+ end
26
+ assert model_class.new.unfriendly_id?
27
+ end
28
+ end
@@ -0,0 +1,40 @@
1
+ require "helper"
2
+
3
+ class ReservedTest < MiniTest::Unit::TestCase
4
+
5
+ include FriendlyId::Test
6
+
7
+ class Journalist < ActiveRecord::Base
8
+ extend FriendlyId
9
+ friendly_id :name
10
+
11
+ after_validation :move_friendly_id_error_to_name
12
+
13
+ def move_friendly_id_error_to_name
14
+ errors.add :name, *errors.delete(:friendly_id) if errors[:friendly_id].present?
15
+ end
16
+ end
17
+
18
+ def model_class
19
+ Journalist
20
+ end
21
+
22
+ test "should reserve 'new' and 'edit' by default" do
23
+ %w(new edit).each do |word|
24
+ transaction do
25
+ assert_raises(ActiveRecord::RecordInvalid) {model_class.create! :name => word}
26
+ end
27
+ end
28
+ end
29
+
30
+ test "should move friendly_id error to name" do
31
+ with_instance_of(model_class) do |record|
32
+ record.errors.add :name, "xxx"
33
+ record.errors.add :friendly_id, "yyy"
34
+ record.move_friendly_id_error_to_name
35
+ assert record.errors[:name].present? && record.errors[:friendly_id].blank?
36
+ assert_equal 2, record.errors.count
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,79 @@
1
+ require "friendly_id/migration"
2
+ require "globalize3"
3
+
4
+ class TranslatedArticle < ActiveRecord::Base
5
+ translates :slug, :title
6
+ end
7
+
8
+ module FriendlyId
9
+ module Test
10
+ class Schema < ActiveRecord::Migration
11
+ class << self
12
+ def down
13
+ CreateFriendlyIdSlugs.down
14
+ tables.each do |name|
15
+ drop_table name
16
+ end
17
+ TranslatedArticle.drop_translation_table!
18
+ end
19
+
20
+ def up
21
+ # TODO: use schema version to avoid ugly hacks like this
22
+ return if @done
23
+ CreateFriendlyIdSlugs.up
24
+
25
+ tables.each do |table_name|
26
+ create_table table_name do |t|
27
+ t.string :name
28
+ t.boolean :active
29
+ end
30
+ end
31
+
32
+ slugged_tables.each do |table_name|
33
+ add_column table_name, :slug, :string
34
+ add_index table_name, :slug, :unique => true
35
+ end
36
+
37
+ # This will be used to test scopes
38
+ add_column :novels, :novelist_id, :integer
39
+ add_column :novels, :publisher_id, :integer
40
+ remove_index :novels, :slug
41
+ add_index :novels, [:slug, :publisher_id, :novelist_id], :unique => true
42
+
43
+ # This will be used to test column name quoting
44
+ add_column :journalists, "strange name", :string
45
+
46
+ # This will be used to test STI
47
+ add_column :journalists, "type", :string
48
+
49
+ # These will be used to test i18n
50
+ add_column :journalists, "slug_en", :string
51
+ add_column :journalists, "slug_es", :string
52
+ add_column :journalists, "slug_de", :string
53
+
54
+ # This will be used to test globalize translations
55
+ TranslatedArticle.create_translation_table! :slug => :string, :title => :string
56
+
57
+ # This will be used to test relationships
58
+ add_column :books, :author_id, :integer
59
+
60
+ @done = true
61
+ end
62
+
63
+ private
64
+
65
+ def slugged_tables
66
+ %w[journalists articles novelists novels manuals translated_articles]
67
+ end
68
+
69
+ def simple_tables
70
+ %w[authors books publishers]
71
+ end
72
+
73
+ def tables
74
+ simple_tables + slugged_tables
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,83 @@
1
+ require "helper"
2
+
3
+ class Novelist < ActiveRecord::Base
4
+ extend FriendlyId
5
+ friendly_id :name, :use => :slugged
6
+ end
7
+
8
+ class Novel < ActiveRecord::Base
9
+ extend FriendlyId
10
+ belongs_to :novelist
11
+ belongs_to :publisher
12
+ friendly_id :name, :use => :scoped, :scope => [:publisher, :novelist]
13
+ end
14
+
15
+ class Publisher < ActiveRecord::Base
16
+ has_many :novels
17
+ end
18
+
19
+ class ScopedTest < MiniTest::Unit::TestCase
20
+
21
+ include FriendlyId::Test
22
+ include FriendlyId::Test::Shared::Core
23
+
24
+ def model_class
25
+ Novel
26
+ end
27
+
28
+ test "should detect scope column from belongs_to relation" do
29
+ assert_equal ["publisher_id", "novelist_id"], Novel.friendly_id_config.scope_columns
30
+ end
31
+
32
+ test "should detect scope column from explicit column name" do
33
+ model_class = Class.new(ActiveRecord::Base) do
34
+ self.abstract_class = true
35
+ extend FriendlyId
36
+ friendly_id :empty, :use => :scoped, :scope => :dummy
37
+ end
38
+ assert_equal ["dummy"], model_class.friendly_id_config.scope_columns
39
+ end
40
+
41
+ test "should allow duplicate slugs outside scope" do
42
+ transaction do
43
+ novel1 = Novel.create! :name => "a", :novelist => Novelist.create!(:name => "a")
44
+ novel2 = Novel.create! :name => "a", :novelist => Novelist.create!(:name => "b")
45
+ assert_equal novel1.friendly_id, novel2.friendly_id
46
+ end
47
+ end
48
+
49
+ test "should not allow duplicate slugs inside scope" do
50
+ with_instance_of Novelist do |novelist|
51
+ novel1 = Novel.create! :name => "a", :novelist => novelist
52
+ novel2 = Novel.create! :name => "a", :novelist => novelist
53
+ assert novel1.friendly_id != novel2.friendly_id
54
+ end
55
+ end
56
+
57
+ test "should raise error if used with history" do
58
+ model_class = Class.new(ActiveRecord::Base) do
59
+ self.abstract_class = true
60
+ extend FriendlyId
61
+ end
62
+
63
+ assert_raises RuntimeError do
64
+ model_class.friendly_id :name, :use => [:scoped, :history]
65
+ end
66
+ end
67
+
68
+ test "should apply scope with multiple columns" do
69
+ transaction do
70
+ novelist = Novelist.create! :name => "a"
71
+ publisher = Publisher.create! :name => "b"
72
+
73
+ novel1 = Novel.create! :name => "c", :novelist => novelist, :publisher => publisher
74
+ novel2 = Novel.create! :name => "c", :novelist => novelist, :publisher => Publisher.create(:name => "d")
75
+ novel3 = Novel.create! :name => "c", :novelist => Novelist.create(:name => "e"), :publisher => publisher
76
+ novel4 = Novel.create! :name => "c", :novelist => novelist, :publisher => publisher
77
+
78
+ assert_equal novel1.friendly_id, novel2.friendly_id
79
+ assert_equal novel2.friendly_id, novel3.friendly_id
80
+ assert novel3.friendly_id != novel4.friendly_id
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,156 @@
1
+ module FriendlyId
2
+ module Test
3
+ module Shared
4
+
5
+ module Slugged
6
+ test "configuration should have a sequence_separator" do
7
+ assert !model_class.friendly_id_config.sequence_separator.empty?
8
+ end
9
+
10
+ test "should make a new slug if the friendly_id method value has changed" do
11
+ with_instance_of model_class do |record|
12
+ record.name = "Changed Value"
13
+ record.save!
14
+ assert_equal "changed-value", record.slug
15
+ end
16
+ end
17
+
18
+ test "should increment the slug sequence for duplicate friendly ids" do
19
+ with_instance_of model_class do |record|
20
+ record2 = model_class.create! :name => record.name
21
+ assert record2.friendly_id.match(/2\z/)
22
+ end
23
+ end
24
+
25
+ test "should not add slug sequence on update after other conflicting slugs were added" do
26
+ with_instance_of model_class do |record|
27
+ old = record.friendly_id
28
+ model_class.create! :name => record.name
29
+ record.save!
30
+ record.reload
31
+ assert_equal old, record.to_param
32
+ end
33
+ end
34
+
35
+ test "should not increment sequence on save" do
36
+ with_instance_of model_class do |record|
37
+ record2 = model_class.create! :name => record.name
38
+ record2.active = !record2.active
39
+ record2.save!
40
+ assert record2.friendly_id.match(/2\z/)
41
+ end
42
+ end
43
+
44
+ test "should create slug on save if the slug is nil" do
45
+ with_instance_of model_class do |record|
46
+ record.slug = nil
47
+ record.save!
48
+ assert_nil record.slug
49
+ record.save!
50
+ refute_nil record.slug
51
+ end
52
+ end
53
+
54
+ test "when validations block save, to_param should return friendly_id rather than nil" do
55
+ my_model_class = Class.new(model_class)
56
+ self.class.const_set("Foo", my_model_class)
57
+ with_instance_of my_model_class do |record|
58
+ record.update_attributes my_model_class.friendly_id_config.slug_column => nil
59
+ record = my_model_class.find(record.id)
60
+ record.class.validate Proc.new {errors[:name] = "FAIL"}
61
+ record.save
62
+ assert_equal record.to_param, record.friendly_id
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ module Core
69
+ test "finds should respect conditions" do
70
+ with_instance_of(model_class) do |record|
71
+ assert_raises(ActiveRecord::RecordNotFound) do
72
+ model_class.where("1 = 2").find record.friendly_id
73
+ end
74
+ end
75
+ end
76
+
77
+ test "should be findable by friendly id" do
78
+ with_instance_of(model_class) {|record| assert model_class.find record.friendly_id}
79
+ end
80
+
81
+ test "should exist? by friendly id" do
82
+ with_instance_of(model_class) do |record|
83
+ assert model_class.exists? record.id
84
+ assert model_class.exists? record.friendly_id
85
+ assert model_class.exists?({:id => record.id})
86
+ assert model_class.exists?(['id = ?', record.id])
87
+ assert !model_class.exists?(record.friendly_id + "-hello")
88
+ assert !model_class.exists?(0)
89
+ end
90
+ end
91
+
92
+ test "should be findable by id as integer" do
93
+ with_instance_of(model_class) {|record| assert model_class.find record.id.to_i}
94
+ end
95
+
96
+ test "should be findable by id as string" do
97
+ with_instance_of(model_class) {|record| assert model_class.find record.id.to_s}
98
+ end
99
+
100
+ test "should be findable by numeric friendly_id" do
101
+ with_instance_of(model_class, :name => "206") {|record| assert model_class.find record.friendly_id}
102
+ end
103
+
104
+ test "to_param should return the friendly_id" do
105
+ with_instance_of(model_class) {|record| assert_equal record.friendly_id, record.to_param}
106
+ end
107
+
108
+ test "should be findable by themselves" do
109
+ with_instance_of(model_class) {|record| assert_equal record, model_class.find(record)}
110
+ end
111
+
112
+ test "updating record's other values should not change the friendly_id" do
113
+ with_instance_of model_class do |record|
114
+ old = record.friendly_id
115
+ record.update_attributes! :active => false
116
+ assert model_class.find old
117
+ end
118
+ end
119
+
120
+ test "instances found by a single id should not be read-only" do
121
+ with_instance_of(model_class) {|record| assert !model_class.find(record.friendly_id).readonly?}
122
+ end
123
+
124
+ test "failing finds with unfriendly_id should raise errors normally" do
125
+ assert_raises(ActiveRecord::RecordNotFound) {model_class.find 0}
126
+ end
127
+
128
+ test "should return numeric id if the friendly_id is nil" do
129
+ with_instance_of(model_class) do |record|
130
+ record.expects(:friendly_id).returns(nil)
131
+ assert_equal record.id.to_s, record.to_param
132
+ end
133
+ end
134
+
135
+ test "should return numeric id if the friendly_id is an empty string" do
136
+ with_instance_of(model_class) do |record|
137
+ record.expects(:friendly_id).returns("")
138
+ assert_equal record.id.to_s, record.to_param
139
+ end
140
+ end
141
+
142
+ test "should return numeric id if the friendly_id is blank" do
143
+ with_instance_of(model_class) do |record|
144
+ record.expects(:friendly_id).returns(" ")
145
+ assert_equal record.id.to_s, record.to_param
146
+ end
147
+ end
148
+
149
+ test "should return nil for to_param with a new record" do
150
+ assert_equal nil, model_class.new.to_param
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+