Javier E. Pérez P. Noviembre 2007 Desarrollo de aplicaciones web usando Catalyst y jQuery
Puntos a tratar ¿Qué es Catalyst? Ventajas de su uso. Requisitos de instalación. Creando un proyecto. Estructura de proyecto. Creación de: Controlador, Modelo, Vista. Agregando módulos. Conexión a base de datos (DBIC). Trabajando con plantillas (TT, TTSite) Trabajando con formularios (FormBuilder). Uso de sessiones. Autorización, autenticación. Depuración. Consejos. Proyectos nacionales desarrollados usando catalyst.
¿Qué es Catalyst? Framework Web escrito usando perl. MVC. Unicode. Servidor de pruebas integrado.
Metodología MVC Modelo De donde se almacenan los datos. Generalmente base de datos. Vista La presentación de los datos. HTML, JSON, RSS, XML, etc.  Controlador Quién maneja las transacciones entre la solicitud del usuario, el proceso y salida de información. Catalyst itself
Ventajas de su uso
Ventajas de su uso Despacho del URL, se trabaja basado en segmentos en vez de querystring's. http://www.google.com/search ?hl=es&client=iceweasel-a&q=catalyst&btnG=Buscar  http://www.administracion.com/usuario ?id=11222333&accion=eliminar QueryString http://www.google.com/search/ lenguale/es/cliente/iceweasel-a/q/catalyst/Buscar  http://www.administracion.com/usuario /11222333/eliminar Basado en segmentos (segment based)
Está respaldado por la gran cantidad de módulos que hospeda cpan. “ there's nothing magical about catalyst, it doesn't get in your way,  it just dispatches urls to actions” Ventajas de su uso use PDF::CreateSimple; sub pdf : Local { my ($self,$c,$mensaje) = @_; # Heredamos el módulo como lo haríamos con cualquier aplicación CGI my $pdfFile = PDF::CreateSimple->new("root/reporte.pdf",undef,'LETTER'); # Pasamos algunos parametros (Esto no es catalyst, es perl) $pdfFile->drawText($mensaje,'Verdana',10,200,450,'black',3); $pdfFile->drawText('Generado con Catalyst','Verdana',10,200,400,'black',3); $pdfFile->drawImage('root/images/catalyst-logo.png',250,300); # esto no envía el pdf para descargar, sino que lo guarda en el disco $pdfFile->closeFile;  # Ahora enviamos al navegador a que muestre el archivo recién guardado $c->res->redirect("/archivo.pdf"); }
Por supuesto, TIMTOWTDI (There Is More Than One Way To Do It)
Requisitos de instalación Paquetes en debian / ubuntu : libcatalyst-perl libcatalyst-modules-perl  Módulos usando cpan: Catalyst El resto que se necesite.  Por ejemplo, en la shell:  cpan CGI::FormBuilder ó usar dh_make para convertir modulos de perl a paquetes debian y así instalarlos.
Estructura establecida de los directorios manteniendo un orden.
Creando un proyecto Creamos proyecto $ catalyst.pl proyecto Nos cambiamos de directorio de trabajo al recien creado (nombre del proyecto) $ cd proyecto Corremos el servidor de pruebas $ perl script/proyecto_server.pl
Creando controlador perl script/sistap_create.pl  controller   unesco_area lib/sistap/Controller/unesco_area.pm package   sistap::Controller::unesco_area; use strict; use warnings; use base  'Catalyst::Controller'; sub  index : Private { my  ( $self, $c ) = @_; $c->response->body( 'Hola mundo' ); } sub  saludo : Local { my  ( $self, $c ) = @_; $c->response->body( 'Hola mundo... de nuevo' ); } 1;
Métodos de despacho Local  (: Local {}) Reconoce el nombre de la acción como primer argumento del controlador. Path  (: Path('foo/bar') { }) Se especifica una ruta absoluta a despachar. Global  (: Global { }) Despacha el primer nivel (como si fuera controlador) Private  (: Private {} ) Especial para funciones reservadas en catalyst (default, index, begin, end, auto) , no es despachada por url. Regex  ( : Regex('^item(\d+)/orden(\d+)$') { } ) Despacha según una expresión regular dada (en todo el sistema) No usa ModRewrite para esto. item4/orden243  es capturada con esa expresión. LocalRegex  ( : LocalRegex('^widget(\d+)$') { } ) Igual que “Regex” pero solo es interpretada en el controlador donde está definida. widget23  es reconocido con la expresión del ejemplo. Chained Se realiza una cadea entre diferentes acciones CaptureArgs() y Args() Se combina con Chained para reconocer la cadena sub saludo  : Local  { my ( $self, $c ) = @_; $c->res->body('dvst'); }
Metodos de despacho Chained / CaptureArgs() / Args() sub  wiki : PathPart('wiki') Chained('/') CaptureArgs(1) { my  ( $self, $c, $page_name ) = @_; # carga la página de nombre $page_name y coloca el objeto en el stash   $c->stash->{var1} = $page_name; } sub  rev : PathPart('rev') Chained('wiki') CaptureArgs(1) { my  ( $self, $c, $revision_id ) = @_; #  Usa el objeto de página que está en el stash y obtiene el número  # de revisión $revision_id.   $c->stash->{var2} = $revision_id  ; } sub  view : PathPart Chained('rev') Args(0) { my  ( $self, $c ) = @_; #  Muestra la revisión de la página en pantalla, my $salida ; $salida =  "Esta pantalla muestra la revision: "  . $c->stash->{var2} ; $salida .=  " de la pagina: "  .  $c->stash->{var1} ; $c->res->body($salida); } URL: http://localhost:3000/wiki/principal/rev/4/view  Salida por pantalla: Esta pantalla muestra la revision: 4 de la pagina: principal sub  view : PathPart Chained('wiki') Args(0) { my  ( $self, $c ) = @_; #  Muestra la revisión de la página en pantalla, my $salida ; $salida =  "Esta pantalla muestra la revision: "  . $c->stash->{var2} ; $salida .=  " de la pagina: "  .  $c->stash->{var1} ; $c->res->body($salida); }
Creando un modelo  (DBIx::Class -> Catalyst::Model::DBIC::Schema) Necesitamos esquema. (archivo lib/sistapDB.pm) package   sistapDB ; =head1 NAME  sistapDB - DBIC Schema Class =cut # Nuetro esquema necesita heredar desde 'DBIx::Class::Schema' use base   qw/DBIx::Class::Schema/ ; # Se necesitan cargas las clases de modelo de base de datos acá __PACKAGE__->load_classes({ sistapDB => [qw/ unesco_area unesco_subarea unesco_categoria /] }); 1 ;
Creando un modelo Se crea una clase por tabla  (archivo lib/sistapDB/unesco_area.pm) ó usamos DBIx::Class::Schema::Loader package   sistapDB::unesco_area; use base  qw/DBIx::Class/;  # Se cargan componentes requeridos por DBIC __PACKAGE__->load_components( qw/PK::Auto Core/ ); # Se asigna el nombre de la base de datos  __PACKAGE__->table( 'sta_unesco_area' ); # Se listan los campos de la tabla __PACKAGE__->add_columns( qw/id codigo nombre descripcion activo / ); # Se indica la llave primaria de la tabla __PACKAGE__->set_primary_key( qw/id/ ); # Asignamos las relaciones  __PACKAGE__->has_many(  subareas => 'sistapDB::unesco_subarea','id_unesco_area' ); 1 ;
Creando un modelo Resultado :  (Archivo: lib/sistap/Model/sistapDB) Script helper :  .script/sistap_create.pl model  sistapDB  DBIC::Schema  \ sistapDB   dbi:Pg:dbname=sistap 'usuario' 'clave' '{ AutoCommit => 1 } ' package DBIC::Model:: sistapDB ; use strict; use base 'Catalyst::Model::DBIC::Schema'; __PACKAGE__->config( schema_class => ' sistapDB ', connect_info => [ 'dbi:Pg:dbname=sistap', 'usuario', 'clave', { AutoCommit => 1 },   ], );
Trabajando con relaciones package sistapDB::unesco_area; #... Se hereda clase ; Carga de componentes ; nombre de la tabla ; nombre columnas ; llave primaria __PACKAGE__->add_columns(qw/id_area nombre/); # empezamos a declarar las relaciones a nivel de ORM __PACKAGE__->has_many( subareas => 'sistapDB::unesco_subarea',' id_area '); package sistapDB::unesco_subarea; #... igual que arriba __PACKAGE__->add_columns(qw/id_subarea  id_area  nombre/); # empezamos a declarar las relaciones a nivel de ORM __PACKAGE__->belongs_to( area => 'sistapDB::unesco_area',' id_area '); __PACKAGE__->has_many( categorias => 'sistapDB::unesco_subarea',' id_subarea '); package sistapDB::unesco_categoria; #... igual que arriba __PACKAGE__->add_columns(qw/id_categoria  id_subarea  nombre/); # empezamos a declarar las relaciones a nivel de ORM __PACKAGE__->belongs_to( subarea => 'sistapDB::unesco_subarea',' id_subarea ');
Usando DBIC en el controlador Métodos más comunes: find  ( SELECT .. LIMIT 1 ) : Obtiene un (1) registro (hash) según patrón de busqueda $area =  $c->model(“sistapDB::unesco_area”)->find(3); $persona =  c->model(“sistapDB::personas”)->find({ nombre => { ILIKE => '%javier%' }}) search  (SELECT *)  : Obtiene un arreglo de registros @productos =  $c->model(“sistapDB::productos”)->search({ codigo => 've' , tipo => 'abc' }); create  (INSERT)  : Crea un nuevo registro según el hash pasado. my $campos = { codigo => $c->req->param("codigo"), nombre => $c->req->param(“nombre”)}; $registro =  $c->model(“sistapDB::productos”)->create($campos); # al asignar la creación del registro a una variable, se obtiene el ResourceSet de la operación $registro->id ; # se obtiene el id del registro recien insertado. update : Actualiza el ResourceSet  $equipo = $c->model(“sistapDB::equipos”)->find(3); $equipo->tipo(“ups”); $equipo->dominio(“administracion”); $equipo->update; delete  : Elimina los registros del resourceSet $c->model(“sistapDB::equipos”)->search({tipo => 'ups'})->delete;
Vistas TTSite Template Toolkit JSON PHP ClearSilver (yahoo, google) ... Predefiniendo una vista.
Vistas (TTSite -> Catalyst::Helper::View::TTSite) ./lib/config/ Configuración de variables (colores, rutas predefinidas, etc) a ser usadas. ./lib/site Plantillas fuentes footer : Pie de página heade r: Encabezado (h1 con nombre de página) html : Esqueleto base (Incluye ttsite.css) layout : Cuerpo (body) del documento. wrapper : Quién se encarga de acoplarlos. ./src Plantillas predefinidas. perl script/sistap_create view TTSite TTSite . |--  lib |  |--  config |  |  |-- col |  |  |-- main |  |  `-- url |  `--  site |  |-- footer |  |-- header |  |-- html |  |-- layout |  `-- wrapper `---  src |-- error.tt2 |-- message.tt2 |-- ttsite.css `-- welcome.tt2
Vistas (Template Toolkit) perl script/sistap_create view TT TT Por defecto, las plantillas se almacenan en root $c->stash->{variable} && [% variable %]
Vistas (JSON) Catalyst::View::JSON perl script/sistap_create view JSON JSON my  @estados = $c->model ( "sistapDB::paises" )->find($id_pais)->estados( undef ,{ order_by  => ' nombre ' }) ; $c->stash ->{estados} = [ map  { {  id_estado  =>  $_->id_estado  ,  nombre_estado  =>  $_->nombre  } }  @estados  ]; $c->forward (' sistap::View::JSON '); my  @estados = $c->model ( "sistapDB::paises" )->find($id_pais)->estados()->all ; $c->stash ->{estados} =  @estados  ; $c->forward (' sistap::View::JSON ');
Vistas Predefiniendo una vista sistap.yml Al trabajar con varias vistas, si no se especifica cual usar por defecto, podemos tener problemas. --- name: sistap default_view: TT authentication: ...
Generación de formularios Catalyst::Controller::FormBuilder
Generación de formularios Se compone en varios archivos para mayor mantenimiento y abstrabción de conceptos. Detalle de formulario root/forms/nombre/comun.fb Controlador lib/sistap/Controller/nombre.pm Plantilla  root/src/nombre/comun.tt
FormBuilder en Catalyst  (CGI::FormBuilder) Descripción del formulario (root/forms/unesco_subarea/comun.fb) Formato yaml. Usen espacios, no tabs. Definición de elementos y sus atributos. Validaciones (Cliente y Servidor) Facilmente aplicable a plantillas. name: unesco_subarea method: post action: /unesco_subarea/operaciones fields: tOperacion: type: hidden id: id: hdn_id type: hidden id_unesco_area: label : Area  type: select id: txt_id_unesco_area disabled : disabled  autocomplete: off codigo: id: txt_codigo disabled : disabled  autocomplete: off required : 1 nombre: id: txt_nombre disabled : disabled  autocomplete: off required: 1  validate : NAME
FormBuilder en Catalyst  (CGI::FormBuilder) Controlador (lib/sistap/Controller/unesco_subarea) package sistap::Controller::unesco_subarea; use strict; use warnings; use base 'Catalyst::Controller ::FormBuilder '; ... sub principal : Path('/mantenimiento/unesco_subarea')  Form('unesco_subarea/comun')  { my ($self, $c) = @_ ; my $form = $self->formbuilder; $form->field( name => 'id_unesco_area', options => [ map { { $_->id_area => $_->nombre } } $c->model(“sistapDB::unesco_area”)->all ] ); if ($form->submitted && $form->validate){ $c->model(“sistapDB::unesco_subarea”)->create($form->fields) ; } }
FormBuilder en Catalyst  (CGI::FormBuilder) Plantilla (root/src/unesco_subarea/comun.tt) ó [% FormBuilder.render %] [% FormBuilder.start -%] [% formFuilder.jshead %] <div id=&quot;form&quot;> [% FormBuilder.field.tOperacion.field -%] [% FormBuilder.field.id_institucion.field -%] <table> <tr> <td> [% FormBuilder.field.nombre.label -%]</td>  <td> [% FormBuilder.field.nombre.field -%]</td>  <td> [% FormBuilder.field.siglas.label -%]</td>  <td> [% FormBuilder.field.siglas.field -%]</td> </tr> <tr> <td colspan=”2”><input type=”submit” value=”enviar”> </td> </tr> </table> [% FormBuilder.end -%]
Variables de session
Agregando módulos  (Catalyst::Plugin) lib/sistap.pm package   sistap; use  strict; use  warnings; use  Catalyst::Runtime  '5.70'; use  Catalyst  qw/ -Debug  ConfigLoader  Static::Simple Authentication Authentication::Store::DBIC Authentication::Credential::Password Authorization::Roles Prototype Dumper Session Session::Store::FastMmap Session::State::Cookie /; our   $VERSION  = '0.01'; __PACKAGE__->config ( name => 'sistap', session => { flash_to_stash => 1 }, form => { messages => ':es_ES' } ); __PACKAGE__->setup ( qw/RequireSSL/ ); 1; -Debug Modo verboso en consola cuando se usa servidor de pruebas. ConfigLoader Se usa para cargar información de proyecto.yml Static::Simple Para que reconozca archivos estaticos (imagenes, css, js) y no trate despacha las rutas si hay coincidencia. Authentication/Autorization* Autenticación (¿Quién tiene acceso?) Autorización (¿de qué tiene acceso?) Prototype Ejectos integrados de script.aculo.us Dumper uso de Data::Dumper para obtener estructuras completas de data. Session* Manejo de sessiones.
Uso de sesiones Agregar plugin de session store cookie (la data) bd archivo state url (QueryString) Variable oculta () cookie (solo id único) Variables de session sólo para el usuario que ejecuta la acción. $c->session->{id} = “hey” ; [% c.session.id %] delete($c->session->{id}) ; $c->session->{id} = undef ; Variable de session para todos los usuarios $c->store_session_data(key,value) $c->store_session_data(“color”,”azul”) $c->get_session_data(key) $c->get_session_data(“color”) $c->delete_session_data(key)
uso de Flash  (BTW: nada que ver con adobe ™  ) Debe estar cargado plugin de sesion ( Catalyst::Plugin::Session ) lib/sistap.pm PACKAGE__->config(  name => 'sistap', session => { flash_to_stash => 1 } ); Controlador $c->flash->{error} = “Pagina invalida” Plantilla (TT) [% error %]
¿Qué es jQuery? Framework de JavaScript Selectores de CSS3. Manipulación de eventos. Ajax. Gran cantidad de plugins. Tabs. Menues. Drag and Drop. Formularios. Dialogos. etc ( http://jquery.com/plugins/ )
jQuery < script  type = &quot;text/javascript&quot;   src = &quot;[% c.uri_for('/js/jquery-latest.pack-1_2_1.js') %]&quot; >  </ script > < script   type = &quot;text/javascript&quot;  >  jQuery.noConflict () ; </ script > < script  type = &quot;text/javascript&quot; >  jQuery (function(){ // El código a ejecutarse cuando se cargue todo el documento acá } // acá también puede haber código, pero no si se disparará aún cuando  // el documento no esté cargado </ script >
jQuery: Selectores Básicamente jQuery(“ selector CSS o xpath ”) jQuery ( &quot;#txt_pais&quot; ). val ( jQuery ( &quot;#modal_pais :selected&quot; ). text ()) Antes:  document.getElementById(“txt_pais”).value =   document.getElementById(&quot;modal_pais&quot;).options[document.getElementById(&quot;modal_pais&quot;).selectedIndex].text jQuery(&quot;.jd_menu li ul li ul&quot;).parent().addClass(&quot;flecha_menu&quot;); Antes: Posiblemente usaría un id por cada elemento y sabiendo quién merece la imagen, se la asigno, sino, del lado del servidor. jQuery(&quot;table.cebra tr:even&quot;).addClass(&quot;resaltado&quot;); Antes: Si se construye la tabla de forma dinámica (php, perl, python, etc), controlar el número del registro y asignar la clase correspondiente. jQuery(&quot;input[@type=text]:visible&quot;).eq(0).focus() Antes: Seguro haría lo mismo pero a pie, pasar por todo el fomulario buscando los elementos visibles y luego situarme en el primer elemento.
Ajax   (ahah)
Ajax   (ahah) Crear vista de JSON. perl script/sistap_create view JSON JSON La información a enviar como respuesta está en el stash. $c->stash->{salida} = “hey”; jQuery captura los datos. jQuery.getJSON( url , param , function(jsonData){ if (jsonData.salida == “hey” ){ alert(“fino”); }else{ alert(“error al obtener la data”); } })
Ajax (JSON) jQuery.getJSON( url , parametros , function(jsonData){ // trabajamos con el resultado... jsonData }); my  $id_pais  =  $c->req->param ( “pais_id” ); my  @estados = $c->model ( &quot;sistapDB::paises&quot; )->find($id_pais)->estados( undef ,{ order_by  => ' nombre ' }) ; $c->stash ->{estados} = [ map  { {  id_estado  =>  $_->id_estado  ,  nombre_estado  =>  $_->nombre  } }  @estados  ]; $c->forward (' sistap::View::JSON '); var  param =   new  Object (); param =  { pais_id :   3 }  ; jQuery.getJSON(  [% c.uri_for('controlador/accion/') %] , param , function(jsonData){ if (jsonData.estados){ for (i= 0 ; jsonData.estados.lenght ; i++){ // tengo jsonData.estados[i].id_estado y jsonData.estados[i].nombre_estado } }else{ console.info(“hubo un error al traer la data”); // incluso se puede capturar si se acabó el tiempo de sesión // de usuario según respuesta y se recarga la página. } })
jQuery: plugins Tabs http://stilbuero.de/jquery/tabs/  menu http://jdsharp.us/jQuery/plugins/jdMenu/  Thickbox http://jquery.com/demo/thickbox/  Form http://www.malsup.com/jquery/form/  Interfaces  (interfaces es para para jQuery, lo que script.aculo.us es para prototype) http://interface.eyecon.ro/  Muchos mas. http://jquery.com/plugins/
Depuración
Depuración javascript (firebug) : console.info()
Depuración Catalyst : $c->log->debug()
Depuración DBIC: export DBIC_TRACE=”1=/tmp/salida.txt”
Consejos Usar uri_for (en las plantillas y controlador). <a href=”[%  c.uri_for('/controlador/accion/param')  %]”>Enlace</a> $c->res->redirect( $c->uri_for('/a/b/c') ); Usar relaciones en ORM (DBIC). Usar Template Toolkit para generar maestros. Usar Flash + redirect para mensajes a usuario. Usar Ajax solo cuando sea justificado.  (No AJfiXiar el sitio)   Usar Unicode BD  (UTF-8) Servidor web  (apache: AddDefaultCharset utf8) Archivo  (Usar un editor que lo soporte, como vim ) Documento ( html: <meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /> ) Usar la función “jQuery” en vez de la función anónima ($)  jQuery.noConflict()
Proyectos libres nacionales desarrollados usando Catalyst  ( Que conozco hasta ahora ) Debian::Package::HTML http://search.cpan.org/~bureado/Debian-Package-HTML-0.1/lib/Debian/Package/HTML.pm  PUBuilder http://blog.bureado.com.ve/?p=307  Sistap: http://sistemas.fsl.fundacite-merida.gob.ve/projects/sistap   TEGZ: http://sistemas.fsl.fundacite-merida.gob.ve/projects/tegz
¿Preguntas?
Gracias por su atención

Desarrollando aplicaciones web usando Catalyst y jQuery

  • 1.
    Javier E. PérezP. Noviembre 2007 Desarrollo de aplicaciones web usando Catalyst y jQuery
  • 2.
    Puntos a tratar¿Qué es Catalyst? Ventajas de su uso. Requisitos de instalación. Creando un proyecto. Estructura de proyecto. Creación de: Controlador, Modelo, Vista. Agregando módulos. Conexión a base de datos (DBIC). Trabajando con plantillas (TT, TTSite) Trabajando con formularios (FormBuilder). Uso de sessiones. Autorización, autenticación. Depuración. Consejos. Proyectos nacionales desarrollados usando catalyst.
  • 3.
    ¿Qué es Catalyst?Framework Web escrito usando perl. MVC. Unicode. Servidor de pruebas integrado.
  • 4.
    Metodología MVC ModeloDe donde se almacenan los datos. Generalmente base de datos. Vista La presentación de los datos. HTML, JSON, RSS, XML, etc. Controlador Quién maneja las transacciones entre la solicitud del usuario, el proceso y salida de información. Catalyst itself
  • 5.
  • 6.
    Ventajas de suuso Despacho del URL, se trabaja basado en segmentos en vez de querystring's. http://www.google.com/search ?hl=es&client=iceweasel-a&q=catalyst&btnG=Buscar http://www.administracion.com/usuario ?id=11222333&accion=eliminar QueryString http://www.google.com/search/ lenguale/es/cliente/iceweasel-a/q/catalyst/Buscar http://www.administracion.com/usuario /11222333/eliminar Basado en segmentos (segment based)
  • 7.
    Está respaldado porla gran cantidad de módulos que hospeda cpan. “ there's nothing magical about catalyst, it doesn't get in your way, it just dispatches urls to actions” Ventajas de su uso use PDF::CreateSimple; sub pdf : Local { my ($self,$c,$mensaje) = @_; # Heredamos el módulo como lo haríamos con cualquier aplicación CGI my $pdfFile = PDF::CreateSimple->new(&quot;root/reporte.pdf&quot;,undef,'LETTER'); # Pasamos algunos parametros (Esto no es catalyst, es perl) $pdfFile->drawText($mensaje,'Verdana',10,200,450,'black',3); $pdfFile->drawText('Generado con Catalyst','Verdana',10,200,400,'black',3); $pdfFile->drawImage('root/images/catalyst-logo.png',250,300); # esto no envía el pdf para descargar, sino que lo guarda en el disco $pdfFile->closeFile; # Ahora enviamos al navegador a que muestre el archivo recién guardado $c->res->redirect(&quot;/archivo.pdf&quot;); }
  • 8.
    Por supuesto, TIMTOWTDI(There Is More Than One Way To Do It)
  • 9.
    Requisitos de instalaciónPaquetes en debian / ubuntu : libcatalyst-perl libcatalyst-modules-perl Módulos usando cpan: Catalyst El resto que se necesite. Por ejemplo, en la shell: cpan CGI::FormBuilder ó usar dh_make para convertir modulos de perl a paquetes debian y así instalarlos.
  • 10.
    Estructura establecida delos directorios manteniendo un orden.
  • 11.
    Creando un proyectoCreamos proyecto $ catalyst.pl proyecto Nos cambiamos de directorio de trabajo al recien creado (nombre del proyecto) $ cd proyecto Corremos el servidor de pruebas $ perl script/proyecto_server.pl
  • 12.
    Creando controlador perlscript/sistap_create.pl controller unesco_area lib/sistap/Controller/unesco_area.pm package sistap::Controller::unesco_area; use strict; use warnings; use base 'Catalyst::Controller'; sub index : Private { my ( $self, $c ) = @_; $c->response->body( 'Hola mundo' ); } sub saludo : Local { my ( $self, $c ) = @_; $c->response->body( 'Hola mundo... de nuevo' ); } 1;
  • 13.
    Métodos de despachoLocal (: Local {}) Reconoce el nombre de la acción como primer argumento del controlador. Path (: Path('foo/bar') { }) Se especifica una ruta absoluta a despachar. Global (: Global { }) Despacha el primer nivel (como si fuera controlador) Private (: Private {} ) Especial para funciones reservadas en catalyst (default, index, begin, end, auto) , no es despachada por url. Regex ( : Regex('^item(\d+)/orden(\d+)$') { } ) Despacha según una expresión regular dada (en todo el sistema) No usa ModRewrite para esto. item4/orden243 es capturada con esa expresión. LocalRegex ( : LocalRegex('^widget(\d+)$') { } ) Igual que “Regex” pero solo es interpretada en el controlador donde está definida. widget23 es reconocido con la expresión del ejemplo. Chained Se realiza una cadea entre diferentes acciones CaptureArgs() y Args() Se combina con Chained para reconocer la cadena sub saludo : Local { my ( $self, $c ) = @_; $c->res->body('dvst'); }
  • 14.
    Metodos de despachoChained / CaptureArgs() / Args() sub wiki : PathPart('wiki') Chained('/') CaptureArgs(1) { my ( $self, $c, $page_name ) = @_; # carga la página de nombre $page_name y coloca el objeto en el stash $c->stash->{var1} = $page_name; } sub rev : PathPart('rev') Chained('wiki') CaptureArgs(1) { my ( $self, $c, $revision_id ) = @_; # Usa el objeto de página que está en el stash y obtiene el número # de revisión $revision_id. $c->stash->{var2} = $revision_id ; } sub view : PathPart Chained('rev') Args(0) { my ( $self, $c ) = @_; # Muestra la revisión de la página en pantalla, my $salida ; $salida = &quot;Esta pantalla muestra la revision: &quot; . $c->stash->{var2} ; $salida .= &quot; de la pagina: &quot; . $c->stash->{var1} ; $c->res->body($salida); } URL: http://localhost:3000/wiki/principal/rev/4/view Salida por pantalla: Esta pantalla muestra la revision: 4 de la pagina: principal sub view : PathPart Chained('wiki') Args(0) { my ( $self, $c ) = @_; # Muestra la revisión de la página en pantalla, my $salida ; $salida = &quot;Esta pantalla muestra la revision: &quot; . $c->stash->{var2} ; $salida .= &quot; de la pagina: &quot; . $c->stash->{var1} ; $c->res->body($salida); }
  • 15.
    Creando un modelo (DBIx::Class -> Catalyst::Model::DBIC::Schema) Necesitamos esquema. (archivo lib/sistapDB.pm) package sistapDB ; =head1 NAME sistapDB - DBIC Schema Class =cut # Nuetro esquema necesita heredar desde 'DBIx::Class::Schema' use base qw/DBIx::Class::Schema/ ; # Se necesitan cargas las clases de modelo de base de datos acá __PACKAGE__->load_classes({ sistapDB => [qw/ unesco_area unesco_subarea unesco_categoria /] }); 1 ;
  • 16.
    Creando un modeloSe crea una clase por tabla (archivo lib/sistapDB/unesco_area.pm) ó usamos DBIx::Class::Schema::Loader package sistapDB::unesco_area; use base qw/DBIx::Class/; # Se cargan componentes requeridos por DBIC __PACKAGE__->load_components( qw/PK::Auto Core/ ); # Se asigna el nombre de la base de datos __PACKAGE__->table( 'sta_unesco_area' ); # Se listan los campos de la tabla __PACKAGE__->add_columns( qw/id codigo nombre descripcion activo / ); # Se indica la llave primaria de la tabla __PACKAGE__->set_primary_key( qw/id/ ); # Asignamos las relaciones __PACKAGE__->has_many( subareas => 'sistapDB::unesco_subarea','id_unesco_area' ); 1 ;
  • 17.
    Creando un modeloResultado : (Archivo: lib/sistap/Model/sistapDB) Script helper : .script/sistap_create.pl model sistapDB DBIC::Schema \ sistapDB dbi:Pg:dbname=sistap 'usuario' 'clave' '{ AutoCommit => 1 } ' package DBIC::Model:: sistapDB ; use strict; use base 'Catalyst::Model::DBIC::Schema'; __PACKAGE__->config( schema_class => ' sistapDB ', connect_info => [ 'dbi:Pg:dbname=sistap', 'usuario', 'clave', { AutoCommit => 1 }, ], );
  • 18.
    Trabajando con relacionespackage sistapDB::unesco_area; #... Se hereda clase ; Carga de componentes ; nombre de la tabla ; nombre columnas ; llave primaria __PACKAGE__->add_columns(qw/id_area nombre/); # empezamos a declarar las relaciones a nivel de ORM __PACKAGE__->has_many( subareas => 'sistapDB::unesco_subarea',' id_area '); package sistapDB::unesco_subarea; #... igual que arriba __PACKAGE__->add_columns(qw/id_subarea id_area nombre/); # empezamos a declarar las relaciones a nivel de ORM __PACKAGE__->belongs_to( area => 'sistapDB::unesco_area',' id_area '); __PACKAGE__->has_many( categorias => 'sistapDB::unesco_subarea',' id_subarea '); package sistapDB::unesco_categoria; #... igual que arriba __PACKAGE__->add_columns(qw/id_categoria id_subarea nombre/); # empezamos a declarar las relaciones a nivel de ORM __PACKAGE__->belongs_to( subarea => 'sistapDB::unesco_subarea',' id_subarea ');
  • 19.
    Usando DBIC enel controlador Métodos más comunes: find ( SELECT .. LIMIT 1 ) : Obtiene un (1) registro (hash) según patrón de busqueda $area = $c->model(“sistapDB::unesco_area”)->find(3); $persona = c->model(“sistapDB::personas”)->find({ nombre => { ILIKE => '%javier%' }}) search (SELECT *) : Obtiene un arreglo de registros @productos = $c->model(“sistapDB::productos”)->search({ codigo => 've' , tipo => 'abc' }); create (INSERT) : Crea un nuevo registro según el hash pasado. my $campos = { codigo => $c->req->param(&quot;codigo&quot;), nombre => $c->req->param(“nombre”)}; $registro = $c->model(“sistapDB::productos”)->create($campos); # al asignar la creación del registro a una variable, se obtiene el ResourceSet de la operación $registro->id ; # se obtiene el id del registro recien insertado. update : Actualiza el ResourceSet $equipo = $c->model(“sistapDB::equipos”)->find(3); $equipo->tipo(“ups”); $equipo->dominio(“administracion”); $equipo->update; delete : Elimina los registros del resourceSet $c->model(“sistapDB::equipos”)->search({tipo => 'ups'})->delete;
  • 20.
    Vistas TTSite TemplateToolkit JSON PHP ClearSilver (yahoo, google) ... Predefiniendo una vista.
  • 21.
    Vistas (TTSite ->Catalyst::Helper::View::TTSite) ./lib/config/ Configuración de variables (colores, rutas predefinidas, etc) a ser usadas. ./lib/site Plantillas fuentes footer : Pie de página heade r: Encabezado (h1 con nombre de página) html : Esqueleto base (Incluye ttsite.css) layout : Cuerpo (body) del documento. wrapper : Quién se encarga de acoplarlos. ./src Plantillas predefinidas. perl script/sistap_create view TTSite TTSite . |-- lib | |-- config | | |-- col | | |-- main | | `-- url | `-- site | |-- footer | |-- header | |-- html | |-- layout | `-- wrapper `--- src |-- error.tt2 |-- message.tt2 |-- ttsite.css `-- welcome.tt2
  • 22.
    Vistas (Template Toolkit)perl script/sistap_create view TT TT Por defecto, las plantillas se almacenan en root $c->stash->{variable} && [% variable %]
  • 23.
    Vistas (JSON) Catalyst::View::JSONperl script/sistap_create view JSON JSON my @estados = $c->model ( &quot;sistapDB::paises&quot; )->find($id_pais)->estados( undef ,{ order_by => ' nombre ' }) ; $c->stash ->{estados} = [ map { { id_estado => $_->id_estado , nombre_estado => $_->nombre } } @estados ]; $c->forward (' sistap::View::JSON '); my @estados = $c->model ( &quot;sistapDB::paises&quot; )->find($id_pais)->estados()->all ; $c->stash ->{estados} = @estados ; $c->forward (' sistap::View::JSON ');
  • 24.
    Vistas Predefiniendo unavista sistap.yml Al trabajar con varias vistas, si no se especifica cual usar por defecto, podemos tener problemas. --- name: sistap default_view: TT authentication: ...
  • 25.
    Generación de formulariosCatalyst::Controller::FormBuilder
  • 26.
    Generación de formulariosSe compone en varios archivos para mayor mantenimiento y abstrabción de conceptos. Detalle de formulario root/forms/nombre/comun.fb Controlador lib/sistap/Controller/nombre.pm Plantilla root/src/nombre/comun.tt
  • 27.
    FormBuilder en Catalyst (CGI::FormBuilder) Descripción del formulario (root/forms/unesco_subarea/comun.fb) Formato yaml. Usen espacios, no tabs. Definición de elementos y sus atributos. Validaciones (Cliente y Servidor) Facilmente aplicable a plantillas. name: unesco_subarea method: post action: /unesco_subarea/operaciones fields: tOperacion: type: hidden id: id: hdn_id type: hidden id_unesco_area: label : Area type: select id: txt_id_unesco_area disabled : disabled autocomplete: off codigo: id: txt_codigo disabled : disabled autocomplete: off required : 1 nombre: id: txt_nombre disabled : disabled autocomplete: off required: 1 validate : NAME
  • 28.
    FormBuilder en Catalyst (CGI::FormBuilder) Controlador (lib/sistap/Controller/unesco_subarea) package sistap::Controller::unesco_subarea; use strict; use warnings; use base 'Catalyst::Controller ::FormBuilder '; ... sub principal : Path('/mantenimiento/unesco_subarea') Form('unesco_subarea/comun') { my ($self, $c) = @_ ; my $form = $self->formbuilder; $form->field( name => 'id_unesco_area', options => [ map { { $_->id_area => $_->nombre } } $c->model(“sistapDB::unesco_area”)->all ] ); if ($form->submitted && $form->validate){ $c->model(“sistapDB::unesco_subarea”)->create($form->fields) ; } }
  • 29.
    FormBuilder en Catalyst (CGI::FormBuilder) Plantilla (root/src/unesco_subarea/comun.tt) ó [% FormBuilder.render %] [% FormBuilder.start -%] [% formFuilder.jshead %] <div id=&quot;form&quot;> [% FormBuilder.field.tOperacion.field -%] [% FormBuilder.field.id_institucion.field -%] <table> <tr> <td> [% FormBuilder.field.nombre.label -%]</td> <td> [% FormBuilder.field.nombre.field -%]</td> <td> [% FormBuilder.field.siglas.label -%]</td> <td> [% FormBuilder.field.siglas.field -%]</td> </tr> <tr> <td colspan=”2”><input type=”submit” value=”enviar”> </td> </tr> </table> [% FormBuilder.end -%]
  • 30.
  • 31.
    Agregando módulos (Catalyst::Plugin) lib/sistap.pm package sistap; use strict; use warnings; use Catalyst::Runtime '5.70'; use Catalyst qw/ -Debug ConfigLoader Static::Simple Authentication Authentication::Store::DBIC Authentication::Credential::Password Authorization::Roles Prototype Dumper Session Session::Store::FastMmap Session::State::Cookie /; our $VERSION = '0.01'; __PACKAGE__->config ( name => 'sistap', session => { flash_to_stash => 1 }, form => { messages => ':es_ES' } ); __PACKAGE__->setup ( qw/RequireSSL/ ); 1; -Debug Modo verboso en consola cuando se usa servidor de pruebas. ConfigLoader Se usa para cargar información de proyecto.yml Static::Simple Para que reconozca archivos estaticos (imagenes, css, js) y no trate despacha las rutas si hay coincidencia. Authentication/Autorization* Autenticación (¿Quién tiene acceso?) Autorización (¿de qué tiene acceso?) Prototype Ejectos integrados de script.aculo.us Dumper uso de Data::Dumper para obtener estructuras completas de data. Session* Manejo de sessiones.
  • 32.
    Uso de sesionesAgregar plugin de session store cookie (la data) bd archivo state url (QueryString) Variable oculta () cookie (solo id único) Variables de session sólo para el usuario que ejecuta la acción. $c->session->{id} = “hey” ; [% c.session.id %] delete($c->session->{id}) ; $c->session->{id} = undef ; Variable de session para todos los usuarios $c->store_session_data(key,value) $c->store_session_data(“color”,”azul”) $c->get_session_data(key) $c->get_session_data(“color”) $c->delete_session_data(key)
  • 33.
    uso de Flash (BTW: nada que ver con adobe ™ ) Debe estar cargado plugin de sesion ( Catalyst::Plugin::Session ) lib/sistap.pm PACKAGE__->config( name => 'sistap', session => { flash_to_stash => 1 } ); Controlador $c->flash->{error} = “Pagina invalida” Plantilla (TT) [% error %]
  • 34.
    ¿Qué es jQuery?Framework de JavaScript Selectores de CSS3. Manipulación de eventos. Ajax. Gran cantidad de plugins. Tabs. Menues. Drag and Drop. Formularios. Dialogos. etc ( http://jquery.com/plugins/ )
  • 35.
    jQuery < script type = &quot;text/javascript&quot; src = &quot;[% c.uri_for('/js/jquery-latest.pack-1_2_1.js') %]&quot; > </ script > < script type = &quot;text/javascript&quot; > jQuery.noConflict () ; </ script > < script type = &quot;text/javascript&quot; > jQuery (function(){ // El código a ejecutarse cuando se cargue todo el documento acá } // acá también puede haber código, pero no si se disparará aún cuando // el documento no esté cargado </ script >
  • 36.
    jQuery: Selectores BásicamentejQuery(“ selector CSS o xpath ”) jQuery ( &quot;#txt_pais&quot; ). val ( jQuery ( &quot;#modal_pais :selected&quot; ). text ()) Antes: document.getElementById(“txt_pais”).value = document.getElementById(&quot;modal_pais&quot;).options[document.getElementById(&quot;modal_pais&quot;).selectedIndex].text jQuery(&quot;.jd_menu li ul li ul&quot;).parent().addClass(&quot;flecha_menu&quot;); Antes: Posiblemente usaría un id por cada elemento y sabiendo quién merece la imagen, se la asigno, sino, del lado del servidor. jQuery(&quot;table.cebra tr:even&quot;).addClass(&quot;resaltado&quot;); Antes: Si se construye la tabla de forma dinámica (php, perl, python, etc), controlar el número del registro y asignar la clase correspondiente. jQuery(&quot;input[@type=text]:visible&quot;).eq(0).focus() Antes: Seguro haría lo mismo pero a pie, pasar por todo el fomulario buscando los elementos visibles y luego situarme en el primer elemento.
  • 37.
    Ajax (ahah)
  • 38.
    Ajax (ahah) Crear vista de JSON. perl script/sistap_create view JSON JSON La información a enviar como respuesta está en el stash. $c->stash->{salida} = “hey”; jQuery captura los datos. jQuery.getJSON( url , param , function(jsonData){ if (jsonData.salida == “hey” ){ alert(“fino”); }else{ alert(“error al obtener la data”); } })
  • 39.
    Ajax (JSON) jQuery.getJSON(url , parametros , function(jsonData){ // trabajamos con el resultado... jsonData }); my $id_pais = $c->req->param ( “pais_id” ); my @estados = $c->model ( &quot;sistapDB::paises&quot; )->find($id_pais)->estados( undef ,{ order_by => ' nombre ' }) ; $c->stash ->{estados} = [ map { { id_estado => $_->id_estado , nombre_estado => $_->nombre } } @estados ]; $c->forward (' sistap::View::JSON '); var param = new Object (); param = { pais_id : 3 } ; jQuery.getJSON( [% c.uri_for('controlador/accion/') %] , param , function(jsonData){ if (jsonData.estados){ for (i= 0 ; jsonData.estados.lenght ; i++){ // tengo jsonData.estados[i].id_estado y jsonData.estados[i].nombre_estado } }else{ console.info(“hubo un error al traer la data”); // incluso se puede capturar si se acabó el tiempo de sesión // de usuario según respuesta y se recarga la página. } })
  • 40.
    jQuery: plugins Tabshttp://stilbuero.de/jquery/tabs/ menu http://jdsharp.us/jQuery/plugins/jdMenu/ Thickbox http://jquery.com/demo/thickbox/ Form http://www.malsup.com/jquery/form/ Interfaces (interfaces es para para jQuery, lo que script.aculo.us es para prototype) http://interface.eyecon.ro/ Muchos mas. http://jquery.com/plugins/
  • 41.
  • 42.
  • 43.
    Depuración Catalyst :$c->log->debug()
  • 44.
    Depuración DBIC: exportDBIC_TRACE=”1=/tmp/salida.txt”
  • 45.
    Consejos Usar uri_for(en las plantillas y controlador). <a href=”[% c.uri_for('/controlador/accion/param') %]”>Enlace</a> $c->res->redirect( $c->uri_for('/a/b/c') ); Usar relaciones en ORM (DBIC). Usar Template Toolkit para generar maestros. Usar Flash + redirect para mensajes a usuario. Usar Ajax solo cuando sea justificado. (No AJfiXiar el sitio) Usar Unicode BD (UTF-8) Servidor web (apache: AddDefaultCharset utf8) Archivo (Usar un editor que lo soporte, como vim ) Documento ( html: <meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /> ) Usar la función “jQuery” en vez de la función anónima ($) jQuery.noConflict()
  • 46.
    Proyectos libres nacionalesdesarrollados usando Catalyst ( Que conozco hasta ahora ) Debian::Package::HTML http://search.cpan.org/~bureado/Debian-Package-HTML-0.1/lib/Debian/Package/HTML.pm PUBuilder http://blog.bureado.com.ve/?p=307 Sistap: http://sistemas.fsl.fundacite-merida.gob.ve/projects/sistap TEGZ: http://sistemas.fsl.fundacite-merida.gob.ve/projects/tegz
  • 47.
  • 48.
    Gracias por suatención