Coelho.Render (coelho v0.1.0)

Copy Markdown View Source

Turns a validated document into HTML.

Rendering is driven by the :render field of each node and mark spec, which takes one of three forms:

  • nil — the node contributes nothing but its children
  • {tag, attrs} — an element, where attrs is either a static list of {name, value} pairs or a function of the node returning such a list
  • fun/2 — full control, receiving the node and its already rendered children as iodata
  • fun/3 — the same, plus the :context given to this render call

Callers can override any of them per call through the :nodes and :marks options, which is how a Phoenix application injects its own markup — mentions, embeds, syntax highlighted code — without changing what is stored.

The :context option carries whatever the render functions need from the application and cannot know on their own. Attachments use it to turn a stored key into a URL at render time, which is what lets signed and expiring URLs work at all — see Coelho.Attachments.

Only validated documents should be rendered. Rendering does not re-check the document against the schema; it trusts Coelho.Document.validate/2 to have run, and raises on anything it does not recognise.

Summary

Functions

Escapes text for inclusion in HTML, attribute values included.

Returns a URL fit to be emitted, or nil for one that is not.

Builds an element from a tag, an attribute list and rendered children.

Renders a document to an HTML string.

Renders a document to iodata.

Builds a childless element, self-closing only if HTML says it is.

Types

opts()

@type opts() :: [
  nodes: %{optional(atom()) => term()},
  marks: %{optional(atom()) => term()},
  context: term()
]

Functions

escape(text)

@spec escape(String.t()) :: String.t()

Escapes text for inclusion in HTML, attribute values included.

safe_url(url)

@spec safe_url(term()) :: String.t() | nil

Returns a URL fit to be emitted, or nil for one that is not.

Validation already rejects unsafe URLs on the way in, but stored documents are not re-validated on the way out — Coelho.Ecto.Type deliberately trusts what is in the column. A row written before the schema tightened, by a direct database write, or under a looser custom schema, would otherwise put javascript: straight into an href. Escaping does not help there: the value is quoted correctly and still executes.

Attributes built with this return nil and are dropped, so a suspect link renders as an <a> without an href rather than as a live one.

tag(name, attrs, inner)

@spec tag(String.t(), [{String.t(), term()}], iodata()) :: iolist()

Builds an element from a tag, an attribute list and rendered children.

to_html(document, schema, opts \\ [])

@spec to_html(map(), Coelho.Schema.t(), opts()) :: String.t()

Renders a document to an HTML string.

to_iodata(document, schema, opts \\ [])

@spec to_iodata(map(), Coelho.Schema.t(), opts()) :: iodata()

Renders a document to iodata.

void_tag(name, attrs)

@spec void_tag(String.t(), [{String.t(), term()}]) :: iolist()

Builds a childless element, self-closing only if HTML says it is.