friendly_id
FriendlyId
A Crystal shard for creating human-readable URLs and slugs. FriendlyId lets you create pretty URLs and slugs for your resources, with support for history tracking and customization.
Installation
- Add the dependency to your
shard.yml
:
dependencies:
friendly_id:
github: dcalixto/friendly_id
[!NOTE] Make sure your database table has a slug column:
ALTER TABLE posts ADD COLUMN slug VARCHAR;
- Run
shards install
Generate and run the required migrations:
crystal ../friendly_id/src/friendly_id/install.cr
This will create the necessary database tables and indexes for FriendlyId to work:
CREATE TABLE friendly_id_slugs (
id BIGSERIAL PRIMARY KEY,
slug VARCHAR NOT NULL,
sluggable_id BIGINT NOT NULL,
sluggable_type VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Setup
Configure FriendlyId in your application:
[!NOTE] set a initializer # friendly_id.cr
require "friendly_id"
FriendlyId.configure do |config|
config.migration_dir = "db/migrations"
end
Update your model's save method to include the generate_slug
method:
class Post
include FriendlyId::Slugged
friendly_id :title
# Model-level slug generation
def save
generate_slug # Generate the slug before saving
@updated_at = Time.utc
if id
@@db.exec <<-SQL, title, slug, body, user_id, created_at, updated_at, id
UPDATE posts
SET title = ?, slug = ?, body = ?, user_id = ?, created_at = ?, updated_at = ?
WHERE id = ?
SQL
else
@@db.exec <<-SQL, title, slug, body, user_id, created_at, updated_at
INSERT INTO posts (title, slug, body, user_id, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?)
SQL
end
self
end
end
Or Update your controller save method to include the generate_slug
method:
class PostsController
def create(env)
title = env.params.body["title"]
body = env.params.body["body"]
user_id = current_user(env).id
post = Post.new(
title: title,
body: body,
user_id: user_id
)
# Controller-level slug generation
post.generate_slug # Generate the slug before saving
if post.save
env.redirect "/posts/#{post.slug}"
else
env.redirect "/posts/new"
end
end
end
Usage
Basic Slugging
class Post
include FriendlyId::Slugged
include FriendlyId::Finders
property id : Int64?
property title : String
property slug : String?
end
post = Post.new("Hello World!")
post.slug # => "hello-world"
The Slug is Update Automatically
post = Post.new("Hello World!")
post.save
puts post.slug # => "hello-world"
post.title = "Updated Title"
post.save
puts post.slug # => "updated-title"
With History Tracking
class Post
include FriendlyId::Slugged
include FriendlyId::History
property id : Int64?
property title : String
property slug : String?
def initialize(@title)
end
end
post = Post.new("Hello World!")
post.save
post.slug # => "hello-world"
post.title = "Updated Title"
post.save
post.slug_history # => ["hello-world"]
Using a Custom Attribute
class User
include FriendlyId::Slugged
property id : Int64?
property name : String
property slug : String?
friendly_id :name # Use name instead of title for slugs
def initialize(@name); end
end
user = User.new("John Doe")
user.save
puts user.slug # => "john-doe"
Friendly ID Support
The FriendlyId::Finders
module provides smart URL slug handling with ID and Historical Slug fallback:
lookup records by:
- Current slug
- Numeric ID
- Historical slugs
class Post
include FriendlyId::Finders
end
Finding Records
# All these will work:
Post.find_by_friendly_id("my-awesome-post") # Current slug
Post.find_by_friendly_id("123") # ID
Post.find_by_friendly_id("old-post-slug") # Historical slug
# Regular find still works
post = Post.find(1)
Configuration
def should_generate_new_friendly_id?
title_changed? || slug.nil?
end
class Post
include DB::Serializable
include FriendlyId::Slugged
include FriendlyId::Finders
include FriendlyId::History
# ... your existing code ...
def should_generate_new_friendly_id?
title_changed? || slug.nil?
end
end
Custom Slug Generation
class Post
include FriendlyId::Slugged
def normalize_friendly_id(value)
value.downcase.gsub(/\s+/, "-")
end
end
URL Helpers
To use friendly URLs in your controller, include the FriendlyId::UrlHelper
module:
# In your Controller
include FriendlyId::UrlHelper
<a href="/posts/<%= friendly_path(post) %>">
<%= post.title %>
</a>
Features
- Slug generation from specified fields
- SEO-friendly URL formatting
- History tracking of slug changes
- Custom slug normalization
- Special character handling
- Database-backed slug storage
Run tests
crystal spec
Contributing
- Fork it
- 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
Daniel Calixto - creator and maintainer
License
MIT License. See LICENSE for details.
friendly_id
- 1
- 0
- 0
- 0
- 3
- about 14 hours ago
- December 2, 2024
Sun, 22 Dec 2024 01:34:09 GMT