Mostly get db-based posts up and running.

This commit is contained in:
Jessica Canady 2024-09-24 12:31:41 -04:00
parent 7210b38757
commit 3378bc6065
Signed by: phoenix
SSH key fingerprint: SHA256:aaLOzOrLi+0n4eDZNQKH97PehwRt6KSE5fYJc+ZRKCQ
13 changed files with 92 additions and 16 deletions

View file

@ -1,5 +1,7 @@
defmodule JOL.Blog do defmodule JOL.Blog do
alias JOL.Blog.Post alias JOL.Blog.{Post, Tag}
alias JOL.Repo
import Ecto.Query
defmodule NotFoundError do defmodule NotFoundError do
defexception [:message, plug_status: 404] defexception [:message, plug_status: 404]
@ -10,17 +12,40 @@ defmodule JOL.Blog do
end end
def unique_tag_list do def unique_tag_list do
[] Repo.all(Tag, order_by: :name)
end end
def recent_posts(num \\ 10) do def recent_posts(num \\ 10) do
[] Repo.all(
from p in Post,
limit: ^num,
order_by: {:desc, :published_at}
)
end end
def get_post_by_slug!(slug) do def get_post_by_slug!(slug) do
Repo.one(
from p in Post,
where: [slug: ^slug],
preload: [:tags]
)
end end
def get_posts_by_tag!(tag) do def get_posts_by_tag!(tag) do
Repo.all(
from p in Post,
join: t in assoc(p, :tags),
preload: [tags: t],
where: t.name == ^tag
)
end
def get_tag_by_id!(id) do
Repo.one(
from t in Tag,
where: t.id == ^id,
preload: [:posts]
)
end end
def format_date(date) do def format_date(date) do

View file

@ -7,6 +7,7 @@ defmodule JOL.Blog.Post do
field :body, :string field :body, :string
field :published_at, :naive_datetime field :published_at, :naive_datetime
field :slug, :string field :slug, :string
many_to_many :tags, JOL.Blog.Tag, join_through: "post_tags"
timestamps(type: :utc_datetime) timestamps(type: :utc_datetime)
end end
@ -15,6 +16,7 @@ defmodule JOL.Blog.Post do
def changeset(post, attrs) do def changeset(post, attrs) do
post post
|> cast(attrs, [:title, :body, :published_at, :slug]) |> cast(attrs, [:title, :body, :published_at, :slug])
|> validate_required([:title, :body, :published_at, :slug]) |> validate_required([:title, :body, :slug])
|> cast_assoc(:tags)
end end
end end

View file

@ -1,9 +1,10 @@
defmodule JOL.Blog.Tags do defmodule JOL.Blog.Tag do
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset
schema "tags" do schema "tags" do
field :name, :string field :name, :string
many_to_many :posts, JOL.Blog.Post, join_through: "post_tags"
timestamps(type: :utc_datetime) timestamps(type: :utc_datetime)
end end
@ -13,5 +14,6 @@ defmodule JOL.Blog.Tags do
tags tags
|> cast(attrs, [:name]) |> cast(attrs, [:name])
|> validate_required([:name]) |> validate_required([:name])
|> unique_constraint([:name])
end end
end end

View file

@ -3,11 +3,11 @@
<h1><%= @post.title %></h1> <h1><%= @post.title %></h1>
<p> <p>
<date><%= JOL.Blog.format_date(@post.date) %></date> <date><%= JOL.Blog.format_date(@post.published_at) %></date>
</p> </p>
<%= raw @post.body %> <%= raw @post.body %>
<p class="post-tags"> <p class="post-tags">
Filed under: <%= Enum.join(@post.tags, ", ") %> Filed under: <%= @post.tags |> Enum.map(& &1.name) |> Enum.join(", ") %>
</p> </p>

View file

@ -12,7 +12,7 @@
<ul class="recent-posts"> <ul class="recent-posts">
<%= for post <- @posts do %> <%= for post <- @posts do %>
<li> <li>
<date><%= JOL.Blog.format_date(post.date) %></date> <date><%= JOL.Blog.format_date(post.published_at) %></date>
<.link href={~p"/blog/#{post.slug}"}><%= post.title %> </.link> <.link href={~p"/blog/#{post.slug}"}><%= post.title %> </.link>
</li> </li>
<% end %> <% end %>

View file

@ -8,11 +8,13 @@ defmodule JOLWeb.TagController do
tags: JOL.Blog.unique_tag_list) tags: JOL.Blog.unique_tag_list)
end end
def tag(conn, %{"tag" => tag}) do def tag(conn, %{"id" => id}) do
tag = JOL.Blog.get_tag_by_id!(id)
conn conn
|> render(:tag, |> render(:tag,
tag: tag, tag: tag.name,
page_title: "Posts Filed Under #{tag}", page_title: "Filed Under #{tag.name}",
posts: JOL.Blog.get_posts_by_tag!(tag)) posts: tag.posts)
end end
end end

View file

@ -3,7 +3,7 @@
<ul> <ul>
<%= for tag <- @tags do %> <%= for tag <- @tags do %>
<li class="tag-list"> <li class="tag-list">
<a href={~p"/tags/#{tag}"}><%= tag %></a> <a href={~p"/tags/#{tag.id}"}><%= tag.name %></a>
</li> </li>
<% end %> <% end %>
</ul> </ul>

View file

@ -2,7 +2,7 @@
<%= for post <- @posts do %> <%= for post <- @posts do %>
<div class="post-summary"> <div class="post-summary">
<p class="post-date"><date><%= post.date %></date></p> <p class="post-date"><date><%= post.published_at %></date></p>
<h3> <.link href={~p"/blog/#{post.slug}"}><%= post.title %> </.link> </h3> <h3> <.link href={~p"/blog/#{post.slug}"}><%= post.title %> </.link> </h3>
<p><%= raw post.lede %></p> <p><%= raw post.lede %></p>
</div> </div>

View file

@ -28,7 +28,7 @@ defmodule JOLWeb.Router do
get "/colophon", PageController, :colophon get "/colophon", PageController, :colophon
get "/tags", TagController, :index get "/tags", TagController, :index
get "/tags/:tag", TagController, :tag get "/tags/:id", TagController, :tag
get "/blog", BlogController, :index get "/blog", BlogController, :index
get "/blog/:slug", BlogController, :show get "/blog/:slug", BlogController, :show

View file

@ -59,7 +59,8 @@ defmodule JOL.MixProject do
{:dns_cluster, "~> 0.1.1"}, {:dns_cluster, "~> 0.1.1"},
{:bandit, "~> 1.2"}, {:bandit, "~> 1.2"},
{:atomex, "~> 0.3.0"}, {:atomex, "~> 0.3.0"},
{:tz, "~> 0.27"} {:tz, "~> 0.27"},
{:toml, "~> 0.7"}
] ]
end end

26
priv/migrate_posts.exs Normal file
View file

@ -0,0 +1,26 @@
alias JOL.Blog.{Parser,Post,Tag}
alias JOL.Repo
import Ecto.Query
post_dir = Application.app_dir(:jol, "priv/posts/blog/")
Path.wildcard("#{post_dir}/**/*.md")
|> Enum.each(fn path ->
{attrs, body} = Parser.parse(path, File.read!(path))
for tag <- attrs.tags do
Repo.insert!(%Tag{name: tag}, on_conflict: :nothing, conflict_target: [:name])
end
post_attrs = %{title: attrs.title,
published_at: attrs.date,
slug: attrs.slug,
body: body,
tags: Repo.all(from t in Tag, where: t.name in ^attrs.tags)
}
%Post{}
|> Repo.preload(:tags)
|> Ecto.Changeset.cast(post_attrs, [:published_at, :slug, :body, :title])
|> Ecto.Changeset.put_assoc(:tags, post_attrs.tags)
|> Repo.insert!()
end)

View file

@ -0,0 +1,10 @@
defmodule JOL.Repo.Migrations.JoinPostsAndTags do
use Ecto.Migration
def change do
create table(:post_tags) do
add :post_id, references(:posts)
add :tag_id, references(:tags)
end
end
end

View file

@ -0,0 +1,8 @@
defmodule JOL.Repo.Migrations.MakeTagNameUnique do
use Ecto.Migration
def change do
create unique_index(:tags, ["name"])
create unique_index(:post_tags, ["post_id", "tag_id"])
end
end