geothird_friendly_id 4.0.9.1
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.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +12 -0
- data/.travis.yml +20 -0
- data/.yardopts +4 -0
- data/Changelog.md +86 -0
- data/Gemfile +15 -0
- data/Guide.rdoc +553 -0
- data/MIT-LICENSE +19 -0
- data/README.md +150 -0
- data/Rakefile +108 -0
- data/WhatsNew.md +95 -0
- data/bench.rb +63 -0
- data/friendly_id.gemspec +43 -0
- data/gemfiles/Gemfile.rails-3.0.rb +21 -0
- data/gemfiles/Gemfile.rails-3.1.rb +22 -0
- data/gemfiles/Gemfile.rails-3.2.rb +22 -0
- data/geothird_friendly_id.gemspec +43 -0
- data/lib/friendly_id/base.rb +291 -0
- data/lib/friendly_id/configuration.rb +80 -0
- data/lib/friendly_id/finder_methods.rb +35 -0
- data/lib/friendly_id/globalize.rb +115 -0
- data/lib/friendly_id/history.rb +134 -0
- data/lib/friendly_id/migration.rb +18 -0
- data/lib/friendly_id/object_utils.rb +50 -0
- data/lib/friendly_id/reserved.rb +68 -0
- data/lib/friendly_id/scoped.rb +149 -0
- data/lib/friendly_id/simple_i18n.rb +95 -0
- data/lib/friendly_id/slug.rb +14 -0
- data/lib/friendly_id/slug_generator.rb +80 -0
- data/lib/friendly_id/slugged.rb +329 -0
- data/lib/friendly_id.rb +114 -0
- data/lib/generators/friendly_id_generator.rb +17 -0
- data/test/base_test.rb +72 -0
- data/test/compatibility/ancestry/Gemfile +8 -0
- data/test/compatibility/ancestry/ancestry_test.rb +34 -0
- data/test/compatibility/threading/Gemfile +8 -0
- data/test/compatibility/threading/Gemfile.lock +37 -0
- data/test/compatibility/threading/threading.rb +45 -0
- data/test/configuration_test.rb +48 -0
- data/test/core_test.rb +48 -0
- data/test/databases.yml +19 -0
- data/test/generator_test.rb +20 -0
- data/test/globalize_test.rb +57 -0
- data/test/helper.rb +87 -0
- data/test/history_test.rb +149 -0
- data/test/object_utils_test.rb +28 -0
- data/test/reserved_test.rb +40 -0
- data/test/schema.rb +79 -0
- data/test/scoped_test.rb +83 -0
- data/test/shared.rb +156 -0
- data/test/simple_i18n_test.rb +133 -0
- data/test/slugged_test.rb +280 -0
- data/test/sti_test.rb +77 -0
- metadata +247 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class ConfigurationTest < MiniTest::Unit::TestCase
|
4
|
+
|
5
|
+
include FriendlyId::Test
|
6
|
+
|
7
|
+
def setup
|
8
|
+
@model_class = Class.new(ActiveRecord::Base) do
|
9
|
+
self.abstract_class = true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
test "should set model class on initialization" do
|
14
|
+
config = FriendlyId::Configuration.new @model_class
|
15
|
+
assert_equal @model_class, config.model_class
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should set options on initialization if present" do
|
19
|
+
config = FriendlyId::Configuration.new @model_class, :base => "hello"
|
20
|
+
assert_equal "hello", config.base
|
21
|
+
end
|
22
|
+
|
23
|
+
test "should raise error if passed unrecognized option" do
|
24
|
+
assert_raises NoMethodError do
|
25
|
+
FriendlyId::Configuration.new @model_class, :foo => "bar"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
test "#use should accept a name that resolves to a module" do
|
30
|
+
refute @model_class < FriendlyId::Slugged
|
31
|
+
@model_class.class_eval do
|
32
|
+
extend FriendlyId
|
33
|
+
friendly_id :hello, :use => :slugged
|
34
|
+
end
|
35
|
+
assert @model_class < FriendlyId::Slugged
|
36
|
+
end
|
37
|
+
|
38
|
+
test "#use should accept a module" do
|
39
|
+
my_module = Module.new
|
40
|
+
refute @model_class < my_module
|
41
|
+
@model_class.class_eval do
|
42
|
+
extend FriendlyId
|
43
|
+
friendly_id :hello, :use => my_module
|
44
|
+
end
|
45
|
+
assert @model_class < my_module
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
data/test/core_test.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require "helper"
|
2
|
+
|
3
|
+
class Book < ActiveRecord::Base
|
4
|
+
extend FriendlyId
|
5
|
+
friendly_id :name
|
6
|
+
end
|
7
|
+
|
8
|
+
class Author < ActiveRecord::Base
|
9
|
+
extend FriendlyId
|
10
|
+
friendly_id :name
|
11
|
+
has_many :books
|
12
|
+
end
|
13
|
+
|
14
|
+
class CoreTest < MiniTest::Unit::TestCase
|
15
|
+
|
16
|
+
include FriendlyId::Test
|
17
|
+
include FriendlyId::Test::Shared::Core
|
18
|
+
|
19
|
+
def model_class
|
20
|
+
Author
|
21
|
+
end
|
22
|
+
|
23
|
+
test "models don't use friendly_id by default" do
|
24
|
+
assert !Class.new(ActiveRecord::Base) {
|
25
|
+
self.abstract_class = true
|
26
|
+
}.respond_to?(:friendly_id)
|
27
|
+
end
|
28
|
+
|
29
|
+
test "model classes should have a friendly id config" do
|
30
|
+
assert model_class.friendly_id(:name).friendly_id_config
|
31
|
+
end
|
32
|
+
|
33
|
+
test "instances should have a friendly id" do
|
34
|
+
with_instance_of(model_class) {|record| assert record.friendly_id}
|
35
|
+
end
|
36
|
+
|
37
|
+
test "instances can be marshaled when a relationship is used" do
|
38
|
+
transaction do
|
39
|
+
author = Author.create :name => 'Philip'
|
40
|
+
author.books.create :name => 'my book'
|
41
|
+
begin
|
42
|
+
assert Marshal.load(Marshal.dump(author))
|
43
|
+
rescue TypeError => e
|
44
|
+
flunk(e.to_s)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/test/databases.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
mysql:
|
2
|
+
adapter: mysql2
|
3
|
+
database: friendly_id_test
|
4
|
+
username: root
|
5
|
+
hostname: localhost
|
6
|
+
encoding: utf8
|
7
|
+
|
8
|
+
postgres:
|
9
|
+
adapter: postgresql
|
10
|
+
host: localhost
|
11
|
+
port: 5432
|
12
|
+
username: postgres
|
13
|
+
database: friendly_id_test
|
14
|
+
encoding: utf8
|
15
|
+
|
16
|
+
sqlite3:
|
17
|
+
adapter: sqlite3
|
18
|
+
database: ":memory:"
|
19
|
+
encoding: utf8
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "helper"
|
2
|
+
require "rails/generators"
|
3
|
+
require "generators/friendly_id_generator"
|
4
|
+
|
5
|
+
class FriendlyIdGeneratorTest < Rails::Generators::TestCase
|
6
|
+
|
7
|
+
tests FriendlyIdGenerator
|
8
|
+
destination File.expand_path("../../tmp", __FILE__)
|
9
|
+
|
10
|
+
setup :prepare_destination
|
11
|
+
|
12
|
+
test "should generate a migration" do
|
13
|
+
begin
|
14
|
+
run_generator
|
15
|
+
assert_migration "db/migrate/create_friendly_id_slugs"
|
16
|
+
ensure
|
17
|
+
FileUtils.rm_rf self.destination_root
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "helper"
|
4
|
+
|
5
|
+
class TranslatedArticle < ActiveRecord::Base
|
6
|
+
translates :slug, :title
|
7
|
+
extend FriendlyId
|
8
|
+
friendly_id :title, :use => :globalize
|
9
|
+
end
|
10
|
+
|
11
|
+
class GlobalizeTest < MiniTest::Unit::TestCase
|
12
|
+
include FriendlyId::Test
|
13
|
+
|
14
|
+
def setup
|
15
|
+
I18n.locale = :en
|
16
|
+
end
|
17
|
+
|
18
|
+
test "should find slug in current locale if locale is set, otherwise in default locale" do
|
19
|
+
transaction do
|
20
|
+
I18n.default_locale = :en
|
21
|
+
article_en = I18n.with_locale(:en) { TranslatedArticle.create(:title => 'a title') }
|
22
|
+
article_de = I18n.with_locale(:de) { TranslatedArticle.create(:title => 'titel') }
|
23
|
+
|
24
|
+
I18n.with_locale(:de) {
|
25
|
+
assert_equal TranslatedArticle.find("titel"), article_de
|
26
|
+
assert_equal TranslatedArticle.find("a-title"), article_en
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
test "should set friendly id for locale" do
|
32
|
+
transaction do
|
33
|
+
article = TranslatedArticle.create!(:title => "War and Peace")
|
34
|
+
article.set_friendly_id("Guerra y paz", :es)
|
35
|
+
article.save!
|
36
|
+
article = TranslatedArticle.find('war-and-peace')
|
37
|
+
I18n.with_locale(:es) { assert_equal "guerra-y-paz", article.friendly_id }
|
38
|
+
I18n.with_locale(:en) { assert_equal "war-and-peace", article.friendly_id }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# https://github.com/svenfuchs/globalize3/blob/master/test/globalize3/dynamic_finders_test.rb#L101
|
43
|
+
# see: https://github.com/svenfuchs/globalize3/issues/100
|
44
|
+
test "record returned by friendly_id should have all translations" do
|
45
|
+
transaction do
|
46
|
+
I18n.with_locale(:en) do
|
47
|
+
article = TranslatedArticle.create(:title => 'a title')
|
48
|
+
Globalize.with_locale(:ja) { article.update_attributes(:title => 'タイトル') }
|
49
|
+
article_by_friendly_id = TranslatedArticle.find("a-title")
|
50
|
+
article.translations.each do |translation|
|
51
|
+
assert_includes article_by_friendly_id.translations, translation
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "bundler/setup"
|
3
|
+
require "minitest/unit"
|
4
|
+
require "mocha"
|
5
|
+
require "active_record"
|
6
|
+
require 'active_support/core_ext/time/conversions'
|
7
|
+
|
8
|
+
|
9
|
+
if ENV["COVERAGE"]
|
10
|
+
require 'simplecov'
|
11
|
+
SimpleCov.start do
|
12
|
+
add_filter "test/"
|
13
|
+
add_filter "friendly_id/migration"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require "friendly_id"
|
18
|
+
|
19
|
+
# If you want to see the ActiveRecord log, invoke the tests using `rake test LOG=true`
|
20
|
+
if ENV["LOG"]
|
21
|
+
require "logger"
|
22
|
+
ActiveRecord::Base.logger = Logger.new($stdout)
|
23
|
+
end
|
24
|
+
|
25
|
+
module FriendlyId
|
26
|
+
module Test
|
27
|
+
|
28
|
+
def self.included(base)
|
29
|
+
MiniTest::Unit.autorun
|
30
|
+
end
|
31
|
+
|
32
|
+
def transaction
|
33
|
+
ActiveRecord::Base.transaction { yield ; raise ActiveRecord::Rollback }
|
34
|
+
end
|
35
|
+
|
36
|
+
def with_instance_of(*args)
|
37
|
+
model_class = args.shift
|
38
|
+
args[0] ||= {:name => "a b c"}
|
39
|
+
transaction { yield model_class.create!(*args) }
|
40
|
+
end
|
41
|
+
|
42
|
+
module Database
|
43
|
+
extend self
|
44
|
+
|
45
|
+
def connect
|
46
|
+
version = ActiveRecord::VERSION::STRING
|
47
|
+
driver = FriendlyId::Test::Database.driver
|
48
|
+
engine = RUBY_ENGINE rescue "ruby"
|
49
|
+
|
50
|
+
ActiveRecord::Base.establish_connection config[driver]
|
51
|
+
message = "Using #{engine} #{RUBY_VERSION} AR #{version} with #{driver}"
|
52
|
+
|
53
|
+
puts "-" * 72
|
54
|
+
if in_memory?
|
55
|
+
ActiveRecord::Migration.verbose = false
|
56
|
+
Schema.up
|
57
|
+
puts "#{message} (in-memory)"
|
58
|
+
else
|
59
|
+
puts message
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def config
|
64
|
+
@config ||= YAML::load(File.open(File.expand_path("../databases.yml", __FILE__)))
|
65
|
+
end
|
66
|
+
|
67
|
+
def driver
|
68
|
+
(ENV["DB"] or "sqlite3").downcase
|
69
|
+
end
|
70
|
+
|
71
|
+
def in_memory?
|
72
|
+
config[driver]["database"] == ":memory:"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Module
|
79
|
+
def test(name, &block)
|
80
|
+
define_method("test_#{name.gsub(/[^a-z0-9']/i, "_")}".to_sym, &block)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
require "schema"
|
85
|
+
require "shared"
|
86
|
+
FriendlyId::Test::Database.connect
|
87
|
+
at_exit {ActiveRecord::Base.connection.disconnect!}
|
@@ -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
|
data/test/schema.rb
ADDED
@@ -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
|
data/test/scoped_test.rb
ADDED
@@ -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
|