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
alias JOL.Blog.Post
alias JOL.Blog.{Post, Tag}
alias JOL.Repo
import Ecto.Query
defmodule NotFoundError do
defexception [:message, plug_status: 404]
@ -10,17 +12,40 @@ defmodule JOL.Blog do
end
def unique_tag_list do
[]
Repo.all(Tag, order_by: :name)
end
def recent_posts(num \\ 10) do
[]
Repo.all(
from p in Post,
limit: ^num,
order_by: {:desc, :published_at}
)
end
def get_post_by_slug!(slug) do
Repo.one(
from p in Post,
where: [slug: ^slug],
preload: [:tags]
)
end
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
def format_date(date) do

View file

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

View file

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

View file

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

View file

@ -12,7 +12,7 @@
<ul class="recent-posts">
<%= for post <- @posts do %>
<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>
</li>
<% end %>

View file

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

View file

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

View file

@ -2,7 +2,7 @@
<%= for post <- @posts do %>
<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>
<p><%= raw post.lede %></p>
</div>

View file

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

View file

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