mil_friendly_id 4.0.9.8

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 (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
+