Goal: have a personal blog, and try out another point in the 'modular app design with elixir' space. Designing OTP systems with elixir had some interesting ideas.
119 lines
3.2 KiB
Elixir
119 lines
3.2 KiB
Elixir
defmodule Blogex do
|
|
@moduledoc """
|
|
Blogex — a multi-blog engine for Phoenix apps, powered by NimblePublisher.
|
|
|
|
Blogex lets you host multiple blogs (e.g. engineering blog, release notes)
|
|
from markdown files in your repo. Posts are compiled into the BEAM at build
|
|
time for instant reads with zero runtime I/O.
|
|
|
|
## Quick Start
|
|
|
|
1. Add `blogex` to your dependencies:
|
|
|
|
```elixir
|
|
def deps do
|
|
[
|
|
{:blogex, "~> 0.1.0"}
|
|
]
|
|
end
|
|
```
|
|
|
|
2. Create your markdown posts:
|
|
|
|
```
|
|
priv/blog/engineering/2026/03-10-our-new-architecture.md
|
|
priv/blog/release-notes/2026/03-01-v2-launch.md
|
|
```
|
|
|
|
Each file has frontmatter + content:
|
|
|
|
```markdown
|
|
%{
|
|
title: "Our New Architecture",
|
|
author: "Jane Doe",
|
|
tags: ~w(elixir architecture),
|
|
description: "How we rebuilt our platform"
|
|
}
|
|
---
|
|
Your markdown content here...
|
|
```
|
|
|
|
3. Define blog modules in your app:
|
|
|
|
```elixir
|
|
defmodule Firehose.EngineeringBlog do
|
|
use Blogex.Blog,
|
|
blog_id: :engineering,
|
|
app: :firehose,
|
|
from: "priv/blog/engineering/**/*.md",
|
|
title: "Engineering Blog",
|
|
description: "Deep dives into our tech stack",
|
|
base_path: "/blog/engineering"
|
|
end
|
|
|
|
defmodule Firehose.ReleaseNotes do
|
|
use Blogex.Blog,
|
|
blog_id: :release_notes,
|
|
app: :firehose,
|
|
from: "priv/blog/release-notes/**/*.md",
|
|
title: "Release Notes",
|
|
description: "What's new in our product",
|
|
base_path: "/blog/releases"
|
|
end
|
|
```
|
|
|
|
4. Register blogs in your config:
|
|
|
|
```elixir
|
|
config :blogex,
|
|
blogs: [Firehose.EngineeringBlog, Firehose.ReleaseNotes]
|
|
```
|
|
|
|
5. Mount routes in your Phoenix router:
|
|
|
|
```elixir
|
|
scope "/blog" do
|
|
pipe_through :browser
|
|
|
|
forward "/engineering", Blogex.Router, blog: Firehose.EngineeringBlog
|
|
forward "/releases", Blogex.Router, blog: Firehose.ReleaseNotes
|
|
end
|
|
```
|
|
|
|
6. Enable live reloading in `config/dev.exs`:
|
|
|
|
```elixir
|
|
live_reload: [
|
|
patterns: [
|
|
...,
|
|
~r"priv/blog/*/.*(md)$"
|
|
]
|
|
]
|
|
```
|
|
|
|
## Architecture (Poncho Pattern)
|
|
|
|
Blogex is designed as a "poncho" — it wraps NimblePublisher and provides
|
|
a clean public API while the inner library does the heavy lifting of
|
|
markdown parsing and compilation. The host app's supervision tree is used
|
|
directly; Blogex adds no processes of its own since all data is compiled
|
|
into module attributes at build time.
|
|
|
|
## Modules
|
|
|
|
* `Blogex.Blog` — macro to define a blog context
|
|
* `Blogex.Post` — post struct
|
|
* `Blogex.Registry` — cross-blog queries
|
|
* `Blogex.Feed` — RSS/Atom feed generation
|
|
* `Blogex.SEO` — meta tags and sitemap generation
|
|
* `Blogex.Components` — Phoenix function components
|
|
* `Blogex.Router` — mountable Plug router
|
|
"""
|
|
|
|
defdelegate blogs, to: Blogex.Registry
|
|
defdelegate get_blog!(blog_id), to: Blogex.Registry
|
|
defdelegate get_blog(blog_id), to: Blogex.Registry
|
|
defdelegate all_posts, to: Blogex.Registry
|
|
defdelegate all_tags, to: Blogex.Registry
|
|
end
|