Skip to content

Commit

Permalink
- some refactoring
Browse files Browse the repository at this point in the history
- added support for blocks to generate slugs
  • Loading branch information
brendan6 committed Oct 13, 2011
1 parent 256fa66 commit caebf29
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 25 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ source "http://rubygems.org"

# Specify your gem's dependencies in has_unique_slug.gemspec
gemspec
gem 'rspec'
28 changes: 18 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,39 @@ Add `gem has_unique_slug` to your Gemfile and run `bundle install` to get it ins

## Usage

Assume you have a Post model that has a title and slug column in which the slug column is generated from the post title.
Assume you have a Post model that has a title and slug column, you can use the following to uniquely parameterize title:

class Post < ActiveRecord::Base
has_unique_slug
end


A unique slug will be generated automatically on creation.
If the generated slug is not unique, "-n" is appended onto the end. n starts at 2 and will increment by 1 until a unique slug is found.
If a slug is provided, one will not be generated, however the same rule applies as the above if the slug is not unique.
If the generated slug is not unique, a number is added onto the end to endure uniqueness. The series starts at 2 and increments
up by one until a unique slug is found.
If a slug is already specified, this slug will be used however the above rules still apply for uniqueness.

You can specify which column to use to generate the slug and which column to use to store the slug. Below is the default:

class Post < ActiveRecord::Base
has_unique_slug :title, :slug
end

has_unique_slug :slug, :title
end

Or if only 1 argument is given, use that column to generate the slug
Or if only 1 argument is given, use that column to store the slug:

class Post < ActiveRecord::Base
has_unique_slug :name # Uses the name column to generate the slug
has_unique_slug :permalink # Uses the permalink column to store the slug
end


Optionally, a block can be provided to generate the slug:

class Car < ActiveRecord::Base
has_unique_slug {|car| "#{car.year} #{car.name}"}
end
Note the space: parameterize will be called on the result of the block to ensure the slug is url friendly.

## TODO:

Would like to write some tests.
- Would like to write some tests.
- Would like to be able to specify scope for uniqueness
- Possibly consider optimizing the method to ensure uniqueness
39 changes: 24 additions & 15 deletions lib/has_unique_slug.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,37 @@ def self.included(base)
base.extend ClassMethods
end

# Builds a slug from the subject_column unless a block is specified.
# If a block is specified, the result of the block is returned.
def build_slug(record, subject_column, &block)
( block_given? ? yield(record) : record[subject_column] ).parameterize
end

module ClassMethods

def has_unique_slug(title_col = "title", slug_col = "slug")
def has_unique_slug(*args, &block)

options = { :scope => nil }
options.merge! args.pop if args.last.is_a? Hash
slug_column, subject_column = args
slug_column ||= :slug
subject_column ||= :title

# Add ActiveRecord Callback
before_create do |record|
before_save do |record|

# Add a slug if slug doesn't exist
if record[slug_col].blank?
record[slug_col] = record[title_col].parameterize
end
slug_prefix = record[slug_column].blank? ? build_slug(record, subject_column, &block) : record[slug_column]

# Ensure the current slug is unique in the database, if not, make it unqiue
i = 2
while not record.class.where("#{slug_col} = ?", record[slug_col]).count.zero?
record[slug_col] = "#{record[slug_col]}-#{i}"
i += 1
end
test_slug, i = slug_prefix, 1
record_scope = record.new_record? ? record.class.scoped : record.class.where("id != ?", record.id)
while not record_scope.where("#{slug_column} = ?", test_slug).count.zero?
test_slug = "#{slug_prefix}-#{(i += 1)}"
end

# Set the slug
record[slug_column] = test_slug
end

# Add instance methods to objects using has_unique_slug
Expand All @@ -33,11 +46,7 @@ def has_unique_slug(title_col = "title", slug_col = "slug")
# Add configuration mechanism
instance_eval <<-EOV
def slug_column
'#{slug_col}'
end
def title_column
'#{title_col}'
'#{slug_column}'
end
EOV

Expand Down
5 changes: 5 additions & 0 deletions spec/has_unique_slug_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require 'spec_helper'

describe HasUniqueSlug
pending "Write test simulating active record"
end
8 changes: 8 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
require 'rubygems'
require 'bundler/setup'

require 'has_unique_slug' # and any other gems you need

RSpec.configure do |config|
# some (optional) config here
end

0 comments on commit caebf29

Please sign in to comment.