Curscatalyst
Upcoming SlideShare
Loading in...5
×

Like this? Share it with your network

Share

Curscatalyst

  • 1,379 views
Uploaded on

 

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
    Be the first to like this
No Downloads

Views

Total Views
1,379
On Slideshare
1,376
From Embeds
3
Number of Embeds
2

Actions

Shares
Downloads
3
Comments
0
Likes
0

Embeds 3

http://www.linkedin.com 2
https://www.linkedin.com 1

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Perl web frameworks Catalyst & Mojolicious Curs avançat de Perl 2012 10/03/2012
  • 2. Perl web frameworks Hola! Diego Kuperman diegok | @freekey
  • 3. Web frameworks ~ MVC
  • 4. Router ~Dispatcher
  • 5. Rutas//prueba-curso/event/23/event/23/where/event/23-una-prueba/picture/3/event/23/picture/3/event/una-prueba/pic/3
  • 6. Rutas//prueba-curso/event/23/event/23/where/event/23-una-prueba/picture/3/event/23/picture/3/event/una-prueba/pic/3
  • 7. Controller
  • 8. Invocado por el dispatcherManipulación de capturas del routerValidacionesPegamento entre otros componentes:modelos y vistasIdealmente poco código: thin controller,fat models.
  • 9. Model ~Storage
  • 10. ModelHabitualmente base de datosLógica de negocioUso fuera de la appTests independientes de la appOtros modelos: git, api-rest, ...
  • 11. View ~Templates / Serializers
  • 12. ViewNormalmente un motor de templatesMUCHAS opciones en CPANTemplate toolkit en CatalystEP en MojoliciousSerialización: JSON, XML, YAML, ...
  • 13. Install ~CPAN
  • 14. Catalyst$ cpanm -n Catalyst::Runtime Catalyst::Devel$ cpanm -n Catalyst::View::TT Catalyst::View::JSON$ cpanm -n Catalyst::Plugin::Unicode::Encoding$ cpanm -n Catalyst::Plugin::Session$ cpanm -n Catalyst::Plugin::Session::Store::File$ cpanm -n Catalyst::Plugin::Session::State::Cookie$ cpanm -n Catalyst::Plugin::Authentication$ cpanm -n Catalyst::Plugin::Authorization::Roles$ cpanm -n Catalyst::Authentication::Store::DBIx::Class$ cpanm -n HTML::FormHandler HTML::FormHandler::Model::DBIC
  • 15. Mojolicious http://mojolicio.us The web in a box$ cpanm -n Mojolicious
  • 16. Catalyst vs Mojolicious
  • 17. Catalyst
  • 18. $ git clone git://github.com/diegok/dbic.curs.barcelona.pm.git$ cd dbic.curs.barcelona.pm$ dzil build; cpanm -n *.tar.gz; dzil clean$ git clone git://github.com/diegok/app.curs.barcelona.pm.git$ cd app.curs.barcelona.pm$ cpanm -n --installdeps .
  • 19. CatalystThe elegant MVC framework
  • 20. Catalyst Crear nueva App$ catalyst.pl MyCatAppcreated "MyCatApp"created "MyCatApp/script"created "MyCatApp/lib"created "MyCatApp/root"created "MyCatApp/root/static"...created "MyCatApp/script/mycatapp_server.pl"created "MyCatApp/script/mycatapp_test.pl"created "MyCatApp/script/mycatapp_create.pl"
  • 21. ├── Changes├── Makefile.PL├── README├── lib│ └── Curs| ├── App│ │ ├── Controller│ │ │ └── Root.pm│ │ ├── Model│ | └── View│ └── App.pm├── curs_app.conf├── curs_app.psgi
  • 22. ├── root│ ├── favicon.ico│ └── static│ └── images│ ├── ...│ └── catalyst_logo.png├── script│ ├── ...│ ├── curs_app_create.pl│ └── curs_app_server.pl└── t ├── 01app.t ├── 02pod.t └── 03podcoverage.t
  • 23. package Curs::App;use Moose;use namespace::autoclean;use Catalyst::Runtime 5.80;use Catalyst qw/ -Debug ConfigLoader Static::Simple/;extends Catalyst;our $VERSION = 0.01;__PACKAGE__->config( name => Curs::App, disable_component_resolution_regex_fallback enable_catalyst_header => 1, # Send X-Cataly);__PACKAGE__->setup();
  • 24. package Curs::App;use Moose;use namespace::autoclean;use Catalyst::Runtime 5.80;use Catalyst qw/ ConfigLoader Static::Simple/;extends Catalyst;our $VERSION = 0.01;__PACKAGE__->config( name => Curs::App, disable_component_resolution_regex_fallback enable_catalyst_header => 1, # Send X-Cataly);__PACKAGE__->setup();
  • 25. $ ./script/curs_app_server.pl -r -d[debug] Debug messages enabled[debug] Statistics enabled[debug] Loaded plugins:.-------------------------------------------------------.| Catalyst::Plugin::ConfigLoader 0.30 |-------------------------------------------------------[debug] Loaded dispatcher "Catalyst::Dispatcher"[debug] Loaded engine "Catalyst::Engine"[debug] Found home "/.../Curs-App"[debug] Loaded Config "/.../Curs-App/curs_app.conf"[debug] Loaded components:.--------------------------------------------+----------.| Class | Type |+--------------------------------------------+----------+| Curs::App::Controller::Root | instance |--------------------------------------------+----------[debug] Loaded Private actions:.-------------+-----------------------------+------------.| Private | Class | Method |+-------------+-----------------------------+------------+| /default | Curs::App::Controller::Root | default || /end | Curs::App::Controller::Root | end || /index | Curs::App::Controller::Root | index |-------------+-----------------------------+------------
  • 26. $ ./script/curs_app_server.pl -r -d[debug] Loaded Path actions:.--------------------------------+-----------------------.| Path | Private |+--------------------------------+-----------------------+| / | /index || /... | /default |--------------------------------+-----------------------[info] Curs::App powered by Catalyst 5.90010HTTP::Server::PSGI: Accepting connections at http://0:3000/
  • 27. package Curs::App::Controller::Root;use Moose; use namespace::autoclean;BEGIN { extends Catalyst::Controller }__PACKAGE__->config(namespace => );sub index :Path :Args(0) { my ( $self, $c ) = @_; $c->response->body($c->welcome_message);}sub default :Path { my ( $self, $c ) = @_; $c->response->body(Page not found); $c->response->status(404);}sub end : ActionClass(RenderView) {}
  • 28. TieneRouter + DispatcherStatic::SimpleController RootAcción por defecto
  • 29. aún no tiene...Vista/sModelo/s+ControllersNinguna gracia!
  • 30. Contexto ~ $c
  • 31. Catalyst::Request$c->request$c->req # alias$c->req->params->{foo};$c->req->cookies->{name};$c->req->headers->content_type;$c->req->base;$c->req->uri_with( { page => 3 } );
  • 32. Catalyst::Response$c->response$c->res # alias$c->res->body(Hello World);$c->res->status(404);$c->res->redirect(http://barcelona.pm);# CGI::Simple::Cookie$c->res->cookies->{foo} = { value => 123 };
  • 33. Catalyst::Log$c->log$c->log->debug(Something happened);$c->log->info(Something you should know);
  • 34. Stash$c->stash( key => value );$c->stash( key ); # value$c->stash->{key} = [1..10];$c->stash->{key}; # [1..10] Dura un request-response completo Paso de datos entre componentes
  • 35. Routes ~Controller actions
  • 36. Nuevo Controller$ ./script/curs_app_create.pl controller Example exists ".../Curs-App/script/lib/Curs/App/Controller" exists ".../Curs-App/script/t"created ".../Curs-App/lib/Curs/App/Controller/Example.pm"created ".../Curs-App/t/controller_Example.t"
  • 37. lib/Curs/App/Controller/Example.pmpackage Curs::App::Controller::Example;use Moose; use namespace::autoclean;BEGIN {extends Catalyst::Controller; }# /examplesub index :Path :Args(0) { my ( $self, $c ) = @_; $c->res->body(Example index match!);}
  • 38. Controller ActionsLiteral match (:Path)Root-level (:Global) = Path(/...)Namespace-prefixed (:Local) = Path(.../)Restricción de argumentos (:Args)
  • 39. /example/cero/...sub cero :Local { my ( $self, $c, @args ) = @_; $c->res->body(Args: . join , , @args);} /example/unosub uno :Local :Args(0) { my ( $self, $c ) = @_; $c->res->body(:Local :Args(0));}
  • 40. /example/dossub dos :Path(dos) :Args(0) { my ( $self, $c ) = @_; $c->res->body(":Path(dos) :Args(0)");} /example/tressub tres :Path(/example/tres) :Args(0) { my ( $self, $c ) = @_; $c->res->body(":Path(/example/tres) :Args(}
  • 41. /hola/mundosub cuatro :Path(/hola) :Args(1) { my ( $self, $c, $arg1 ) = @_; $c->res->body("Hola $arg1!");}
  • 42. Controller Actions Pattern-match :Regex() & :LocalRegex()
  • 43. /item23/order32sub cinco :Regex(^item(d+)/order(d+)$) { my ( $self, $c ) = @_; my $item = $c->req->captures->[0]; my $order = $c->req->captures->[1]; $c->res->body( "(cinco) Item: $item | Order: $order" );}
  • 44. /example/item23/order32sub seis :LocalRegex(^item(d+)/order(d+)$) { my ( $self, $c ) = @_; my $item = $c->req->captures->[0]; my $order = $c->req->captures->[1]; $c->res->body( "(seis) Item: $item | Order: $order" );}
  • 45. Controller Actions Privadas & control flow :Private forward() & detach()
  • 46. sub now :Local :Args(0) { my ( $self, $c ) = @_; $c->forward(stash_now); $c->detach(say_now); $c->log->debug(ouch!);}sub stash_now :Private { my ( $self, $c ) = @_; $c->stash( now => DateTime->now );}sub say_now :Private { my ( $self, $c ) = @_; $c->res->body($c->stash->{now});}
  • 47. Built-in special actions
  • 48. Default controller actionsub default : Path {} Como default, con mas precedenciasub index :Path Args(0) {}
  • 49. Antes de la acción, solo una vezsub begin :Private {} Despues de la acción, solo una vezsub end :Private {}Despues de begin, de menos especifico a mas especificosub auto :Private {} Si retorna false se salta hasta end()
  • 50. Chained actions :Chained
  • 51. sub with_now : PathPart(example/now) Chained( / ) CaptureArgs( 0 ) { my ( $self, $c ) = @_; $c->forward(stash_now);}sub show_now : PathPart(show) Chained( with_now ) Args( 0 ) { my ( $self, $c ) = @_; $c->detach(say_now);}
  • 52. Chained es MUY potente, pero antes tenemos queañadir algunas cosas mas...
  • 53. VistasTemplate toolkit + JSON
  • 54. $ script/curs_app_create.pl view Web TTexists ".../Curs-App/script/../lib/Curs/App/View"exists ".../Curs-App/script/../t"created ".../Curs-App/script/../lib/Curs/App/View/Web.pm"created ".../Curs-App/script/../t/view_Web.t"
  • 55. lib/Curs/App/View/Web.pmCurs::App::View::Web;use Moose;extends Catalyst::View::TT;__PACKAGE__->config( TEMPLATE_EXTENSION => .tt, CATALYST_VAR => c, TIMER => 0, ENCODING => utf-8 WRAPPER => layout, render_die => 1,);1;
  • 56. lib/Curs/App.pm__PACKAGE__->config( # ... View::Web => { INCLUDE_PATH => [ __PACKAGE__->path_to(root, src), __PACKAGE__->path_to(root, lib), ], },);
  • 57. root/lib/layout<!DOCTYPE HTML><html lang="en-us"> <head> <meta http-equiv="Content-type" content="text/ <title>Curs avançat de Perl 2012</title> <link rel="stylesheet" href="/css/style.css" t </head> <body> [% content %] </body></html>
  • 58. TT y layout en su sitio,hora de cambiar la home
  • 59. root/src/index.tt<h1>[% message %]</h1> lib/Curs/App/Controller/Root.pmsub index :Path :Args(0) { my ( $self, $c ) = @_; $c->stash( message => Hola mundo!, template => index.tt );}
  • 60. $ ./script/curs_app_create.pl view JSON JSON exists "lib/Curs/App/View" exists "t/"created "lib/Curs/App/View/JSON.pm"created "t/view_JSON.t"
  • 61. lib/Curs/App.pm__PACKAGE__->config({ ... View::JSON => { expose_stash => json, # defaults to ev }, default_view => Web,});
  • 62. Uso de View::JSONsub status :Path(/status) :Args(0) { my ( $self, $c ) = @_; $c->stash( json => { value => testing } ); $c->forward(View::JSON);}
  • 63. Modelo DBIC
  • 64. Curs::Schema$ script/curs_app_create.pl model DB DBIC::Schema Curs::Schemaexists ".../Curs-App/script/../lib/Curs/App/Model"exists ".../Curs-App/script/../t"created ".../Curs-App/script/../lib/Curs/App/Model/DB.pm"created ".../Curs-App/script/../t/model_DB.t"
  • 65. Config por defecto curs_app.confname Curs::App<Model::DB> connect_info dbi:SQLite:dbname=curs_schema connect_info connect_info <connect_info> sqlite_unicode 1 RaiseError 1 </connect_info></Model::DB>
  • 66. Deploy!$ ./script/schema_deploy.plCreating sql/Curs-Schema-1-SQLite.sql => done.Making initial deploy (ddbb has no version) => done.
  • 67. Nuestro schema es un componente más ahora!sub action :Local { my ( $self, $c ) = @_; $c->res->body( $c->model(DB::User)->first->email );}
  • 68. Authentication &Authorization
  • 69. Catalyst::Plugin::Authentication & Catalyst::Plugin:Authorization::Roles + Catalyst::Plugin::Session
  • 70. lib/Curs/App.pmuse Catalyst qw/ ... Session Session::State::Cookie Session::Store::File Authentication Authorization::Roles/;
  • 71. __PACKAGE__->config( ... Plugin::Authentication => { default_realm => users, realms => { users => { credential => { class => Password, password_field => password, password_type => self_check, }, store => { class => DBIx::Class, user_model => DB::User, role_relation => roles, role_field => name, id_field => email } } }
  • 72. } } },);Nuevos metodos en la app$c->authenticate( email => $email, password => $pwd);$c->user_exists;$c->user;
  • 73. Todo listoNecesitamos un form para login :-(
  • 74. HTML::FormHandler al rescate! :-)
  • 75. lib/Curs/App/Form/Login.pmpackage Curs::App::Form::Login;use HTML::FormHandler::Moose;extends HTML::FormHandler;use Email::Valid;has_field email => ( type => Text, required => 1, apply => [{ check => sub { Email::Valid->address( $_[0] ) }, message => Must be a valid email addres }]);
  • 76. lib/Curs/App/Form/Login.pmhas_field password => ( type => Password, required => 1);has_field submit => ( type => Submit, value => Login);
  • 77. Ahora sí!
  • 78. Un controller nuevo para auth$ ./script/curs_app_create.pl controller Auth...
  • 79. lib/Curs/App/Controller/Auth.pmpackage Curs::App::Controller::Auth;use Moose; use namespace::autoclean;BEGIN {extends Catalyst::Controller; }use Curs::App::Form::Login;
  • 80. sub login :Path(/login) Args(0) { my ( $self, $c ) = @_; my $form = Curs::App::Form::Login->new(); my $creds = { email => $form->value->{email}, password => $form->value->{password} }; if ( $form->process( params => $c->req->params if ( $c->authenticate( $creds ) ) { $c->detach(after_login_redirect); } else { $form->field(password)->add_error( Inva } } $c->stash( template => auth/login.tt, form => $form );}
  • 81. root/src/auth/login.tt<div id="login"> [% form.render %]</div>
  • 82. =head2 need_login Ensure user exists on the chain.=cutsub need_login :PathPart( ) Chained( / ) CaptureArgs( 0 ) { my ( $self, $c ) = @_; unless ( $c->user_exists ) { $c->session->{after_login_path} = / . $c-> $c->res->redirect( $c->uri_for_action( $c->controller(Auth) ->action_for(login) ) ); $c->detach; }}
  • 83. =head2 need_role_admin Ensure user with the admin role.=cutsub need_role_admin :PathPart(admin) Chained(need_login) CaptureArgs(0) { my ( $self, $c ) = @_; unless ( $c->check_user_roles( admin ) ) { $c->res->body(You need admin role for this $c->detach(); }}
  • 84. En otro controller... perdido en otra galaxia ...
  • 85. =head2 element_chainBase chain for actions related to one user=cutsub element_chain :PathPart(user) Chained(/auth/need_login) CaptureArgs(1) { my ( $self, $c, $user_id ) = @_; $c->stash( user => $c->model(DB::User) ->find( $user_id ) ); unless ( $c->stash->{user} ) { $c->detach( /error/element_not_found, [ u }}
  • 86. sub view :PathPart() Chained(element_chain) Args(0) { my ( $self, $c ) = @_; $c->stash( template => user/view.tt );}sub delete :PathPart() Chained(element_chain) Args(0) { my ( $self, $c ) = @_; $c->stash->{user}->delete; # ...}
  • 87. Plugin vsTraitFor
  • 88. Plugin global vsPlugin for component
  • 89. TraitFor Controller Role para el controller
  • 90. package Catalyst::TraitFor::Controller::WithDateuse MooseX::MethodAttributes::Role;use namespace::autoclean;use DateTime;has stash_key => ( is => ro, default => datafter auto => sub { my ( $self, $c ) = @_; $c->stash( $self->stash_key => DateTime->now};sub auto : Private { 1 }
  • 91. Traits locales
  • 92. package Curs::App::TraitFor::Controller::WithDBIuse MooseX::MethodAttributes::Role;use namespace::autoclean;require model_name;require base_chain;has stash_key => ( is => ro, default => sub { lc @{[split /::/, shift->model_name ]}[- });
  • 93. ...sub item :PathPart() Chained(base_chain) Cap my ( $self, $c, $id ) = @_; $c->stash->{ $self->stash_key } = $c->model( $self->model_name )->find($ || $c->detach(missing);}sub missing { my ( $self, $c ) = @_; $c->res->code(404); $c->res->body(Not found!);}1;
  • 94. A consumir!
  • 95. package Curs::App::Controller::Event;use Moose; use namespace::autoclean;BEGIN {extends Catalyst::Controller}has model_name => ( is => ro, default => DB::with Curs::App::TraitFor::Controller::WithDBICsub base_chain :PathPart(event) Chained(/) CaptureArgs(1) {}sub delete :PathPart(delete) Chained(item) A my ( $self, $c ) = @_; $c->stash->{event}->delete;}
  • 96. https://metacpan.org/search?q=catalyst 896 results
  • 97. Plack(ya lo estamos usando)
  • 98. $ cpanm -n Starman...$ starman curs_app.psgi2012/03/10-11:25:36 Starman::Server(type Net::Server::PreFork) starting! pid(73661)Binding to TCP port 5000 on host *Setting gid to "20 20 20 204 100 98 81 80 79 61
  • 99. Más Catalyst IRC#catalyst en irc.perl.org.#catalyst-dev en irc.perl.org (desarrollo). Mailing listshttp://lists.scsys.co.uk/mailman/listinfo/catalysthttp://lists.scsys.co.uk/mailman/listinfo/catalyst-dev
  • 100. Manual Ejercicioshttps://metacpan.org/module/Catalyst::ManualAñadir un metodo (API) que deje verdatos de UN usuario en JSON:/user/1/json Extra: vista json que devuelva array de usuarios (sin repetir codigo)Añadir una vista que liste los eventos(Creados en la práctica anterior)Crear una acción (solo para admins), unformulario y su plantilla para crear unevento y otra para editarlo.