Webmachine is an Erlang project I’ve known and used for years,
especially thanks to my experience at Basho. One of the things that
made me proud to use it was its opinionated nature about HTTP, namely,
that in most cases you don’t have to manually set status codes or
supply response headers. Instead, you supply predicate functions that
answer very specific questions about your HTTP resource, and the
Webmachine FSM calls those to determine how to respond to each
request. This leads to a very declarative style, which is a great fit
for functional programming, and it becomes simple to extend your
resource with more capabilities.
In contrast, the trend in the Elixir community is to use Plug, often
in conjunction with Phoenix. Plug is very much in the style of Rack
and WSGI which came before it, and I’ve ranted about the problems with
those before (and will not repeat here).
That said, someone has
“ported” Webmachine to Elixir
and provided a complicated macro-based DSL with which to declare
resources. When I
ported Webmachine to Ruby,
I eschewed DSLs for a simple inherit-and-override model which was
incredibly successful and easy to understand, in my opinion, not to
mention faster and more efficient than hacks using instance_eval.
In this post, I hope to convince you that Elixir’s simplicity and
direct interaction with Erlang libraries makes it possible to use
Webmachine in your Elixir project, without a rewrite or translation
Before I dive into it, you should be aware some caveats.
Most Elixir projects use Cowboy or Elli for the
webserver. Unfortunately, Webmachine is only compatible with
mochiweb; although previously efforts have been made to connect Yaws
and Cowboy, those have never completed.
There’s some cruft in Webmachine, simply because it was started
before 2009. I hope it won’t be too onerous to work around.
At the time of writing, I’m running Elixir 1.2.3 atop Erlang/OTP
18.2.1 on Mac OS/X, both installed via homebrew.
I’ll assume you know a little bit about Elixir syntax and idioms, and
won’t add too much discussion of the basics.
Let’s start by making a new project with mix, and then we’ll add
Webmachine and a basic resource that returns some HTML. I’ll be
roughly following the
Chris Meiklejohn and I gave to LambdaJam in 2013, which is a very
basic Twitter clone called “Tweeter”.
Now we can add Webmachine to the project and compile. First, edit
mix.exs to add Webmachine.
Fetch the dependencies and compile!
My First Resource
Now that we’ve verified we can build everything, let’s hook up the
webserver and a basic resource to serve some content. To do that, we
need to start the server as a child of our application
supervisor. Luckily, we generated a supervisor when we ran mix
new. Edit lib/tweeter.ex to look like this:
Now run the app and go to http://127.0.0.1:8080/.
You should get a very “Web 1.0” 404 page. Hit Ctrl-C a ENTER in the
shell to exit the Mix process. Let’s add a “Hello, World” resource so
we can get something other than the 404 page. Following Elixir
conventions, we’ll build out our source tree to separate concerns.
Below is what we’ll put into hello.ex, along with a little
explanation of why.
That isn’t too bad, right? You do have some strange 3-tuple return
values. What are they?
The 3-tuple return values thread the request/response data and the
resource state through the functions, while the first element of each
is the callback-specific return value. So, for ping/2 we need to
return :pong for success, and to_html/2 needs to return the
response body. This pattern will repeat as we add more functionality
to our resources.
Now let’s hook it up to the webserver in tweeter.ex:
What we’ve modified here is the “dispatch list”, or “routes” as they
are called in other frameworks. The  in the first position says
we’ll match the root URL, or /. The second position is the module
name of our resource. The third position is any additional arguments
to initialize our resource (passed to init/1 on startup). Now we can
try it out! Run mix run --no-halt again and refresh your
browser. You should see “Hello, World!” on the screen.
In the next installment, we’ll learn some more parts of Webmachine
resources to serve up some static content.