86 lines
2.1 KiB
Elixir
86 lines
2.1 KiB
Elixir
defmodule Blogex.Post do
|
|
@moduledoc """
|
|
Struct representing a single blog post.
|
|
|
|
Posts are parsed from markdown files with frontmatter metadata.
|
|
The filename determines the date and slug:
|
|
|
|
priv/blog/engineering/2026/03-10-our-new-architecture.md
|
|
|
|
Frontmatter example:
|
|
|
|
%{
|
|
title: "Our New Architecture",
|
|
author: "Jane Doe",
|
|
tags: ~w(elixir architecture),
|
|
description: "How we rebuilt our platform"
|
|
}
|
|
---
|
|
Your markdown content here...
|
|
"""
|
|
|
|
@enforce_keys [:id, :title, :author, :body, :description, :date]
|
|
defstruct [
|
|
:id,
|
|
:title,
|
|
:author,
|
|
:body,
|
|
:description,
|
|
:date,
|
|
:blog,
|
|
tags: [],
|
|
published: true
|
|
]
|
|
|
|
@type t :: %__MODULE__{
|
|
id: String.t(),
|
|
title: String.t(),
|
|
author: String.t(),
|
|
body: String.t(),
|
|
description: String.t(),
|
|
date: Date.t(),
|
|
tags: [String.t()],
|
|
blog: atom(),
|
|
published: boolean()
|
|
}
|
|
|
|
@type visibility :: :draft | :scheduled | :live
|
|
|
|
@doc "Returns the visibility of a post: :draft, :scheduled, or :live."
|
|
def visibility(%__MODULE__{published: false}), do: :draft
|
|
|
|
def visibility(%__MODULE__{published: true, date: date}) do
|
|
if Date.after?(date, Date.utc_today()), do: :scheduled, else: :live
|
|
end
|
|
|
|
@doc "Returns days until a scheduled post goes live, or nil."
|
|
def days_until_live(%__MODULE__{} = post) do
|
|
case visibility(post) do
|
|
:scheduled -> Date.diff(post.date, Date.utc_today())
|
|
_ -> nil
|
|
end
|
|
end
|
|
|
|
@doc """
|
|
Build callback for NimblePublisher.
|
|
|
|
Extracts the date from the filename path and merges with frontmatter attrs.
|
|
The `blog` atom is injected by the parent Blog module.
|
|
"""
|
|
def build(filename, attrs, body) do
|
|
[year, month_day_id] =
|
|
filename
|
|
|> Path.rootname()
|
|
|> Path.split()
|
|
|> Enum.take(-2)
|
|
|
|
[month, day, id] = String.split(month_day_id, "-", parts: 3)
|
|
date = Date.from_iso8601!("#{year}-#{month}-#{day}")
|
|
|
|
struct!(
|
|
__MODULE__,
|
|
[id: id, date: date, body: body] ++ Map.to_list(attrs)
|
|
)
|
|
end
|
|
end
|