AvramSlugify
AvramSlugify generates slugs for database columns. These slugs can be used for creating nice looking URLs and permalinks.
Installation
-
Add the dependency to your
shard.yml:dependencies: avram_slugify: github: luckyframework/avram_slugify version: ~> 0.1 -
Run
shards install -
Require the shard after requiring Avram
If using Lucky, require the shard in your src/shards.cr file after requiring Avram:
# In src/shards.cr
# Put this after `require "avram"`
require "avram_slugify"
If not using Lucky, require the shard after Avram:
# In whichever file you require your shards
# Put this after `require "avram"`
require "avram_slugify"
Usage
Let's say you have an Article model with 2 String columns, slug and
title. You can use AvramSlugify.set to set the slug column to a slugified
version of the title.
class SaveArticle < Article::SaveOperation
before_save do
AvramSlugify.set slug,
using: title,
query: ArticleQuery.new
end
end
- The first argument is the attribute you want to store the generated slug
on. In this case
slug, but it could be any String attribute. - The second argument is called a slug candidate. In this case
titleis the slug candidate. - The third argument is the query to use when checking for slug uniqueness.
So if the value of the slug candidate title is "Avram is a great ORM", the
slug value will be set to avram-is-a-great-orm.
Finding records
You can find record with Avram's built-in query methods.
ArticleQuery.new.slug("avram-is-a-great-orm").first
Or if you want to add a shortcut you can do something like this:
class ArticleQuery < Article::BaseQuery
def find(slug_or_id : String | Int64) : Article
if slug_or_id.is_a?(Int64)
previous_def(slug_or_id)
else
slug(slug_or_id).first
end
end
end
# Find by slug
ArticleQuery.find("avram-is-a-great-orm")
# Find by id
ArticleQuery.find(1_i64)
# Can be scoped like any other query
ArticleQuery.new.account_id(account.id).find("avram-is-a-great-orm")
What if the generated slug is not unique?
If the slug is not unique, a UUID will be appended to the first slug
candidate (the attribute passed to using).
So if an Article has a slug with hello-world and then you try to save a new
Article with a title set to "Hello World", the slug will not be unique. To
make the slug unique AvramSlugify will append a UUID to the slug.
For example: hello-world-3fa569f5-6678-4f77-a281-fb1b9d850407
Using multiple slug candidates
To make it less likely that AvramSlugify will have to append a UUID, you can
provide multiple slug candidates in using.
For example, you could do using: [title, author_email]. If the generated
slug from title is already taken, AvramSlugify will try to generate a slug
from author_email. If that doesn't work it will append a UUID to title
Scoping uniqueness check
Let's say an Article belongs to an Account and you want slugs to be unique per
account. Here's how you'd do that:
class SaveArticle < Article::SaveOperation
# This means you will need to pass in an account when saving/updating
# https://luckyframework.org/guides/database/validating-saving#passing-extra-data-to-operations
needs account : Account
before_save do
AvramSlugify.set slug.
using: title,
# Use the Account to query against Articles in the same Account
query: ArticleQuery.new.account_id(@account.id)
end
end
Combining multiple attributes for a slug
Let's say you have a User with a invite_code that you'd like to be generated
from the first_name and last_name.
You can give an array of attributes and they will be combined when generating the slug:
AvramSlugify.set invite_code,
using: [[first_name, last_name]],
query: UserQuery.new
So if first_name is "Jane" and last_name is "Adler", the generated
slug for invite_code will be "jane-adler".
You must put the array in another array. If you did just
[first_name, last_name]AvramSlugify would usefirst_nameby default andlast_nameif thefirst_nameis not unique
You can also use multiple slug candidates for fallbacks by adding more slug
candidates to the array passed to using:
[
nickname,
[first_name, last_name],
[first_name, last_name, location]
]
Using strings as slug candidates
Occassionally you may want to use a string as a slug candidate:
using: ["first-#{first_name.value}"]
What if the slug candidate is blank?
Avram::Attributes can be nil or empty strings so if the slug candidate's
value is nil or an empty string the slug value will be unchanged.
What if the slug is already set?
AvramSlugify will not overwrite an existing slug.
If you want to reset a slug, first set the slug value to nil, then run
AvramSlugify.set:
slug.value = nil
AvramSlugify.set slug,
using: title,
query: ArticleQuery.new
Contributing
- Fork it (https://github.com/luckyframework/avram_slugify/fork)
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create a new Pull Request
Contributors
- Paul Smith - creator and maintainer