diff --git a/config/dev.exs b/config/dev.exs index 2cdfb60..0f0ac39 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -58,7 +58,8 @@ config :jol, JOLWeb.Endpoint, patterns: [ ~r"priv/static/(?!uploads/).*(js|css|png|jpeg|jpg|gif|svg)$", ~r"priv/gettext/.*(po)$", - ~r"lib/jol_web/(controllers|live|components)/.*(ex|heex)$" + ~r"lib/jol_web/(controllers|live|components)/.*(ex|heex)$", + ~r"posts/*/.*(md)$" ] ] diff --git a/lib/jol/blog.ex b/lib/jol/blog.ex index 230dfbe..05fd6bd 100644 --- a/lib/jol/blog.ex +++ b/lib/jol/blog.ex @@ -1,4 +1,8 @@ defmodule JOL.Blog do + defmodule NotFoundError do + defexception [:message, plug_status: 404] + end + use NimblePublisher, build: JOL.Blog.Post, from: Application.app_dir(:jol, "priv/posts/**/*.md"), @@ -8,6 +12,22 @@ defmodule JOL.Blog do @posts Enum.sort_by(@posts, & &1.date, {:desc, Date}) @tags @posts |> Enum.flat_map(& &1.tags) |> Enum.uniq() |> Enum.sort() - def posts, do: @posts + def all_posts, do: @posts def unique_tag_list, do: @tags + + def recent_posts(num \\ 10) do + Enum.take(all_posts(), num) + end + + def get_post_by_slug!(slug) do + Enum.find(all_posts(), &(&1.slug == slug)) || + raise NotFoundError, "post ``slug=#{slug}` not found" + end + + def get_posts_by_tag!(tag) do + case Enum.filter(all_posts(), &(tag in &1.tags)) do + [] -> raise NotFoundError, "posts tagged `#{tag}` not found" + posts -> posts + end + end end diff --git a/lib/jol/blog/parser.ex b/lib/jol/blog/parser.ex index 4b191d4..f265ba5 100644 --- a/lib/jol/blog/parser.ex +++ b/lib/jol/blog/parser.ex @@ -24,7 +24,8 @@ defmodule JOL.Blog.Parser do title: toml_attrs["title"], draft: toml_attrs["draft"], tags: toml_attrs["taxonomies"]["tags"], - date: toml_attrs["date"] + date: toml_attrs["date"], + slug: toml_attrs["slug"] } parsed_body = String.trim(body) diff --git a/lib/jol/blog/post.ex b/lib/jol/blog/post.ex index 71a95a4..fe35a33 100644 --- a/lib/jol/blog/post.ex +++ b/lib/jol/blog/post.ex @@ -1,6 +1,6 @@ defmodule JOL.Blog.Post do - @enforce_keys [:author, :draft, :title, :body, :tags, :date] - defstruct [:author, :draft, :title, :body, :tags, :date] + @enforce_keys [:author, :title, :body, :tags, :date, :slug] + defstruct [:author, :draft, :title, :body, :tags, :date, :slug] def build(_filename, attrs, body) do struct!(__MODULE__, [author: "Jessica Phoenix Canady", body: body] ++ Map.to_list(attrs)) diff --git a/lib/jol_web/controllers/blog_controller.ex b/lib/jol_web/controllers/blog_controller.ex new file mode 100644 index 0000000..265c246 --- /dev/null +++ b/lib/jol_web/controllers/blog_controller.ex @@ -0,0 +1,13 @@ +defmodule JOLWeb.BlogController do + use JOLWeb, :controller + + alias JOL.Blog + + def index(conn, _params) do + render(conn, "index.html", posts: Blog.recent_posts()) + end + + def show(conn, %{"slug" => slug}) do + render(conn, "show.html", post: Blog.get_post_by_slug!(slug)) + end +end diff --git a/lib/jol_web/controllers/blog_html.ex b/lib/jol_web/controllers/blog_html.ex new file mode 100644 index 0000000..65978d6 --- /dev/null +++ b/lib/jol_web/controllers/blog_html.ex @@ -0,0 +1,5 @@ +defmodule JOLWeb.BlogHTML do + use JOLWeb, :html + + embed_templates "blog_html/*" +end diff --git a/lib/jol_web/controllers/blog_html/index.heex b/lib/jol_web/controllers/blog_html/index.heex new file mode 100644 index 0000000..60c57c6 --- /dev/null +++ b/lib/jol_web/controllers/blog_html/index.heex @@ -0,0 +1,7 @@ +

The Blog

+ + diff --git a/lib/jol_web/controllers/blog_html/show.heex b/lib/jol_web/controllers/blog_html/show.heex new file mode 100644 index 0000000..ee07900 --- /dev/null +++ b/lib/jol_web/controllers/blog_html/show.heex @@ -0,0 +1,13 @@ +<.link href={~p"/blog"}>← All posts + +

<%= @post.title %>

+ +

+ by <%= @post.author %> +

+ +

+ Tagged as <%= Enum.join(@post.tags, ", ") %> +

+ +<%= raw @post.body %> diff --git a/lib/jol_web/router.ex b/lib/jol_web/router.ex index b30e113..ecdd57b 100644 --- a/lib/jol_web/router.ex +++ b/lib/jol_web/router.ex @@ -17,7 +17,8 @@ defmodule JOLWeb.Router do scope "/", JOLWeb do pipe_through :browser - get "/", PageController, :home + get "/blog", BlogController, :index + get "/blog/:slug", BlogController, :show end # Other scopes may use custom stacks. diff --git a/priv/posts/blog/lan-voice-chat.md b/priv/posts/blog/lan-voice-chat.md index 4b537c3..ce46ffc 100644 --- a/priv/posts/blog/lan-voice-chat.md +++ b/priv/posts/blog/lan-voice-chat.md @@ -1,4 +1,5 @@ +++ +slug = "mumble_voice_chat" title = "LAN Voice Chat with Mumble" date = 2024-04-16 11:51:33-04:00 draft = false diff --git a/priv/posts/blog/odyssey-gen2-kvm.md b/priv/posts/blog/odyssey-gen2-kvm.md index f43ad25..71b0945 100644 --- a/priv/posts/blog/odyssey-gen2-kvm.md +++ b/priv/posts/blog/odyssey-gen2-kvm.md @@ -1,4 +1,5 @@ +++ +slug = "odyssey_ark_gen2_kvm" title = "HOWTO: Use the KVM in the Odyssey Ark Gen2" draft = false date = 2024-01-02 14:00:00-05:00 diff --git a/priv/posts/blog/steam-deck-touchscreen-fix.md b/priv/posts/blog/steam-deck-touchscreen-fix.md index 663b9a1..e3954f6 100644 --- a/priv/posts/blog/steam-deck-touchscreen-fix.md +++ b/priv/posts/blog/steam-deck-touchscreen-fix.md @@ -1,5 +1,6 @@ +++ title = "HOWTO: Fix Steam Deck Unresponsive Touchscreen" +slug = "fix_steam_deck_touchscreen" date = 2024-01-07 13:40:00-05:00 [taxonomies] diff --git a/priv/posts/blog/the-names-we-discard.md b/priv/posts/blog/the-names-we-discard.md index 4914c16..f6e7153 100644 --- a/priv/posts/blog/the-names-we-discard.md +++ b/priv/posts/blog/the-names-we-discard.md @@ -1,5 +1,6 @@ +++ title = "The Names We Discard" +slug = "names_we_discard" date = 2024-01-30 15:53:22-05:00 [taxonomies] diff --git a/test/lib/blog/parser_test.exs b/test/lib/blog/parser_test.exs index 219db4a..23b5564 100644 --- a/test/lib/blog/parser_test.exs +++ b/test/lib/blog/parser_test.exs @@ -6,6 +6,7 @@ defmodule JOL.Blog.ParserTest do content = """ +++ title = "test post" + slug = "test_post" draft = false date = 2024-01-02 14:00:00-05:00 @@ -29,6 +30,10 @@ defmodule JOL.Blog.ParserTest do assert post.attrs.title == "test post" end + test "parses the title from zola-style posts", post do + assert post.attrs.title == "test_post" + end + test "parses the tags from zola-style posts", post do assert post.attrs.tags == ["howto", "hardware"] end