defmodule Solid.Tag do
@moduledoc """
This module define behaviour for tags.
To implement new tag you need to create new module that implement the `Tag` behaviour:
defmodule MyCustomTag do
import NimbleParsec
@behaviour Solid.Tag
@impl true
def spec(_parser) do
space = Solid.Parser.Literal.whitespace(min: 0)
ignore(string("{%"))
|> ignore(space)
|> ignore(string("my_tag"))
|> ignore(space)
|> ignore(string("%}"))
end
@impl true
def render(_tag, _context, _options) do
[text: "my first tag"]
end
end
- `spec` define how to parse your tag
- `render` define how to render your tag
Then add the tag to your parser
defmodule MyParser do
use Solid.Parser.Base, custom_tags: [MyCustomTag]
end
Then pass the custom parser as option
"{% my_tag %}"
|> Solid.parse!(parser: MyParser)
|> Solid.render()
Control flow tags can change the information Liquid shows using programming logic.
More info: https://shopify.github.io/liquid/tags/control-flow/
"""
alias Solid.Context
@doc """
Build and return `NimbleParsec` expression to parse your tag. There are some helper expressions that can be used:
- `Solid.Parser.Literal`
- `Solid.Parser.Variable`
- `Solid.Parser.Argument`
"""
@callback spec(module) :: NimbleParsec.t()
@doc """
Define how to render your tag.
Third argument are the options passed to `Solid.render/3`
"""
@callback render(list(), Solid.Context.t(), keyword()) ::
{list(Solid.Template.rendered_data()), Solid.Context.t()} | String.t()
@doc """
Basic custom tag spec that accepts optional arguments
"""
@spec basic(String.t()) :: NimbleParsec.t()
def basic(name) do
import NimbleParsec
space = Solid.Parser.Literal.whitespace(min: 0)
ignore(Solid.Parser.BaseTag.opening_tag())
|> ignore(string(name))
|> ignore(space)
|> tag(optional(Solid.Parser.Argument.arguments()), :arguments)
|> ignore(Solid.Parser.BaseTag.closing_tag())
end
@doc """
Evaluate a tag and return the condition that succeeded or nil
"""
@spec eval(any, Context.t(), keyword()) :: {iolist | nil, Context.t()}
def eval(tag, context, options) do
case do_eval(tag, context, options) do
{text, context} -> {text, context}
text when is_binary(text) -> {[text: text], context}
text -> {text, context}
end
end
defp do_eval([], _context, _options), do: nil
defp do_eval([{tag_module, tag_data}], context, options) do
tag_module.render(tag_data, context, options)
end
end