Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
*.sw?
.DS_Store
coverage
coverage
.rvmrc
23 changes: 19 additions & 4 deletions lib/slug/slug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,30 @@ module ClassMethods
#
# Options:
# * <tt>:column</tt> - the column the slug will be saved to (defaults to <tt>:slug</tt>)
# * <tt>:validate_uniquness_if</tt> - proc to determine whether uniqueness validation runs, same format as validates_uniquness_of :if
# * <tt>:validate_uniqueness_if</tt> - proc to determine whether uniqueness validation runs, same format as validates_uniquness_of :if
# * <tt>:validate_uniqueness_scope</tt> - the column to scope the uniqueness check to
#
# Slug will take care of validating presence and uniqueness of slug.

# Before create, Slug will generate and assign the slug if it wasn't explicitly set.
# Note that subsequent changes to the source column will have no effect on the slug.
# If you'd like to update the slug later on, call <tt>@model.set_slug</tt>
def slug source, opts={}
class_inheritable_accessor :slug_source, :slug_column
class_inheritable_accessor :slug_source, :slug_column, :slug_scope
include InstanceMethods

self.slug_source = source

self.slug_column = opts.has_key?(:column) ? opts[:column] : :slug

uniqueness_opts = {}
uniqueness_opts[:if] = opts[:validate_uniqueness_if] if opts.key?(:validate_uniqueness_if)
uniqueness_opts[:if] = opts[:validate_uniqueness_if] if opts.key?(:validate_uniqueness_if)

if opts.key?( :validate_uniqueness_scope )
uniqueness_opts[:scope] = self.slug_scope = opts[:validate_uniqueness_scope]
end


validates self.slug_column, :presence => { :message => "cannot be blank. Is #{self.slug_source} sluggable?" }
validates self.slug_column, :uniqueness => uniqueness_opts
validates self.slug_column, :format => { :with => /^[a-z0-9-]+$/, :message => "contains invalid characters. Only downcase letters, numbers, and '-' are allowed." }
Expand Down Expand Up @@ -113,7 +119,7 @@ def assign_slug_sequence

# Returns the next unique index for a slug.
def next_slug_sequence
last_in_sequence = self.class.where("#{self.slug_column} LIKE ?", self[self.slug_column] + '%').order("CAST(REPLACE(#{self.slug_column},'#{self[self.slug_column]}-','') AS UNSIGNED) DESC").first
last_in_sequence = find_last_in_sequence
if last_in_sequence.nil?
return 0
else
Expand All @@ -122,5 +128,14 @@ def next_slug_sequence
return current + 1
end
end

def find_last_in_sequence
if self.slug_scope.present?

self.class.where("#{self.slug_column} LIKE ? and #{self.slug_scope} = ?", self[self.slug_column] + '%', self[self.slug_scope]).order("CAST(REPLACE(#{self.slug_column},'#{self[self.slug_column]}-','') AS UNSIGNED) DESC").first
else
self.class.where("#{self.slug_column} LIKE ?", self[self.slug_column] + '%').order("CAST(REPLACE(#{self.slug_column},'#{self[self.slug_column]}-','') AS UNSIGNED) DESC").first
end
end
end
end
4 changes: 2 additions & 2 deletions slug.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Gem::Specification.new do |s|
"test/models.rb",
"test/schema.rb",
"test/test_helper.rb",
"test/test_slug.rb"
"test/slug_test.rb"
]
s.homepage = %q{http://github.com/bkoski/slug}
s.rdoc_options = ["--charset=UTF-8"]
Expand All @@ -40,7 +40,7 @@ Gem::Specification.new do |s|
"test/models.rb",
"test/schema.rb",
"test/test_helper.rb",
"test/test_slug.rb"
"test/slug_test.rb"
]

if s.respond_to? :specification_version then
Expand Down
11 changes: 11 additions & 0 deletions test/models.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,15 @@ class Event < ActiveRecord::Base
def title_for_slug
"#{title}-#{location}"
end
end

class Portfolio < ActiveRecord::Base
has_many :projects
end

class Project < ActiveRecord::Base

belongs_to :portfolio
slug :title, :validate_uniqueness_scope => :portfolio_id

end
9 changes: 9 additions & 0 deletions test/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,15 @@
t.column "title", "string"
t.column "location", "string"
t.column "slug", "string"
end

create_table "portfolios", :force => true do |t|
end

create_table "projects", :force => true do |t|
t.column "title", "string"
t.column "slug", "string"
t.column "portfolio_id", "integer"
end

end
45 changes: 41 additions & 4 deletions test/test_slug.rb → test/slug_test.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
require File.dirname(__FILE__) + '/test_helper'
# encoding: UTF-8
require 'test_helper'

class TestSlug < Test::Unit::TestCase
class SlugTest < Test::Unit::TestCase

def setup
def setup
Article.delete_all
Person.delete_all
end
Expand Down Expand Up @@ -44,7 +45,6 @@ def setup
should "set validation error if source column is empty" do
article = Article.create
assert !article.valid?
require 'ruby-debug'
assert article.errors[:slug]
end

Expand Down Expand Up @@ -245,6 +245,43 @@ def setup
article_13 = Article.create!(:headline => 'latest from lybia')
assert_equal 'latest-from-lybia-12', article_13.slug
end
end

context "uniqueness with scope" do

setup do
@portfolio_1 = Portfolio.create!
@portfolio_2 = Portfolio.create!
end

context "with same project name in @portfolio_1 and @portfolio_2" do

setup do
@project_1 = @portfolio_1.projects.create!(:title => "My Project")
@project_2 = @portfolio_2.projects.create!(:title => "My Project")
end

should "set @project_1 slug to my-project" do
assert_equal "my-project", @project_1.slug
end

should "set @project_2 slug to my-project" do
assert_equal "my-project", @project_2.slug
end

context "with the same project name in the same portfolio" do
setup do
@project_3 = @portfolio_1.projects.create!(:title => "My Project")
end

should "set @project_3 slug to my-project-2" do
assert_equal "my-project-1", @project_3.slug
end
end

end


end

end