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.
Hat Tip to Tjaco Oostdijk for his great article on Phoenix, Markdown and template engines.
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.