NAME

Venus::Template - Template Class

ABSTRACT

Template Class for Perl 5

SYNOPSIS

package main;

use Venus::Template;

my $template = Venus::Template->new(
  'From: <{{ email }}>',
);

# $template->render;

# "From: <>"

DESCRIPTION

This package provides a simple yet powerful templating system for Perl 5 that supports variable interpolation, loops, and conditional blocks. The templating system is designed to only operate on raw Perl data and as such does not support operators, filters, virtual method, etc.

Syntax

Tags

The opening and closing tags are {{ and }}. Content between these tags can be:

  • Simple path access (e.g., {{ name }})

  • Nested path access (e.g., {{ user.email }})

  • Control structures (described below)

To include literal tags in your template, escape them using either:

  • Single backslash: \{{ and \}}

  • Escaped braces: \{\{ and \}\}

Tokens

Variable substitution uses dot notation to access nested data:

# access hash key 'name' in hash 'user'
{{ user.name }}

# access hash key 'name' in first element of array 'users'
{{ users.0.name }}

# access deeply nested hash keys
{{ profile.address.zip }}

Loops

Loops iterate over arrays using the for control structure:

{{ for users }}
  Name: {{ user.name }}
{{ end users }}

Special loop variables are available within for blocks:

  • {{ loop.index }} - Zero-based iteration counter

  • {{ loop.place }} - One-based iteration counter

  • {{ loop.item }} - Current item value (useful for arrays of simple values)

For nested loops, parent loop information is accessible via:

  • {{ loop.parent }} - Immediate parent loop's data

  • {{ loop.level.0 }} - First loop

  • {{ loop.level.1 }} - Second loop

  • {{ loop.level.2 }} - Third loop

Controls

Conditional blocks use if, if not, and optional else:

{{ if user.admin }}
  Admin: {{ user.name }}
{{ else user.admin }}
  User: {{ user.name }}
{{ end user.admin }}

{{ if not user.active }}
  Account inactive
{{ end user.active }}

Structure

The template engine expects variables to be provided as hashrefs. Arrays should be provided as arrayrefs. When iterating over arrays:

  • Arrays of hashrefs: Access hash keys via {{ user.name }}

  • Arrays of simple values: Access values via {{ loop.item }}

Example data structure:

{
  user => {
    name => 'Alice',
    admin => 1,
    profile => {
      email => 'alice@example.com'
    }
  },
  items => [
    'a', 'b', 'c'
  ],
  users => [
    {
      name => 'Bob',
      role => 'user'
    },
    {
      name => 'Carol',
      role => 'admin'
    }
  ]
}

Context

The template engine expects variables to be provided via the "context" attribute, or as an argument to the "render" method after the template.

Setting context during object construction:

package main;

use Venus::Template;

my $template = Venus::Template->new(context => {email => 'alice@example.com'});

$template->render('From: <{{ email }}>');

Setting context during template rendering:

package main;

use Venus::Template;

my $template = Venus::Template->new(value => 'From: <{{ email }}>');

$template->render(undef, {email => 'alice@example.com'});

ATTRIBUTES

This package has the following attributes:

context

context(hashref $context) (hashref)

The context attribute is read-write, accepts (hashref) values, and is optional.

Since 4.15

context example 1
# given: synopsis

package main;

my $set_context = $template->context({});

# {}
context example 2
# given: synopsis

# given: example-1 context

package main;

my $get_context = $template->context;

# {}

INHERITS

This package inherits behaviors from:

Venus::Kind::Utility

INTEGRATES

This package integrates behaviors from:

Venus::Role::Accessible

Venus::Role::Buildable

Venus::Role::Explainable

Venus::Role::Valuable

METHODS

This package provides the following methods:

new

new(any @args) (Venus::Template)

The new method constructs an instance of the package.

Since 4.15

new example 1
package main;

use Venus::Template;

my $new = Venus::Template->new;

# bless(..., "Venus::Template")
new example 2
package main;

use Venus::Template;

my $new = Venus::Template->new('hello world');

# bless(..., "Venus::Template")
new example 3
package main;

use Venus::Template;

my $new = Venus::Template->new(value => 'hello world');

# bless(..., "Venus::Template")
new example 4
package main;

use Venus::Template;

my $new = Venus::Template->new(context => {greeting => 'hello'});

# bless(..., "Venus::Template")

render

render(string $template, hashref $context) (string)

The render method processes the template by replacing the tokens and control structurs with the appropriate replacements and returns the result. Note: The rendering process expects variables to be hashrefs and sets (arrayrefs) of hashrefs. Within a for loop, special values are available under the loop variable:

  • {{ loop.index }} - Zero-based index of current iteration

  • {{ loop.place }} - One-based index of current iteration

  • {{ loop.item }} - Current item value (especially useful for arrays of simple values, e.g. strings)

  • {{ loop.parent }} - Access to parent loop's metadata (if within nested loops)

  • {{ loop.level }} - Access to multi-level parent loop metadata (if within nested loops)

For nested loops, you can also access specific loop levels using zero-based numbering:

  • {{ loop.level.0 }} - Top-level loop

  • {{ loop.level.1 }} - One level under the top-level loop

  • {{ loop.level.2 }} - Two levels under the top-level loop

Since 0.01

render example 1
# given: synopsis;

my $result = $template->render;

# "From: <>"
render example 2
# given: synopsis;

$template->value(
  'From: {{ if name }}{{ name }}{{ end name }} <{{ email }}>',
);

$template->context({
  email => 'noreply@example.com',
});

my $result = $template->render;

# "From:  <noreply@example.com>"
render example 3
# given: synopsis;

$template->value(
  'From: {{ if name }}{{ name }}{{ end name }} <{{ email }}>',
);

$template->context({
  name => 'No-Reply',
  email => 'noreply@example.com',
});

my $result = $template->render;

# "From: No-Reply <noreply@example.com>"
render example 4
package main;

use Venus::Template;

my $template = Venus::Template->new(q(
  {{ for chat.messages }}
  {{ user.name }}: {{ message }}
  {{ end chat.messages }}
));

$template->context({
  chat => { messages => [
    { user => { name => 'user1' }, message => 'ready?' },
    { user => { name => 'user2' }, message => 'ready!' },
    { user => { name => 'user1' }, message => 'lets begin!' },
  ]}
});

my $result = $template->render;

# user1: ready?
# user2: ready!
# user1: lets begin!
render example 5
package main;

use Venus::Template;

my $template = Venus::Template->new(q(
  {{ for chat.messages }}
  {{ if user.legal }}
  {{ user.name }} [18+]: {{ message }}
  {{ else user.legal }}
  {{ user.name }} [-18]: {{ message }}
  {{ end user.legal }}
  {{ end chat.messages }}
));

$template->context({
  chat => { messages => [
    { user => { name => 'user1', legal => 1 }, message => 'ready?' },
    { user => { name => 'user2', legal => 0 }, message => 'ready!' },
    { user => { name => 'user1', legal => 1 }, message => 'lets begin!' },
  ]}
});

my $result = $template->render;

# user1 [18+]: ready?
# user2 [-18]: ready!
# user1 [18+]: lets begin!
render example 6
package main;

use Venus::Template;

my $template = Venus::Template->new(q(
  {{ for chat.messages }}
  {{ if user.admin }}@{{ end user.admin }}{{ user.name }}: {{ message }}
  {{ end chat.messages }}
));

$template->context({
  chat => { messages => [
    { user => { name => 'user1', admin => 1 }, message => 'ready?' },
    { user => { name => 'user2', admin => 0 }, message => 'ready!' },
    { user => { name => 'user1', admin => 1 }, message => 'lets begin!' },
  ]}
});

my $result = $template->render;

# @user1: ready?
# user2: ready!
# @user1: lets begin!
render example 7
package main;

use Venus::Template;

my $template = Venus::Template->new(q(
  {{ for chat.messages }}
  [{{ loop.place }}] {{ user.name }}: {{ message }}
  {{ end chat.messages }}
));

$template->context({
  chat => { messages => [
    { user => { name => 'user1' }, message => 'ready?' },
    { user => { name => 'user2' }, message => 'ready!' },
    { user => { name => 'user1' }, message => 'lets begin!' },
  ]}
});

my $result = $template->render;

# [1] user1: ready?
# [2] user2: ready!
# [3] user1: lets begin!
render example 8
package main;

use Venus::Template;

my $template = Venus::Template->new(q(
  {{ for chat.messages }}
  [{{ loop.index }}] {{ user.name }}: {{ message }}
  {{ end chat.messages }}
));

$template->context({
  chat => { messages => [
    { user => { name => 'user1' }, message => 'ready?' },
    { user => { name => 'user2' }, message => 'ready!' },
    { user => { name => 'user1' }, message => 'lets begin!' },
  ]}
});

my $result = $template->render;

# [0] user1: ready?
# [1] user2: ready!
# [2] user1: lets begin!
render example 9
package main;

use Venus::Template;

my $template = Venus::Template->new(q(
  {{ for names }}
  [{{ loop.place }}] {{ loop.item }}
  {{ end names }}
));

$template->context({
  names => ['user 1', 'user 2', 'user 3'],
});

my $result = $template->render;

# [1] user 1
# [2] user 2
# [3] user 3
render example 10
package main;

use Venus::Template;

my $template = Venus::Template->new(q(
  {{ for teams }}
  Team {{ loop.item }}:
  {{ for players }}
  [{{ loop.place }}] {{ loop.item }} (Team {{ loop.parent.item }})
  {{ end players }}
  {{ end teams }}
));

$template->context({
  teams => ['A', 'B'],
  players => ['Player 1', 'Player 2'],
});

my $result = $template->render;

# Team A:
# [1] Player 1 (Team A)
# [2] Player 2 (Team A)
# Team B:
# [1] Player 1 (Team B)
# [2] Player 2 (Team B)
render example 11
package main;

use Venus::Template;

my $template = Venus::Template->new(q(
  {{ for chapters }}
  Chapter {{ loop.level.0.place }}: {{ loop.level.0.item }}
  {{ for sections }}
  {{ loop.level.1.place }}.{{ loop.level.0.place }} {{ loop.level.0.item }}
  {{ end sections }}
  {{ end chapters }}
));

$template->context({
  chapters => ['Intro', 'Methods'],
  sections => ['Overview', 'Details']
});

my $result = $template->render;

# Chapter 1: Intro
# 1.1 Overview
# 1.2 Details
# Chapter 2: Methods
# 2.1 Overview
# 2.2 Details

OPERATORS

This package overloads the following operators:

operation: ("")

This package overloads the "" operator.

example 1

# given: synopsis;

my $result = "$template";

# "From: <>"

example 2

# given: synopsis;

my $result = "$template, $template";

# "From: <>, From: <>"
operation: (~~)

This package overloads the ~~ operator.

example 1

# given: synopsis;

my $result = $template ~~ 'From: <>';

# 1

AUTHORS

Awncorp, awncorp@cpan.org

LICENSE

Copyright (C) 2022, Awncorp, awncorp@cpan.org.

This program is free software, you can redistribute it and/or modify it under the terms of the Apache license version 2.0.