Combo.Template.CEExEngine.DeclarativeAssigns (combo v0.10.0)

View Source

The declarative API of assigns.

Summary

Functions

The macro for setting up declarative assigns.

Declares an attribute for a component.

Declares a slot for a component. See slot/3 for more information.

Declares a slot for a component.

Functions

__using__(opts \\ [])

(macro)

The macro for setting up declarative assigns.

It will:

Examples

use Combo.Template.CEExEngine.DeclarativeAssigns

Or,

use Combo.Template.CEExEngine.DeclarativeAssigns, global_attr_prefixes: ~w(x-)

attr(name, type, opts \\ [])

(macro)

Declares an attribute for a component.

Arguments

An attribute is declared by its name, type, and options.

  • name - an atom defining the name of the attribute. Note that attributes cannot define the same name as any other attributes or slots declared or the same component.
  • type - an atom defining the type of the attribute.
  • opts - a keyword list of options. Defaults to [].

Types

The following types are supported:

NameDescription
:anyany term (including nil)
:stringany binary string
:atomany atom (including true, false, and nil)
:booleanany boolean
:integerany integer
:floatany float
:listany list of any arbitrary types
:mapany map of any arbitrary types
:funany function
{:fun, arity}any function of arity
A struct moduleany module that defines a struct with defstruct/1
:globalany common HTML attributes, plus those defined by :global_attr_prefixes

Options

  • :required - marks an attribute as required. If a caller does not pass the given attribute, a compile-time warning is issued.

  • :default - the default value if the attribute if not set. If this option is not set and the attribute is not given, accessing the attribute will fail unless a value is explicitly set with Combo.Template.CEExEngine.Assigns.assign_new/3.

  • :examples - a non-exhaustive list of values accepted by the attribute, used for documentation purposes.

  • :values - an exhaustive list of values accepted by the attributes. If a caller passes a literal not contained in this list, a compile warning is issued.

  • :doc - documentation for the attribute.

Compile-Time validations

CEExEngine performs some validations of attributes at compile-time.

When attributes are defined, CEExEngine will warn at compile-time on the caller if:

  • A required attribute of a component is missing.
  • An unknown attribute is given.
  • A literal attribute (such as value="string" or value, but not value={expr}) is given, but the type does not match. The following types currently support literal validation: :string, :atom, :boolean, :integer, :float, :map and :list.
  • A literal attribute is given, but it is not a member of the :values list.

CEExEngine does not perform any validation at runtime. This means the type information is mostly used for documentation and reflection purposes.

On the side of the component itself, defining attributes enhances the development experience:

  • The default value of all attributes will be added to the assigns map upfront.
  • Attribute documentation is generated for the component.
  • Required struct types are annotated and emit compilation warnings. For example, if you specify attr :user, User, required: true and then you write @user.non_valid_field in your template, a warning will be emitted.
  • Calls made to the component are tracked for reflection and validation purposes.

Documentation generation

Public components that define attributes will have their attribute types and docs injected into the function's documentation, depending on the value of the @doc module attribute:

  • if @doc is a string, the attribute docs are injected into that string. The optional placeholder [INSERT ASSIGNS_DOCS] can be used to specify where in the string the docs are injected. Otherwise, the docs are appended to the end of the @doc string.
  • if @doc is unspecified, the attribute docs are used as the default @doc string.
  • if @doc is false, the attribute docs are omitted entirely.

The injected attribute docs are formatted as a markdown list:

  • name (:type) (required) - attr docs. Defaults to :default.

By default, all attributes will have their types and docs injected into the function @doc string. To hide a specific attribute, you can set the value of :doc to false.

Examples

attr :name, :string, required: true
attr :age, :integer, required: true

def celebrate(assigns) do
  ~CE"""
  <p>
    Happy birthday {@name}!
    You are {@age} years old.
  </p>
  """
end

slot(name, opts \\ [])

(macro)

Declares a slot for a component. See slot/3 for more information.

slot(name, opts, block)

(macro)

Declares a slot for a component.

Arguments

  • name - an atom defining the name of the slot. Note that slots cannot define the same name as any other slots or attributes declared for the same component.
  • opts - a keyword list of options. Defaults to [].
  • block - a code block containing calls to attr/3. Defaults to nil.

Options

  • :required - marks a slot as required. If a caller does not pass a value for a required slot, a compilation warning is emitted. Otherwise, an omitted slot will default to [].
  • :validate_attrs - when set to false, no warning is emitted when a caller passes attributes to a slot defined without a do block. Defaults to true.
  • :doc - documentation for the slot. Any slot attributes declared will have their documentation listed alongside the slot.

Slot attributes

A named slot may declare attributes by passing a block with calls to attr/3.

Unlike attributes, slot attributes cannot accept the :default option. Passing one will result in a compile warning being issued.

The Default Slot

The default slot can be declared by passing :inner_block as the name of the slot.

Note that the :inner_block slot declaration cannot accept a block, because it doesn't support slot attributes. Passing one will result in a compilation error.

Compile-Time validations

CEExEngine performs some validation of slots at compile-time.

When slots are defined, CEExEngine will warn at compilation time on the caller if:

  • A required slot of a component is missing.
  • An unknown slot is given.
  • An unknown slot attribute is given.

On the side of the component itself, defining slots enhances the development experience:

  • Slot documentation is generated for the component.
  • Calls made to the component are tracked for reflection and validation purposes.

Documentation generation

Public components that define slots will have their docs injected into the function's documentation, depending on the value of the @doc module attribute:

  • if @doc is a string, the slot docs are injected into that string. The optional placeholder [INSERT ASSIGNS_DOCS] can be used to specify where in the string the docs are injected.

Otherwise, the docs are appended to the end of the @doc string.

  • if @doc is unspecified, the slot docs are used as the default @doc string.
  • if @doc is false, the slot docs are omitted entirely.

The injected slot docs are formatted as a markdown list:

  • name (required) - slot docs. Accepts attributes:
    • name (:type) (required) - attr docs. Defaults to :default.

By default, all slots will have their docs injected into the function @doc string. To hide a specific slot, you can set the value of :doc to false.

Example

slot :header
slot :inner_block, required: true
slot :footer

def modal(assigns) do
  ~CE"""
  <div class="modal">
    <div class="modal-header">
      {render_slot(@header) || "Modal"}
    </div>
    <div class="modal-body">
      {render_slot(@inner_block)}
    </div>
    <div class="modal-footer">
      {render_slot(@footer) || submit_button()}
    </div>
  </div>
  """
end

As shown in the example above, render_slot/1 returns nil when an optional slot is declared and none is given. This can be used to attach default behaviour.