Basic Usage¶
Assuming the installation has completed with no errors you are now ready to start creating WebDyne pages and applications.
Integrating Perl into HTML¶
Some code fragments to give a very high-level overview of how WebDyne can be implemented. First the most basic usage example:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- Note the perl tags -->
Hello World <perl> localtime() </perl>
</body>
</html>
So far not too exciting - after all we are mixing code and content. Lets try again:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- Empty perl tag this time, but with method name as attribute -->
Hello World <perl method="hello"/>
</body>
</html>
__PERL__
sub hello { return localtime }
Better - at least code and content are distinctly separated. Note that whatever the Perl code returns at the end of the routine is what is displayed. Although WebDyne will happily display returned strings or scalars, it is more efficient to return a scalar reference, e.g.:
# Works
#
sub greeting { print "Hello World" }
# Is the same as
#
sub greeting { return "Hello World" }
sub greeting { my $var="Hello World"; return $var }
# But best is
#
sub greeting { my $var="Hello World"; return \$var }
# This will cause an error
#
sub greeting { return undef }
# If you don't want to display anything return \undef,
#
sub greeting { return \undef }
# This will fail also
#
sub greeting { return 0 }
# If you want "0" to be displayed ..
#
sub greeting { return \0 }
Perl code in WebDyne pages must always return a
non-undef/non-0/non-empty string value (i.e. it must return something
that evals as "true"). If the code returns a non-true value (e.g. 0,
undef, '') then WebDyne assumes an error has occurred in the routine. If
you actually want to run some Perl code, but not display anything, you
should return a reference to undef, (\undef)
, e.g.:
sub log { &dosomething; return \undef }
Up until now all the Perl code has been contained within the WebDyne
file. The following example shows an instance where the code is
contained in a separate Perl module, which should be available somewhere
in the @INC
path.
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- Perl tag with call to external module method -->
Hello World <perl handler="Digest::MD5::md5_hex"/>
</body>
</html>
If not already resident the module (in this case "Digest::MD5") will be
loaded by WebDyne, so it must be available somewhere in the @INC
path.
Use of the <perl> tag for in-line code.¶
The above examples show several variations of the <perl> tag in use. Perl code that is enclosed by <perl>..</perl> tags is called in-line code:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<pre>
<!-- Perl tag containing perl code which generates output -->
<perl>
for (0..3) {
print "Hello World\n"
}
# Must return a positive value, but don't want anything
# else displayed, so use \undef
#
return \undef;
</perl>
</pre>
</body>
</html>
This is the most straight-forward use of Perl within a HTML document, but does not really make for easy reading - the Perl code and HTML are intermingled. It may be OK for quick scripts etc, but a page will quickly become hard to read if there is a lot of in-line Perl code interspersed between the HTML.
in-line Perl can be useful if you want a "quick" computation, e.g. insertion of the current year:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- Very quick and dirty block of perl code -->
Copyright (C) <perl>(localtime())[5]+1900</perl> Foobar Gherkin corp.
</body>
</html>
Which can be pretty handy, but looks a bit cumbersome - the tags interfere with the flow of the text, making it harder to read. For this reason in-line perl can also be flagged in a WebDyne page using the shortcuts !{! .. !}, or by the use of processing instructions (<? .. ?>) e.g.:
<html>
<head><title>Hello World</title></head>
<body>
<!-- Same code with alternative denotation -->
The time is: !{! localtime() !}
<p>
The time is: <? localtime() ?>
</body>
</html>
The !{! .. !} denotation can also be used in tag attributes (processing instructions, and <perl> tags cannot):
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- Perl code can be used in tag attributes also -->
<font color="!{! (qw(red blue green))[rand 3] !}">
Hello World
</font>
</body>
</html>
Use of the <perl> tag for non-inline code.¶
Any code that is not co-mingled with the HTML of a document is non-inline code. It can be segmented from the content HTML using the __PERL__ delimiter, or by being kept in a completely different package and referenced as an external Perl subroutine call. An example of non-inline code:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- Empty perl tag this time, but with method name as attribute -->
Hello World <perl method="hello"/>
</body>
</html>
__PERL__
sub hello { return localtime }
Note that the <perl> tag in the above example is explicitly closed and does not contain any content. However non-inline code can enclose HTML or text within the tags:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- The perl method will be called, but "Hello World" will not be displayed ! -->
<perl method="hello">
Hello World
</perl>
</body>
</html>
__PERL__
sub hello { return localtime() }
But this is not very interesting so far - the "Hello World" text is not displayed when the example is run !
In order for text or HTML within a non-inline perl block to be displayed, it must be "rendered" into the output stream by the WebDyne engine. This is done by calling the render() method. Let's try that again:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- The perl method will be called, and this time the "Hello World" will be displayed-->
<perl method="hello">
Hello World
</perl>
</body>
</html>
__PERL__
sub hello {
my $self=shift();
$self->render();
}
And again, this time showing how to render the text block multiple times. Note that an array reference is returned by the Perl routine - this is fine, and is interpreted as an array of HTML text, which is concatenated and send to the browser.
<html>
<head><title>Hello World</title></head>
<body>
<!-- The "Hello World" text will be rendered multiple times -->
<perl method="hello">
<p>
Hello World
</perl>
</body>
</html>
__PERL__
sub hello {
my $self=shift();
my @html;
for (0..3) { push @html, $self->render() };
return \@html;
}
Same output using the $self->print() method:
<html>
<head><title>Hello World</title></head>
<body>
<!-- The "Hello World" text will be rendered multiple times -->
<perl method="hello">
<p>
Hello World
</perl>
</body>
</html>
__PERL__
sub hello {
# Note use of $self->print()
#
my $self=shift();
for (0..3) { $self->print($self->render()) };
return \undef;
}
Alternate output methods from Perl handlers¶
When calling a perl handler from a .psp file at some stage you will want your code to deliver output to the browser. Various examples have been given throughout this document, here is a summary of various output options:
<start_html>
<pre>
<perl handler="handler1" />
<perl handler="handler2" />
<perl handler="handler3" />
<perl handler="handler4" />
<perl handler="handler5" />
<perl handler="handler6" chomp />
<perl handler="handler7" />
<perl handler="handler8" />
__PERL__
# Different ways of sending output to the browser
#
sub handler1 {
# Simplest - just return a scalar variable
#
my $text='Hello World 1';
return $text;
}
sub handler2 {
# Scalar ref better because if var is empty (undef) won't trigger and error
#
my $text='Hello World 2';
return \$text;
}
sub handler3 {
# Returning an array ref is OK
#
my @text=('Hello', 'World', 3);
# This won't work
#
#return @text
# Returning an array ref is OK - note it won't auto insert spaces though !
#
return \@text
}
sub handler4 {
# Print something also using the print statement
#
my $text='Hello World 4';
print $text;
print "\n";
# Printing a scalar ref is OK
#
print \$text;
}
sub handler5 {
# Print arrays
#
my @text=('Hello ', 'World ', 5);
print @text;
# Print new line manually, or turn on autonewline -
# see next example;
#
print "\n";
# Array refs are OK
#
print \@text;
# Printing hash ref's won't work ! This will fail
#
# print { a=>1, b=>2 }
return \undef;
}
sub handler6 {
# You can print using a webdyne method handler
#
my $self=shift();
# Text we want to print
#
my $text="Hello World 6\n";
my @text=('Hello ', 'World ', 6, "\n");
# These all work
#
$self->print($text);
$self->print(\$text);
$self->print(@text);
$self->print(\@text);
return \undef;
}
sub handler7 {
# You can print using a webdyne method handler
#
my $self=shift();
# Text we want to print
#
my $text="Hello World 7";
my @text=('Hello ', 'World ', 7);
# Turn on autonew line to print "\n" at end of every call
#
$self->autonewline(1);
# These work
#
$self->print($text);
$self->print(\$text);
# These put a CR between every element in the array
$self->print(@text);
$self->print(\@text);
# Turn off autonewline and return
#
$self->autonewline(0);
return \undef;
}
sub handler8 {
# The say() method is supported also
#
use feature 'say';
my $self=shift();
my $text='Hello World 8';
# These will print, but won't send newline - say() won't send \n to TIEHANDLE
#
say($text);
say($text);
$self->print("\n");
# Use this instead
#
$self->say($text, $text);
}
Passing parameters to subroutines¶
The behaviour of a called __PERL__ subroutine can be modified by passing parameters which it can act on:
<html>
<head><title>Hello World</title></head>
<body>
<!-- The "Hello World" text will be rendered with the param name -->
<perl method="hello" param="Alice"/>
<p>
<perl method="hello" param="Bob"/>
<p>
<!-- We can pass an array or hashref also - see variables section for more info on this syntax -->
<perl method="hello_again" param="%{ firstname=>'Alice', lastname=>'Smith' }"/>
</body>
</html>
__PERL__
sub hello {
my ($self, $param)=@_;
return \"Hello world $param"
}
sub hello_again {
my ($self, $param_hr)=@_;
my $firstname=$param_hr->{'firstname'};
my $lastname =$param_hr->{'lastname'};
return \"Hello world $firstname $lastname";
}
Notes about __PERL__ sections¶
Code in __PERL__ sections has some particular properties.
__PERL__ code is only executed once. Subroutines defined in a
__PERL__ section can be called as many times as you want, but the
code outside of subroutines is only executed the first time a page is
loaded. No matter how many times it is run, in the following code $i
will always be 1:
<html>
<head><title>Hello World</title></head>
<body>
<perl method="hello"/>
<p>
<perl method="hello"/>
</body>
</html>
__PERL__
my $i=0;
$i++;
my $x=0;
sub hello {
# Note x may not increment as you expect because you will probably
# get a different Apache process each time you load this page
#
return sprintf("value of i: $i, value of x in PID $$: %s", $x++)
}
Lexical variables are not accessible outside of the __PERL__ section due to the way perl's eval() function works. The following example will fail:
<html>
<head><title>Hello World</title></head>
<body>
The value of $i is !{! \$i !}
</body>
</html>
__PERL__
my $i=5;
Package defined vars declared in a __PERL__ section do work, with caveats:
<html>
<head><title>Hello World</title></head>
<body>
<!-- Does not work -->
The value of $i is !{! $::i !}
<p>
<!-- Ugly hack, does work though -->
The value of $i is !{! ${__PACKAGE__.::i} !}
<p>
<!-- Probably best to just do this though -->
The value of $i is !{! &get_i() !}
<p>
<!-- Or this - see variable substitution section -->
<perl method="render_i">
The value of $i is ${i}
</perl>
</body>
</html>
__PERL__
our $i=5;
sub get_i { \$i }
sub render_i { shift()->render(i=>$i) }
See the Variables/Substitution section for clean ways to insert variable contents into the page.
Variables¶
WebDyne starts to get more useful when variables are used to modify the content of a rendered text block. A simple example:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- The var ${time} will be substituted for the correspondingly named render parameter -->
<perl method="hello">
Hello World ${time}
</perl>
</body>
</html>
__PERL__
sub hello {
my $self=shift();
my $time=localtime();
$self->render( time=>$time );
}
Note the passing of the time
value as a parameter to be substituted
when the text is rendered.
Combine this with multiple call to the render() routine to display dynamic data:
<html>
<head><title>Hello World</title></head>
<body>
<!-- Multiple variables can be supplied at once as render parameters -->
<perl method="hello0">
<p>
Hello World ${time}, loop iteration ${i}.
</perl>
<br>
<br>
<perl method="hello1">
<p>
Hello World ${time}, loop iteration ${i}.
</perl>
</body>
</html>
__PERL__
sub hello0 {
my $self=shift();
my @html;
my $time=localtime();
for (my $i=0; $i<3; $i++) {
push @html, $self->render( time=>$time, i=>$i)
};
return \@html;
}
sub hello1 {
# Alternate syntax using print
#
my $self=shift();
my $time=localtime();
for (my $i=0; $i<3; $i++) {
print $self->render( time=>$time, i=>$i)
};
return \undef
}
Variables can also be used to modify tag attributes:
<html>
<head><title>Hello World</title></head>
<body>
<!-- Render paramaters also work in tag attributes -->
<perl method="hello">
<p>
<font color="${color}">
Hello World
</font>
</perl>
</body>
</html>
__PERL__
sub hello {
my $self=shift();
my @html;
for (0..3) {
my $color=(qw(red green yellow blue orange))[rand 5];
push @html, $self->render( color=>$color );
}
\@html;
}
Other variable types are available also, including:
-
@{var,var,..}
for arrays, e.g.@{'foo', 'bar'}
-
%{key=>value, key=>value, ..}
for hashes e.g.%{ a=>1, b=>2 }
-
+{varname}
for CGI form parameters, e.g.+{firstname}
-
*{varname}
for environment variables, e.g.*{HTTP_USER_AGENT}
-
^{requestmethod}
for Apache request ($r=Apache->request
) object methods, e.g.^{protocol}
. Only available for in Apache/mod_perl, and only useful for request methods that return a scalar value.
The following template uses techniques and tags discussed later, but should provide an example of potential variable usage:
<html>
<head>
<title>Variables</title>
</head>
<body>
<!-- Environment variables -->
<p>
<!-- Short Way -->
Mod Perl Version: *{MOD_PERL}
<br>
<!-- Same as Perl code -->
Mod Perl Version:
<perl> \$ENV{'MOD_PERL'} </perl>
<!-- Apache request record methods. Only methods that return a scalar result are usable -->
<p>
<!-- Short Way -->
Request Protocol: ^{protocol}
<br>
<!-- Same as Perl code -->
Request Protocol:
<perl> my $self=shift(); my $r=$self->r(); \$r->protocol() </perl>
<!-- CGI params -->
<form>
Your Name:
<p><textfield name="name" default="Test" size="12">
<p><submit name="Submit">
</form>
<p>
<!-- Short Way -->
You Entered: +{name}
<br>
<!-- Same as Perl code -->
You Entered:
<perl> my $self=shift(); my $cgi_or=$self->CGI(); \$cgi_or->param('name') </perl>
<br>
<!-- CGI vars are also loaded into the %_ global var, so the above is the same as -->
You Entered:
<perl> $_{'name'} </perl>
<!-- Arrays -->
<form>
<p>
Favourite colour 1:
<p><popup_menu name="popup_menu" values="@{qw(red green blue)}">
<!-- Hashes -->
<p>
Favourite colour 2:
<p><popup_menu name="popup_menu"
values="%{red=>Red, green=>Green, blue=>Blue}">
</form>
</body>
</html>
Shortcut Tags¶
Previous versions of WebDyne used Lincoln Stein's CGI.pm module to render tags, and supported CGI.pm shortcut tags such as <start_html>, <popup_menu> etc. Modern versions of WebDyne do not use CGI.pm in any modules, having ported tag generation to HTML::Tiny. Support for shortcut tags is preserved though - they provide a quick and easy way to generate simple web pages.
Quick pages using shortcut <start_html>, <end_html> tags¶
For rapid development you can take advantage of the <start_html> and <end_html> tags. The following page generates compliant HTML (view the page source after loading it to see for yourself):
The <start_html> tag generates all the <html>, <head>, <title> tags etc needed for a valid HTML page plus an opening body tag. Just enter the body content, then optionally finish with <end_html> to generate the closing <body> and <html> tags (optional because the page is automatically closed if these are omitted). See the tag reference section but here is an example using several of the properties available in the <start_html> tag including loading multiple scripts and stylesheets:
<start_html title="Hello World"
style="@{qw(https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.css https://fonts.googleapis.com/css2?family=Inter:wght@400;600&family=Playfair+Display:wght@700&display=swap)}"
script="@{qw(https://cdn.jsdelivr.net/npm/aos@2.3.4/dist/aos.js https://cdn.jsdelivr.net/npm/typed.js@2.1.0/dist/typed.umd.js)}"
>
<p>
<h2 id="typed"></h2>
<div data-aos="fade-up">I animate on load and scroll !</div>
<script>
AOS.init({
duration: 1000, // 1s animations
once: true // animate only once
});
</script>
<script>
new Typed('#typed', {
strings: ["Lorem", "Ipsum", "!{! localtime !}"],
typeSpeed: 50,
backSpeed: 25,
loop: true
});
</script>
Caution
If make sure any attributes using the @{..}
or %{..}
convention are
on one line - the parser will not interpret them correctly if spanning
multiple lines.
If using the <start_html> shortcut tag you can optionally insert
default stylesheets and/or <head> sections from the Webdyne
configuration file. E.g if in your webdyne.conf.pl
file you have the
following:
$_={
'WebDyne::Constant' => {
# Inserted as <meta> into <head> section
#
WEBDYNE_META => {
# These all rendered as <meta name="$key" content="$value">
viewport => 'width=device-width, initial-scale=1.0',
author => 'Bob Foobar',
# This one rendered as <meta http-equiv="X-UA-Compatible" content="IE=edge">
'http-equiv=X-UA-Compatible' => 'IE=edge',
'http-equiv=refresh' => '5; url=https://www.example.com'
}
# This is inserted inside the <html> starting tag, works for <start_html> or straight <html>
#
WEBDYNE_HTML_PARAM => {
lang => 'de'
}
# This is inserted before the </head> closing tag, works for <start_html> or straight <html>
#
WEBDYNE_HEAD_INSERT => << 'END'
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css">
<style>
:root { --pico-font-size: 85% }
body { padding-top: 10px; padding-left: 10px;
</style>
END
}
}
Then any .psp
file with a <start_html> tag will have the following
content:
<!DOCTYPE html><html lang="de">
<head>
<title>Untitled Document</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="author" content="Bob Foobar">
<meta http-equiv="refresh" content="5; url=https://www.example.com">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.classless.min.css">
<style>
:root { --pico-font-size: 85% }
</style>
</head>
HTML Forms using <popup_menu>,<checkbox_group> and other tags¶
The CGI.pm module presented several shortcut tags for generating HTML forms. These tags have been recreated in WebDyne and act in a similar way.
The manual page for CGI.pm contains the following synopsis example:
use CGI qw/:standard/;
print header,
start_html('A Simple Example'),
h1('A Simple Example'),
start_form,
"What's your name? ",textfield('name'),p,
"What's the combination?", p,
checkbox_group(-name=>'words',
-values=>['eenie','meenie','minie','moe'],
-defaults=>['eenie','minie']), p,
"What's your favorite color? ",
popup_menu(-name=>'color',
-values=>['red','green','blue','chartreuse']),p,
submit,
end_form,
hr;
if (param()) {
print "Your name is",em(param('name')),p,
"The keywords are: ",em(join(", ",param('words'))),p,
If the example was ported to a WebDyne compatible page it might look something like this:
<!-- The same form from the CGI example -->
<start_html title="A simple example">
<h1>A Simple Example</h1>
<start_form>
<p>
What's your name ?
<p><textfield name="name">
<p>
What's the combination ?
<p><checkbox_group
name="words" values="@{qw(eenie meenie minie moe)}" defaults="@{qw(eenie minie)}">
<p>
What's your favourite color ?
<p><popup_menu name="color" values="@{qw(red green blue chartreuse)}">
<p><submit>
<end_form>
<hr>
<!-- This section only rendered when form submitted -->
<perl method="answers">
<p>
Your name is: <em>+{name}</em>
<p>
The keywords are: <em>${words}</em>
<p>
Your favorite color is: <em>+{color}</em>
</perl>
__PERL__
sub answers {
my $self=shift();
my $cgi_or=$self->CGI();
if ($cgi_or->param()) {
my $words=join(",", $cgi_or->param('words'));
return $self->render( words=>$words )
}
else {
return \undef;
}
}
Note
When using the WebDyne form tags, state (previous form values) are preserved after the Submit button is presented. This makes building single page application simple as there is no need to implement logic to adjust options in a traditional HTML form to reflect the user's choice.
More on HTML shortcut tags in forms¶
Tags such as <popup_menu> output traditional HTML form tags such as <select><option>...</select>, but they have the advantage of allowing Perl data types as attributes. Take the following example:
<popup_menu value="%{red=>Red, green=>Green, blue=>Blue}"/>
it is arguably easier to read than:
<select name="values" tabindex="1">
<option value="green">Green</option>
<option value="blue">Blue</option>
<option value="red">Red</option>
</select>
So there is some readability benefit, however the real advantage shows when we consider the next example:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- Generate all country names for picklist -->
<form>
Your Country ?
<perl method="countries">
<popup_menu values="${countries_ar}" default="Australia">
</perl>
</form>
</body>
</html>
__PERL__
use Locale::Country;
sub countries {
my $self=shift();
my @countries = sort { $a cmp $b } all_country_names();
$self->render( countries_ar=>\@countries );
}
All values for the menu item were pre-populated from one WebDyne variable - which saves a significant amount of time populating a "countries" style drop-down box.
Access to HTML form responses and query strings¶
Once a form is submitted you will want to act on responses. There are several ways to do this - you can access a CGI::Simple object instance in any WebDyne template by calling the CGI() method to obtain form responses:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<!-- Note use of CGI.pm derived textfield tag -->
<form>
Enter your name:
<p><textfield name="name">
<p><submit>
</form>
<!-- And print out name if we have it -->
<perl method="hello">
Hello ${name}, pleased to meet you.
</perl>
</body>
</html>
__PERL__
sub hello {
my $self=shift();
# Get CGI instance
#
my $cgi_or=$self->CGI();
# Use CGI.pm param() method. Could also use other
# methods like keywords(), Vars() etc.
#
my $name=$cgi_or->param('name');
$self->render( name=>$name);
}
From there you can all any method supported by the CGI::Simple module -
see the CGI::Simple manual page (man CGI::Simple
) or review on CPAN:
CGI::Simple
Since one of the most common code tasks is to access query parameters,
WebDyne stores them in the special %_
global variable before any user
defined Perl methods are called. For example:
<html>
<head><title>Hello World</title></head>
<body>
<p>
<form>
Enter your name:
<p><textfield name="name">
<p><submit>
</form>
<!-- Use the %_ global var, other options below -->
<p>
Hello <? uc($_{'name'} || 'Anonymous') ?>, pleased to meet you.
<!-- Quick and dirty, no perl code at all -->
<p>
Hello +{name}, pleased to meet you.
<!-- Traditional, using the CGI::Simple param() call in the hello1 sub -->
<p>
<perl method="hello1">
Hello ${name}, pleased to meet you.
</perl>
<!-- Quicker method using %_ global var in the hello2 sub -->
<p>
<perl method="hello2">
Hello ${name}, pleased to meet you.
</perl>
<!-- Quick and dirty using inline Perl. Note use of \ to prevent error if param empty -->
<p>
Hello !{! \$_{name} !}, pleased to meet you.
</body>
</html>
__PERL__
sub hello1 {
my $self=shift();
my $cgi_or=$self->CGI();
my $name=$cgi_or->param('name');
$self->render( name=>$name);
}
sub hello2 {
my $self=shift();
# Quicker method of getting name param
#
my $name=$_{'name'};
$self->render( name=>$name);
}