Fiasco is yet another web framework. Why? Because you can never have too many. Or maybe you can. But Fiasco is different. Fiasco is simple. So pathetically simple you'll probably laugh at it and point and call it names like "Spamooo" or something. It's designed to make it ridiculously easy to go from static pages, template driven pages to fully dynamic pages. But the best way to explain is to show. So let's do a little "Hello World" example. ============================ The very simplest example ============================ Make a directory in your web root. Let's call it "my_test". % mkdir my_test % cd my_test % fiasco init Actually, we didn't really need to run the "fiasco init" but it will be useful later. What it does is set up everything Apache needs to serve this directoy with Fiasco by generating a .htaccess file. However developing under Apache can be a pain because it doesn't pick up changes in modules. So let's start up the Fiasco test server % fiasco server fiasco is started on port 8888 - use http://localhost:8888/ to visit it % Again, this isn't really necessary at the moment but it makes the development iteration more straight forward in the future. Now create a page % echo "Hello World!" > index.html and visit http://localhost:8888/ And Viola! "Hello world!" "Oh, you complete smart arse Simon," you might say "That's not very clever and/or useful" and you'd be right. But you have to admit it is simple. And patience gentle traveller and seething cynic. Let's make things more interesting. ============================ A more complicated example ============================ % echo "" > header % echo "" > footer % echo "[% INCLUDE header %]Hello World![% INCLUDE footer %]" > hi.tt And visit http://localhost:8888/hi.html And you'll get "Hello World again!" - the template has been automatically rendered. now % echo "[% INCLUDE header %]Hajime Mashite![% INCLUDE footer %]" \ > nihau.tt and http://localhost:8888/nihau.html And you'll get "Hajime Mashite!". All dead easy and makes it easy to reuse template elements. ============================ Further down the spiral ============================ Fiasco works with deeper paths too. If you make a directory called 'deeper' and then a file in that called 'wotcha.tt' % mkdir deeper % echo "greets!" > deeper/wotcha.tt and then go to http://localhost:8888/deeper/wotcha.html Then that works too. ============================ Dynamic and Everything! ============================ Now, let's put a bit of dynamism in our page. Is that even a word? Who cares?! Onwards and upwards! First create a file that looks like [% INCLUDE header %] [% greeting %] [% INCLUDE footer %] and call it dynamic.tt now create a file called dynamic.pm that looks like package dynamic; use strict; sub handle { my $handler = shift; return { greeting => "Hello World!" }; } 1; And go to http://localhost:8888/dynamic.html And you'll see "Hello World!" again. Now modify dynamic.pm again and make it look like package dynamic; use strict; sub handle { my $handler = shift; my $greeting = ('jp' eq $handler->param('lang')) ? "Hajime Mashite!" : "Hello World!"; return { greeting => $greeting }; } 1; Now go to http://localhost:8888/dynamic.html And you'll see "Hello World!" as normal but, go to http://localhost:8888/dynamic.html?lang=jp and you'll see "Hajime Mashite!" ============================ Verboten, mein freunde! ============================ Let's lock it down shall we? You don't want everyone to be able to be greeted by your web page, do we? First create a .htpasswd file in your Fiasco root % htpasswd -c .htpasswd testuser testpasswd then create a package called dynamic.pm package dynamic; use strict; use base (Fiasco::Authenticated); sub handle { my $handler = shift; my $greeting = ('jp' eq $handler->param('lang')) ? "Hajime Mashite!" : "Hello World!"; return { greeting => $greeting }; } 1; Now when you go to http://localhost:8888/dynamic.html you'll be asked to type in a username and password. Go ahead! Type in the 'testuser' and 'testpasswd'. "But," you might readily opine "I already have my users in a LDAP/ Database/PAM setup and don't want to have to add them all to an .htpasswd file". And you know what - you're right. First, modify dynamic.pm to change what it inherits from. package dynamic; use strict; use base (authenticated); sub handle { my $handler = shift; my $greeting = ('jp' eq $handler->param('lang') ? "Hajime Mashite!" : "Hello World!"; return { greeting => $greeting }; } 1; Now create authenticated.pm package authenticated; use strict; use base qw(Fiasco::Authenticated); sub authenticate { my $handler = shift; my $user = shift; my $pass = shift; # in reality you'd go check LDAP or your DB here return ($user eq 'test' and $pass eq 'test'); } 1; Now, when you go to http://localhost:8888/dynamic.html the only username and password that will work is test/test. But, this is a security flaw! Anyone going to http://localhost:8888/authenticated.html They'll see the text for authenticated.pm! This would be bad! So, package authenticated; use strict; use base qw(Fiasco::Authenticated Fiasco::Forbidden); sub authenticate { my $handler = shift; my $user = shift; my $pass = shift; return ($user eq 'test' and $pass eq 'test'); } 1; by adding the Fiasco::Forbidden the page will automatically be shielded from prying eyes. ============================ Sessions ============================ HTTP and hence web apps are stateless which means that if you want to persist information between pages and requests then you have to stick it all as hidden inputs on forms. Or you can use sessions. Create a file call persist.tt [% INCLUDE header %] Count is [% count %] [% INCLUDE footer %] Now create a package called persist.pm package persist; use strict; sub handle { my $handler = shift; my $count = $handler->session('count')++; return { count => $count }; } 1; Now every time you go to http://localhost:8888/persist.html The count will go up. ============================ Go your own way ============================ Now, you might not always want to produce HTML. And lo! the fabulousness that is Fiasco can handle this too. Create boring.pm package boring; use strict; sub content_type { return 'text/plain'; } 1; and boring.tt Hello World! And everything will just work. ============================ Roll your own ============================ But that's not very interesting is it? package exciting; use strict; use GD; sub content_type { my $handler = shift; return "image/".$handler->extension; } sub handle { my $handler = shift; my $image = make_gd_image(); my $what = $handler->extension; return $image->$what; } sub make_gd_image { # do some stuff to make an image # ... } 1; And now, if you go to http://localhost:8888/exciting.png or http://localhost:8888/exciting.jpg or http://localhost:8888/exciting.gif Then the image will be displayed correctly. See, by returning a scalar rather than a hash reference Fiasco assumes that you are in full control of your faculties and know what you're doing and it will just display whatever you pass back. ============================ Defaulting on your Paths ============================ If a file doesn't exist in a path Fiasco will walk back up the directory tree looking for files called _default.tt and _default.pm. So, for example if you wanted to change our greeting system we could do % mkdir greeting and then in greeting make a _default.tt that looks like [% greeting %] and a _default.pm that looks like package _default; sub handle { my $handler = shift; my $name = $handler->name; my $greeting = "Hello World!"; if ($name eq 'japanese') { $greeting = "Hajime Mashite!"; } elsif ($name eq 'german') { $greeting = "Guten tag!"; } elsif ($name eq 'french') { $greeting = "Bonjour touts le monde!"; } return { greeting => $greeting }; } 1; Then http://localhost:8888/greeting/japanese http://localhost:8888/greeting/german http://localhost:8888/greeting/french will do what you expect and http://localhost:8888/greeting/ will have "Hello World!" But you can get more creative than that. In the Fiasco root (the one we created called my_test) create _default.tt that looks like [% message %] Then remove your greeting/ directory and create a _default.pm that looks like package _default; my %translations = ( french => { greeting => "Bonjour", farewell => "Aurevoir", }, german => { greeting => "Guten tag", farewell => "Auf wiedersehn", }, japanese => { greeting => "Hajime mashite", farewell => "Sayonara", }, english => { greeting => "Hello", farewell => "Goodbye", }, ); sub handle { my $handler = shift; my $path = $handler->path; my $name = $handler->name || "english"; return { message => $translations{$name}->{$path} }; } 1; and http://localhost:8888/greeting/japanese http://localhost:8888/greeting/german http://localhost:8888/greeting/french http://localhost:8888/greeting/ will do the same as before but http://localhost:8888/farewell/japanese http://localhost:8888/farewell/german http://localhost:8888/farewell/french http://localhost:8888/farewell/ will do what you expect. ============================ Explicit return codes ============================ package odd; sub handle { my $handler = shift; my $rand = int(rand(3)); if ($rand == 0) { $handler->return_code('OK'); return "A-okreturn_code('NOT_FOUND'); return; } else { $handler->return_code('FORBIDDEN'); return; } } 1; ============================ Redirects ============================ Redirects are easy. Simply pass an absolute or relative url to the redirect method. package somepage; use strict; sub handle { my $handler = shift; unless ($handler->user) { $handler->redirect("login.html"); } return { current_user => $handler->user }; } 1; ============================ Headers ============================ Want to add some miscellanous headers to your response? package extra_headers; use strict; sub handle { my $handler = shift; my $time = time; $handler->header('X-Current-Time') = $time; return { time => $time }; } 1;