NAME
Catalyst::View::ByCode - Templating using pure Perl code
SYNOPSIS
# 1) use the helper to create your View
myapp_create.pl view ByCode ByCode
# 2) inside your Controllers do business as usual:
sub
index
:Path :Args(0) {
my
(
$self
,
$c
) =
@_
;
# unless defined as default_view in your config, specify:
$c
->stash->{current_view} =
'ByCode'
;
$c
->stash->{title} =
'Hello ByCode'
;
# if omitted, would default to
# controller_namespace / action_namespace .pl
$c
->stash->{template} =
'hello.pl'
;
}
# 3) create a simple template eg 'root/bycode/hello.pl
# REMARK:
# use 'c' instead of '$c'
# prefer 'stash->{...}' to 'c->stash->{...}'
template {
html {
head {
title { stash->{title} };
load
Js
=>
'site.js'
;
load
Css
=>
'site.css'
;
};
body {
div header.noprint {
ul.topnav {
li {
'home'
};
li {
'surprise'
};
};
};
div content {
h1 { stash->{title} };
div {
'hello.pl is running!'
};
img(
src
=>
'/static/images/catalyst_logo.png'
);
};
};
};
};
# 274 characters without white space
# 4) expect to get this HTML generated:
<html>
<head>
<title>Hello ByCode!</title>
</script>
</head>
<body>
<div id=
"header"
style=
"noprint"
>
<ul class=
"topnav"
>
<li>home</li>
<li>surprise</li>
</ul>
</div>
<div class=
"content"
>
<h1>Hello ByCode!</h1>
<div>hello.pl is running!</div>
<img src=
"/static/images/catalyst_logo.png"
/>
</div>
</body>
</html>
# 453 characters without white space
DESCRIPTION
Catalyst::View::ByCode
tries to offer an efficient, fast and robust solution for generating HTML and XHTML markup using standard perl code encapsulating all nesting into code blocks.
Instead of typing opening and closing HTML-Tags we simply call a sub named like the tag we want to generate:
div {
'hello'
}
generates:
<div>hello</div>
There is no templating language you will have to learn, no quirks with different syntax rules your editor might not correctly follow and no indentation problems.
The whole markup is initially constructed as a huge tree-like structure in memory keeping every reference as long as possible to allow greatest flexibility and enable deferred construction of every building block until the markup is actially requested.
Every part of the markup can use almost every type of data with some reasonable behavior during markup generation.
Tags
Every tag known in HTML (or defined in HTML::Tagset to be precise) gets exported to a template's namespace during its compilation and can be used as expected. However, there are some exceptions which would collide with CORE subs or operators
- choice
-
generates a <select> tag
- link_tag
-
generates a <link> tag
- trow
-
generates a <tr> tag
- tcol
-
generates a <td> tag
- subscript
-
generates a <sub> tag
- superscript
-
generates a <sup> tag
- meta_tag
-
generates a <meta> tag
- quote
-
generates a <q> tag
- strike
-
generates a <s<gt> tag
- map_tag
-
generates a <map> tag
Internally, every tag subroutine is defined with a prototype like
sub
div(;&@) { ... }
Thus, the first argument of this sub is expected to be a coderef, which allows to write code like the examples above. Nesting tags is just a matter of nesting calls into blocks.
Content
There are several ways to generate content which is inserted between the opening and the closing tag:
The return value of the last expression of a code block will get appended to the content inside the tag. The content will get escaped when needed.
To append any content (getting escaped) at any point of the markup generation, the
OUT
glob can be used:print
OUT
'some content here.'
;
To append unescaped content eg JavaScript or the content of another markup-generating subsystem like
HTML::FormFu
simple use the <RAW> glob:print
RAW
'<?xxx must be here for internal reasons ?>'
;
Attributes
As usual for Perl, there is always more than one way to do it:
- old-school perl
-
# appending attributes after tag
div { ... content ... }
id
=>
'top'
,
class
=>
'noprint silver'
,
style
=>
'display: none'
;
the content goes into the curly-braced code block immediately following the tag. Every extra argument after the code block is converted into the tag's attributes.
- special content
-
# using special methods
div {
id
'top'
;
class
'noprint silver'
;
attr
style
=>
'display: none'
;
'content'
};
Every attribute may be added to the latest opened tag using the
attr
sub. However, there are some shortcuts:- id 'name'
-
is equivalent to
attr id =
'name'> - class 'class'
-
is the same as
attr class =
'class'>However, the
class
method is special. It allows to specify a space-separated string, a list of names or a combination of both. Class names prefixed with-
or+
are treated special. After a minus prefixed class name every following name is subtracted from the previous list of class names. After a plus prefixed name all following names are added to the class list. A list of class names without a plus/minus prefix will start with an empty class list and then append all subsequentially following names.div.foo { class
'abc def ghi'
}; will yield
'abc def ghi'
div.foo { class
'+def xyz'
}; will yield
'foo def xyz'
div.foo { class
'-foo +bar'
}; will yield
'bar'
- on handler => 'some javascript code'
-
produces the same result as
attr onhandler =
'some javascript code'>div {
on
click
=>
q{alert('you clicked me')}
;
};
- tricky arguments
-
div top.noprint.silver(
style
=>
'display: none'
) {
'content'
};
- even more tricky arguments
-
div top.noprint.silver(
style
=> {
display
=>
'none'
}) {
'content'
};
- tricky arguments and CamelCase
-
div top.noprint.silver(
style
=> {
marginTop
=>
'20px'
}) {
'content'
};
marginTop
ormargin_top
will get converted tomargin-top
.
Every attribute may have almost any datatype you might think of:
- scalar
-
Scalar values are taken verbatim.
- hashref
-
Hash references are converted to semicolon-delimited pairs of the key, a colon and a value. The perfect solution for building inline CSS. Well, I know, nobody should do something, but sometimes...
Keys consisting of underscore characters and CAPITAL letters are converted to dash-separated names.
dataTarget
ordata_target
both becomedata-target
. - arrayref
-
Array references are converted to space separated things.
- coderef -- FIXME: do we like this?
-
no idea if we like this
- other refs
-
all other references simply are stringified. This allows the various objects to forward stringification to their class-defined code.
Special Methods
Building Reusable blocks
You might build a reusable block line the following calls:
block
'block_name'
=>
sub
{ ... };
# or shorter:
block block_name { ... };
The block might get used like a tag:
block_name { ... some content ... };
If a block-call contains a content it can get rendered inside the block using the special sub block_content
. A simple example makes this clearer:
# define a block:
block infobox {
my
$headline
= attr(
'headline'
) ||
'untitled'
;
my
$id
= attr(
'id'
);
my
$class
= attr(
'class'
);
div.infobox {
id
$id
if
(
$id
);
class
$class
if
(
$class
);
div.head {
$headline
};
div.info { block_content };
};
};
# later we use the block:
infobox some_id.someclass(
headline
=>
'Our Info'
) {
'just my 2 cents'
};
# this HTML will get generated:
<div class=
"someclass"
id=
"some_id"
>
<div class=
"head"
>Our Info</div>
<div class=
"info"
>just
my
2 cents</div>
</div>
every block defined in a package is auto-added to the packages @EXPORT
array and mangled in a special way to make the magic calling syntax work after importing it into another package.
CONFIGURATION
A simple configuration of a derived Controller could look like this:
__PACKAGE__->config(
# Change extension (default: .pl)
extension
=>
'.pl'
,
# Set the location for .pl files (default: root/bycode)
root_dir
=> cat_app->path_to(
'root'
,
'bycode'
),
# This is your wrapper template located in root_dir (default: wrapper.pl)
wrapper
=>
'wrapper.pl'
,
# all these modules are use()'d automatically
include
=> [Some::Module Another::Package],
);
METHODS
process
fulfill the request (called from Catalyst)
AUTHOR
Wolfgang Kinkeldei, <wolfgang@kinkeldei.de>
LICENSE
This library is free software, you can redistribute it and/or modify it under the same terms as Perl itself.