module Mongoid::SleepingKingStudios::Sluggable

Adds a :slug field that stores a short, url-friendly reference string, useful for human-readable urls. By default, the slug field is automatically overwritten from the specified base attribute before validation. To enable setting the slug manually, use the :lockable option; otherwise, the :slug= writer is set to private.

@example Setting up the slug:

class SluggableDocument
  include Mongoid::Document
  include Mongoid::SleepingKingStudios::Sluggable

  field :title, :type => String

  slugify :title
end # class

@see ClassMethods#slugify

@since 0.1.0

Public Class Methods

apply(base, attribute, options) click to toggle source

@api private

Sets up the sluggable relation, creating fields, accessors and validations.

@param [Class] base The base class into which the concern is mixed in. @param [String, Symbol] attribute The base field used to determine

the value of the slug. When this field is changed via its writer
method, the slug will be updated.

@param [Hash] options The options for the relation.

@since 0.6.0

# File lib/mongoid/sleeping_king_studios/sluggable.rb, line 42
def self.apply base, attribute, options
  name = :sluggable
  validate_options    name, options
  meta = characterize name, options, Metadata
  meta[:attribute] = attribute

  relate base, name, meta

  define_fields      base, meta
  define_accessors   base, meta
  define_helpers     base, meta
  define_validations base, meta
end
define_accessors(base, metadata) click to toggle source

@api private

Redefines the writer for the base attribute to overwrite the value of the slug field unless the slug is locked. If the Lockable option is selected, redefines the writer for the slug field to lock the slug when set manually; otherwise, makes the writer for the slug field private.

@param [Class] base The base class into which the concern is mixed in. @param [Metadata] metadata The metadata for the relation.

@since 0.6.0

# File lib/mongoid/sleeping_king_studios/sluggable.rb, line 67
def self.define_accessors base, metadata
  base.re_define_method :"#{metadata.attribute}=" do |value|
    self[metadata.attribute.to_s] = value
    unless metadata.lockable? && self['slug_lock']
      self['slug'] = metadata.value_to_slug value
    end # unless
  end # method

  if metadata.lockable?
    base.re_define_method :slug= do |value|
      self['slug'] = value
      self['slug_lock'] = true
    end # method
  else
    base.send :private, :slug=
  end # if
end
define_fields(base, metadata) click to toggle source

@api private

Creates a slug field of type String on the base class. If the Lockable option is selected, also creates a slug_lock field of type Boolean.

@param [Class] base The base class into which the concern is mixed in. @param [Metadata] metadata The metadata for the relation.

@since 0.6.0

# File lib/mongoid/sleeping_king_studios/sluggable.rb, line 94
def self.define_fields base, metadata
  base.send :field, :slug, :type => String

  if metadata.lockable?
    base.send :field, :slug_lock, :type => Boolean, :default => false
  end # if
end
define_helpers(base, metadata) click to toggle source

@api private

Creates the ::slugify_all! class-level helper method.

@param [Class] base The base class into which the concern is mixed in. @param [Metadata] metadata The metadata for the relation.

@since 0.7.7

# File lib/mongoid/sleeping_king_studios/sluggable.rb, line 110
def self.define_helpers base, metadata
  instance_methods = Module.new

  instance_methods.send :define_method, :generate_slug! do
    value = metadata.value_to_slug(send metadata.attribute)
    if slug.blank?
      self[:slug] = value
      self.set :slug => value if persisted?
    elsif slug != value && !(metadata.lockable? && slug_lock)
      self[:slug] = value
      self.set :slug => value if persisted?
    end # if
  end # method generate_slug!

  instance_methods.send :define_method, :to_slug do
    metadata.value_to_slug(send metadata.attribute)
  end # method to_slug

  base.include instance_methods

  # Define class-level helpers.
  class_methods = Module.new

  class_methods.send :define_method, :slugify_all! do
    all.map &:generate_slug!
  end # class method slugify_all!

  class_methods.send :define_method, :value_to_slug do |value|
    metadata.value_to_slug(value)
  end # class method value_to_slug

  base.extend class_methods
end
define_validations(base, metadata) click to toggle source

@api private

Sets a validation on the slug field that validates the presence of the slug, and that the value is of a valid format (lower-case characters a-z, digits 0-9, and hyphens “-”).

@param [Class] base The base class into which the concern is mixed in. @param [Metadata] metadata The metadata for the relation.

@since 0.6.0

# File lib/mongoid/sleeping_king_studios/sluggable.rb, line 154
def self.define_validations base, metadata
  base.validates :slug,
    :presence => true,
    :format => {
      :with => /\A[a-z0-9\-]+\z/,
      :message => 'must be lower-case characters a-z, digits 0-9, and hyphens "-"'
    } # end format
end
valid_options() click to toggle source

Returns a list of options that are valid for this concern.

@return [Array<Symbol>] The list of valid options.

@since 0.6.0

Calls superclass method
# File lib/mongoid/sleeping_king_studios/sluggable.rb, line 168
def self.valid_options
  super + %i(
    lockable
  ) # end array
end