Skip to content

Instantly share code, notes, and snippets.

@lukebergen
Created January 7, 2013 16:09
Show Gist options
  • Save lukebergen/4476129 to your computer and use it in GitHub Desktop.
Save lukebergen/4476129 to your computer and use it in GitHub Desktop.
module Mongoid
module Sluggable
def self.included(klass)
klass.field :slug, type: String, default: ""
klass.before_validation :generate_slug
klass.after_save :add_slug_history
klass.validates :slug, presence: true
klass.validate :slug_uniqueness
klass.validate :slug_history_path
klass.has_many :slug_histories, as: :sluggable
klass.extend(ClassMethods)
end
def slug_uniqueness
if slug_changed? && !embedded?
unless slug_collisions.empty?
errors.add(:slug, "(#{self.slug}) not unique")
end
end
end
def slug_history_path
collisions = slug_history_collisions
unless collisions.empty?
errors.add(:slug, "(#{self.resource_uri}) exists as a source in a slug history")
end
end
def slug_base
self.name
end
def generate_slug
self.slug = build_slug_name if slug.blank?
self.slug = slug.split('/').map(&:parameterize).join('/')
ind = 2
until (slug_collisions + slug_history_collisions).empty?
self.slug = self.build_slug_name(ind)
ind += 1
end
return self.slug
end
def slug_history_collisions
histories = SlugHistory.where(source: self.resource_uri).to_a
histories.delete_if { |x| x.sluggable_id == self.id }
end
def build_slug_name(ind = nil)
slug = File.join(*[self.slug_base].flatten.compact.map {|x| x.parameterize})
slug += "-#{ind}" if ind
return slug
end
def slug_collisions
self.class.where(slug: /^#{self.slug}$/i) - [self]
end
def add_slug_history
if self.slug_was && self.slug_changed?
self.slug_histories.create(source: self.slug_was, destination: self.slug)
end
end
# Full URI Methods
def resource_uri(slug = self.slug)
"/#{self.class.name.downcase}/#{self.slug}"
end
module ClassMethods
def by_slug_or_id(slug)
self.any_of({id: slug}, {slug: /^#{slug.to_s}/i})
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment