Combo.Template (combo v0.10.0)

View Source

Compiling and rendering templates.

In practice, we rarely use Combo.Template directly. Instead, we use Combo.HTML which is built on top of it.

Template languages

A template language is a specialized markup language for building templates.

Templates

Templates are the content written in various template languages.

Template files

A template file is a file containing template, and filename has the following structure: <NAME>.<FORMAT>.<ENGINE>, such as welcome.html.eex.

Template engines

A template engine is a module for compiling template files into Elixir's quoted expressions.

And, some engines supports template sigils, which are for compiling inline templates into Elixir's quoted expressions.

Custom template engines

Combo supports custom template engines.

See Combo.Template.Engine for more information on the API required to be implemented by custom engines.

Once template engines are defined, you can use them via the :engines option:

config :combo, :template,
  engines: [
    eex: CustomEExEngine,
    exs: CustomExsEngine
  ]

Format encoders

Besides template engines, Combo has the concept of format encoders.

Format encoders work per format and are responsible for encoding a given format to a string. For example, when rendering JSON, templates may return a regular Elixir map. Then, the JSON format encoder is invoked to convert it to JSON.

See Combo.Template.FormatEncoder for more information on the API required to be implemented by custom format encoders.

Once format encoders are defined, you can use them via the :format_encoders option:

config :combo, :template,
  format_encoders: [
    html: CustomHTMLEncoder
    json: CustomJSONEncoder
  ]

Summary

Functions

Ensures that __mix_recompile__?/0 will be defined.

Compiles a function for each template in the given root.

Embeds external template files into the module as functions.

Returns all template engines as a map.

Returns all template paths in a given template root.

Returns the format encoder for a given format.

Returns all format encoders as a map.

Returns the hash of all template paths in the given root.

Renders template from module.

Renders the template and returns iodata.

Renders the template to string.

Types

path()

@type path() :: binary()

root()

@type root() :: binary()

Functions

__using__(opts)

(macro)

Ensures that __mix_recompile__?/0 will be defined.

compile_all(converter, root, pattern \\ "*", engines \\ nil)

(macro)

Compiles a function for each template in the given root.

converter is an anonymous function that receives the template path and returns the function name (as a string).

For example, to compile all .eex templates in a given directory, you might do:

Combo.Template.compile_all(
  &(&1 |> Path.basename() |> Path.rootname(".eex")),
  __DIR__,
  "*.eex"
)

If the directory has templates named foo.eex and bar.eex, they will be compiled into the functions foo/1 and bar/1 that receive the template assigns as argument.

You may optionally pass a keyword list of engines. If a list is given, we will lookup and compile only this subset of engines. If none is passed (nil), the default list returned by engines/0 is used.

embed_templates(pattern, opts \\ [])

(macro)

Embeds external template files into the module as functions.

This macro is built on top of compile_all/3.

Options

  • :root - The root directory to embed template files. Defaults to the directory of current module (__DIR__).
  • :suffix - The string value to append to the embedded function names. By default, function names will be the name of the template file excluding the format and engine.

Examples

Imagine a directory listing:

 pages
    about.html.ceex
    sitemap.xml.eex

To embed the templates into a module, we can define a module like this:

defmodule DemoWeb.Pages do
  import Combo.Template, only: [embed_templates: 1]

  # a wildcard pattern is used to select all files within a directory
  embed_templates "pages/*"
end

Now, the module will have about/1 and sitemap/1 functions.

Multiple invocations of embed_templates is also supported, which can be useful if we have more than one template format. For example:

defmodule DemoWeb.Pages do
  import Combo.Template, only: [embed_templates: 2]

  embed_templates "pages/*.html", suffix: "_html"
  embed_templates "pages/*.xml", suffix: "_xml"
end

Now, the module will have about_html and sitemap_xml functions.

engines()

@spec engines() :: %{required(atom()) => module()}

Returns all template engines as a map.

find_all(root, pattern \\ "*", engines \\ engines())

@spec find_all(root(), pattern :: String.t(), %{required(atom()) => module()}) :: [
  path()
]

Returns all template paths in a given template root.

format_encoder(format)

@spec format_encoder(format :: String.t()) :: module() | nil

Returns the format encoder for a given format.

format_encoders()

@spec format_encoders() :: %{required(String.t()) => module()}

Returns all format encoders as a map.

hash(root, pattern \\ "*", engines \\ engines())

@spec hash(root(), pattern :: String.t(), %{required(atom()) => module()}) :: binary()

Returns the hash of all template paths in the given root.

Used by Combo to check if a given root path requires recompilation.

render(module, template, format, assigns)

Renders template from module.

For a module called DemoWeb.UserHTML and template "index.html.ceex", it will:

  • First attempt to call DemoWeb.UserHTML.index(assigns)

  • Then fallback to DemoWeb.UserHTML.render("index.html", assigns)

  • Raise otherwise

It expects the the module, the template as a string, the format, and a set of assigns.

Notice that this function returns the inner representation of a template. If you want the encoded template as a result, use render_to_iodata/4 instead.

Examples

Combo.Template.render(DemoWeb.UserHTML, "index", "html", name: "Charie Brown")
#=> {:safe, "Hello, Charlie Brown"}

render_to_iodata(module, template, format, assigns)

Renders the template and returns iodata.

render_to_string(module, template, format, assigns)

Renders the template to string.