Web::Simple
A Perl Web Nano-Framework
http://cleverdomain.org/wstechtalk/
Minimal Dependencies
- Web::Simple’s dependency list:
- Plack
- Moo
- Data::Dumper::Concise
- Syntax::Keyword::Gather
- warnings::illegalproto
- Zero XS dependencies
- Fast, easy deploys to any system.
Minimal Apps
- Apps can be as little as a single file, no scaffolding needed.
Minimal Framework
- No component resolution (use Bread::Board if you want it)
- No enforced MVC
- No fancy request/response classes
- No opinions
Really, Really Fast
- “Parse on demand” approach
- File uploads, POST data, even URL params aren’t processed unless a route asks for them
- You don’t pay for what you don’t use
- Fast enough to use as a CGI replacement, with no persistent process
PSGI Native
- Web::Simple’s entire design is based on PSGI
- Only the second framework designed for PSGI (first was Tatsumaki)
- Sub-dispatch to PSGI apps
- Can bridge together other frameworks
- Or provide a routing layer for something like Magpie
- Middleware
Example App
package GuestBook::Web;
use Web::Simple;
use Guestbook;
use JSON::XS 'encode_json';
use Text::Caml;
has 'guestbook' => (is => 'lazy'); sub _build_guestbook { Guestbook->new }
has 'mustache' => (is => 'lazy'); sub _build_mustache { Text::Caml->new }
sub dispatch_request {
}
GuestBook::Web->run_if_script;
Example App (contd.)
sub dispatch_request {
sub (GET + /guestbook) {
my $self = shift;
my @messages = $self->guestbook->all_messages;
sub (.html) {
[ 200, [ 'Content-Type' => 'text/html' ],
[ $self->mustache->render_file('guestbook.html', { messages => \@messages }) ]
];
},
sub (.json) {
[ 200, [ 'Content-Type' => 'text/json' ],
[ encode_json(\@messages) ]
];
}
},
sub (POST + /guestbook + %:name=&:message=) {
my $self = shift;
$self->guestbook->add_message({ name => $_{name}, message => $_{message} });
[ 302, [ 'Location' => '/guestbook.html' ], [] ];
}
}
GuestBook Class
package Guestbook;
use Moo;
use MooX::Types::MooseLike::Base ':all';
has messages => (
is => 'rw',
isa => ArrayRef,
lazy => 1,
default => sub { [] },
);
sub add_message {
my ($self, $message) = @_;
push @{$self->messages}, $message;
}
sub all_messages {
my ($self) = @_;
return @{$self->messages};
}
1;
Template
<html>
<head>
<title>Cool Guestbook</title>
<link rel="stylesheet" type="text/css" href="/static/style.css">
</head>
<body>
<form method="post">
<label for="name">Name:</label>
<input type="text" id="name" name="name">
<label for="message">Message:</label>
<textarea name="message" rows="4" cols="80"></textarea>
<input type="submit">
</form>
<hr>
{{#messages}}
<div>
<b>{{name}}:</b> {{message}}
</div>
{{/messages}}
{{^messages}}No messages.{{/messages}}
</body>
</html>
guestbook.html
What was that sub thing?!
sub (POST + /guestbook + %:name=&:message=) {
my $self = shift;
$self->guestbook->add_message({
name => $_{name},
message => $_{message},
});
[ 302, [ 'Location' => '/guestbook.html' ], [] ];
}
- That’s a subroutine prototype, kind of…
- Perl stores the prototypes for anonymous subs, but doesn’t parse them.
- HTTP method matching
- Filename + extension matching
- GET/POST params
- Optional/required, single/list
- Upload handler
%_
hash
Moo?!
- Minimalist Object Orientation
- “Almost, but not quite, two thirds of Moose”
- Pure Perl
- Very small startup cost
- Compatible with Moose — mix and match without fear
Middleware?
sub dispatch_request {
sub (GET + /guestbook) {
}
}
Middleware
sub dispatch_request {
sub (GET) {
Plack::Middleware::Deflater->new;
},
sub (GET + /guestbook) {
}
}
Plack App Dispatch
sub dispatch_request {
sub (GET + /guestbook) {
},
sub (/static/...) {
Plack::App::File->new({ root => '.' })
}
}
/...
scopes the app under the given path.
Other Features
- CLI-mode testing
run_test_request
gives you free Plack::Test integration
- Static file generation (“slashdot mode”)
Web::Dispatch
- Part of Web::Simple but can be used independently
- Sub prototype insanity is optional
- Web::Dispatch::HTTPMethods adds extra RESTiness
Community
IRC: irc.perl.org #web-simple
Demo