SlideShare une entreprise Scribd logo
1  sur  103
Télécharger pour lire hors ligne
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.

Contenu connexe

Tendances

Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkJeremy Kendall
 
Keeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro frameworkKeeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro frameworkJeremy Kendall
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hopeMarcus Ramberg
 
Perl: Hate it for the Right Reasons
Perl: Hate it for the Right ReasonsPerl: Hate it for the Right Reasons
Perl: Hate it for the Right ReasonsMatt Follett
 
Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Anatoly Sharifulin
 
Any event intro
Any event introAny event intro
Any event introqiang
 
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...Arc & Codementor
 
Slim RedBeanPHP and Knockout
Slim RedBeanPHP and KnockoutSlim RedBeanPHP and Knockout
Slim RedBeanPHP and KnockoutVic Metcalfe
 
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)Dotan Dimet
 
A reviravolta do desenvolvimento web
A reviravolta do desenvolvimento webA reviravolta do desenvolvimento web
A reviravolta do desenvolvimento webWallace Reis
 
Operation Oriented Web Applications / Yokohama pm7
Operation Oriented Web Applications / Yokohama pm7Operation Oriented Web Applications / Yokohama pm7
Operation Oriented Web Applications / Yokohama pm7Masahiro Nagano
 
Mojolicious: what works and what doesn't
Mojolicious: what works and what doesn'tMojolicious: what works and what doesn't
Mojolicious: what works and what doesn'tCosimo Streppone
 
YAPC::Asia 2010 Twitter解析サービス
YAPC::Asia 2010 Twitter解析サービスYAPC::Asia 2010 Twitter解析サービス
YAPC::Asia 2010 Twitter解析サービスYusuke Wada
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkJeremy Kendall
 
The promise of asynchronous PHP
The promise of asynchronous PHPThe promise of asynchronous PHP
The promise of asynchronous PHPWim Godden
 
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 webclkao
 

Tendances (20)

Keeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro frameworkKeeping it small: Getting to know the Slim micro framework
Keeping it small: Getting to know the Slim micro framework
 
Keeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro frameworkKeeping it small - Getting to know the Slim PHP micro framework
Keeping it small - Getting to know the Slim PHP micro framework
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hope
 
Perl: Hate it for the Right Reasons
Perl: Hate it for the Right ReasonsPerl: Hate it for the Right Reasons
Perl: Hate it for the Right Reasons
 
Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!Mojolicious. Веб в коробке!
Mojolicious. Веб в коробке!
 
Any event intro
Any event introAny event intro
Any event intro
 
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
Building Modern and Secure PHP Applications – Codementor Office Hours with Be...
 
Slim RedBeanPHP and Knockout
Slim RedBeanPHP and KnockoutSlim RedBeanPHP and Knockout
Slim RedBeanPHP and Knockout
 
Perl5i
Perl5iPerl5i
Perl5i
 
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
Mojolicious - Perl Framework for the Real-Time Web (Lightning Talk)
 
A reviravolta do desenvolvimento web
A reviravolta do desenvolvimento webA reviravolta do desenvolvimento web
A reviravolta do desenvolvimento web
 
Operation Oriented Web Applications / Yokohama pm7
Operation Oriented Web Applications / Yokohama pm7Operation Oriented Web Applications / Yokohama pm7
Operation Oriented Web Applications / Yokohama pm7
 
Mojolicious: what works and what doesn't
Mojolicious: what works and what doesn'tMojolicious: what works and what doesn't
Mojolicious: what works and what doesn't
 
YAPC::Asia 2010 Twitter解析サービス
YAPC::Asia 2010 Twitter解析サービスYAPC::Asia 2010 Twitter解析サービス
YAPC::Asia 2010 Twitter解析サービス
 
PHP5.5 is Here
PHP5.5 is HerePHP5.5 is Here
PHP5.5 is Here
 
Keeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro FrameworkKeeping it Small: Getting to know the Slim Micro Framework
Keeping it Small: Getting to know the Slim Micro Framework
 
The promise of asynchronous PHP
The promise of asynchronous PHPThe promise of asynchronous PHP
The promise of asynchronous PHP
 
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
 
Perl Web Client
Perl Web ClientPerl Web Client
Perl Web Client
 
Blog Hacks 2011
Blog Hacks 2011Blog Hacks 2011
Blog Hacks 2011
 

Similaire à Curscatalyst

Bag Of Tricks From Iusethis
Bag Of Tricks From IusethisBag Of Tricks From Iusethis
Bag Of Tricks From IusethisMarcus Ramberg
 
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 websitesLindsay Holmwood
 
Railsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slideshareRailsconf2011 deployment tips_for_slideshare
Railsconf2011 deployment tips_for_slidesharetomcopeland
 
Api Design
Api DesignApi Design
Api Designsartak
 
Web applications with Catalyst
Web applications with CatalystWeb applications with Catalyst
Web applications with Catalystsvilen.ivanov
 
WordPress 運用を支える Perl
WordPress 運用を支える PerlWordPress 運用を支える Perl
WordPress 運用を支える Perl鉄次 尾形
 
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 jQueryTatsuhiko Miyagawa
 
Getting Started with Capistrano
Getting Started with CapistranoGetting Started with Capistrano
Getting Started with CapistranoLaunchAny
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda 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
 
Keep it simple web development stack
Keep it simple web development stackKeep it simple web development stack
Keep it simple web development stackEric Ahn
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐいHisateru Tanaka
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebookguoqing75
 
Quality Use Of Plugin
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of PluginYasuo Harada
 
PSGI and Plack from first principles
PSGI and Plack from first principlesPSGI and Plack from first principles
PSGI and Plack from first principlesPerl Careers
 
Catalyst patterns-yapc-eu-2016
Catalyst patterns-yapc-eu-2016Catalyst patterns-yapc-eu-2016
Catalyst patterns-yapc-eu-2016John Napiorkowski
 
PerlDancer for Perlers (FOSDEM 2011)
PerlDancer for Perlers (FOSDEM 2011)PerlDancer for Perlers (FOSDEM 2011)
PerlDancer for Perlers (FOSDEM 2011)xSawyer
 

Similaire à Curscatalyst (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
 
WordPress 運用を支える Perl
WordPress 運用を支える PerlWordPress 運用を支える Perl
WordPress 運用を支える Perl
 
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
 
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
 
関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい関西PHP勉強会 php5.4つまみぐい
関西PHP勉強会 php5.4つまみぐい
 
4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook4069180 Caching Performance Lessons From Facebook
4069180 Caching Performance Lessons From Facebook
 
Quality Use Of Plugin
Quality Use Of PluginQuality Use Of Plugin
Quality Use Of Plugin
 
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)
 

Dernier

Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfAddepto
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 3652toLead Limited
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebUiPathCommunity
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embeddingZilliz
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024The Digital Insurer
 

Dernier (20)

Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Gen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdfGen AI in Business - Global Trends Report 2024.pdf
Gen AI in Business - Global Trends Report 2024.pdf
 
Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365Ensuring Technical Readiness For Copilot in Microsoft 365
Ensuring Technical Readiness For Copilot in Microsoft 365
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Dev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio WebDev Dives: Streamline document processing with UiPath Studio Web
Dev Dives: Streamline document processing with UiPath Studio Web
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
Training state-of-the-art general text embedding
Training state-of-the-art general text embeddingTraining state-of-the-art general text embedding
Training state-of-the-art general text embedding
 
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptxE-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
E-Vehicle_Hacking_by_Parul Sharma_null_owasp.pptx
 
My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024My INSURER PTE LTD - Insurtech Innovation Award 2024
My INSURER PTE LTD - Insurtech Innovation Award 2024
 

Curscatalyst

  • 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.