SlideShare uma empresa Scribd logo
1 de 103
Baixar para ler offline
Perl web frameworks
   Catalyst & Mojolicious
     Curs avançat de Perl 2012
            10/03/2012
Perl web frameworks
          Hola!
      Diego Kuperman
      diegok | @freekey
Web frameworks
       ~
     MVC
Router
    ~
Dispatcher
Rutas
/
/prueba-curso
/event/23
/event/23/where
/event/23-una-prueba/picture/3
/event/23/picture/3
/event/una-prueba/pic/3
Rutas
/
/prueba-curso
/event/23
/event/23/where
/event/23-una-prueba/picture/3
/event/23/picture/3
/event/una-prueba/pic/3
Controller
Invocado por el dispatcher
Manipulación de capturas del router

Validaciones
Pegamento entre otros componentes:
modelos y vistas
Idealmente poco código: thin controller,
fat models.
Model
   ~
Storage
Model
Habitualmente base de datos
Lógica de negocio

Uso fuera de la app
Tests independientes de la app
Otros modelos: git, api-rest, ...
View
          ~
Templates / Serializers
View
Normalmente un motor de templates

MUCHAS opciones en CPAN
Template toolkit en Catalyst
EP en Mojolicious
Serialización: JSON, XML, YAML, ...
Install
  ~
CPAN
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
Mojolicious
                   http://mojolicio.us
                   The web in a box
$ cpanm -n Mojolicious
Catalyst vs Mojolicious
Catalyst
$ 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 .
Catalyst
The elegant MVC framework
Catalyst
                   Crear nueva App
$ catalyst.pl MyCatApp
created "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"
├──   Changes
├──   Makefile.PL
├──   README
├──   lib
│     └── Curs
|         ├── App
│         │   ├── Controller
│         │   │   └── Root.pm
│         │   ├── Model
│         |   └── View
│         └── App.pm
├──   curs_app.conf
├──   curs_app.psgi
├──   root
│     ├── favicon.ico
│     └── static
│         └── images
│             ├── ...
│             └── catalyst_logo.png
├──   script
│     ├── ...
│     ├── curs_app_create.pl
│     └── curs_app_server.pl
└──   t
      ├── 01app.t
      ├── 02pod.t
      └── 03podcoverage.t
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();
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();
$ ./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      |
'-------------+-----------------------------+------------'
$ ./script/curs_app_server.pl -r -d

[debug] Loaded Path actions:
.--------------------------------+-----------------------.
| Path                           | Private               |
+--------------------------------+-----------------------+
| /                              | /index                |
| /...                           | /default              |
'--------------------------------+-----------------------'
[info] Curs::App powered by Catalyst 5.90010
HTTP::Server::PSGI: Accepting connections at http://0:3000/
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') {}
Tiene
Router + Dispatcher
Static::Simple

Controller Root
Acción por defecto
aún no tiene...
Vista/s
Modelo/s

+Controllers
Ninguna gracia!
Contexto
  ~
  $c
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 } );
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' };
Catalyst::Log
$c->log
$c->log->debug('Something happened');
$c->log->info('Something you should know');
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
Routes
        ~
Controller actions
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"
lib/Curs/App/Controller/Example.pm
package Curs::App::Controller::Example;
use Moose; use namespace::autoclean;
BEGIN {extends 'Catalyst::Controller'; }
# /example
sub index :Path :Args(0) {
    my ( $self, $c ) = @_;
    $c->res->body('Example index match!');
}
Controller Actions
Literal match (:Path)

Root-level (:Global) = Path('/...')
Namespace-prefixed (:Local) = Path('.../')
Restricción de argumentos (:Args)
/example/cero/...
sub cero :Local {
    my ( $self, $c, @args ) = @_;
    $c->res->body('Args: ' . join ', ', @args);
}


           /example/uno
sub uno :Local :Args(0) {
    my ( $self, $c ) = @_;
    $c->res->body(':Local :Args(0)');
}
/example/dos
sub dos :Path('dos') :Args(0) {
    my ( $self, $c ) = @_;
    $c->res->body(":Path('dos') :Args(0)");
}


           /example/tres
sub tres :Path('/example/tres') :Args(0) {
    my ( $self, $c ) = @_;
    $c->res->body(":Path('/example/tres') :Args(
}
/hola/mundo
sub cuatro :Path('/hola') :Args(1) {
    my ( $self, $c, $arg1 ) = @_;
    $c->res->body("Hola $arg1!");
}
Controller Actions
     Pattern-match
    :Regex() & :LocalRegex()
/item23/order32
sub 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"
    );
}
/example/item23/order32
sub 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"
    );
}
Controller Actions
  Privadas & control flow
            :Private
      forward() & detach()
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});
}
Built-in special actions
Default controller action
sub default : Path {}

     Como default, con mas precedencia
sub index :Path Args(0) {}
Antes de la acción, solo una vez
sub begin :Private {}

     Despues de la acción, solo una vez
sub end :Private {}

Despues de begin, de menos especifico a mas
                especifico
sub auto :Private {}

     Si retorna false se salta hasta end()
Chained actions
    :Chained
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');
}
Chained es MUY potente,
 pero antes tenemos que
añadir algunas cosas mas...
Vistas
Template toolkit
      +
   JSON
$ script/curs_app_create.pl view Web TT
exists ".../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"
lib/Curs/App/View/Web.pm
Curs::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;
lib/Curs/App.pm
__PACKAGE__->config(
 # ...
 'View::Web' => {
  INCLUDE_PATH => [
    __PACKAGE__->path_to('root', 'src'),
    __PACKAGE__->path_to('root', 'lib'),
  ],
 },
);
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>
TT y layout en su sitio,
hora de cambiar la home
root/src/index.tt
<h1>[% message %]</h1>

       lib/Curs/App/Controller/Root.pm
sub index :Path :Args(0) {
    my ( $self, $c ) = @_;
    $c->stash(
        message => 'Hola mundo!',
        template => 'index.tt'
    );
}
$ ./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"
lib/Curs/App.pm
__PACKAGE__->config({
    ...
    'View::JSON' => {
        expose_stash => 'json', # defaults to ev
    },
    default_view => 'Web',
});
Uso de View::JSON
sub status :Path('/status') :Args(0) {
    my ( $self, $c ) = @_;
    $c->stash(
        json => { value => 'testing' }
    );
    $c->forward('View::JSON');
}
Modelo
 DBIC
Curs::Schema
$ script/curs_app_create.pl model DB DBIC::Schema Curs::Schema
exists ".../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"
Config por defecto
                 curs_app.conf
name 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>
Deploy!
$ ./script/schema_deploy.pl
Creating sql/Curs-Schema-1-SQLite.sql => done.
Making initial deploy (ddbb has no version) => done.
Nuestro schema es un
    componente más ahora!
sub action :Local {
  my ( $self, $c ) = @_;

    $c->res->body(
      $c->model('DB::User')->first->email
    );
}
Authentication
     &
Authorization
Catalyst::Plugin::Authentication
                       &
   Catalyst::Plugin:Authorization::Roles
            + Catalyst::Plugin::Session
lib/Curs/App.pm
use Catalyst qw/
    ...

     Session
     Session::State::Cookie
     Session::Store::File

     Authentication
     Authorization::Roles
/;
__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'
          }
        }
      }
}
       }
     },
);

Nuevos metodos en la app
$c->authenticate(
    email    => $email,
    password => $pwd
);
$c->user_exists;

$c->user;
Todo listo
Necesitamos un form para login
             :-(
HTML::FormHandler
  al rescate! :-)
lib/Curs/App/Form/Login.pm
package 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
    }]
);
lib/Curs/App/Form/Login.pm
has_field 'password' => (
    type => 'Password',
    required => 1
);

has_field 'submit'   => (
    type => 'Submit',
    value => 'Login'
);
Ahora sí!
Un controller nuevo para
             auth
$ ./script/curs_app_create.pl controller Auth
...
lib/Curs/App/Controller/Auth.pm
package Curs::App::Controller::Auth;
use Moose; use namespace::autoclean;
BEGIN {extends 'Catalyst::Controller'; }
use Curs::App::Form::Login;
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
  );
}
root/src/auth/login.tt
<div id="login">
  [% form.render %]
</div>
=head2 need_login
 Ensure user exists on the chain.
=cut
sub 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;
    }
}
=head2 need_role_admin
 Ensure user with the admin role.
=cut
sub 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();
  }
}
En otro controller
... perdido en otra galaxia ...
=head2 element_chain
Base chain for actions related to one user
=cut
sub 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
    }
}
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;
  # ...
}
Plugin
   vs
TraitFor
Plugin global
        vs
Plugin for component
TraitFor Controller
  Role para el controller
package Catalyst::TraitFor::Controller::WithDate
use MooseX::MethodAttributes::Role;
use namespace::autoclean;
use DateTime;

has 'stash_key' => ( is => 'ro', default => 'dat
after 'auto' => sub {
    my ( $self, $c ) = @_;
    $c->stash( $self->stash_key => DateTime->now
};
sub auto : Private { 1 }
Trait's locales
package Curs::App::TraitFor::Controller::WithDBI
use MooseX::MethodAttributes::Role;
use namespace::autoclean;

require 'model_name';
require 'base_chain';
has stash_key => (
    is      => 'ro',
    default => sub {
        lc @{[split /::/, shift->model_name ]}[-
    }
);
...
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;
A consumir!
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::WithDBIC'

sub base_chain :PathPart('event')
                Chained('/') CaptureArgs(1) {}

sub delete :PathPart('delete') Chained('item') A
    my ( $self, $c ) = @_;
    $c->stash->{event}->delete;
}
https://metacpan.org/search?q=catalyst

          896 results
Plack
(ya lo estamos usando)
$ cpanm -n Starman
...
$ starman curs_app.psgi
2012/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
Más Catalyst
                  IRC
#catalyst en irc.perl.org.

#catalyst-dev en irc.perl.org (desarrollo).
              Mailing lists
http://lists.scsys.co.uk/mailman/listinfo/catalyst

http://lists.scsys.co.uk/mailman/listinfo/catalyst-
dev
Manual
           Ejercicios
https://metacpan.org/module/Catalyst::Manual
Añadir un metodo (API) que deje ver
datos 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), un
formulario y su plantilla para crear un
evento y otra para editarlo.

Mais conteúdo relacionado

Mais procurados

優しいWAFの作り方
優しいWAFの作り方優しいWAFの作り方
優しいWAFの作り方
techmemo
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
guoqing75
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
patter
 
Smolder @Silex
Smolder @SilexSmolder @Silex
Smolder @Silex
Jeen Lee
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
yiditushe
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
jsmith92
 
AnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time webAnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time web
clkao
 

Mais procurados (20)

Perl5i
Perl5iPerl5i
Perl5i
 
Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!Hypers and Gathers and Takes! Oh my!
Hypers and Gathers and Takes! Oh my!
 
Ansible leveraging 2.0
Ansible leveraging 2.0Ansible leveraging 2.0
Ansible leveraging 2.0
 
How to develop modern web application framework
How to develop modern web application frameworkHow to develop modern web application framework
How to develop modern web application framework
 
WordPress 運用を支える Perl
WordPress 運用を支える PerlWordPress 運用を支える Perl
WordPress 運用を支える Perl
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6
 
All I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web FrameworkAll I Need to Know I Learned by Writing My Own Web Framework
All I Need to Know I Learned by Writing My Own Web Framework
 
優しいWAFの作り方
優しいWAFの作り方優しいWAFの作り方
優しいWAFの作り方
 
Hacking ansible
Hacking ansibleHacking ansible
Hacking ansible
 
Silex Cheat Sheet
Silex Cheat SheetSilex Cheat Sheet
Silex Cheat Sheet
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
 
symfony on action - WebTech 207
symfony on action - WebTech 207symfony on action - WebTech 207
symfony on action - WebTech 207
 
Smolder @Silex
Smolder @SilexSmolder @Silex
Smolder @Silex
 
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
The worst Ruby codes I’ve seen in my life - RubyKaigi 2015
 
Facebook的缓存系统
Facebook的缓存系统Facebook的缓存系统
Facebook的缓存系统
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
SPL: The Missing Link in Development
SPL: The Missing Link in DevelopmentSPL: The Missing Link in Development
SPL: The Missing Link in Development
 
AnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time webAnyMQ, Hippie, and the real-time web
AnyMQ, Hippie, and the real-time web
 
BASH Variables Part 1: Basic Interpolation
BASH Variables Part 1: Basic InterpolationBASH Variables Part 1: Basic Interpolation
BASH Variables Part 1: Basic Interpolation
 
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK MeetupScaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
Scaling Symfony2 apps with RabbitMQ - Symfony UK Meetup
 

Semelhante a Perl web frameworks

Railsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshareRailsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshare
tomcopeland
 
Web applications with Catalyst
Web applications with CatalystWeb applications with Catalyst
Web applications with Catalyst
svilen.ivanov
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Tatsuhiko Miyagawa
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
Yehuda Katz
 
Yapc::Asia 2008 Tokyo - Easy system administration programming with a framewo...
Yapc::Asia 2008 Tokyo - Easy system administration programming with a framewo...Yapc::Asia 2008 Tokyo - Easy system administration programming with a framewo...
Yapc::Asia 2008 Tokyo - Easy system administration programming with a framewo...
Gosuke Miyashita
 

Semelhante a Perl web frameworks (20)

Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From Iusethis
 
Burn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websitesBurn down the silos! Helping dev and ops gel on high availability websites
Burn down the silos! Helping dev and ops gel on high availability websites
 
Catalyst MVC
Catalyst MVCCatalyst MVC
Catalyst MVC
 
Railsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshareRailsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshare
 
Api Design
Api DesignApi Design
Api Design
 
Web applications with Catalyst
Web applications with CatalystWeb applications with Catalyst
Web applications with Catalyst
 
Writing Pluggable Software
Writing Pluggable SoftwareWriting Pluggable Software
Writing Pluggable Software
 
Hacking Movable Type
Hacking Movable TypeHacking Movable Type
Hacking Movable Type
 
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQueryRemedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
Remedie: Building a desktop app with HTTP::Engine, SQLite and jQuery
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hope
 
Getting Started with Capistrano
Getting Started with CapistranoGetting Started with Capistrano
Getting Started with Capistrano
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Yapc::Asia 2008 Tokyo - Easy system administration programming with a framewo...
Yapc::Asia 2008 Tokyo - Easy system administration programming with a framewo...Yapc::Asia 2008 Tokyo - Easy system administration programming with a framewo...
Yapc::Asia 2008 Tokyo - Easy system administration programming with a framewo...
 
Keep it simple web development stack
Keep it simple web development stackKeep it simple web development stack
Keep it simple web development stack
 
Quality Use Of Plugin
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of Plugin
 
Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!
 
PSGI and Plack from first principles
PSGI and Plack from first principlesPSGI and Plack from first principles
PSGI and Plack from first principles
 
Catalyst patterns-yapc-eu-2016
Catalyst patterns-yapc-eu-2016Catalyst patterns-yapc-eu-2016
Catalyst patterns-yapc-eu-2016
 
PerlDancer for Perlers (FOSDEM 2011)
PerlDancer for Perlers (FOSDEM 2011)PerlDancer for Perlers (FOSDEM 2011)
PerlDancer for Perlers (FOSDEM 2011)
 
Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)Best Practices in Plugin Development (WordCamp Seattle)
Best Practices in Plugin Development (WordCamp Seattle)
 

Último

+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
?#DUbAI#??##{{(☎️+971_581248768%)**%*]'#abortion pills for sale in dubai@
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
Joaquim Jorge
 

Último (20)

2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
Boost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdfBoost Fertility New Invention Ups Success Rates.pdf
Boost Fertility New Invention Ups Success Rates.pdf
 
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemkeProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
ProductAnonymous-April2024-WinProductDiscovery-MelissaKlemke
 
Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024Axa Assurance Maroc - Insurer Innovation Award 2024
Axa Assurance Maroc - Insurer Innovation Award 2024
 
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
+971581248768>> SAFE AND ORIGINAL ABORTION PILLS FOR SALE IN DUBAI AND ABUDHA...
 
Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024Partners Life - Insurer Innovation Award 2024
Partners Life - Insurer Innovation Award 2024
 
Data Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt RobisonData Cloud, More than a CDP by Matt Robison
Data Cloud, More than a CDP by Matt Robison
 
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, AdobeApidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
Apidays New York 2024 - Scaling API-first by Ian Reasor and Radu Cotescu, Adobe
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
presentation ICT roal in 21st century education
presentation ICT roal in 21st century educationpresentation ICT roal in 21st century education
presentation ICT roal in 21st century education
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?What Are The Drone Anti-jamming Systems Technology?
What Are The Drone Anti-jamming Systems Technology?
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
Apidays Singapore 2024 - Building Digital Trust in a Digital Economy by Veron...
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
Artificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and MythsArtificial Intelligence: Facts and Myths
Artificial Intelligence: Facts and Myths
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)Powerful Google developer tools for immediate impact! (2023-24 C)
Powerful Google developer tools for immediate impact! (2023-24 C)
 

Perl web frameworks

  • 1. Perl web frameworks Catalyst & Mojolicious Curs avançat de Perl 2012 10/03/2012
  • 2. Perl web frameworks Hola! Diego Kuperman diegok | @freekey
  • 4.
  • 5. Router ~ Dispatcher
  • 9. Invocado por el dispatcher Manipulación de capturas del router Validaciones Pegamento entre otros componentes: modelos y vistas Idealmente poco código: thin controller, fat models.
  • 10. Model ~ Storage
  • 11. Model Habitualmente base de datos Lógica de negocio Uso fuera de la app Tests independientes de la app Otros modelos: git, api-rest, ...
  • 12. View ~ Templates / Serializers
  • 13. View Normalmente un motor de templates MUCHAS opciones en CPAN Template toolkit en Catalyst EP en Mojolicious Serialización: JSON, XML, YAML, ...
  • 15. 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
  • 16. Mojolicious http://mojolicio.us The web in a box $ cpanm -n Mojolicious
  • 19. $ 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 .
  • 21. Catalyst Crear nueva App $ catalyst.pl MyCatApp created "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"
  • 22. ├── Changes ├── Makefile.PL ├── README ├── lib │ └── Curs | ├── App │ │ ├── Controller │ │ │ └── Root.pm │ │ ├── Model │ | └── View │ └── App.pm ├── curs_app.conf ├── curs_app.psgi
  • 23. ├── root │ ├── favicon.ico │ └── static │ └── images │ ├── ... │ └── catalyst_logo.png ├── script │ ├── ... │ ├── curs_app_create.pl │ └── curs_app_server.pl └── t ├── 01app.t ├── 02pod.t └── 03podcoverage.t
  • 24. 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();
  • 25. 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();
  • 26. $ ./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 | '-------------+-----------------------------+------------'
  • 27. $ ./script/curs_app_server.pl -r -d [debug] Loaded Path actions: .--------------------------------+-----------------------. | Path | Private | +--------------------------------+-----------------------+ | / | /index | | /... | /default | '--------------------------------+-----------------------' [info] Curs::App powered by Catalyst 5.90010 HTTP::Server::PSGI: Accepting connections at http://0:3000/
  • 28.
  • 29. 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') {}
  • 34. 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' };
  • 36. 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
  • 37. Routes ~ Controller actions
  • 38. 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"
  • 39. lib/Curs/App/Controller/Example.pm package Curs::App::Controller::Example; use Moose; use namespace::autoclean; BEGIN {extends 'Catalyst::Controller'; } # /example sub index :Path :Args(0) { my ( $self, $c ) = @_; $c->res->body('Example index match!'); }
  • 40. Controller Actions Literal match (:Path) Root-level (:Global) = Path('/...') Namespace-prefixed (:Local) = Path('.../') Restricción de argumentos (:Args)
  • 41. /example/cero/... sub cero :Local { my ( $self, $c, @args ) = @_; $c->res->body('Args: ' . join ', ', @args); } /example/uno sub uno :Local :Args(0) { my ( $self, $c ) = @_; $c->res->body(':Local :Args(0)'); }
  • 42. /example/dos sub dos :Path('dos') :Args(0) { my ( $self, $c ) = @_; $c->res->body(":Path('dos') :Args(0)"); } /example/tres sub tres :Path('/example/tres') :Args(0) { my ( $self, $c ) = @_; $c->res->body(":Path('/example/tres') :Args( }
  • 43. /hola/mundo sub cuatro :Path('/hola') :Args(1) { my ( $self, $c, $arg1 ) = @_; $c->res->body("Hola $arg1!"); }
  • 44. Controller Actions Pattern-match :Regex() & :LocalRegex()
  • 45. /item23/order32 sub 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" ); }
  • 46. /example/item23/order32 sub 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" ); }
  • 47. Controller Actions Privadas & control flow :Private forward() & detach()
  • 48. 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}); }
  • 50. Default controller action sub default : Path {} Como default, con mas precedencia sub index :Path Args(0) {}
  • 51. Antes de la acción, solo una vez sub begin :Private {} Despues de la acción, solo una vez sub end :Private {} Despues de begin, de menos especifico a mas especifico sub auto :Private {} Si retorna false se salta hasta end()
  • 52. Chained actions :Chained
  • 53. 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'); }
  • 54. Chained es MUY potente, pero antes tenemos que añadir algunas cosas mas...
  • 56. $ script/curs_app_create.pl view Web TT exists ".../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"
  • 57. lib/Curs/App/View/Web.pm Curs::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;
  • 58. lib/Curs/App.pm __PACKAGE__->config( # ... 'View::Web' => { INCLUDE_PATH => [ __PACKAGE__->path_to('root', 'src'), __PACKAGE__->path_to('root', 'lib'), ], }, );
  • 59. 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>
  • 60. TT y layout en su sitio, hora de cambiar la home
  • 61. root/src/index.tt <h1>[% message %]</h1> lib/Curs/App/Controller/Root.pm sub index :Path :Args(0) { my ( $self, $c ) = @_; $c->stash( message => 'Hola mundo!', template => 'index.tt' ); }
  • 62.
  • 63. $ ./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"
  • 64. lib/Curs/App.pm __PACKAGE__->config({ ... 'View::JSON' => { expose_stash => 'json', # defaults to ev }, default_view => 'Web', });
  • 65. Uso de View::JSON sub status :Path('/status') :Args(0) { my ( $self, $c ) = @_; $c->stash( json => { value => 'testing' } ); $c->forward('View::JSON'); }
  • 67. Curs::Schema $ script/curs_app_create.pl model DB DBIC::Schema Curs::Schema exists ".../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"
  • 68. Config por defecto curs_app.conf name 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>
  • 69. Deploy! $ ./script/schema_deploy.pl Creating sql/Curs-Schema-1-SQLite.sql => done. Making initial deploy (ddbb has no version) => done.
  • 70. Nuestro schema es un componente más ahora! sub action :Local { my ( $self, $c ) = @_; $c->res->body( $c->model('DB::User')->first->email ); }
  • 71. Authentication & Authorization
  • 72. Catalyst::Plugin::Authentication & Catalyst::Plugin:Authorization::Roles + Catalyst::Plugin::Session
  • 73. lib/Curs/App.pm use Catalyst qw/ ... Session Session::State::Cookie Session::Store::File Authentication Authorization::Roles /;
  • 74. __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' } } }
  • 75. } } }, ); Nuevos metodos en la app $c->authenticate( email => $email, password => $pwd ); $c->user_exists; $c->user;
  • 76. Todo listo Necesitamos un form para login :-(
  • 77. HTML::FormHandler al rescate! :-)
  • 78. lib/Curs/App/Form/Login.pm package 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 }] );
  • 79. lib/Curs/App/Form/Login.pm has_field 'password' => ( type => 'Password', required => 1 ); has_field 'submit' => ( type => 'Submit', value => 'Login' );
  • 81. Un controller nuevo para auth $ ./script/curs_app_create.pl controller Auth ...
  • 82. lib/Curs/App/Controller/Auth.pm package Curs::App::Controller::Auth; use Moose; use namespace::autoclean; BEGIN {extends 'Catalyst::Controller'; } use Curs::App::Form::Login;
  • 83. 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 ); }
  • 84. root/src/auth/login.tt <div id="login"> [% form.render %] </div>
  • 85. =head2 need_login Ensure user exists on the chain. =cut sub 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; } }
  • 86. =head2 need_role_admin Ensure user with the admin role. =cut sub 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(); } }
  • 87. En otro controller ... perdido en otra galaxia ...
  • 88. =head2 element_chain Base chain for actions related to one user =cut sub 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 } }
  • 89. 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; # ... }
  • 90. Plugin vs TraitFor
  • 91. Plugin global vs Plugin for component
  • 92. TraitFor Controller Role para el controller
  • 93. package Catalyst::TraitFor::Controller::WithDate use MooseX::MethodAttributes::Role; use namespace::autoclean; use DateTime; has 'stash_key' => ( is => 'ro', default => 'dat after 'auto' => sub { my ( $self, $c ) = @_; $c->stash( $self->stash_key => DateTime->now }; sub auto : Private { 1 }
  • 95. package Curs::App::TraitFor::Controller::WithDBI use MooseX::MethodAttributes::Role; use namespace::autoclean; require 'model_name'; require 'base_chain'; has stash_key => ( is => 'ro', default => sub { lc @{[split /::/, shift->model_name ]}[- } );
  • 96. ... 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;
  • 98. 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::WithDBIC' sub base_chain :PathPart('event') Chained('/') CaptureArgs(1) {} sub delete :PathPart('delete') Chained('item') A my ( $self, $c ) = @_; $c->stash->{event}->delete; }
  • 101. $ cpanm -n Starman ... $ starman curs_app.psgi 2012/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
  • 102. Más Catalyst IRC #catalyst en irc.perl.org. #catalyst-dev en irc.perl.org (desarrollo). Mailing lists http://lists.scsys.co.uk/mailman/listinfo/catalyst http://lists.scsys.co.uk/mailman/listinfo/catalyst- dev
  • 103. Manual Ejercicios https://metacpan.org/module/Catalyst::Manual Añadir un metodo (API) que deje ver datos 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), un formulario y su plantilla para crear un evento y otra para editarlo.