Skip to content

Commit

Permalink
Fix findings
Browse files Browse the repository at this point in the history
  • Loading branch information
treagod committed Sep 5, 2024
1 parent cb46f0a commit f577f7b
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 19 deletions.
8 changes: 4 additions & 4 deletions spec/marten/db/field/slug_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describe Marten::DB::Field::Slug do

article.save

article.slug.not_nil!.starts_with?("my-first-article-").should be_true
article.slug.not_nil!.should eq("my-first-article")
end

it "raises an error if an invalid field is targetted" do
Expand Down Expand Up @@ -85,23 +85,23 @@ describe Marten::DB::Field::Slug do

article.save

article.slug.not_nil!.starts_with?("berraschungsmoment").should be_true
article.slug.not_nil!.should eq("berraschungsmoment")
end

it "removes emoji and special characters and slugifies the title" do
article = Marten::DB::Field::SlugSpec::Article.new(title: "🚀 TRAVEL & PLACES")

article.save

article.slug.not_nil!.starts_with?("travel-places").should be_true
article.slug.not_nil!.should eq("travel-places")
end

it "trims leading and trailing whitespace and slugifies the title" do
article = Marten::DB::Field::SlugSpec::Article.new(title: " Test Article ")

article.save

article.slug.not_nil!.starts_with?("test-article").should be_true
article.slug.not_nil!.should eq("test-article")
end

it "retains a custom slug if provided" do
Expand Down
17 changes: 6 additions & 11 deletions src/marten/core/sluggable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ module Marten
module Core
# The Sluggable module provides functionality for generating URL-friendly slugs from strings.
module Sluggable
extend self

NON_ALPHANUMERIC_RE = /[^\w\s-]/
WHITESPACE_HYPHEN_RE = /[-\s]+/
NON_ASCII_RE = /[^\x00-\x7F]/
private NON_ALPHANUMERIC_RE = /[^\w\s-]/
private WHITESPACE_HYPHEN_RE = /[-\s]+/
private NON_ASCII_RE = /[^\x00-\x7F]/

# Generates a slug from the given value, ensuring the resulting slug does not exceed the specified max_size.
#
Expand All @@ -15,16 +13,13 @@ module Marten
# 2. Converting the string to lowercase.
# 3. Replacing sequences of whitespace and hyphens with a single hyphen.
# 4. Removing non-ASCII characters.
# 5. Truncating the slug to fit within the max_size, minus the size of a randomly generated suffix.
# 6. Stripping trailing hyphens and underscores of the slug without suffix, and appending the suffix.
# 5. Stripping trailing hyphens and underscores of the slug.
# 6. Truncate the slug to the maximum size
def generate_slug(value, max_size)
suffix = "-#{Random::Secure.hex(4)}"

slug = value.gsub(NON_ALPHANUMERIC_RE, "").downcase
slug = slug.gsub(WHITESPACE_HYPHEN_RE, "-")
slug = slug.gsub(NON_ASCII_RE, "")

slug[...(max_size - suffix.size)].strip("-_") + suffix
slug[...(max_size)].strip("-_")
end
end
end
Expand Down
12 changes: 8 additions & 4 deletions src/marten/db/field/slug.cr
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module Marten
module Field
# Represents a slug field.
class Slug < String
private getter slugify
include Core::Sluggable

def initialize(
@id : ::String,
Expand All @@ -25,12 +25,14 @@ module Marten
# No-op max_size automatic checks...
end

def validate(record, value)
if slugify?(value)
slug = Core::Sluggable.generate_slug(record.get_field_value(slugify.not_nil!).to_s, max_size)
def prepare_save(record, new_record = false)
if slugify?(record.get_field_value(id))
slug = generate_slug(record.get_field_value(slugify.not_nil!).to_s, max_size)
record.set_field_value(id, slug)
end
end

def validate(record, value)
return if !value.is_a?(::String)

# Leverage string's built-in validations (max size).
Expand All @@ -45,6 +47,8 @@ module Marten
super if slugify.nil?
end

private getter slugify

private def slugify?(value)
slugify && (value.nil? || (value.is_a?(::String) && value.blank?))
end
Expand Down

0 comments on commit f577f7b

Please sign in to comment.