# @author David Cuadrado module MongoidExt # Slugizes a given key # Usage: # class MyModel # include Mongoid::Document # include MongoidExt::Slugizer # field :name # slug_key :name, :callback_type => :before_save # end # module Slugizer def self.included(klass) klass.class_eval do extend ClassMethods extend Finder field :slug, :type => String, :index => true end end def to_param self.slug.blank? ? self.id.to_s : self.slug end protected def generate_slug return false if self[self.class.slug_key].blank? max_length = self.class.slug_options[:max_length] min_length = self.class.slug_options[:min_length] || 0 slug = self[self.class.slug_key].parameterize.to_s slug = slug[0, max_length] if max_length if slug.size < min_length slug = nil end if slug && self.class.slug_options[:add_prefix] key = UUIDTools::UUID.random_create.hexdigest[0,4] #optimize self.slug = key+"-"+slug else self.slug = slug end end module ClassMethods # marks a field as sluggable (default key is :name) # == Parameters # @param [Symbol] key the field to be slugized # @param [Hash] options options to configure the process # @return # def slug_key(key = :name, options = {}) @slug_options ||= options @callback_type ||= begin type = options[:callback_type] || :before_validation send(type, :generate_slug) type end @slug_key ||= key end class_eval do attr_reader :slug_options end end module Finder # finds a document by slug or id # @param [Strig] id slug or id # @param [Hash] options additional conditions def by_slug(id, options = {}) self.where(options.merge({:slug => id})).first || self.where(options.merge({:_id => id})).first end alias :find_by_slug_or_id :by_slug end end end Mongoid::Criteria.send(:include, MongoidExt::Slugizer::Finder)