Simple chat with Ajax + Perl
Ajax is short for Asynchronous JavaScript and XML. In the case of this mini-application, we are actually using JSON instead of XML as our data language. With a good code library like jQuery, which this uses, data format becomes largely a matter of personal preference.
Ajax itself is not a technology but a technique relying on different web technologies. It essentially moves sever interaction to the background so that nav
Like all web technologies it is not inherently difficult. It is actually simple. Ajax just relies on so many small parts, special cases, clients, and possible back-ends that it becomes a serious challenge to do from scratch.
[DOM, XML, JSON, server side, databases, cookies, etc.]
Lucky for you I am here to show you a minimal but functional Ajax application; the only thing missing is verified users + cookies. Lucky for me a huge group of smart web hackers has put together excellent tools to abstract and simplify everything we want to do.
Part one: The JavaScript toolkit
There are several high profile public JavaScript toolkits including
Dojo in particular seems powerful for building big stuff. I haven’t used it though. Prototype is, I believe, the oldest of them all and the years have not been kind. It should probably be avoided entirely.
For this sample Ajax application we use jQuery. jQuery is a remarkably compact and intuitive little kit. For anyone who is used to hacking on the DOM and doing CSS, it is simple to use and powerful. I haven’t had to use anything else since I discovered it. It has a nice community and a lot of plugins.
Part two: The back-end, Perl
You could write a back-end in any language you like. Some obvious choices include
They all have their strong points and they are all widely used. We are going to pick Perl for the same two reasons we usually use Perl
- It’s fun to write, probably faster than anything for prototyping applications,
- and the CPAN.
What is the CPAN? I’m glad you asked. It’s the Comprehensive Perl Archive Network and it’s why a certain hacker said, “90% of every Perl application is already written.” There is Perl code to do almost every possible programming task and you can usually find it on the CPAN. What we find for our Ajax demo is
- the venerable CGI.pm, for simple HTTP headers and POST/GET handling;
- Storable, simple and fast storage and retrieval of complex data;
- JSON, JavaScript Object Notation, to handle marshalling JavaScript data;
- and Template::Toolkit (aka TT2), to do XHTML of this page and the chat pane.
Essentially now, we’re just setting options and writing the design of the application; the flow and appearance. There is little code to be done.
Part…
Till I have time to write more, please just take a look at the code. Feel free to copy it to play with.
The self-printing code for this page
#!/usr/bin/perl
use warnings;
no warnings "uninitialized";
use strict;
use Template;
use CGI ();
use Storable ();
use JSON ();
my $Chat_Db = "/tmp/ajax_chat_db.store";
my $Max_Rows = 25;
if ( CGI::request_method() eq "GET" )
{
print CGI::header(-content_type => "text/html; charset=utf-8",
-status => 200 );
open my $self, "<", $0 or die "Can't open self for script display";
my $code = join "", <$self>;
$code =~ s/
my $tt2 = Template->new({});
$tt2->process(\*DATA, {
max_rows => $Max_Rows,
code => $code,
post_url => CGI::url(-relative => 1),
})
or warn $Template::ERROR;
}
elsif ( CGI::request_method() eq "POST" )
{
# check if self is caller or die 403
if ( CGI::referer() ne CGI::url(-full => 1) )
{
warn CGI::referer();
warn CGI::url(-full => 1);
print CGI::header(-content_type => "text/html; charset=utf-8",
-status => 403 );
print "<h1>Forbidden!</h1>";
exit 0;
}
my $chat = { chat => CGI::param("chat") || "" };
$chat = JSON::objToJson($chat);
$chat = JSON::jsonToObj($chat);
my $chat_data;
if ( $chat->{chat} )
{
$chat_data = update_chat($chat->{chat});
}
else
{
$chat_data = get_chat_data();
}
$chat_data = get_chat_data();
print CGI::header(
-content_type => "application/json; charset=utf-8",
-status => 200 );
print JSON::objToJson({chat => $chat_data});
}
else
{
print CGI::header(-content_type => "text/html; charset=utf-8",
-status => 405 );
exit 0;
}
sub update_chat {
my $line = shift;
my $chat_data = get_chat_data();
my $count = ref($chat_data->[0]) ? $chat_data->[0]{count} : 0;
if ( $line )
{
unshift @{$chat_data}, { count => ++$count,
text => $line };
pop @{$chat_data} while scalar(@{$chat_data}) > $Max_Rows;
store_chat_data($chat_data);
}
return $chat_data;
}
sub get_chat_data {
my $data;
eval {
$data = Storable::lock_retrieve($Chat_Db);
};
if ( $@ or ! $data or
$data->[0]->{count} > 999 ) # roll-over if it gets "full"
{
$data = initialize_chat_data();
}
return $data;
}
sub store_chat_data {
my $data = shift;
Storable::lock_nstore $data, $Chat_Db;
}
sub initialize_chat_data {
unlink($Chat_Db);
my $db = [ { count => 1,
text => "[ your turn, type something! ]" } ];
Storable::lock_nstore $db, $Chat_Db;
return $db;
}
sub ip2rgb {
require List::Util;
my @ip = $ENV{REMOTE_ADDR} =~ /(\d+)/g;
my $r = eval "@{[ join '^', @ip ]}";
my $g = List::Util::sum(@ip) % 255;
my ( $b ) = sort @ip;
return "rgb($r,$g,$b)";
}
__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<title>Minimum-chat with Ajax + Perl</title>
<script type="text/javascript" src="/js/jquery.js"></script>
<style type="text/css" title="currentStyle" media="screen">
body, html {
font-size:.85em;
line-height:1.2em;
width:100%;
padding:0;
margin:0;
font-family:verdana,sans-serif;
text-align:center;
}
#content {
width:728px;
text-align:left;
margin:0 auto;
}
#chat {
font-size:12px;
line-height:16px;
font-family:helvetica,sans-serif;
border: 2px groove teal;
width:36%;
padding:12px 6px;
float:right;
margin:0 0 1ex 1em;
background-color:#eff;
text-align:center;
}
input[type="text"] {
width:90%;
margin:1ex 0 0 0;
padding:1px 3px;
height:18px;
border:1px groove black;
background-color:#ffe;
}
#chat p {
text-align:left;
margin:0;
padding:0;
color:#400;
}
#code {
border:1px dotted #400;
padding:1ex 1em;
background-color:#feefe0;
}
</style>
<script type="text/javascript">//<![CDATA[
var interval = 30 * 1000;
$(document).ready(function(){
$.ajaxSetup({
dataType:"json"
,url:"[% post_url %]"
,type:"POST"
,timeout:( interval / 3 )
});
updateChat();
checkInterval = setInterval("updateChat()",interval);
});
var lastId = 0;
function updateChat (chatText) {
if ( ! chatText ) chatText = "";
var chatData = new Object({chat:chatText});
$.ajax({
data:chatData
,error: function(req,err) {
$("#chat").prepend("<p>Error in udpateChat: "
+ req.status + " " + err + "</p>")
}
,success: function(json){
chat = json.chat.reverse();
for ( i = 0 ; i < chat.length ; i++ )
{
var count = chat[i].count;
//alert( count + " <= " + lastId );
if ( count > lastId )
{
lastId = count;
var p = document.createElement("p");
var txt = document.createTextNode(chat[i].text);
var space = document.createTextNode(" ");
$(p).append(count + ")");
$(p).append(space);
$(p).append(txt);
$(p).append(txt);
$(p).attr("id",count);
$(p).css("display","none");
$("#chat").prepend(p);
$(p).slideDown("slow");
}
}
$("#chat").find("p:gt([% max_rows - 1 %])").slideUp("slow",
function() {$("#chat").find("p:gt([% max_rows - 1 %])").remove();} );
}
});
}
//]]> </script>
</head>
<body>
<div id="content">
<form method="post" id="chat"
onsubmit="updateChat(this.chat.value); this.chat.value = ''; return false;">
<script type="text/javascript">//<![CDATA[
document.write('\<input type="text" maxlength="100" name="chat" \/\>');
//]]> </script>
<noscript>Enable JavaScript to use this chat</noscript>
</form>
<h1>Simple chat with <acronym title="Asynchronous JavaScript and
XML">Ajax</acronym> + Perl</h1>
[% FILTER html_para %]
<acronym title="Asynchronous JavaScript and XML"><a
href="http://en.wikipedia.org/wiki/Ajax_(programming)">Ajax</a></acronym>
is short for Asynchronous JavaScript and <abbr title="eXtensible
Markup Language"><a
href="http://en.wikipedia.org/wiki/XML">XML</a></abbr>. In the case of
this mini-application, we are actually using <abbr title="JavaScript
Object Notation">JSON</abbr> instead of <abbr title="eXtensible Markup
Language">XML</abbr> as our data language. With a good code library
like jQuery, which this uses, data format becomes largely a matter of
personal preference.
Ajax itself is not a technology but a technique relying on different
web technologies. It essentially moves sever interaction to the
background so that nav
Like all web technologies it is not inherently difficult. It is
actually simple. Ajax just relies on so many small parts, special
cases, clients, and possible back-ends that it becomes a serious
challenge to do from scratch.
[DOM, XML, JSON, server side, databases, cookies, etc.]
Lucky for you I am here to show you a minimal but functional Ajax
application; the only thing missing is verified users + cookies. Lucky
for me a huge group of smart web hackers has put together excellent
tools to abstract and simplify everything we want to do.
[% END %]
<h2>Part one: The JavaScript toolkit</h2>
[% FILTER html_para %]
There are several high profile public JavaScript toolkits including
<ul>
<li><a href="http://dojotoolkit.org/">Dojo</a>,</li>
<li><a href="http://mochikit.com/">MochiKit</a>,</li>
<li><a href="http://www.prototypejs.org/">Prototype</a>,</li>
<li>and <a href="http://jquery.com/">jQuery</a>.</li>
</ul>
Dojo in particular seems powerful for building big stuff. I haven’t
used it though. Prototype is, I believe, the oldest of them all and
the years have not been kind. It should probably be avoided entirely.
For this sample Ajax application we use jQuery. jQuery is a remarkably
compact and intuitive little kit. For anyone who is used to hacking on
the DOM and doing CSS, it is simple to use and powerful. I haven’t had
to use anything else since I discovered it. It has a nice community
and a lot of plugins.
[% END %]
<h2>Part two: The back-end, Perl</h2>
[% FILTER html_para %]
You could write a back-end in any language you like. Some obvious choices include
<ul>
<li><a href="http://www.php.net/">PHP</a>,</li>
<li><a href="http://java.sun.com/">Java</a>,</li>
<li><a href="http://en.wikipedia.org/wiki/C++">C++</a>,</li>
<li>and <a href="http://www.perl.com/">Perl</a>.</li>
</ul>
They all have their strong points and they are all widely used. We are
going to pick Perl for the same two reasons we usually use Perl
<ul>
<li>It’s fun to write, probably faster than anything for prototyping
applications,</li>
<li>and the <abbr title="Comprehensive Perl Archive Network"><a
href="http://search.cpan.org/">CPAN</a></abbr>.</li>
</ul>
What is the CPAN? I’m glad you asked. It’s the Comprehensive Perl
Archive Network and it’s why <a
href="http://perlmonks.org/?node=dragonchild">a certain hacker</a>
said, “90% of every Perl application is already written.” There is Perl
code to do almost every possible programming task and you can usually
find it on the CPAN. What we find for our Ajax demo is
<ul>
<li>the venerable <a
href="http://search.cpan.org/dist/CGI.pm">CGI.pm</a>, for simple HTTP
headers and POST/GET handling;</li>
<li><a
href="http://search.cpan.org/dist/Storable">Storable</a>, simple and
fast storage and retrieval of complex data;</li>
<li><a href="http://search.cpan.org/dist/JSON">JSON</a>, JavaScript
Object Notation, to handle marshalling JavaScript data;</li>
<li>and <a
href="http://search.cpan.org/dist/Template-Toolkit">Template::Toolkit</a>
(aka TT2), to do XHTML of this page and the chat pane.</li>
</ul>
Essentially now, we’re just setting options and writing the design of
the application; the flow and appearance. There is little code to be
done.
[% END %]
<h2>Part…</h2>
<p>
Till I have time to write more, please just take a look at the code.
Feel free to copy it to play with.
</p>
<h2>The self-printing code for this page</h2>
<pre id="code">
[% code | html %]
</pre>
</div>
</body>
</html>