Mostly get db-based posts up and running.
This commit is contained in:
parent
7210b38757
commit
3378bc6065
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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>
|
||||||
|
|
|
@ -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 %>
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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
|
||||||
|
|
3
mix.exs
3
mix.exs
|
@ -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
26
priv/migrate_posts.exs
Normal 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)
|
10
priv/repo/migrations/20240924153630_join_posts_and_tags.exs
Normal file
10
priv/repo/migrations/20240924153630_join_posts_and_tags.exs
Normal 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
|
|
@ -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
|
Loading…
Reference in a new issue