I am digging deeper and deeper into Elixir and recently I have been using Phoenix to play around with some web apps. Soon I was to deal with EEx, which stands for Embedded Elixir and is a templating engine for Elixir. It works like Jinja for Python or ERB for Ruby.

EEx has a wonderful feature that is also used in Phoenix where the template is available as a function in the View module. It then gets called with the assigns as a parameter and returns just a binary with the content.

I wondered how this was implemented internally and of course you can invoke it also outside of the framework (and use it as a template for e.g. configruation files). First of all create the template in an example.eex file:

Hello, <%= name %>!

And create a Render module that will render the template and a function

defmodule Render do
  require EEx

  EEx.function_from_file(:def, :greet, "example.eex", [:name])
end

You can then use the defined function named greet and call it:

iex> Render.greet("Dominik")
"Hello, Dominik!\n"

The code behind this makes heavy use of meta programming features in Elixir. There is a defmacro that loads and compiles the template and then defines a function for the given name that uses the compiled template.

defmacro function_from_file(kind, name, file, args \\ [], options \\ []) do
  quote bind_quoted: binding do
    info = Keyword.merge options, [file: file, line: 1]
    args = Enum.map args, fn arg -> {arg, [line: 1], nil} end
    compiled = EEx.compile_file(file, info)

    @external_resource file
    @file file
    case kind do
      :def  -> def(unquote(name)(unquote_splicing(args)), do: unquote(compiled))
      :defp -> defp(unquote(name)(unquote_splicing(args)), do: unquote(compiled))
    end
  end
end

Source: https://github.com/elixir-lang/elixir/blob/master/lib/eex/lib/eex.ex

Of course this is not only useful for the web but also for configuration files or other files that can be generated by Elixir code.

Read more