Documentation
¶
Overview ¶
Package friendlyid is a pure-Go (CGO=0), MRI-faithful reimplementation of the Ruby friendly_id gem's slug engine: slug normalization (the ActiveSupport parameterize semantics), candidate generation with collision suffixing, an optional slug history, and the friendly finder that resolves a slug — or an old historical slug — back to a record id, falling back to a numeric id.
Everything host-specific (the base attribute to slugify, uniqueness lookups, and history persistence) is an injectable seam, so the engine runs with no database and a go-embedded-ruby (rbgo) binding can wire the seams to ActiveRecord.
Index ¶
- Variables
- func Parameterize(s, sep string) string
- type Config
- type Finder
- type MemStore
- func (m *MemStore) Assign(id, slug string) error
- func (m *MemStore) CurrentID(slug string) (string, bool)
- func (m *MemStore) Exists(slug string) bool
- func (m *MemStore) Finder(byID func(id string) bool) Finder
- func (m *MemStore) HistoryID(slug string) (string, bool)
- func (m *MemStore) Slugs() []Slug
- type Slug
Constants ¶
This section is empty.
Variables ¶
var ( // ErrBlank is returned when a base string normalizes to an empty slug. ErrBlank = errors.New("friendlyid: blank slug") // ErrReserved is returned when a normalized slug is on the reserved-words list. ErrReserved = errors.New("friendlyid: reserved slug") // ErrNilExists is returned when Resolve is called without an existence check. ErrNilExists = errors.New("friendlyid: nil exists function") // ErrNotFound is the friendly finder's RecordNotFound analogue. ErrNotFound = errors.New("friendlyid: record not found") // ErrTaken is returned when assigning a slug already current for another record. ErrTaken = errors.New("friendlyid: slug already taken") )
Functions ¶
func Parameterize ¶
Parameterize turns an arbitrary string into a slug using the ActiveSupport parameterize semantics with sep as the separator: transliterate accented Latin characters to ASCII, lower-case, replace every run of characters outside [a-z0-9] with a single separator, and trim leading/trailing separators.
Types ¶
type Config ¶
type Config struct {
// Separator replaces runs of non-alphanumeric characters in a slug.
Separator string
// SequenceSeparator joins a base slug and a numeric sequence when resolving a
// collision (e.g. "post" -> "post-2"). Defaults to Separator when empty.
SequenceSeparator string
// Reserved is a case-insensitive blocklist of words that may never be used as
// a slug (friendly_id's reserved_words), e.g. "new", "edit".
Reserved []string
// MaxLength truncates a normalized slug to at most this many bytes when > 0.
MaxLength int
}
Config controls slug generation. The zero Config is usable: Separator defaults to "-" and SequenceSeparator to "-" when empty (see normalized()).
func (Config) Candidates ¶
Candidates returns the ordered slug candidates friendly_id tries for base plus any extra discriminators: the base slug first, then the base joined with each discriminator by the sequence separator. Empty/reserved candidates are skipped. The first returned candidate is always the normalized base (when valid).
func (Config) Normalize ¶
Normalize produces the canonical friendly id for base: parameterized, reserved words rejected (returning ErrReserved), truncated to MaxLength, and rejected when empty (ErrBlank). It is the value friendly_id stores as the slug.
func (Config) Resolve ¶
func (c Config) Resolve(base string, exists func(slug string) bool, extra ...string) (string, error)
Resolve returns the first candidate for which exists reports false — the slug that can be assigned without colliding. When every candidate collides it falls back to sequential suffixing of the base ("base-2", "base-3", …) until a free slug is found. base must normalize to a non-empty, non-reserved slug.
type Finder ¶
type Finder struct {
// BySlug resolves the model's current slug column. Required.
BySlug func(slug string) (id string, found bool)
// ByHistory resolves a slug recorded in the slug history. Nil disables history
// (friendly_id without the :history module).
ByHistory func(slug string) (id string, found bool)
// ByID reports whether a record exists for a raw primary key. Nil disables the
// numeric-id fallback.
ByID func(id string) (found bool)
}
Finder implements friendly_id's Model.friendly.find(input): resolve a current slug to a record id, then (when history is enabled) an old historical slug, and finally fall back to treating the input as a raw primary key. Each lookup is an injectable seam so the finder runs with no database; an rbgo binding wires them to ActiveRecord.
type MemStore ¶
type MemStore struct {
// contains filtered or unexported fields
}
MemStore is an in-memory reference slug store for a single sluggable type. It tracks each record's current slug plus the full history of slugs it ever held, and exposes the seams a Config/Finder needs: Exists (uniqueness among current slugs), CurrentID (the model's slug column), and HistoryID (an old slug). An rbgo binding replaces it with an ActiveRecord-backed store; tests use it directly.
func NewMemStore ¶
NewMemStore returns an empty store for records of sluggableType.
func (*MemStore) Assign ¶
Assign records slug as id's current slug. The record's previous current slug (if any and different) is retained only in history, so an old slug keeps resolving to the record. A slug already current for a different record is rejected with ErrTaken.
func (*MemStore) CurrentID ¶
CurrentID returns the record id whose current slug is slug (the BySlug seam).
func (*MemStore) Exists ¶
Exists reports whether slug is currently assigned to some record — the uniqueness check Config.Resolve needs.
func (*MemStore) Finder ¶
Finder builds a Finder wired to this store, with the numeric-id fallback checking byID.
