Web Applications in 2009 Jonathan Rockway http://blog.jrock.us/ ---- Where are we? ---- Where are we? Why are we here? ---- Where are we? Why are we here? What can we do while we're here? ---- Where are we? Why are we here? What can we do while we're here? Where to next? ---- 42 ---- Web applications ---- 1994 The web was cool ---- What did Perl do? ---- Easy to program Easy to start programming ---- Better than shell or c ---- (Later) Lots of modules Easy to continue programming ---- (Time passes) ---- 2000 The web is not cool ---- Fast growth Legacy code (PERL sucks) ---- What did other languages do? ---- PHP HTML and oh-yeah-some-code (Even easier to get started) ---- Python "Maintainability" ---- Easy to continue programming Impossible to do something bad!1!! ---- Ruby Perl-but-fun ---- Other languages Millions of dollars worth of marketing ---- Where is Perl? "Boring", "dead". ---- 2009 The web is cool again ---- But applications are much bigger Times have changed ---- Perl can be cool again ---- PHP No modules No sane abstractions (mysql_real_quote_string) ---- Python There's only one way to do it. ---- Python There's only one way to do it: wrong. (Their way, not yours.) ---- Ruby Can anyone say monkey-patch? (Fun trick, but now you can't use modules anymore. Not so fun.) ---- Java Frameworks for building frameworks to edit XML frameworks for building XML editing building frameworks? (Also, factory factory factories.) ---- Perl now ---- Learned from its own mistakes before others have had the chance ---- Now we can do new things ---- Perl is in a great position for new web apps ---- (We are trying to be good, not trying to be "not Perl".) ---- So what do we have now? ---- A great community ---- Evolution ---- New modules every day ---- Free code That you don't write That makes your apps better! ---- New ideas every ... week? ---- Plan for the future Know that bad stuff will get better ---- Conferences every month ---- (Perl's "oral tradition" keeps it alive and fresh.) ---- New paradigms every year ---- New paradigms every so often ---- Moose ---- A lot of excitement ---- Why use Moose and Catalyst? ---- * meta layer * built-in server * clean OO * chained dispatch * attributes ---- No! ---- You use it because thousands of other people do ---- They find bugs in your app before you do ---- They add features for free ---- They help you debug at 3am before a demo ---- People are the best feature ---- And oh yeah, the code is nice too. ---- Perl was cool, made web easy ---- Times changed, other things didn't. (Web 1.5?) ---- Perl is changing ---- Now a great fit for web apps both simple and extremely complex ---- Not dead. ---- Today ---- What cool things can you do with Perl? ---- Making complex apps ---- Divide the complex app into small parts. ---- The less dependencies the better ---- Moose make the hard parts easy ---- (Moose also make the easy parts easy) ---- Web Application ---- The important part is the data model. ---- Make sure that every thing your app does can be done without the HTML and HTTP ---- (Single best thing you can do for your app. Moose or not.) ---- Minimize dependencies ---- (Not doing this is what made PERL suck.) ---- Bad example: package Foo; use Moose; has entire_application => ( is => 'rw', isa => 'Catalyst', required => 1, ); ---- sub foo { my $self = shift; $self->entire_application ->session ->{foo}{bar} = 42; } ---- No! ---- Do this instead: has session => ( is => 'ro', isa => 'Session', required => 1, ); ---- sub foo { $self ->session ->{foo}{bar} = 42; } ---- Why? ---- Reuse ---- Don't tie parts of an app to the app ---- Why? "It's only for this app" ---- Wrong. ---- It's for your app ---- AND for the test suite ---- (You may not reuse components between "applications", but you will within the same app.) ---- OK, boring and not shiny. But very important. ---- Next step: avoid tests. ---- Let your app check itself for corruption. ---- Type constraints ---- MooseX::Types ---- package Foo; use Moose; use Moose::Util::TypeConstraints; subtype 'Foo' => as 'Str' => where { /foo/ }; has 'thing' => ( isa => 'Foo' ); ---- Sort of works ---- Problem arises when you want to reuse Foo ---- package TypeLibrary; use MooseX::Types -declare => ['Foo']; use MooseX::Types::Moose qw(Str); subtype Foo, as Str, where { /foo/ }; ---- package Module1; use Moose; use TypeLibrary qw(Foo); has foo => ( isa => Foo, ); ---- Types end up with a super-secret name. You don't need to care, just use the sub. ---- Also, super special magic: use MooseX::Types::Moose qw(ArrayRef Str); # ... has foo => ( isa => ArrayRef[Str] ); ---- One problem: package Class; use MooseX::Types qw(Str); Class->new->Str; # Uh....... ---- Need a namespace cleaner ---- use namespace::clean; ---- Pretends things above it were never imported... except in its scope ---- package Class; use Moose; use MooseX::Types qw(Str); use namespace::clean -except => 'meta'; ---- Newer way: use Moose; use namespace::autoclean; use MX::Types; # order no longer relevant ---- Non-method utils: package Class; use Moose; sub add { $_[0] + $_[1] } use namespace::clean -except => 'meta'; sub method { add($self->a, $self->b) } ---- Class->new->method; # ok Class->add; # not possible Class::add; # not possible ---- What about that sugar earlier? ArrayRef[Str] ---- Thanks to mst++ and rafl++ we have lots of new syntax ---- MooseX::Declare ---- use MooseX::Declare; class Bar extends Foo with Role { has ...; method quux(Str $a, Int $b){ $self->... } } ---- TryCatch use TryCatch; try { die 'OH NOES!!1'; } catch ( $e ) { say "Got some exception $e" } ---- TryCatch use TryCatch; try { die 'OH NOES!!1'; } catch ( Str $e ) { say "Got some exception $e" } ---- TryCatch use TryCatch; try { die 'OH NOES!!'; } catch ( Str $e where { /NOES/ } ){ say "Got some exception $e" } ---- use MooseX::Declare; use MooseX::MultiMethods; use MooseX::Types -declare => [qw/A B/]; use namespace::autoclean; ---- class Test { has 'a' => ( is => 'ro', isa => A, required => 1, ); has 'b' => ( is => 'ro', isa => B, required => 1, ); ---- multi method add_to(A $a){ $self->a( $self->a + $a ); } multi method add_to(B $a){ $self->b( $self->b + $b ); } } ---- All this sugar is not a web app! ---- Important anyway ---- Too bloated for small apps? ---- No, always use Moose ---- One-liners should be Moose ---- perl -ne 'print if /foo/' ---- Impossible to test! ---- use MooseX::Declare; class Grep with MooseX::Getopt with MooseX::Runnable { has 'pattern' => ( is => 'ro', isa => 'RegexpRef', required => 1, ); ---- method select_line( Str $line ) { my $rx = $self->pattern return $line =~ /$rx/; } ---- method run() { while( my $line = <> ){ print $line if $self->select_line( $line ); } return 0; } ---- Advantages? ---- mx-run $ mx-run -Ilib Grep --help $ mx-run -Ilib Grep --pattern foo $ mx-run -Ilib +PAR Grep $ ./grep --help # run anywhere ---- Testing ---- my $grep = Grep-> new( pattern => qr/foo/ ); ok $grep->select_line('foo'); ok !$grep->select_line('bar'); ---- More web-like stuff? ---- HTTP::Engine ---- CGI replacement ---- Write an app once run on any server ---- HTTP, HTTP::POE, FastCGI, mod_perl, CGI, ... ---- my $e = HTTP::Engine->new( interface => { module => 'ServerSimple', args => { port => '4242', }, request_handler => \&handler, }, ); $e->run; ---- sub handler { my $req = shift; return HTTP::Engine::Response->new( body => 'Hello, world.', ); } ---- Easy as can be ---- Add routing with Path::Router ---- my $router = Path::Router->new; $router->add_route('blog' => ( defaults => { controller => 'blog', action => 'index', }, target => sub { whatever }, )); ---- $router->add_route('blog/:year/:month/:day' => ( defaults => { controller => 'blog', action => 'show_date', }, validations => { year => qr/\d{4}/, month => 'NumericMonth', day => subtype('Int' => where { $_ <= 31 }), } )); ---- # get introspectable match object $router->match('/blog/2008/4/17'); $router->match($req->uri->path); ---- Roll-your-own web framework ---- Modular modules No "Frameworks" ---- JavaScript ---- It can't be ignored anymore. ---- So, the CPAN doesn't. ---- JS $ cpanp install JS::jQuery $ js-cpan jQuery.js $ ln -s `js-cpan jQuery.js` jQuery.js ---- Depend on JavaScript libraries Locate and install them ---- Problems with JavaScript Too much roll-your-own RPC ---- Perl solution, JSORB ---- __ /\ \ /\_\ ____ ___ _ __ \ \ \____ \/\ \ /',__\ / __`\ /\`'__\ \ \ '__`\ \ \ \ /\__, `\/\ \L\ \\ \ \/ \ \ \L\ \ _\ \ \ \/\____/\ \____/ \ \_\ \ \_,__/ /\ \_\ \ \/___/ \/___/ \/_/ \/___/ \ \____/ \/___/ ---- package Math::Simple use Moose; sub add { $_[0] + $_[1] } ---- my $d = JSORB::Dispatcher::Path->new( namespace => JSORB::Reflector::Package->new( introspector => Math::Simple->meta, procedure_list => [ { name => 'add', spec => [ ('Int', 'Int') => 'Int' ] } ], )->namespace, ); ---- Make a server my $s = JSORB::Server::Simple->new( dispatcher => $d, port => 8080, ); $s->run; ---- var c = new JSORB.Client ({ base_url : 'http://localhost:8080/', }) c.call({ method : '/math/simple/add', params : [ 2, 2 ] }, function (result) { alert(result) }); ---- Use from Catalyst package MyApp::Controller; use Moose; BEGIN { extend 'Catalyst::Controller' }; __PACKAGE__->config( 'Action::JSORB' => $d, ); sub call : Local : ActionClass(JSORB) {} # also WithInvocant ---- (Framework independence!) ---- More app stuff... ---- My app is slow. ---- My app is slow. Why? ---- Profile ---- Perl profilers suck ---- $ perl -d:DProf myapp.pl Segmentation fault (core dumped) ---- Worry no more Devel::NYTProf ---- $ perl -d:NYTProf myapp.pl $ nytprofhtml ---- Profiling techniques * reuse ---- my $slow = Slow::Module->new; for( 1..100 ){ $slow->request; } ---- Persistent apps need to quit ---- New "pet technique" Event loops ---- Old: POE New: AnyEvent (runs on POE) ---- Why event loops? ---- Save memory. "Scale". ---- Flow Compose blocking operations into one my $done = AnyEvent->condvar; ... $done->wait; ---- $done->cb( sub { say "Got ", $done->wait; }) ---- my $h = AnyEvent::Handle->new( fh => $fh, ); $h->push_read( json => sub { $done->send( $_[1]) }, ); ---- How is this helpful? ---- Components ---- Live REPL in your app ---- Non-blocking DB access ---- Almost mandatory for Perl IMO. ---- Try now? (use HTTP::Engine::Interface::POE) ---- More data management ---- KiokuDB ---- Store any in-memory structure, forever. ---- use KiokuDB; my $k = KiokuDB->connect('hash'); my $s = $k->new_scope; my $id = $k->store({ anything => 'in perl' }); $k->load( $id ); ---- KiokuDB has made my web apps simpler and faster ---- REPLs = Debugger 2.0 Devel::REPL ---- Extensions ---- Carp::REPL REPL on "die" ---- CatalystX::REPL Start up Cat app, be a REPL ---- Stylish Generic REPL-over-TCP Emacs bindings! ---- Stuff I shouldn't talk about ---- Ernst Describe Moose classes Translate them to "interesting" forms. ---- package Class; use Ernst; has password => ( traits => ['MetaDescription'], is => 'ro', isa => Authen::Passphrase, description => { traits => ['CSS'], class => 'password', }, ); ---- Template::Refine No code in templates! No templates in code! ---- use Template::Refine::Fragment; use Template::Refine::Utils qw(replace_text); print Template::Refine::Fragment-> new_from_string('Foo: ')-> process( replace_text { $text } '#foo' )-> render; ---- Haven't had time to play with this more. So you should instead. ---- Perl now Easy to write complex apps ---- Web apps are complex ---- Modern Perl make complex apps fun ---- Where are we going? ---- Unified Framework ---- Framework = bad ---- "Universal ways to write extensions" ---- Unified Framework For runnable Perl things For persistent Perl apps For web apps ---- Runnable Perl things ---- MooseX::Runnable use FindBin qw($Bin); use lib "$Bin/../lib"; use Foo; Foo->new_with_options->run; ---- Tedious, one script to rule them all: mx-run ---- mx-run --help ---- mx-run +Daemonize Script mx-run +PAR Script mx-run +Persist Script ---- Next layer "Eventful" ---- Eventful Componentized persistent Perl apps ---- Logging Configuration REPL HTTP::Server ---- Web stuff HTTP::Engine Already working ---- Frameworks Use more than one in one app ---- Catalyst Continuity plain Path::Router Ox (Ernst::Web) ---- Future of Perl Unity ---- Unity Don't compromise, always have exactly what you want and ONLY what you want ---- Interested in more? Join irc: #moose, #catalyst, #epo, ... ---- Ask me at the hackathon! ----