Skip to content

Commit 0baf0fa

Browse files
committed
Various OG image improvements
Some better naming and switching from controller to plug, that just feels a lot better.
1 parent d8bf097 commit 0baf0fa

8 files changed

Lines changed: 115 additions & 87 deletions

File tree

lib/jola_dev/og_image.ex

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
defmodule JolaDev.OGImage do
22
@moduledoc """
33
Per-page Open Graph image lookup. All images are rendered at compile time
4-
via `JolaDev.OGImage.Renderer` and baked into the `@baked` module
5-
attribute, then served at runtime from `JolaDevWeb.OGImageController`.
4+
via `JolaDev.OGImage.Renderer` and baked into the `@images` module
5+
attribute, then served at runtime from `JolaDevWeb.Plugs.OGImage`.
66
77
See `JolaDev.OGImage.Renderer` for the rendering primitives.
88
"""
99

1010
alias JolaDev.OGImage.Renderer
1111

1212
@dev_mode Application.compile_env!(:jola_dev, :og_image_dev_mode)
13-
@baked if not @dev_mode,
14-
do:
15-
Map.new(Renderer.all_slugs(), fn slug ->
16-
{title, description} = Renderer.content_for(slug)
17-
{slug, Renderer.generate_bytes(title, description)}
18-
end)
13+
@images if not @dev_mode,
14+
do:
15+
Map.new(Renderer.all_slugs(), fn slug ->
16+
{title, description} = Renderer.content_for(slug)
17+
{slug, Renderer.generate_bytes(title, description)}
18+
end)
1919

2020
@doc """
2121
Returns the public asset path for a slug's OG image. Used by controllers,
@@ -26,9 +26,9 @@ defmodule JolaDev.OGImage do
2626
@doc """
2727
Returns `{:ok, png_bytes}` for a known slug, `:error` otherwise.
2828
"""
29-
def bytes_for(slug) do
30-
if @baked do
31-
Map.fetch(@baked, slug)
29+
def image_for(slug) do
30+
if @images do
31+
Map.fetch(@images, slug)
3232
else
3333
case Renderer.content_for(slug) do
3434
{title, description} ->

lib/jola_dev_web/controllers/og_image_controller.ex

Lines changed: 0 additions & 27 deletions
This file was deleted.

lib/jola_dev_web/endpoint.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ defmodule JolaDevWeb.Endpoint do
3030
gzip: Mix.env() == :prod,
3131
only: JolaDevWeb.static_paths()
3232

33+
plug JolaDevWeb.Plugs.OGImage
3334
plug JolaDevWeb.Plugs.BlogRedirect
3435
plug JolaDevWeb.Plugs.StripTrailingSlash
3536

lib/jola_dev_web/plugs/og_image.ex

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
defmodule JolaDevWeb.Plugs.OGImage do
2+
@moduledoc """
3+
Serves Open Graph preview images from `JolaDev.OGImage`. Intercepts any
4+
`/images/og/<slug>.png` request, looks up the baked PNG bytes, and sends
5+
them with appropriate cache headers. Falls through for unknown slugs so
6+
Phoenix's router can render a 404.
7+
"""
8+
9+
@behaviour Plug
10+
11+
import Plug.Conn
12+
13+
@dev_mode Application.compile_env!(:jola_dev, :og_image_dev_mode)
14+
@cache_control if @dev_mode, do: "public, max-age=0", else: "public, max-age=31536000"
15+
16+
@impl Plug
17+
def init(opts), do: opts
18+
19+
@impl Plug
20+
def call(%Plug.Conn{request_path: "/images/og/" <> rest} = conn, _) do
21+
slug = String.replace_suffix(rest, ".png", "")
22+
23+
case JolaDev.OGImage.image_for(slug) do
24+
{:ok, bytes} ->
25+
conn
26+
|> put_resp_content_type("image/png")
27+
|> put_resp_header("cache-control", @cache_control)
28+
|> send_resp(200, bytes)
29+
|> halt()
30+
31+
:error ->
32+
# Let the request fall through so Phoenix handles the 404
33+
conn
34+
end
35+
end
36+
37+
def call(conn, _), do: conn
38+
end

lib/jola_dev_web/router.ex

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,6 @@ defmodule JolaDevWeb.Router do
4242
get "/posts", BlogController, :index
4343
get "/posts/tag/:tag", BlogController, :tag
4444
get "/posts/:id", BlogController, :show
45-
46-
get "/images/og/*slug", OGImageController, :show
4745
end
4846

4947
scope "/", JolaDevWeb do

test/jola_dev/og_image_test.exs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,28 @@ defmodule JolaDev.OGImageTest do
1010
end
1111
end
1212

13-
describe "bytes_for/1" do
13+
describe "image_for/1" do
1414
test "returns the baked PNG bytes for a known static slug" do
15-
assert {:ok, bytes} = OGImage.bytes_for("home")
15+
assert {:ok, bytes} = OGImage.image_for("home")
1616
assert <<137, "PNG\r\n", 26, "\n", _rest::binary>> = bytes
1717
end
1818

1919
test "returns the baked PNG bytes for a known post slug" do
2020
post = List.first(JolaDev.Blog.all_posts())
2121

22-
assert {:ok, bytes} = OGImage.bytes_for("posts/#{post.id}")
22+
assert {:ok, bytes} = OGImage.image_for("posts/#{post.id}")
2323
assert <<137, "PNG\r\n", 26, "\n", _rest::binary>> = bytes
2424
end
2525

2626
test "returns the baked PNG bytes for a known tag slug" do
2727
tag = List.first(JolaDev.Blog.all_tags())
2828

29-
assert {:ok, bytes} = OGImage.bytes_for("posts/tag/#{tag}")
29+
assert {:ok, bytes} = OGImage.image_for("posts/tag/#{tag}")
3030
assert <<137, "PNG\r\n", 26, "\n", _rest::binary>> = bytes
3131
end
3232

3333
test "returns :error for an unknown slug" do
34-
assert OGImage.bytes_for("no-such-page") == :error
34+
assert OGImage.image_for("no-such-page") == :error
3535
end
3636
end
3737
end

test/jola_dev_web/controllers/og_image_controller_test.exs

Lines changed: 0 additions & 42 deletions
This file was deleted.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
defmodule JolaDevWeb.Plugs.OGImageTest do
2+
use JolaDevWeb.ConnCase, async: true
3+
alias JolaDevWeb.Plugs.OGImage
4+
5+
describe "call/2" do
6+
test "serves a PNG for a static slug", %{conn: conn} do
7+
conn =
8+
conn
9+
|> Map.put(:request_path, "/images/og/home.png")
10+
|> OGImage.call([])
11+
12+
assert conn.status == 200
13+
assert get_resp_header(conn, "content-type") == ["image/png; charset=utf-8"]
14+
assert <<137, "PNG\r\n", 26, "\n", _rest::binary>> = conn.resp_body
15+
assert conn.halted
16+
end
17+
18+
test "serves a PNG for a known post slug", %{conn: conn} do
19+
post = List.first(JolaDev.Blog.all_posts())
20+
21+
conn =
22+
conn
23+
|> Map.put(:request_path, "/images/og/posts/#{post.id}.png")
24+
|> OGImage.call([])
25+
26+
assert conn.status == 200
27+
assert <<137, "PNG\r\n", 26, "\n", _rest::binary>> = conn.resp_body
28+
end
29+
30+
test "serves a PNG for a known tag slug", %{conn: conn} do
31+
tag = List.first(JolaDev.Blog.all_tags())
32+
33+
conn =
34+
conn
35+
|> Map.put(:request_path, "/images/og/posts/tag/#{tag}.png")
36+
|> OGImage.call([])
37+
38+
assert conn.status == 200
39+
assert <<137, "PNG\r\n", 26, "\n", _rest::binary>> = conn.resp_body
40+
end
41+
42+
test "falls through for an unknown OG slug", %{conn: conn} do
43+
original = Map.put(conn, :request_path, "/images/og/no-such-page.png")
44+
45+
result = OGImage.call(original, [])
46+
47+
assert result == original
48+
refute result.halted
49+
end
50+
51+
test "passes through for paths outside /images/og/", %{conn: conn} do
52+
original = Map.put(conn, :request_path, "/posts/running-local-models-on-m4")
53+
54+
result = OGImage.call(original, [])
55+
56+
assert result == original
57+
refute result.halted
58+
end
59+
end
60+
end

0 commit comments

Comments
 (0)