Created
December 20, 2014 02:03
-
-
Save pat/e0d88b6ce8ee24f7c098 to your computer and use it in GitHub Desktop.
Slugging logic out of the model.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# For when you're adding the slug column to a model with existing records: | |
SlugGenerator.slug User, :slug |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Need a slug to be there, be unique, and follow an appropriate format. | |
validates :slug, presence: true, uniqueness: true, | |
format: { | |
with: /\A[a-z0-9\-]+\z/, | |
message: 'must be lowercase letters, numbers and dashes only' | |
} | |
# Generate slug by the name column | |
before_validation SlugCallback.new(:slug, [:name]), on: :create | |
# Update the slug if the name changes | |
before_validation SlugCallback.new(:slug, [:name]), on: :update, if: :name_changed? |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SlugCallback | |
attr_reader :column, :parts | |
def initialize(column, parts = [:name]) | |
@column, @parts = column, parts | |
end | |
def before_validation(instance) | |
SlugGenerator.new(column, parts, instance).call | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class SlugGenerator | |
CLEAN_REGEX_PAIRS = [[/[\s_]+/, '-'], [/[^\w\-]/, ''], [/\-+/, '-']] | |
def self.slug(klass, column, parts = [:name]) | |
klass.where(column => nil).each do |instance| | |
new(column, parts, instance).call | |
instance.save! | |
end | |
end | |
def initialize(column, parts, instance) | |
@column, @parts, @instance = column, parts, instance | |
end | |
def call | |
instance.send "#{column}=", unique_slug | |
end | |
private | |
attr_reader :column, :parts, :instance | |
def generated_slug | |
slug_parts.compact.collect { |part| sluggify part }.join('-') | |
end | |
def sluggify(string) | |
CLEAN_REGEX_PAIRS.inject(string.downcase) do |string, pair| | |
string.gsub pair.first, pair.last | |
end | |
end | |
def slug_available?(slug) | |
existing = instance.class.where(column => slug).first | |
existing.nil? || existing.id == instance.id | |
end | |
def slug_parts | |
parts.collect { |part| instance.send part } | |
end | |
def unique_slug | |
slug = generated_slug | |
return slug if slug_available?(slug) | |
index = 1 | |
index += 1 until slug_available?("#{slug}-#{index}") | |
"#{slug}-#{index}" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment