From 7023089ee8e3dfbc92ba4bac47f2a19c99a07d57 Mon Sep 17 00:00:00 2001 From: Graeme Nelson Date: Mon, 28 Mar 2011 21:49:24 -0700 Subject: [PATCH 1/3] added ability to add scope to the slug, and fixed up the test so rake test works --- .gitignore | 3 +- lib/slug/slug.rb | 23 ++++++++++++--- test/models.rb | 11 +++++++ test/schema.rb | 9 ++++++ test/{test_slug.rb => slug_test.rb} | 45 ++++++++++++++++++++++++++--- 5 files changed, 82 insertions(+), 9 deletions(-) rename test/{test_slug.rb => slug_test.rb} (87%) 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/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 From cd5d953f8d176e4646562380e62290b736476ce6 Mon Sep 17 00:00:00 2001 From: Graeme Nelson Date: Mon, 28 Mar 2011 21:58:52 -0700 Subject: [PATCH 2/3] updated the gemspec to reflect the rename of test_slug.rb to slug_test.rb --- slug.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slug.gemspec b/slug.gemspec index 2a363a3..c1405a0 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"] From 6f56904b6b20cefad814fc8a40108d6db7dce18a Mon Sep 17 00:00:00 2001 From: Graeme Nelson Date: Mon, 28 Mar 2011 22:00:26 -0700 Subject: [PATCH 3/3] oops missed renaming another instance of test_slug.rb to slug_test.rb --- slug.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slug.gemspec b/slug.gemspec index c1405a0..bd86abf 100644 --- a/slug.gemspec +++ b/slug.gemspec @@ -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