slug

Slug provides simple, straightforward slugging for your ActiveRecord models.

Slug is based on code from Norman Clarke’s fantastic friendly_id project and Nick Zadrozny’s friendly_identifier.

What’s different:

  • Unlike friendly_id, slugs are stored directly in your model’s table. friendly_id stores its data in a separate sluggable table, which enables cool things like slug versioning–but forces yet another join when trying to do complex find_by_slugs.

  • Like friendly_id, diacritics (accented characters) are stripped from slug strings.

  • The number of options is manageable

INSTALLATION

Add the gem to your Gemfile

gem 'slug'

of your rails project.

This is tested with Rails 5.1.4, MRI Ruby 2.4.1

USAGE

It’s up to you to set up the appropriate column in your model. By default, Slug saves the slug to a column called ‘slug’, so in most cases you’ll just want to add

t.string :slug

to your create migration.

I’d also suggest adding a unique index on the slug field in your migration

add_index :model_name, :slug, :unique => true

Though Slug uses validates_uniqueness_of to ensue the uniqueness of your slug, two concurrent INSERTs could conceivably try to set the same slug. Unlikely, but it’s worth adding a unique index as a backstop.

Once your table is migrated, just add

slug :source_field

to your ActiveRecord model. :source_field is the column you’d like to base the slug on–for example, it might be :headline.

INSTANCE METHOD AS SOURCE COLUMN

The source column isn’t limited to just database attributes–you can specify any instance method. This is handy if you need special formatting on your source column before it’s slugged, or if you want to base the slug on several attributes.

For example:

class Article < ActiveRecord::Base
  slug :title_for_slug

  def title_for_slug
    "#{headline}-#{publication_date.year}-#{publication_date.month}"
  end
end

would use headline-pub year-pub month as the slug source.

From here, you can work with your slug as if it were a normal column–find_by_slug and named scopes will work as they do for any other column.

OPTIONS

There are two options:

  • :column is the slug_column, if for some reason you don’t want your slugs stored in a column called “slug”

  • :generic_default is a boolean which, if true, will help you avoid ActiveRecord::ValidationError exceptions on the slug column.

COLUMN OPTION

If you want to save the slug in a database column that isn’t called slug, just pass the :column option. For example

slug :headline, :column => :web_slug

would generate a slug based on headline and save it to web_slug.

GENERIC DEFAULT OPTION

You can avoid ActiveRecord::ValidationError exceptions that can occur if the source column is empty, blank, or only contains filtered characters. Simply set generic_default: true. For example

slug :headline, generic_default: true

will generate a slug based on your model name if the headline is blank.

This is useful if the source column (e.g. headline) is based on user-generated input or can be blank (nil or empty).

Some prefer to get the exception in this case. Others want to get a good slug and move on.

NOTES

  • Slug validates presence and uniqueness of the slug column. If you pass something that isn’t sluggable as the source (for example, say you set the headline to ‘—’), a validation error will be set. (See the :generic_default option for avoiding this.

  • Slug doesn’t update the slug if the source column changes. If you really need to regenerate the slug, call @model.set_slug before save.

  • If a slug already exists, Slug will automatically append a ‘-n’ suffix to your slug to make it unique. The second instance of a slug is ‘-1’.

  • If you don’t like the slug formatting or the accented character stripping doesn’t work for you, it’s easy to override Slug’s formatting functions. Check the source for details.

AUTHOR

Ben Koski, [email protected]

with generous contributions from:

  • Derek Willis, @derekwillis

  • others listed in the commit history, which can be found in the

GitHub contributor list