introducing modern perl
TRANSCRIPT
What is Perl
Introducing
Modern Perl
Dave CrossMagnum Solutions [email protected]
Modern Perl
Perl has been around for a long time
Achieved great popularity in the 1990s
The duct tape of the internet
Perl has moved on a lot since then
Many people's perceptions of Perl haven't
Modern Perl
Keeping up to date with Perl
Surveying modern modules and techniques
A lot to cover
Only an overview
Plenty of pointers to more information
What We Will Cover
Template Toolkit
DateTime
DBIx::Class
TryCatch
What We Will Cover
Moose
autodie
Catalyst
PSGI/Plack
Schedule
09:00 Begin
11:00 Coffee break
13:00 Lunch
14:00 Begin
16:00 Coffee break
17:00 End
Resources
Slides available on-linehttp://mag-sol.com/train/yapc/2010
Also see Slidesharehttp://www.slideshare.net/davorg/slideshows
Get Satisfactionhttp://getsatisfaction.com/magnum
Template Toolkit
Templates
Many people use templates to produce web pages
Advantages are well known
Standard look and feel (static/dynamic)
Reusable components
Separation of code logic from display logic
Different skill-sets (HTML vs Perl)
DIY Templating
Must be easy - so many people do it
See perlfaq4
How can I expand variables in text strings?
DIY Templating
$text =
'this has a $foo in it and a $bar';
%user_defs = (
foo => 23,
bar => 19,
);
$text =~ s/\$(\w+)/$user_defs{$1}/g;
Don't do that
Templating Options
Dozens of template modules on CPAN
Text::Template, HTML::Template, Mason, Template Toolkit
Many, many more
Questions to considerHTML only?
Template language
Template Toolkit
http://tt2.org/
Very powerful
Both web and non-web
Simple template language
Plugins give access to much of CPAN
Can use Perl code if you wantBut don't do that
Good Book Too!
The Template Equation
Data + Template = Output
Data + Alternative Template = Alternative Output
Different views of the same data
Only the template changes
Simple TT Example
use Template;
use My::Object;
my ($id, $format) = @ARGV;
$format ||= 'html';
my $obj = My::Object->new($id)
or die;
my $tt = Template->new;
$tt->process("$format.tt",
{ obj => $obj },
"$id.$format")
or die $tt->error;
html.tt
[% obj.name %]
[% obj.name %]
[% obj.desc %]
[% FOREACH child IN obj.children -%]-
[% child.name %]
[% END %]
text.tt
[% obj.name | upper %]
Image: [% obj.img %]
[% obj.desc %]
[% FOREACH child IN obj.children -%]
* [% child.name %]
[% END %]Adding New Formats
No new code required
Just add new output template
Perl programmer need not be involved
Equation Revisited
Data + Template = OutputTemplate Toolkit
Template + Output = DataTemplate::Extract
Data + Output = TemplateTemplate::Generate
DateTime
Dates & Times
Perl has built-in functions to handle dates and times
time seconds since 1st Jan 1970
localtime convert to human-readable
timelocal (in Time::Local) inverse of localtime
strftime (in POSIX) formatting dates and times
Dates & Times on CPAN
Look to CPAN for a better answer
Dozens of date/time modules on CPAN
Date::Manip is almost never what you want
Date::Calc, Date::Parse, Class::Date, Date::Simple, etc
Which one do you choose?
Perl DateTime Project
http://datetime.perl.org/
"The DateTime family of modules present a unified way to handle dates and times in Perl"
"unified" is good
Dozens of modules that work together in a consistent fashion
Using DateTime
use DateTime;
my $dt = DateTime->now;
say $dt; # 2010-08-02T10:06:07
say $dt->dmy; # 2010-08-02
say $dt->hms; # 10:06:07Using DateTime
use DateTime;
my $dt = DateTime->new(year => 2010,
month => 8,
day => 2);
say $dt->ymd('/'); # 2010/08/02
say $dt->month; # 8
say $dt->month_name; # AugustArithmetic
A DateTime object is a point in time
For date arithmetic you need a duration
Number of years, weeks, days, etc
Arithmetic
use DateTime;
my $dt = DateTime->new(year => 2010,
month => 8,
day => 2);
my $two_weeks =
DateTime::Duration->new(weeks => 2);
$dt += $two_weeks;
say $dt;
# 2010-08-16T00:00:00Formatting Output
use DateTime;
my $dt = DateTime->new(year => 2010,
month => 4,
day => 14);
say $dt->strftime('%A, %d %B %Y');
# Wednesday, 14 April 2010strftime uses UNIX standards
Control input format with DateTime::Format::Strptime
Parsing & Formatting
Ready made parsers and formatters for popular date and time formats
DateTime::Format::HTTP
DateTime::Format::MySQL
DateTime::Format::Excel
DateTime::Format::Babythe big hand is on...
Alternative Calendars
Handling non-standard calendars
DateTime::Calendar::Julian
DateTime::Calendar::Hebrew
DateTime::Calendar::Mayan
DateTime::Fiction::JRRTolkien::Shire
Calendar Examples
use DateTime::Calendar::Mayan;
my $dt = DateTime::Calendar::Mayan->now;
say $dt->date; # 12.19.17.9.13use DateTime::Fiction::JRRTolkien::Shire;
my $dt =
DateTime::Fiction::JRRTolkien::Shire->now;
say $dt->on_date; # Trewsday 14 Afterlithe 7474DBIx::Class
Object Relational Mapping
Mapping database relations into objects
Tables (relations) map onto classes
Rows (tuples) map onto objects
Columns (attributes) map onto attributes
Don't write SQL
SQL Is Tedious
Select the id and name from this table
Select all the details of this row
Select something about related tables
Update this row with these values
Insert a new record with these values
Delete this record
Replacing SQL
Instead of
SELECT *
FROM my_table
WHERE my_id = 10and then dealing with the prepare/execute/fetch code
Replacing SQL
We can write
use My::Object;
# warning! not a real orm
my $obj = My::Object->retrieve(10)Or something similar
Writing An ORM Layer
Not actually that hard to do yourself
Each class needs an associated table
Each class needs a list of columns
Create simple SQL for basic CRUD operations
Don't do that
Perl ORM Options
Plenty of choices on CPAN
Class::DBI
Rose::DB::Object
ORLite
DBIx::ClassThe current favourite
Fey::ORM
DBIx::Class
Standing on the shoulders of giants
Learning from problems in Class::DBI
More flexible
More powerful
DBIx::Class Example
Modeling a CD collection
Three tables
artist (artistid, name)
cd (cdid, artist, title)
track (trackid, cd, title)
Main Schema
Define main schema class
Music/DB.pm
package Music::DB;
use base qw/DBIx::Class::Schema/;
__PACKAGE__->load_classes();
1;Object Classes - Artist
Music/DB/Artist.pm
package Music::DB::Artist;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/Core/);
__PACKAGE__->table('artist');
__PACKAGE__->add_columns(qw/ artistid
name /);
__PACKAGE__->set_primary_key('artistid');
__PACKAGE__->has_many(cds =>
'Music::DB::Cd');
1;Object Classes- CD
Music/DB/CD.pm
package Music::DB::CD;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/Core/);
__PACKAGE__->table('cd');
__PACKAGE__->add_columns(qw/ cdid artist
title year /);
__PACKAGE__->set_primary_key('cdid');
__PACKAGE__->belongs_to(
artist => 'Music::DB:Artist');
1;Inserting Artists
my $schema =
Music::DB->connect($dbi_str);
my @artists = ('Florence + The Machine',
'Belle and Sebastian');
my $art_rs = $schema->resultset('Artist');
foreach (@artists) {
$art_rs->create({ name => $_ });
}Inserting CDs
Hash of Artists and CDs
my %cds = (
'Lungs' =>
'Florence + The Machine',
'The Boy With The Arab Strap' =>
'Belle and Sebastian',
'Dear Catastrophe Waitress' =>
'Belle and Sebastian',
);Inserting CDs
Find each artist and insert CD
foreach (keys $cds) {
my ($artist) = $art_rs->search(
{ name => $cds{$_} }
);
$artist->add_to_cds({
title => $_,
});
}Retrieving Data
Get CDs by artist
my $name = 'Belle and Sebastian';
my ($artist) = $art_rs->search({
name => $name,
});
foreach ($artist->cds) {
say $_->title;
}Searching for Data
Search conditions can be more complex
Alternatives
$rs->search({year => 2006},
{year => 2007});Like$rs->search({name =>
{ 'like', 'Dav%' }});Searching for Data
Combinations
$rs->search({forename =>
{ 'like', 'Dav%' },
surname => 'Cross' });Don't Repeat Yourself
There's a problem with this approach
Information is repeated
Columns and relationships defined in the database schema
Columns and relationships defined in class definitions
Repeated Information
CREATE TABLE artist (
artistid INTEGER PRIMARY KEY,
name TEXT NOT NULL
);Repeated Information
package Music::DB::Artist;
use base qw/DBIx::Class/;
__PACKAGE__->load_components(qw/Core/);
__PACKAGE__->table('artist');
__PACKAGE__->add_columns(qw/ artistid
name /);
__PACKAGE__->set_primary_key('artistid');
__PACKAGE__->has_many(cds =>
'Music::DB::Cd');
1;Database Metadata
Some people don't put enough metadata in their databases
Just tables and columns
No relationships. No constraints
You may as well make each column VARCHAR(255)
Database Metadata
Describe your data in your database
It's what your database is for
It's what your database does best
No Metadata (Excuse 1)
"This is the only application that will ever access this database"
Nonsense
All data will be shared eventually
People will update your database using other applications
Can you guarantee that someone won't use mysql to update your database?
No Metadata (Excuse 2)
"Database doesn't support those features"
Nonsense
MySQL 3.x is not a databaseIt's a set of data files with a vaguely SQL-like query syntax
MySQL 4.x is a lot better
MySQL 5.x is most of the way there
Don't be constrained by using inferior tools
DBIC::Schema::Loader
Creates classes by querying your database metadata
No more repeated data
We are now DRY
Schema definitions in one place
But...
Performance problems
Performance Problems
You don't really want to generate all your class definitions each time your program is run
Need to generate the classes in advance
dump_to_dir method
Regenerate classes each time schema changes
Alternative Approach
Need one canonical definition of the data tables
Doesn't need to be SQL DDL
Could be Perl code
Generate DDL from DBIx::Class definitionsHarder approach
Might need to generate ALTER TABLE
Conclusions
ORM is a bridge between relational objects and program objects
Avoid writing SQL in common cases
DBIx::Class is the currently fashionable module
Caveat: ORM may be overkill for simple programs
More Information
Manual pages (on CPAN)
DBIx::Class
DBIx::Class::Manual::*
DBIx::Class::Schema::Loader
Mailing list (Google for it)
TryCatch
Error Handling
How do you handle errors in your code?
Return error values from subroutines
sub get_object {
my ($class, $id) = @_;
if (my $obj = find_obj_in_db($id)) {
return $obj;
} else {
return;
}
}Problems
What if someone doesn't check return code?
my $obj = MyClass->get_object(100);
print $obj->name; # errorCaller assumes that $obj is a valid object
Bad things follow
Basic Exceptions
Throw an exception instead
sub get_object {
my ($class, $id) = @_;
if (my $obj = find_obj_in_db($id)) {
return $obj;
} else {
die No object found with id: $id;
}
}Now caller has to deal with exceptions
Dealing with Exceptions
Use eval to catch exceptions
my $obj = eval {
MyClass->get_object(100)
};
if ($@) {
# handle exception...
} else {
print $obj->name; # error
}Exceptions as Objects
$@ can be set to an object
sub get_object {
my ($class, $id) = @_;
if (my $obj = find_obj_in_db($id)) {
return $obj;
} else {
die MyException->new(
type => 'obj_not_found',
id => $id,
);
}
}Try Catch
TryCatch adds syntactic sugar
try {
...
}
catch ($e) {
...
}Looks a lot like many other languages
TryCatch with Scalar
try {
some_function_that_might_die();
}
catch ($e) {
if ($e =~ /some error/) {
# handle error
} else {
die $e;
}
}TryCatch with Object
try {
some_function_that_might_die();
}
catch ($e) {
if ($e->type eq 'file') {
# handle error
} else {
die $e;
}
}Better Checks
try {
some_function_that_might_die();
}
catch (My::Error $e) {
# handle error
}Even Better Checks
try {
some_function_that_might_die();
}
catch (HTTP::Error $e
where { $e->code == 404 }) {
# handle 404 error
}Multiple Checks
try {
some_function_that_might_die();
}
catch (HTTP::Error $e where { $e->code == 404 }) {
# handle 404 error
}
catch (HTTP::Error $e where { $e->code == 500 }) {
# handle 500 error
}
catch (HTTP::Error $e) {
# handle other HTTP error
}
catch ($e) {
# handle other error
}More Information
perldoc TryCatch
See also Try::Tiny
Moose
Moose
A complete modern object system for Perl 5
Based on Perl 6 object model
Built on top of Class::MOPMOP - Meta Object Protocol
Set of abstractions for components of an object system
Classes, Objects, Methods, Attributes
An example might help
Moose Example
package Point;
use Moose;
has 'x' => (isa => 'Int',
is => 'ro');
has 'y' => (isa => 'Int',
is => 'rw');
sub clear {
my $self = shift;
$self->{x} = 0;
$self->y(0);
}Understanding Moose
There's a lot going on here
use MooseLoads Moose environment
Makes our class a subclass of Moose::Object
Turns on strict and warnings
Creating Attributes
has 'x' => (isa => 'Int',
is => 'ro')Creates an attribute called 'x'Constrainted to be an integer
Read-only accessor
has 'y' => (isa => 'Int',
is => 'rw')Defining Methods
sub clear {
my $self = shift;
$self->{x} = 0;
$self->y(0);
}Standard method syntax
Uses generated method to set y
Direct hash access for x
Subclassing
package Point3D;
use Moose;
extends 'Point';
has 'z' => (isa => 'Int');
after 'clear' => sub {
my $self = shift;
$self->{z} = 0;
};Subclasses
extends 'Point'Similar to use base
Overwrites @ISA instead of appending
has 'z' => (isa = 'Int')Adds new attribute 'z'
No accessor function - private attribute
Extending Methods
after 'clear' => sub {
my $self = shift;
$self->{z} = 0;
};New clear method for subclass
Called after method for superclass
Cleaner than $self->SUPER::clear()
Creating Objects
Moose classes are used just like any other Perl class
$point = Point->new(x => 1, y => 2);
$p3d = Point3D->new(x => 1,
y => 2,
z => 3);More About Attributes
Use the has keyword to define your class's attributes
has 'first_name' => ( is => 'rw' );
Use is to define rw or ro
Omitting is gives an attribute with no accessors
Getting & Setting
By default each attribute creates a method of the same name.
Used for both getting and setting the attribute
$dave->first_name('Dave');
say $dave->first_name;
Change Accessor Name
Change accessor names using reader and writer
has 'name' => (
is => 'rw',
reader => 'get_name',
writer => 'set_name',
);See also MooseX::FollowPBP
Required Attributes
Moose class attributes are optional
Change this with required
has 'name' => (
is => 'ro',
required => 1,
);Forces constructor to expect a name
Although that name could be undef
Attribute Defaults
Set a default value for an attribute with default
has 'size' => (
is => 'rw',
default => 'medium',
);Can use a subroutine reference
has 'size' => (
is => 'rw',
default => \&rand_size,
);Attribute Properties
lazyOnly populate attribute when queried
triggerSubroutine called after the attribute is set
isaSet the type of an attribute
Many more
More Moose
Many more options
Support for concepts like delegation and roles
Powerful plugin supportMooseX::*
Lots of work going on in this area
autodie
More on Exceptions
Exceptions force callers to deal with error conditions
But you have to explicitly code to throw exceptions
open my $fh, 'args->[-1], "\n";
warn 'File: ', $e->file, "\n";
warn 'Function: ', $e->function, "\n";
warn 'Package: ', $e->package, "\n";
warn 'Caller: ', $e->caller, "\n";
warn 'Line: ', $e->line, "\n";
}Nicer Errors Too
$ perl -Mautodie -E'open my $fh, "not-there"'
Can't open($fh, 'not-there'): No such file or directory at -e line 1More Information
perldoc Fatal
perldoc autodie
autodie - The art of Klingon Programming http://perltraining.com.au/tips/2008-08-20.html
Catalyst
MVC Frameworks
MVC frameworks are a popular way to write applicationsParticularly web applications
M, V and C
ModelData storage & data access
ViewData presentation layer
ControllerBusiness logic to glue it all together
MVC Examples
Ruby on Rails
Django (Python)
Struts (Java)
CakePHP
Many examples in most languages
Perl has many options
MVC in Perl
MaypoleThe original Perl MVC framework
CGI::ApplicationSimple MVC for CGI programming
JiftyDeveloped and used by Best Practical
CatalystCurrently the popular choice
Newer MVC in Perl
Dancer
Squatting
Mojolicious
Catalyst
MVC framework in Perl
Building on other heavily-used tools
Model uses DBIx::Class
View uses Template Toolkit
These are just defaults
Can use anything you want
Simple Catalyst App
Assume we already have modelCD database from DBIx::Class section
Use catalyst.pl to create project
$ catalyst.pl CD
created "CD"
created "CD/script"
created "CD/lib"
created "CD/root"
... many more ...What Just Happened?
Catalyst just generated a lot of useful stuff for us
Test web serversStandalone and FastCGI
Configuration files
Test stubs
Helpers for creating models, views and controllers
A Working Application
We already have a working application
$ CD/script/cd_server.pl
... lots of output
[info] CD powered by Catalyst 5.7015
You can connect to your server at http://localhost:3000Of course, it doesn't do much yet
Simple Catalyst App
Next Steps
Use various helper programs to create models and views for your application
Write controller code to tie it all together
Many plugins to handle various parts of the processAuthentication
URL resolution
Session handling
etc...
Create a View
$ script/cd_create.pl view Default TT
exists "/home/dave/training/cdlib/CD/script/../lib/CD/View"
exists "/home/dave/training/cdlib/CD/script/../t"
created "/home/dave/training/cdlib/CD/script/../lib/CD/View/Default.pm"
created "/home/dave/training/cdlib/CD/script/../t/view_Default.t"Remove Default Message
In lib/CD/Controller/Root.pm
sub index :Path :Args(0) {
my ( $self, $c ) = @_;
# Hello World
$c->response_body($c->welcome_message);
}Remove response_body line
Default behaviour is to render index.tt
Need to create that
index.tt
root/index.tt
CDs
[% FOREACH cd IN [ 1 .. 10 ] %]- CD [% cd %]
[% END %]
New Front Page
Adding Data
Of course that's hard-coded data
Need to add a model class
And then more views
And some controllers
There's a lot to do
I recommend working through a tutorial
Easier Catalyst
A lot of web applications do similar things
Given a database
Produce screens to edit the data
Surely most of this can be automated
It's called Catalyst::Plugin::AutoCRUD
(Demo)
Cat::Plugin::AutoCRUD
Does a lot of work
On the fly
For every request
No security on table updates
So it's not right for every project
Very impressive though
Conclusions
There's a lot to bear in mind when writing a web app
Using the right framework can help
Catalyst is the most popular Perl framework
As powerful as any other frameworkIn any language
Lots of work still going on
Large team, active development
Recommended Book
The Definitive Guide to CatalystKieren Diment
Matt S Trout
PSGI/Plack
PSGI/Plack
PSGI is an interface between Perl web applications and web servers, and Plack is a Perl module and toolkit that contains PSGI middleware, helpers and adapters to web servers.http://plackperl.org/
PSGI/Plack
PSGI is a specification (based on Python's WSGI)
Plack is a reference implementation (based on Ruby's Rack)
The Problem
There are many ways to write web applications
There are many ways to write web applications in Perl
Each is subtly different
Hard to move an application between server architectures
Server Architectures
CGI
FastCGI
mod_perl
etc...
Frameworks
There are many Perl web application frameworks
Each creates applications in subtly different ways
Hard to port applications between them
The Goal
What if we had a specification that allowed us to easily move web applications between server architectures and frameworks
PSGI is that specification
PSGI Application
my $app = sub {
my $env = shift;
return [
200,
[ Content-Type, text/plain ],
[ Hello World ],
];
};PSGI Application
A code reference
Passed a reference to an environment hash
Returns a reference to a three-element arrayStatus code
Headers
Body
A Code Reference
my $app = sub {
my $env = shift;
return [
200,
[ Content-Type, text/plain ],
[ Hello World ],
];
};A Code Reference
my $app = sub {
my $env = shift;
return [
200,
[ Content-Type, text/plain ],
[ Hello World ],
];
};Environment Hash
my $app = sub {
my $env = shift;
return [
200,
[ Content-Type, text/plain ],
[ Hello World ],
];
};Environment Hash
my $app = sub {
my $env = shift;
return [
200,
[ Content-Type, text/plain ],
[ Hello World ],
];
};Return Array Ref
my $app = sub {
my $env = shift;
return [
200,
[ Content-Type, text/plain ],
[ Hello World ],
];
};Return Array Ref
my $app = sub {
my $env = shift;
return [
200,
[ Content-Type, text/plain ],
[ Hello World ],
];
};Running PSGI App
Put code in app.psgi
Drop in into a configured PSGI-aware web server
Browse to URL
PSGI-Aware Server
Plack contains a simple test server called plackup
$ plackup app.psgi
HTTP::Server::PSGI: Accepting connections at http://localhost:5000/Plack::Request
Plack::Request turns the environment into a request object
use Plack::Request;
use Data::Dumper;
my $app = sub {
my $req = Plack::Request->new(shift);
return [
200,
[ 'Content-type', 'text/plain' ],
[ Dumper $req ],
];
}Plack::Response
Plack::Response builds a PSGI response object
use Plack::Request;
use Plack::Response;
use Data::Dumper;
my $app = sub {
my $req = Plack::Request->new(shift);
my $res = Plack::Response->new(200);
$res->content_type('text/plain');
$res->body(Dumper $req);
return $res->finalize;
}Middleware
Middleware wraps around an application
Returns another PSGI application
Simple spec makes this easy
Plack::Middleware::*
Plack::Builder adds middleware configuration language
Middleware Example
use Plack::Builder;
use Plack::Middleware::Runtime;
my $app = sub {
my $env = shift;
return [
200,
[ 'Content-type', 'text/plain' ],
[ 'Hello world' ],
]
};
builder {
enable 'Runtime';
$app;
}Middleware Example
$ HEAD http://localhost:5000
200 OK
Date: Tue, 20 Jul 2010 20:25:52 GMT
Server: HTTP::Server::PSGI
Content-Length: 11
Content-Type: text/plain
Client-Date: Tue, 20 Jul 2010 20:25:52 GMT
Client-Peer: 127.0.0.1:5000
Client-Response-Num: 1
X-Runtime: 0.000050Middleware Example
$ HEAD http://localhost:5000
200 OK
Date: Tue, 20 Jul 2010 20:25:52 GMT
Server: HTTP::Server::PSGI
Content-Length: 11
Content-Type: text/plain
Client-Date: Tue, 20 Jul 2010 20:25:52 GMT
Client-Peer: 127.0.0.1:5000
Client-Response-Num: 1
X-Runtime: 0.000050Plack::App::*
Ready-made solutions for common situations
Plack::App::CGIBinCgi-bin replacement
Plack::App::DirectoryServe files with directory index
Plack::App::URLMapMap apps to different paths
Plack::App::*
Many more bundled with Plack
Configured using Plack::Builder
Plack::App::CGIBin
use Plack::App::CGIBin;
use Plack::Builder;
my $app = Plack::App::CGIBin->new(
root => '/var/www/cgi-bin'
)->to_app;
builder {
mount '/cgi-bin' => $app;
};Plack::App::Directory
use Plack::App::Directory;
my $app = Plack::App::Directory->new(
root => '/home/dave/Dropbox/psgi'
)->to_app;Framework Support
Many modern Perl applications already support PSGI
Catalyst, CGI::Application, Dancer, Jifty, Mason, Maypole, Mojolicious, Squatting, Web::Simple
Many more
Catalyst Support
Catalyst::Engine::PSGI
use MyApp;
MyApp->setup_engine('PSGI');
my $app = sub { MyApp->run(@_) };Also Catalyst::Helper::PSGI
script/myapp_create.pl PSGI
PSGI Server Support
Many new web servers support PSGI
Starman, Starlet, Twiggy, Corona, HTTP::Server::Simple::PSGI
Perlbal::Plugin::PSGI
PSGI Server Support
nginx support
mod_psgi
Plack::Handler::Apache2
Plack::Handler::Apache2
SetHandler perl-script
PerlResponseHandler Plack::Handler::Apache2
PerlSetVar psgi_app /path/to/app.psgiPSGI/Plack Summary
PSGI is a specification
Plack is an implementation
PSGI makes your life easier
Most of the frameworks and server you use already support PSGI
No excuse not to use it
Further Information
perldoc PSGI
perldoc Plack
http://plackperl.org/
http://blog.plackperl.org/
http://github.com/miyagawa/Plack
#plack on irc.perl.org
Modern Perl Summary
Perl has changed a lot in the last five years
Perl continues to grow and change
Perl is not a scripting language
Perl is a powerful and flexible programming language
Perl is a great way to get your job done
That's all folks
Any questions?
Click to edit the outline text formatSecond Outline LevelThird Outline LevelFourth Outline LevelFifth Outline LevelSixth Outline LevelSeventh Outline LevelEighth Outline LevelNinth Outline Level
YAPC::Europe2nd August 2010