Phoenix is a great web framework. It can do more traditional MVC server-side web app work or get fancy with ultra-responsive web apps via sockets and LiveView.
One thing that Phoenix normally doesn’t do is static site generation.
I say normally because I love static site generation for things like marketing websites or launch pages or blogs. With blogs in particular, being able to write in markdown in any text editor is a kind of super power. It reduces friction significantly enough to materially change the volume of words & post output for all two of my lovely readers out there. And of course, they are dead simple to deploy and host.
So now that I’m big into Elixir, and I had a marketing blog on a different site that was a bit fragile and crufty, I figured there must be a way to:
Turns out there is.
There are two important libraries for doing markdown in a Phoenix app:
Earmark is a simple markdown processor. You give it markdown and it spits out HTML.
If you have some markdown stored in a variable, say named
@markdown_blurb, you could do the following:
<%= raw(Earmark.as_html!(@markdown_blurb)) %>
phoenix_markdown uses earmark internally. It is a Phoenix template engine implementation, so with a bit of config, you can simply render a markdown file like you would any html.eex template file. For example, given
some_markdown.html.md, you could render it like so:
<% render("some_markdown.html") %>
Given these two pieces you have some great options for writing markdown and publishing it to the web.
As stated above, I adore static site generation. Phoenix doesn’t really have any native capability to spit out static copies of a given route.
Just rendering templates doesn’t work, because you lose anything that might get assigned in the controllers.
There is one place however, that allows you to process a route as if a browser hit it and inspect the results: The unit test framework.
In a nutshell, to get a static site copy, all I did was create a unit test file called
exporter_test.exs and then give it a list of routes. You can get the response of a given route with simple unit test macros like so:
conn = Phoenix.ConnTest.build_conn() conn = get(conn, route_path) resp = html_response(conn, 200)
Then it’s just writing the
resp variable to a file.
I ended up writing the file directly into the
priv/static directory. That directory already has all the css, js, images and whatnot already.
It’s a simple matter of using rsync to push it up to a static host after that.
The entire method to write a single route to
priv/static looks like this:
So currently, to deploy all I do is
mix test and then run a simple deploy script that rsync’s
priv/static to the www root of the cloud instance.
There are lots of places for improvement. I probably should create a custom mix task. If you delete a markdown file, that file won’t be deleted in the priv directory.
But for a quick and dirty static site generator, this trick works surprisingly well.