Skip to content

Commit 88a1b76

Browse files
committed
feat: Slugify tag names
Uses the already installed dependency `slugify` instead of depending on `MDEx.anchorize/1`, and exposed via `Tableau.Extension.Common`. I added the `slug` configuration to the site configuration because I believe that such conversion should be consistent for a site. Resolves: #160
1 parent 953145c commit 88a1b76

5 files changed

Lines changed: 59 additions & 7 deletions

File tree

lib/tableau.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ defmodule Tableau do
1010
* `:converters` - mapping of file extensions to converter module. Defaults to `[md: Tableau.MDExConverter]`
1111
* `:markdown` - keyword
1212
* `:mdex` - keyword - Options to pass to `MDEx.to_html/2`
13+
* `:slug` - keyword - Options to pass to `Slug.slugify/2`
1314
1415
### Example
1516
@@ -25,6 +26,9 @@ defmodule Tableau do
2526
md: Tableau.MDExConverter,
2627
dj: MySite.DjotConverter
2728
],
29+
slug: [
30+
lowercase: false
31+
],
2832
markdown: [
2933
mdex: [
3034
extension: [

lib/tableau/config.ex

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ defmodule Tableau.Config do
1010
out_dir: "_site",
1111
timezone: "Etc/UTC",
1212
reload_log: false,
13+
slug: [],
1314
converters: [md: Tableau.MDExConverter],
1415
markdown: [mdex: []]
1516
]
@@ -32,6 +33,13 @@ defmodule Tableau.Config do
3233
optional(:reload_log) => bool(),
3334
optional(:converters) => keyword(values: atom()),
3435
optional(:markdown) => keyword(values: list()),
36+
optional(:slug) =>
37+
keyword(%{
38+
optional(:separator) => oneof([str(), int()]),
39+
optional(:lowercase) => bool(),
40+
optional(:truncate) => int(),
41+
optional(:ignore) => oneof([str(), list(oneof([str(), int()]))])
42+
}),
3543
optional(:base_path) => str(),
3644
url: str()
3745
},

lib/tableau/extensions/common.ex

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,27 @@ defmodule Tableau.Extension.Common do
88
wildcard |> Path.wildcard() |> Enum.sort()
99
end
1010

11+
@doc """
12+
Transform strings from any language into slugs using `Slug.slugify/1`.
13+
14+
Returns the original string if the slug cannot be generated.
15+
"""
16+
def slugify(string, overrides \\ [])
17+
18+
def slugify(string, %{site: %{config: config}}) do
19+
Slug.slugify(string, config.slug) || string
20+
end
21+
22+
def slugify(string, config) when is_map(config) do
23+
slugify(string)
24+
end
25+
26+
def slugify(string, overrides) do
27+
{:ok, config} = Tableau.Config.get()
28+
29+
Slug.slugify(string, Keyword.merge(config.slug, overrides)) || string
30+
end
31+
1132
@doc """
1233
Build content entries from a list of paths.
1334

lib/tableau/extensions/tag_extension.ex

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ defmodule Tableau.TagExtension do
66
77
The `@page` assign passed to the `layout` provided in the configuration is described by `t:page/0`.
88
9+
Tag names will be converted to slugs using `Slug.slugify/2` with options provided in Tableau configuration.
10+
911
## Configuration
1012
1113
- `:enabled` - boolean - Extension is active or not.
@@ -79,6 +81,8 @@ defmodule Tableau.TagExtension do
7981

8082
import Schematic
8183

84+
alias Tableau.Extension.Common
85+
8286
@type page :: %{
8387
title: String.t(),
8488
tag: String.t(),
@@ -89,7 +93,8 @@ defmodule Tableau.TagExtension do
8993
@type tag :: %{
9094
title: String.t(),
9195
tag: String.t(),
92-
permalink: String.t()
96+
permalink: String.t(),
97+
slug: String.t()
9398
}
9499

95100
@type tags :: %{
@@ -119,9 +124,10 @@ defmodule Tableau.TagExtension do
119124
tags =
120125
for post <- posts, tag <- post |> Map.get(:tags, []) |> Enum.uniq(), reduce: Map.new() do
121126
acc ->
122-
permalink = Path.join(permalink, tag)
127+
slug = Common.slugify(tag, token)
128+
permalink = Path.join(permalink, slug)
123129

124-
tag = %{title: tag, permalink: permalink, tag: tag}
130+
tag = %{title: tag, permalink: permalink, tag: tag, slug: slug}
125131
Map.update(acc, tag, [post], &[post | &1])
126132
end
127133

test/tableau/extensions/tag_extension_test.exs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ defmodule Tableau.TagExtensionTest do
1212
# dedups tags
1313
post(1, tags: ["post", "post"]),
1414
# post can have multiple tags, includes posts from same tag
15-
post(2, tags: ["til", "post"]),
15+
# tags will be converted to slugs for linking
16+
post(2, tags: ["til", "post", "Today I Learned"]),
1617
post(3, tags: ["recipe"])
1718
]
1819

@@ -27,9 +28,20 @@ defmodule Tableau.TagExtensionTest do
2728

2829
assert %{
2930
tags: %{
30-
%{tag: "post", title: "post", permalink: "/tags/post"} => [%{title: "Post 2"}, %{title: "Post 1"}],
31-
%{tag: "recipe", title: "recipe", permalink: "/tags/recipe"} => [%{title: "Post 3"}],
32-
%{tag: "til", title: "til", permalink: "/tags/til"} => [%{title: "Post 2"}]
31+
%{tag: "post", title: "post", permalink: "/tags/post", slug: "post"} => [
32+
%{title: "Post 2"},
33+
%{title: "Post 1"}
34+
],
35+
%{tag: "recipe", title: "recipe", permalink: "/tags/recipe", slug: "recipe"} => [%{title: "Post 3"}],
36+
%{tag: "til", title: "til", permalink: "/tags/til", slug: "til"} => [%{title: "Post 2"}],
37+
%{
38+
tag: "Today I Learned",
39+
title: "Today I Learned",
40+
permalink: "/tags/today-i-learned",
41+
slug: "today-i-learned"
42+
} => [
43+
%{title: "Post 2"}
44+
]
3345
},
3446
graph: graph
3547
} = token
@@ -39,6 +51,7 @@ defmodule Tableau.TagExtensionTest do
3951
assert Enum.any?(vertices, &page_with_permalink?(&1, "/tags/post"))
4052
assert Enum.any?(vertices, &page_with_permalink?(&1, "/tags/recipe"))
4153
assert Enum.any?(vertices, &page_with_permalink?(&1, "/tags/til"))
54+
assert Enum.any?(vertices, &page_with_permalink?(&1, "/tags/today-i-learned"))
4255

4356
assert Layout in vertices
4457
end

0 commit comments

Comments
 (0)