friendly_id4 4.0.0.beta6 → 4.0.0.pre
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.
- data/README.md +47 -68
- data/Rakefile +13 -108
- data/lib/friendly_id.rb +97 -117
- data/lib/friendly_id/scoped.rb +17 -116
- data/lib/friendly_id/slugged.rb +89 -183
- data/lib/friendly_id/test.rb +23 -0
- data/lib/friendly_id/test/generic.rb +84 -0
- data/lib/friendly_id/version.rb +9 -0
- data/test/core_test.rb +54 -16
- data/test/scoped_test.rb +39 -35
- data/test/slugged_test.rb +45 -64
- data/test/test_helper.rb +23 -0
- metadata +61 -125
- data/.gemtest +0 -0
- data/.gitignore +0 -11
- data/.yardopts +0 -4
- data/WhatsNew.md +0 -142
- data/bench.rb +0 -63
- data/friendly_id.gemspec +0 -31
- data/lib/friendly_id/base.rb +0 -134
- data/lib/friendly_id/configuration.rb +0 -78
- data/lib/friendly_id/finder_methods.rb +0 -20
- data/lib/friendly_id/history.rb +0 -64
- data/lib/friendly_id/migration.rb +0 -18
- data/lib/friendly_id/model.rb +0 -22
- data/lib/friendly_id/object_utils.rb +0 -40
- data/lib/friendly_id/reserved.rb +0 -46
- data/lib/friendly_id/slug.rb +0 -6
- data/lib/friendly_id/slug_sequencer.rb +0 -82
- data/lib/generators/friendly_id_generator.rb +0 -24
- data/test/base_test.rb +0 -54
- data/test/config/mysql.yml +0 -5
- data/test/config/mysql2.yml +0 -5
- data/test/config/postgres.yml +0 -6
- data/test/config/sqlite3.yml +0 -3
- data/test/configuration_test.rb +0 -27
- data/test/helper.rb +0 -90
- data/test/history_test.rb +0 -55
- data/test/object_utils_test.rb +0 -26
- data/test/reserved_test.rb +0 -26
- data/test/schema.rb +0 -56
- data/test/shared.rb +0 -118
- data/test/sti_test.rb +0 -48
data/lib/friendly_id/slug.rb
DELETED
@@ -1,82 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
# This class offers functionality to check slug strings for uniqueness and,
|
3
|
-
# if necessary, append a sequence to ensure it.
|
4
|
-
class SlugSequencer
|
5
|
-
attr_reader :sluggable
|
6
|
-
|
7
|
-
def initialize(sluggable)
|
8
|
-
@sluggable = sluggable
|
9
|
-
end
|
10
|
-
|
11
|
-
# Given a slug, get the next available slug in the sequence.
|
12
|
-
def next
|
13
|
-
sequence = conflict.slug.split(separator)[1].to_i
|
14
|
-
next_sequence = sequence == 0 ? 2 : sequence.next
|
15
|
-
"#{normalized}#{separator}#{next_sequence}"
|
16
|
-
end
|
17
|
-
|
18
|
-
# Generate a new sequenced slug.
|
19
|
-
def generate
|
20
|
-
if new_record? or slug_changed?
|
21
|
-
conflict? ? self.next : normalized
|
22
|
-
else
|
23
|
-
sluggable.friendly_id
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Whether or not the model instance's slug has changed.
|
28
|
-
def slug_changed?
|
29
|
-
separator = Regexp.escape friendly_id_config.sequence_separator
|
30
|
-
base != sluggable.current_friendly_id.try(:sub, /#{separator}[\d]*\z/, '')
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
def base
|
36
|
-
sluggable.send friendly_id_config.base
|
37
|
-
end
|
38
|
-
|
39
|
-
def column
|
40
|
-
sluggable.connection.quote_column_name friendly_id_config.query_field
|
41
|
-
end
|
42
|
-
|
43
|
-
def conflict?
|
44
|
-
!! conflict
|
45
|
-
end
|
46
|
-
|
47
|
-
def conflict
|
48
|
-
unless defined? @conflict
|
49
|
-
@conflict = conflicts.first
|
50
|
-
end
|
51
|
-
@conflict
|
52
|
-
end
|
53
|
-
|
54
|
-
def conflicts
|
55
|
-
pkey = sluggable.class.primary_key
|
56
|
-
value = sluggable.send pkey
|
57
|
-
scope = sluggable.class.where("#{column} = ? OR #{column} LIKE ?", normalized, wildcard)
|
58
|
-
scope = scope.where("#{pkey} <> ?", value) unless sluggable.new_record?
|
59
|
-
scope = scope.order("LENGTH(#{column}) DESC, #{column} DESC")
|
60
|
-
end
|
61
|
-
|
62
|
-
def friendly_id_config
|
63
|
-
sluggable.friendly_id_config
|
64
|
-
end
|
65
|
-
|
66
|
-
def new_record?
|
67
|
-
sluggable.new_record?
|
68
|
-
end
|
69
|
-
|
70
|
-
def normalized
|
71
|
-
@normalized ||= sluggable.normalize_friendly_id(base)
|
72
|
-
end
|
73
|
-
|
74
|
-
def separator
|
75
|
-
friendly_id_config.sequence_separator
|
76
|
-
end
|
77
|
-
|
78
|
-
def wildcard
|
79
|
-
"#{normalized}#{separator}%"
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'rails/generators'
|
2
|
-
require 'rails/generators/migration'
|
3
|
-
|
4
|
-
# This generator adds a migration for the {FriendlyId::History
|
5
|
-
# FriendlyId::History} addon.
|
6
|
-
class FriendlyIdGenerator < Rails::Generators::Base
|
7
|
-
include Rails::Generators::Migration
|
8
|
-
|
9
|
-
source_root File.expand_path('../../friendly_id', __FILE__)
|
10
|
-
|
11
|
-
# Copies the migration template to db/migrate.
|
12
|
-
def copy_files(*args)
|
13
|
-
migration_template 'migration.rb', 'db/migrate/create_friendly_id_slugs.rb'
|
14
|
-
end
|
15
|
-
|
16
|
-
# Taken from ActiveRecord's migration generator
|
17
|
-
def self.next_migration_number(dirname) #:nodoc:
|
18
|
-
if ActiveRecord::Base.timestamped_migrations
|
19
|
-
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
20
|
-
else
|
21
|
-
"%.3d" % (current_migration_number(dirname) + 1)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
data/test/base_test.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
require File.expand_path("../helper.rb", __FILE__)
|
2
|
-
|
3
|
-
class CoreTest < MiniTest::Unit::TestCase
|
4
|
-
include FriendlyId::Test
|
5
|
-
|
6
|
-
test "friendly_id should accept a base and a hash" do
|
7
|
-
klass = Class.new(ActiveRecord::Base) do
|
8
|
-
extend FriendlyId
|
9
|
-
friendly_id :foo, :use => :slugged, :slug_column => :bar
|
10
|
-
end
|
11
|
-
assert klass < FriendlyId::Slugged
|
12
|
-
assert_equal :foo, klass.friendly_id_config.base
|
13
|
-
assert_equal :bar, klass.friendly_id_config.slug_column
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
test "friendly_id should accept a block" do
|
18
|
-
klass = Class.new(ActiveRecord::Base) do
|
19
|
-
extend FriendlyId
|
20
|
-
friendly_id :foo do |config|
|
21
|
-
config.use :slugged
|
22
|
-
config.base = :foo
|
23
|
-
config.slug_column = :bar
|
24
|
-
end
|
25
|
-
end
|
26
|
-
assert klass < FriendlyId::Slugged
|
27
|
-
assert_equal :foo, klass.friendly_id_config.base
|
28
|
-
assert_equal :bar, klass.friendly_id_config.slug_column
|
29
|
-
end
|
30
|
-
|
31
|
-
test "the block passed to friendly_id should be evaluated before arguments" do
|
32
|
-
klass = Class.new(ActiveRecord::Base) do
|
33
|
-
extend FriendlyId
|
34
|
-
friendly_id :foo do |config|
|
35
|
-
config.base = :bar
|
36
|
-
end
|
37
|
-
end
|
38
|
-
assert_equal :foo, klass.friendly_id_config.base
|
39
|
-
end
|
40
|
-
|
41
|
-
test "should allow defaults to be set via a block" do
|
42
|
-
begin
|
43
|
-
FriendlyId.defaults do |config|
|
44
|
-
config.base = :foo
|
45
|
-
end
|
46
|
-
klass = Class.new(ActiveRecord::Base) do
|
47
|
-
extend FriendlyId
|
48
|
-
end
|
49
|
-
assert_equal :foo, klass.friendly_id_config.base
|
50
|
-
ensure
|
51
|
-
FriendlyId.instance_variable_set :@defaults, nil
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
data/test/config/mysql.yml
DELETED
data/test/config/mysql2.yml
DELETED
data/test/config/postgres.yml
DELETED
data/test/config/sqlite3.yml
DELETED
data/test/configuration_test.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require File.expand_path("../helper", __FILE__)
|
2
|
-
|
3
|
-
class ConfigurationTest < MiniTest::Unit::TestCase
|
4
|
-
|
5
|
-
include FriendlyId::Test
|
6
|
-
|
7
|
-
def setup
|
8
|
-
@model_class = Class.new(ActiveRecord::Base)
|
9
|
-
end
|
10
|
-
|
11
|
-
test "should set model class on initialization" do
|
12
|
-
config = FriendlyId::Configuration.new @model_class
|
13
|
-
assert_equal @model_class, config.model_class
|
14
|
-
end
|
15
|
-
|
16
|
-
test "should set options on initialization if present" do
|
17
|
-
config = FriendlyId::Configuration.new @model_class, :base => "hello"
|
18
|
-
assert_equal "hello", config.base
|
19
|
-
end
|
20
|
-
|
21
|
-
test "should raise error if passed unrecognized option" do
|
22
|
-
assert_raises NoMethodError do
|
23
|
-
FriendlyId::Configuration.new @model_class, :foo => "bar"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
end
|
data/test/helper.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
$: << File.expand_path("../../lib", __FILE__)
|
2
|
-
$: << File.expand_path("../", __FILE__)
|
3
|
-
$:.uniq!
|
4
|
-
|
5
|
-
require "rubygems"
|
6
|
-
require "bundler/setup"
|
7
|
-
require "mocha"
|
8
|
-
require "minitest/unit"
|
9
|
-
require "active_record"
|
10
|
-
# require "active_support/core_ext/class"
|
11
|
-
|
12
|
-
if ENV["COVERAGE"]
|
13
|
-
require 'simplecov'
|
14
|
-
SimpleCov.start do
|
15
|
-
add_filter "test/"
|
16
|
-
add_filter "friendly_id/migration"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
require "friendly_id"
|
21
|
-
|
22
|
-
# If you want to see the ActiveRecord log, invoke the tests using `rake test LOG=true`
|
23
|
-
if ENV["LOG"]
|
24
|
-
require "logger"
|
25
|
-
ActiveRecord::Base.logger = Logger.new($stdout)
|
26
|
-
end
|
27
|
-
|
28
|
-
module FriendlyId
|
29
|
-
module Test
|
30
|
-
|
31
|
-
def self.included(base)
|
32
|
-
MiniTest::Unit.autorun
|
33
|
-
end
|
34
|
-
|
35
|
-
def transaction
|
36
|
-
ActiveRecord::Base.transaction { yield ; raise ActiveRecord::Rollback }
|
37
|
-
end
|
38
|
-
|
39
|
-
def with_instance_of(*args)
|
40
|
-
model_class = args.shift
|
41
|
-
args[0] ||= {:name => "a"}
|
42
|
-
transaction { yield model_class.create!(*args) }
|
43
|
-
end
|
44
|
-
|
45
|
-
module Database
|
46
|
-
extend self
|
47
|
-
|
48
|
-
def connect
|
49
|
-
ActiveRecord::Base.establish_connection config
|
50
|
-
version = ActiveRecord::VERSION::STRING
|
51
|
-
driver = FriendlyId::Test::Database.driver
|
52
|
-
message = "Using #{RUBY_ENGINE} #{RUBY_VERSION} AR #{version} with #{driver}"
|
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(config_file))
|
65
|
-
end
|
66
|
-
|
67
|
-
def config_file
|
68
|
-
File.expand_path("../config/#{driver}.yml", __FILE__)
|
69
|
-
end
|
70
|
-
|
71
|
-
def driver
|
72
|
-
(ENV["DB"] or "sqlite3").downcase
|
73
|
-
end
|
74
|
-
|
75
|
-
def in_memory?
|
76
|
-
config["database"] == ":memory:"
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
class Module
|
83
|
-
def test(name, &block)
|
84
|
-
define_method("test_#{name.gsub(/[^a-z0-9']/i, "_")}".to_sym, &block)
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
require "schema"
|
89
|
-
require "shared"
|
90
|
-
FriendlyId::Test::Database.connect
|
data/test/history_test.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
require File.expand_path("../helper.rb", __FILE__)
|
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.friendly_id_slugs.empty?}
|
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, FriendlyIdSlug.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, FriendlyIdSlug.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
|
-
assert found = model_class.find_by_friendly_id(old_friendly_id)
|
43
|
-
assert !found.readonly?
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
test "should raise error if used with scoped" do
|
48
|
-
model_class = Class.new(ActiveRecord::Base)
|
49
|
-
model_class.extend FriendlyId
|
50
|
-
assert_raises RuntimeError do
|
51
|
-
model_class.friendly_id :name, :use => [:history, :scoped]
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
data/test/object_utils_test.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require File.expand_path("../helper.rb", __FILE__)
|
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)
|
23
|
-
model_class.table_name = "authors"
|
24
|
-
assert model_class.new.unfriendly_id?
|
25
|
-
end
|
26
|
-
end
|
data/test/reserved_test.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require File.expand_path("../helper.rb", __FILE__)
|
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
|
-
end
|
11
|
-
|
12
|
-
|
13
|
-
def model_class
|
14
|
-
Journalist
|
15
|
-
end
|
16
|
-
|
17
|
-
test "should reserve 'new' and 'edit' by default" do
|
18
|
-
["new", "edit"].each do |word|
|
19
|
-
transaction do
|
20
|
-
assert_raises(ActiveRecord::RecordInvalid) {model_class.create! :name => word}
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
|
data/test/schema.rb
DELETED
@@ -1,56 +0,0 @@
|
|
1
|
-
require "friendly_id/migration"
|
2
|
-
|
3
|
-
module FriendlyId
|
4
|
-
module Test
|
5
|
-
class Schema < ActiveRecord::Migration
|
6
|
-
class << self
|
7
|
-
def down
|
8
|
-
CreateFriendlyIdSlugs.down
|
9
|
-
tables.each do |name|
|
10
|
-
drop_table name
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
def up
|
15
|
-
CreateFriendlyIdSlugs.up
|
16
|
-
|
17
|
-
tables.each do |table_name|
|
18
|
-
create_table table_name do |t|
|
19
|
-
t.string :name
|
20
|
-
t.boolean :active
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
slugged_tables.each do |table_name|
|
25
|
-
add_column table_name, :slug, :string
|
26
|
-
add_index table_name, :slug, :unique => true
|
27
|
-
end
|
28
|
-
|
29
|
-
# This will be used to test scopes
|
30
|
-
add_column :novels, :novelist_id, :integer
|
31
|
-
remove_index :novels, :slug
|
32
|
-
|
33
|
-
# This will be used to test column name quoting
|
34
|
-
add_column :journalists, "strange name", :string
|
35
|
-
|
36
|
-
# This will be used to test STI
|
37
|
-
add_column :journalists, "type", :string
|
38
|
-
end
|
39
|
-
|
40
|
-
private
|
41
|
-
|
42
|
-
def slugged_tables
|
43
|
-
["journalists", "articles", "novelists", "novels", "manuals"]
|
44
|
-
end
|
45
|
-
|
46
|
-
def simple_tables
|
47
|
-
["authors", "books"]
|
48
|
-
end
|
49
|
-
|
50
|
-
def tables
|
51
|
-
simple_tables + slugged_tables
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|