### Defining and Using Custom Filters in Liquex
Source: https://github.com/handlecommerce/liquex/blob/main/README.md
Provides an example of creating a custom filter module (`CustomFilter`) that extends Liquex's filter capabilities. It shows how to import standard filters and define a new one.
```elixir
defmodule CustomFilter do
# Import all the standard liquid filters
use Liquex.Filter
def scream(value, _), do: String.upcase(value) <> "!"
end
context = Liquex.Context.new(%{}, filter_module: CustomFilter)
{:ok, template_ast} = Liquex.parse("{{'Hello World' | scream}}")
{result, _} = Liquex.render!(template_ast, context)
result |> to_string()
iex> "HELLO WORLD!"
```
--------------------------------
### If/Elsif/Else Conditionals in Liquid
Source: https://context7.com/handlecommerce/liquex/llms.txt
Demonstrates the use of Liquid's if, elsif, and else tags for conditional rendering. Supports comparison and logical operators, as well as 'contains' checks. The example shows rendering based on product availability.
```elixir
{:ok, ast} = Liquex.parse("{% if product.available %}
In Stock
{% elsif product.preorder %}
Pre-order Available
{% else %}
Out of Stock
{% endif %}")
{result, _} = Liquex.render!(ast, %{
"product" => %{"available" => false, "preorder" => true}
})
IO.puts(to_string(result))
```
```elixir
{:ok, ast} = Liquex.parse("{% if user.age >= 18 %}
Adult content allowed
{% endif %}")
```
--------------------------------
### Add Liquex Dependency to mix.exs
Source: https://github.com/handlecommerce/liquex/blob/main/README.md
Add the liquex package to your project's dependencies in mix.exs to install it.
```elixir
def deps do
[
{:liquex, "~> 0.13.0"}
]
end
```
--------------------------------
### Configure Local File System for Render Tags
Source: https://context7.com/handlecommerce/liquex/llms.txt
Initialize a LocalFileSystem to enable the {% render %} tag for partial template inclusion.
```elixir
# Create a local file system pointing to templates directory
file_system = Liquex.LocalFileSystem.new("/path/to/templates")
# Custom pattern (default is "_%s.liquid")
file_system = Liquex.LocalFileSystem.new("/path/to/templates", "%s.html")
# Create context with file system
context = Liquex.Context.new(
%{"product" => %{"title" => "Shirt", "price" => 25}},
file_system: file_system
)
# Main template using render tag
# Assumes /path/to/templates/_product_card.liquid exists
{:ok, ast} = Liquex.parse("""
{% render 'product_card' with product %}
""")
{result, _} = Liquex.render!(ast, context)
```
--------------------------------
### Basic Template Parsing and Rendering in Elixir
Source: https://github.com/handlecommerce/liquex/blob/main/README.md
Demonstrates the fundamental usage of Liquex for parsing a Liquid template string and rendering it with a given context.
```elixir
iex> {:ok, template_ast} = Liquex.parse("Hello {{ name }}!")
iex> {content, _context} = Liquex.render!(template_ast, %{"name" => "World"})
iex> content |> to_string()
"Hello World!"
```
--------------------------------
### Initializing Liquex Simple Cache
Source: https://github.com/handlecommerce/liquex/blob/main/README.md
Demonstrates how to initialize the built-in ETS-based cache for the render tag. Caching is disabled by default.
```elixir
:ok = Liquex.Cache.SimpleCache.init()
context = Context.new(%{...}, cache: Liquex.Cache.SimpleCache)
```
--------------------------------
### Create Rendering Context
Source: https://context7.com/handlecommerce/liquex/llms.txt
Use Liquex.Context.new/2 to create a rendering context. Options include custom filters, file systems for partials, static environments, and caching.
```elixir
context = Liquex.Context.new(%{"title" => "My Page", "items" => [1, 2, 3]})
```
```elixir
defmodule MyFilters do
use Liquex.Filter
def scream(value, _), do: String.upcase(value) <> "!"
end
context = Liquex.Context.new(
%{"greeting" => "hello"},
filter_module: MyFilters
)
{:ok, ast} = Liquex.parse("{{ greeting | scream }}")
{result, _context} = Liquex.render!(ast, context)
IO.puts(to_string(result))
```
```elixir
context = Liquex.Context.new(
%{"page_title" => "Home"},
static_environment: %{"site_name" => "My Site"}
)
```
```elixir
file_system = Liquex.LocalFileSystem.new("/templates")
context = Liquex.Context.new(%{}, file_system: file_system)
```
```elixir
:ok = Liquex.Cache.SimpleCache.init()
context = Liquex.Context.new(%{}, cache: Liquex.Cache.SimpleCache)
```
--------------------------------
### Liquex.Context.new/2 - Create a Rendering Context
Source: https://context7.com/handlecommerce/liquex/llms.txt
Creates a new context with variables and configuration options for rendering. Supports custom filters, file systems for partials, static environments, and caching.
```APIDOC
## Liquex.Context.new/2 - Create a Rendering Context
### Description
Creates a new context with variables and configuration options for rendering. Supports custom filters, file systems for partials, static environments, and caching.
### Method
`Liquex.Context.new/2`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Request Example
```elixir
# Basic context with variables
context = Liquex.Context.new(%{"title" => "My Page", "items" => [1, 2, 3]})
# Context with custom filter module
defmodule MyFilters do
use Liquex.Filter
def scream(value, _), do: String.upcase(value) <> "!"
end
context = Liquex.Context.new(
%{"greeting" => "hello"},
filter_module: MyFilters
)
{:ok, ast} = Liquex.parse("{{ greeting | scream }}")
{result, _context} = Liquex.render!(ast, context)
IO.puts(to_string(result))
# Output: HELLO!
# Context with static environment (accessible in nested render tags)
context = Liquex.Context.new(
%{"page_title" => "Home"},
static_environment: %{"site_name" => "My Site"}
)
# Context with local file system for render tag
file_system = Liquex.LocalFileSystem.new("/templates")
context = Liquex.Context.new(%{}, file_system: file_system)
# Context with caching enabled
:ok = Liquex.Cache.SimpleCache.init()
context = Liquex.Context.new(%{}, cache: Liquex.Cache.SimpleCache)
```
### Response
#### Success Response (200)
- **context** (Context) - A new rendering context.
#### Response Example
```elixir
context
```
```
--------------------------------
### Implementing and Using Custom Tags in Liquex
Source: https://github.com/handlecommerce/liquex/blob/main/README.md
Details how to create a custom tag (`CustomTag`) by implementing the `Liquex.Tag` behaviour, including parsing logic and rendering. It also shows how to integrate this custom tag into a `Liquex.Parser`.
```elixir
defmodule CustomTag do
@moduledoc false
@behaviour Liquex.Tag
import NimbleParsec
@impl true
# Parse <>
def parse() do
text =
lookahead_not(string(">>"))
|> utf8_char([])
|> times(min: 1)
|> reduce({Kernel, :to_string, []})
|> tag(:text)
ignore(string("<<"))
|> optional(text)
|> ignore(string(">>"))
end
@impl true
def render(contents, context) do
{result, context} = Liquex.render!(contents, context)
{["Custom Tag: ", result], context}
end
end
defmodule CustomParser do
use Liquex.Parser, tags: [CustomTag]
end
iex> document = Liquex.parse!("<>", CustomParser)
iex> {result, _} = Liquex.render!(document, context)
iex> result |> to_string()
"Custom Tag: Hello World!"
```
--------------------------------
### Enable Template Caching
Source: https://context7.com/handlecommerce/liquex/llms.txt
Use the built-in SimpleCache or implement the Liquex.Cache behaviour for custom caching strategies.
```elixir
# Initialize the simple ETS-based cache
:ok = Liquex.Cache.SimpleCache.init()
# Create context with caching enabled
context = Liquex.Context.new(
%{"products" => products},
cache: Liquex.Cache.SimpleCache,
cache_prefix: "store_1" # Optional: for multi-tenant caching
)
# Implement custom cache using Cachex
defmodule MyCache do
@behaviour Liquex.Cache
@impl true
def fetch(key, func) do
case Cachex.get(:my_cache, key) do
{:ok, nil} ->
value = func.()
Cachex.put(:my_cache, key, value)
value
{:ok, value} ->
value
end
end
end
context = Liquex.Context.new(%{}, cache: MyCache)
```
--------------------------------
### Apply String Filters
Source: https://context7.com/handlecommerce/liquex/llms.txt
Demonstrates common string manipulation filters like upcase, truncate, and replace.
```elixir
{:ok, ast} = Liquex.parse("""
{{ "hello world" | upcase }}
{{ "HELLO WORLD" | downcase }}
{{ "hello world" | capitalize }}
{{ " hello " | strip }}
{{ "Hello World" | truncate: 8 }}
{{ "Hello World" | truncatewords: 1 }}
{{ "hello" | append: " world" }}
{{ "world" | prepend: "hello " }}
{{ "hello world" | replace: "world", "universe" }}
{{ "hello world world" | replace_first: "world", "universe" }}
{{ "hello world" | remove: "world" }}
{{ "hello world" | split: " " | first }}
{{ "hello\nworld" | newline_to_br }}
{{ "hello world" | strip_html }}
{{ "/my path/" | url_encode }}
{{ "%2Fmy+path%2F" | url_decode }}
""")
{result, _} = Liquex.render!(ast)
```
--------------------------------
### Liquex.render!/2 - Render a Parsed Template
Source: https://context7.com/handlecommerce/liquex/llms.txt
Renders a parsed Liquid AST document with the provided context (variables). Returns a tuple of IO data and the updated context.
```APIDOC
## Liquex.render!/2 - Render a Parsed Template
### Description
Renders a parsed Liquid AST document with the provided context (variables). Returns a tuple of IO data and the updated context.
### Method
`Liquex.render!/2`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Request Example
```elixir
# Basic rendering with a map
{:ok, ast} = Liquex.parse("Hello {{ name }}!")
{result, _context} = Liquex.render!(ast, %{"name" => "World"})
IO.puts(to_string(result))
# Output: Hello World!
# Rendering with atom keys (indifferent access)
{:ok, ast} = Liquex.parse("Price: {{ product.price }}")
{result, _context} = Liquex.render!(ast, %{product: %{price: 19.99}})
IO.puts(to_string(result))
# Output: Price: 19.99
# Rendering with nested variables
{:ok, ast} = Liquex.parse("{{ user.address.city }}")
context = %{
"user" => %{
"address" => %{
"city" => "San Francisco"
}
}
}
{result, _context} = Liquex.render!(ast, context)
IO.puts(to_string(result))
# Output: San Francisco
```
### Response
#### Success Response (200)
- **result** (IO Data) - The rendered output.
- **context** (Context) - The updated rendering context.
#### Response Example
```elixir
{result, _context}
```
```
--------------------------------
### Render Parsed Liquid Template
Source: https://context7.com/handlecommerce/liquex/llms.txt
Use Liquex.render!/2 to render a Liquid AST with a given context. It returns the rendered output and the updated context. Supports map and atom-based keys for context variables.
```elixir
{:ok, ast} = Liquex.parse("Hello {{ name }}!")
{result, _context} = Liquex.render!(ast, %{"name" => "World"})
IO.puts(to_string(result))
```
```elixir
{:ok, ast} = Liquex.parse("Price: {{ product.price }}")
{result, _context} = Liquex.render!(ast, %{product: %{price: 19.99}})
IO.puts(to_string(result))
```
```elixir
{:ok, ast} = Liquex.parse("{{ user.address.city }}")
context = %{
"user" => %{
"address" => %{
"city" => "San Francisco"
}
}
}
{result, _context} = Liquex.render!(ast, context)
IO.puts(to_string(result))
```
--------------------------------
### Use Logical Operators in Liquex
Source: https://context7.com/handlecommerce/liquex/llms.txt
Demonstrates using 'and' within an if statement.
```elixir
{:ok, ast} = Liquex.parse("""
{% if user.admin and user.active %}
Admin Panel Access
{% endif %}
""")
```
--------------------------------
### Use Case/When Tag in Liquex
Source: https://context7.com/handlecommerce/liquex/llms.txt
Matches a variable against multiple values using a switch-like structure.
```elixir
{:ok, ast} = Liquex.parse("""
{% case shipping_method %}
{% when "standard" %}
Arrives in 5-7 business days
{% when "express" %}
Arrives in 2-3 business days
{% when "overnight" %}
Arrives tomorrow
{% else %}
Contact us for shipping info
{% endcase %}
""")
{result, _} = Liquex.render!(ast, %{"shipping_method" => "express"})
IO.puts(to_string(result))
# Output: Arrives in 2-3 business days
```
--------------------------------
### Create Custom Filter Modules
Source: https://context7.com/handlecommerce/liquex/llms.txt
Defines custom filter logic by implementing the Liquex.Filter behaviour.
```elixir
defmodule MyCustomFilters do
use Liquex.Filter
# Simple filter: value is first argument, context is last
def scream(value, _context), do: String.upcase(value) <> "!"
# Filter with additional arguments
def repeat(value, times, _context) do
String.duplicate(to_string(value), times)
end
# Filter with keyword arguments
def img_url(path, size, [{"crop", direction}, {"filter", filter}], _context) do
"https://cdn.example.com/#{path}?size=#{size}&crop=#{direction}&filter=#{filter}"
end
# Filter with optional keyword argument
def money(amount, [{"currency", currency}], _context) do
"#{currency}#{:erlang.float_to_binary(amount / 1, decimals: 2)}"
end
end
# Using custom filters
context = Liquex.Context.new(
%{"greeting" => "hello", "product_image" => "shirt.jpg"},
filter_module: MyCustomFilters
)
{:ok, ast} = Liquex.parse("""
{{ greeting | scream }}
{{ "ha" | repeat: 3 }}
{{ product_image | img_url: "400x400", crop: "center", filter: "sharpen" }}
{{ 29.99 | money: currency: "$" }}
""")
{result, _} = Liquex.render!(ast, context)
IO.puts(to_string(result))
# Output:
# HELLO!
# hahaha
# https://cdn.example.com/shirt.jpg?size=400x400&crop=center&filter=sharpen
# $29.99
```
--------------------------------
### Parse Liquid Template to AST
Source: https://context7.com/handlecommerce/liquex/llms.txt
Use Liquex.parse/2 to convert a Liquid template string into an Abstract Syntax Tree (AST). An optional custom parser module can be provided for extended tag syntax. The parse! variant raises an error on failure.
```elixir
{:ok, ast} = Liquex.parse("Hello {{ name }}!")
```
```elixir
{:ok, ast} = Liquex.parse("{{ product.title | upcase }}")
```
```elixir
{:ok, ast} = Liquex.parse("{% if user.logged_in %}
Welcome back, {{ user.name }}!
{% else %}
Please log in.
{% endif %}")
```
```elixir
{:ok, ast} = Liquex.parse("<>", MyCustomParser)
```
```elixir
ast = Liquex.parse!("Hello {{ name }}!")
```
--------------------------------
### Apply Default Filter
Source: https://context7.com/handlecommerce/liquex/llms.txt
Provides fallback values for nil, false, or empty inputs.
```elixir
{:ok, ast} = Liquex.parse("""
{{ product.title | default: "Untitled" }}
{{ missing_var | default: "Not found" }}
{{ "" | default: "Empty string replaced" }}
""")
{result, _} = Liquex.render!(ast, %{"product" => %{}})
IO.puts(to_string(result))
# Output: Untitled, Not found, Empty string replaced
```
--------------------------------
### Use For Loop in Liquex
Source: https://context7.com/handlecommerce/liquex/llms.txt
Iterates over arrays and ranges, providing access to the forloop object.
```elixir
{:ok, ast} = Liquex.parse("""