marker v1.1.1 Marker

Marker

Marker strives to be the most convenient tool for writing html markup in Elixir. It allows writing markup with Elixir syntax, while reaching the performance of precompiled templates.

Here’s an example to give you an idea how Marker looks:

use Marker

name = "Vincent"

html do
  body do
    div do
      h3 "Person"
      p name, class: "name"
      p 2 * 19, class: "age"
    end
  end
end

The above will result in:

{:safe,
"<!doctype html>\n<html><body><div><h3>Person</h3><p class='name'>Vincent</p><p class='age'>38</p></div></body></html>"}

Marker escapes all strings by default. In order to prevent an already escaped result from being escaped again when passed as an argument to another element, compiled results are wrapped in a {:safe, ...} tuple to mark the result as escaped. The Phoenix framework uses this idiom too. Besides escaping strings, Marker provides the Marker.Encoder protocol for encoding terms to one of Markers internal data types. See the protocol documentation for more info.

Marker is very flexible with the arguments you can pass to its element macro’s:

iex> use Marker
...> Marker
iex> div 42
{:safe, "<div>42</div>"}
iex> div do: 42
{:safe, "<div>42</div>"}
iex> div do
...>   42
...> end
{:safe, "<div>42</div>"}
iex> div class: "test"
{:safe, "<div class='test'></div>"}
iex> div [class: "test"], 42
{:safe, "<div class='test'>42</div>"}
iex> div 42, class: "test"
{:safe, "<div class='test'>42</div>"}
iex> div class: "test" do
...>   42
...> end
{:safe, "<div class='test'>42</div>"}

You can basicly do anything you like with argument order, as long as attributes are always a Keyword literal.

Components

Marker provides components as a convenient abstraction. Under the hood components define a macro, that can be called just like elements, that calls a hidden template function containing the body of the component. The component macro provides two variables: content and attrs. content contains expressions from the do block and is always a list. attrs contains the attributes and is always a map.

An example makes this all probably much easier to understand, so here are a few components that could make using Bootstrap simpler:

defmodule MyComponents do
  use Marker
  import Marker.Component

  component :form_input do
    custom_classes = attrs[:class] || ""
    div class: "form-group" do
      label attrs[:label], for: attrs[:id]
      input id: attrs[:id],
            type: attrs[:type],
            class: "form-control " <> custom_classes
            placeholder: attrs[:placeholder],
            value: attrs[:value]
    end
  end

  component :form_select do
    custom_classes = attrs[:class] || ""
    div class: "form-group" do
      label attrs[:label], for: attrs[:id]
      select content, id: attrs[:id], class: "form-control " <> custom_classes
    end
  end

  def test do
    html body do
      form do
        form_input id: "form-address", label: "Address", placeholder: "Fill in address"
        form_select id: "form-country", label: "Country", class: "country-select" do
          option "Netherlands", value: "NL"
          option "Belgium", value: "BE"
          option "Luxembourg", value: "LU"
        end
      end
    end
  end
end

If you want to use components from another module, don’t forget to require or import the module, since components are defined as macros.

Custom elements

Marker also allow you to define custom elements like this:

defmodule MyElements do
  use Marker.Element, tags: [:my_element, :another_one]
end

You can now use your custom elements like the default elements:

use MyElements

my_element id: 42 do
  another_one "Hello world"
end

Which will result in:

{:safe, "<my_element id='42'><another_one>Hello world</another_one></my_element>"}

Installation

Add marker to your list of dependencies in mix.exs:

def deps do
  [{:marker, "~> 1.0.0"}]
end

Using Marker with the Phoenix framework

Integrating Marker in your Phoenix project is very simple. It even makes working with Phoenix somewhat easier, since you don’t need any templates anymore.

First add marker to your dependencies, then add Marker functions and components to your views and call them from the view’s render function:

defmodule MyProject.PageView do
  use MyProject.Web, :view
  use Marker
  import Marker.Component

  component :greeter do
    div do
      h3 "Hello #{attrs.name}!"
      p "(from Marker)"
    end
  end

  def render "index.html", assigns do
    article do
      greeter name: "World"
    end
  end
end

If you plan to use Marker for all your views, you can add the use Marker and import Marker.Component directives to the <Project>.Web __using__ macro, so you don’t need to specify these in every view.

Background

Marker is the successor of Eml. While Eml has many more features than Marker, sometimes less really is more. Apart from writing markup with Elixir syntax, Eml also supports parsing of HTML and provides extensive querying capabilities. However, I personally almost never used all these extra features, while writing markup had some unpleasant corner cases, fundamental to Eml’s design.

You could say that where Eml is like a swiss army knife, Marker tries to do one thing and do it as good as possible.

License

Marker is Copyright (C) 2016 by Vincent Siliakus and released under the Apache License, Version 2.0.

Summary

Macros

Types

Macros

a(content_or_attrs \\ nil, maybe_content \\ nil)
abbr(content_or_attrs \\ nil, maybe_content \\ nil)
address(content_or_attrs \\ nil, maybe_content \\ nil)
area(content_or_attrs \\ nil, maybe_content \\ nil)
article(content_or_attrs \\ nil, maybe_content \\ nil)
aside(content_or_attrs \\ nil, maybe_content \\ nil)
audio(content_or_attrs \\ nil, maybe_content \\ nil)
b(content_or_attrs \\ nil, maybe_content \\ nil)
base(content_or_attrs \\ nil, maybe_content \\ nil)
bdi(content_or_attrs \\ nil, maybe_content \\ nil)
bdo(content_or_attrs \\ nil, maybe_content \\ nil)
blockquote(content_or_attrs \\ nil, maybe_content \\ nil)
body(content_or_attrs \\ nil, maybe_content \\ nil)
br(content_or_attrs \\ nil, maybe_content \\ nil)
button(content_or_attrs \\ nil, maybe_content \\ nil)
canvas(content_or_attrs \\ nil, maybe_content \\ nil)
caption(content_or_attrs \\ nil, maybe_content \\ nil)
cite(content_or_attrs \\ nil, maybe_content \\ nil)
code(content_or_attrs \\ nil, maybe_content \\ nil)
col(content_or_attrs \\ nil, maybe_content \\ nil)
colgroup(content_or_attrs \\ nil, maybe_content \\ nil)
content(content_or_attrs \\ nil, maybe_content \\ nil)
data(content_or_attrs \\ nil, maybe_content \\ nil)
datalist(content_or_attrs \\ nil, maybe_content \\ nil)
dd(content_or_attrs \\ nil, maybe_content \\ nil)
del(content_or_attrs \\ nil, maybe_content \\ nil)
details(content_or_attrs \\ nil, maybe_content \\ nil)
dfn(content_or_attrs \\ nil, maybe_content \\ nil)
div(content_or_attrs \\ nil, maybe_content \\ nil)
dl(content_or_attrs \\ nil, maybe_content \\ nil)
dt(content_or_attrs \\ nil, maybe_content \\ nil)
em(content_or_attrs \\ nil, maybe_content \\ nil)
embed(content_or_attrs \\ nil, maybe_content \\ nil)
fieldset(content_or_attrs \\ nil, maybe_content \\ nil)
figcaption(content_or_attrs \\ nil, maybe_content \\ nil)
figure(content_or_attrs \\ nil, maybe_content \\ nil)
footer(content_or_attrs \\ nil, maybe_content \\ nil)
form(content_or_attrs \\ nil, maybe_content \\ nil)
h1(content_or_attrs \\ nil, maybe_content \\ nil)
h2(content_or_attrs \\ nil, maybe_content \\ nil)
h3(content_or_attrs \\ nil, maybe_content \\ nil)
h4(content_or_attrs \\ nil, maybe_content \\ nil)
h5(content_or_attrs \\ nil, maybe_content \\ nil)
h6(content_or_attrs \\ nil, maybe_content \\ nil)
head(content_or_attrs \\ nil, maybe_content \\ nil)
header(content_or_attrs \\ nil, maybe_content \\ nil)
hr(content_or_attrs \\ nil, maybe_content \\ nil)
html(content_or_attrs \\ nil, maybe_content \\ nil)
i(content_or_attrs \\ nil, maybe_content \\ nil)
iframe(content_or_attrs \\ nil, maybe_content \\ nil)
img(content_or_attrs \\ nil, maybe_content \\ nil)
input(content_or_attrs \\ nil, maybe_content \\ nil)
ins(content_or_attrs \\ nil, maybe_content \\ nil)
kbd(content_or_attrs \\ nil, maybe_content \\ nil)
keygen(content_or_attrs \\ nil, maybe_content \\ nil)
label(content_or_attrs \\ nil, maybe_content \\ nil)
legend(content_or_attrs \\ nil, maybe_content \\ nil)
li(content_or_attrs \\ nil, maybe_content \\ nil)
link(content_or_attrs \\ nil, maybe_content \\ nil)
main(content_or_attrs \\ nil, maybe_content \\ nil)
map(content_or_attrs \\ nil, maybe_content \\ nil)
mark(content_or_attrs \\ nil, maybe_content \\ nil)
meta(content_or_attrs \\ nil, maybe_content \\ nil)
meter(content_or_attrs \\ nil, maybe_content \\ nil)
noscript(content_or_attrs \\ nil, maybe_content \\ nil)
object(content_or_attrs \\ nil, maybe_content \\ nil)
ol(content_or_attrs \\ nil, maybe_content \\ nil)
optgroup(content_or_attrs \\ nil, maybe_content \\ nil)
option(content_or_attrs \\ nil, maybe_content \\ nil)
output(content_or_attrs \\ nil, maybe_content \\ nil)
p(content_or_attrs \\ nil, maybe_content \\ nil)
param(content_or_attrs \\ nil, maybe_content \\ nil)
pre(content_or_attrs \\ nil, maybe_content \\ nil)
progress(content_or_attrs \\ nil, maybe_content \\ nil)
q(content_or_attrs \\ nil, maybe_content \\ nil)
rp(content_or_attrs \\ nil, maybe_content \\ nil)
rt(content_or_attrs \\ nil, maybe_content \\ nil)
ruby(content_or_attrs \\ nil, maybe_content \\ nil)
s(content_or_attrs \\ nil, maybe_content \\ nil)
samp(content_or_attrs \\ nil, maybe_content \\ nil)
script(content_or_attrs \\ nil, maybe_content \\ nil)
section(content_or_attrs \\ nil, maybe_content \\ nil)
select(content_or_attrs \\ nil, maybe_content \\ nil)
shadow(content_or_attrs \\ nil, maybe_content \\ nil)
small(content_or_attrs \\ nil, maybe_content \\ nil)
source(content_or_attrs \\ nil, maybe_content \\ nil)
span(content_or_attrs \\ nil, maybe_content \\ nil)
string(content_or_attrs \\ nil, maybe_content \\ nil)
style(content_or_attrs \\ nil, maybe_content \\ nil)
sub(content_or_attrs \\ nil, maybe_content \\ nil)
summary(content_or_attrs \\ nil, maybe_content \\ nil)
sup(content_or_attrs \\ nil, maybe_content \\ nil)
table(content_or_attrs \\ nil, maybe_content \\ nil)
tbody(content_or_attrs \\ nil, maybe_content \\ nil)
td(content_or_attrs \\ nil, maybe_content \\ nil)
template(content_or_attrs \\ nil, maybe_content \\ nil)
textarea(content_or_attrs \\ nil, maybe_content \\ nil)
tfoot(content_or_attrs \\ nil, maybe_content \\ nil)
th(content_or_attrs \\ nil, maybe_content \\ nil)
thead(content_or_attrs \\ nil, maybe_content \\ nil)
time(content_or_attrs \\ nil, maybe_content \\ nil)
title(content_or_attrs \\ nil, maybe_content \\ nil)
tr(content_or_attrs \\ nil, maybe_content \\ nil)
track(content_or_attrs \\ nil, maybe_content \\ nil)
u(content_or_attrs \\ nil, maybe_content \\ nil)
ul(content_or_attrs \\ nil, maybe_content \\ nil)
var(content_or_attrs \\ nil, maybe_content \\ nil)
video(content_or_attrs \\ nil, maybe_content \\ nil)
wbr(content_or_attrs \\ nil, maybe_content \\ nil)
xmp(content_or_attrs \\ nil, maybe_content \\ nil)