app/models/concerns/setup/slug.rb
module Setup
module Slug
extend ActiveSupport::Concern
included do
field :slug, type: String
validates_length_of :slug, maximum: 255
before_save :validate_slug!
end
def validate_slug!
throw(:abort) unless validate_slug
end
def validate_slug
check_taken = true
unless slug.present?
candidate =
if (candidate = slug_candidate).blank?
'default'
else
candidate.squeeze(' ').tr(' ', '_')
end
i = candidate.length - 1
while candidate.length.positive? && i == candidate.length - 1
i -= 1 while i >= 0 && candidate[i] =~ /\A\.|[a-z]|[A-Z]|\d|_\Z/
if i == candidate.length - 1
candidate = candidate.to(i - 1)
i = candidate.length - 1
end
end
candidate = candidate.from(i + 1)
candidate = 'default' if candidate.empty?
if (candidate = candidate.split('.')).length > 1
candidate.pop
candidate = candidate.last
else
candidate = candidate[0]
end
candidate = candidate.underscore.gsub(/ +/, '_').downcase
candidate = 'slug' if candidate.blank?
i = 1
while slug_taken?(candidate)
candidate = candidate.gsub(/_\d*\Z/, '') + "_#{i += 1}"
end
self.slug = candidate
check_taken = false
end
if slug =~ /\A([a-z]|_|\d)+\Z/
errors.add(:slug, 'is taken') if check_taken && slug_taken?(slug)
else
errors.add(:slug, 'is not valid')
end
errors.blank?
end
protected
def slug_candidate
try(:name)
end
def slug_taken?(slug)
self.class.where(slug: slug, :id.nin => [id]).present?
end
end
end