diff --git a/.gitignore b/.gitignore
index 0e33e78..0867678 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
*.sw?
.DS_Store
-coverage
\ No newline at end of file
+coverage
+.rvmrc
\ No newline at end of file
diff --git a/lib/slug/slug.rb b/lib/slug/slug.rb
index e149e2f..4acd227 100644
--- a/lib/slug/slug.rb
+++ b/lib/slug/slug.rb
@@ -8,7 +8,8 @@ module ClassMethods
#
# Options:
# * :column - the column the slug will be saved to (defaults to :slug)
- # * :validate_uniquness_if - proc to determine whether uniqueness validation runs, same format as validates_uniquness_of :if
+ # * :validate_uniqueness_if - proc to determine whether uniqueness validation runs, same format as validates_uniquness_of :if
+ # * :validate_uniqueness_scope - the column to scope the uniqueness check to
#
# Slug will take care of validating presence and uniqueness of slug.
@@ -16,7 +17,7 @@ module ClassMethods
# 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 @model.set_slug
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
@@ -24,8 +25,13 @@ def slug source, opts={}
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." }
@@ -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
@@ -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
\ No newline at end of file
diff --git a/slug.gemspec b/slug.gemspec
index 2a363a3..bd86abf 100644
--- a/slug.gemspec
+++ b/slug.gemspec
@@ -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"]
@@ -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
diff --git a/test/models.rb b/test/models.rb
index 98b2d67..5842cfc 100644
--- a/test/models.rb
+++ b/test/models.rb
@@ -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
\ No newline at end of file
diff --git a/test/schema.rb b/test/schema.rb
index 2ad8968..aa23f62 100644
--- a/test/schema.rb
+++ b/test/schema.rb
@@ -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
\ No newline at end of file
diff --git a/test/test_slug.rb b/test/slug_test.rb
similarity index 87%
rename from test/test_slug.rb
rename to test/slug_test.rb
index 9517e96..d64295c 100644
--- a/test/test_slug.rb
+++ b/test/slug_test.rb
@@ -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
@@ -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
@@ -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
\ No newline at end of file