I prefer explicit boilerplate code over metaprogramming. Code generators are a great way to quickly produce the repetitive boilerplate code — if you can get them to generate the code you want. Luckily, with Phoenix, you can.
How to override Phoenix generator templates
Elixir’s Phoenix framework comes with a few useful generators out of the box, which are implemented as Mix tasks. My favourite is
phx.gen.html. It will generate a schema, migration, context, controllers, view and templates. It is comparable to the scaffold generator in Ruby on Rails.
The most interesting thing to customise about this generator is the view template files it creates. Although you could also customise the generated controller, schema and context; the templates are the most likely to not contain the code you want.
Phoenix’ generators support loading the template files from your project directory. For example, if you create a file
priv/templates/phx.gen.html/index.html.eex, that file will be used instead of the built-in template for
index.html.eex. It is up to you to decide which templates to override; Phoenix will fall back to the default templates where necessary.
Generating useful files using EEx
The template files will be processed through EEx, Elixir’s template language. This allows for interpolating dynamic values into the template. The most interesting variable that is available in these templates is
schema, which is a
Mix.Phoenix.Schema struct. Among others, this struct contains these values:
singular: the schema name in plural or singular form, such as
human_singular: human-friendly versions of the above, such as
route_helper: the first part of the routing function names, such as the
attrs: the list of attributes you defined for your schema on the command-line. You can use this to generate form fields or table columns.
Two types of EEx tags
The EEx templates have some special tags to distinguish between actual Elixir code blocks to be executed at file generation time, and code blocks to be executed at runtime (see the EEx documentation about tags). Your template might contain some code like this:
<%%= link "New <%= schema.human_singular %>", to: Routes.<%= schema.route_helper %>_path(@conn, :new) %>
When you invoke the generator, this will become a regular source file like this:
<%= link "New post", to: Routes.post_path(@conn, :new) %>
EEx tags using a double
% will become regular tags, while EEx tags using a single
% will be evaluated as the files are created by the generator.
Phoenix default templates
The best way to review how this all works together, is by looking at the default templates that Phoenix includes. You can find them in the Phoenix source at
priv/templates. Copying the default files is also a great way to get started with customising the templates for your project.
What you can’t do
Phoenix’ generators are nice and simple. They do just about all you need them to do. Compared to the generators we find in Rails, they have a few shortcomings:
- There is no easy way to create your own generators.
- There is no shortcut to quickly copy over the default templates into your own project.
- There is no easy way to customise the logic in the existing generators, other than tweaking the file templates.
- There is no easy way for libraries to hook into generators and provide alternative implementations or extensions.
These are pretty advanced features that are, I think, rarely used. Leaving them out means you can read the source code for Phoenix generators in a few minutes and quickly get a handle on how things work.
Phoenix’ generators are nice and simple and a great way to write simple boilerplate code that is easy to tweak. The implementation code is simple enough to read through yourself. If you customise the template files, especially those for the view templates, you can get a lot of value out of these generators.