Cultured Perl: Perl y la nube Amazon, Parte 5

Usted ya ha analizado el código; ahora podrá escanear las plantillas del sitio mod_perl completo

Esta serie de cinco partes lo lleva a recorrer la construcción de un sitio Web simple para compartir fotos usando Perl y Apache para acceder a Amazon Simple Storage Service (S3) y Amazon SimpleDB. En esta entrega, analizará las plantillas del sitio mod_perl completo, incluyendo una plantilla para elaboración de índices, tres para carga (general, formularios S3, y agregados de URL), una para búsqueda de imágenes y comentarios, y una para búsqueda recursiva de comentarios para una imagen (o encadenamiento descendente).

Teodor Zlatanov, Programmer, Gold Software Systems

photo- teodor zlatanovTeodor Zlatanov obtuvo un Máster en Ingeniería de Sistemas en la Universidad de Boston en 1999. Desde 1992 ha trabajado como programador, usando Perl, Java, C, y C++. Está interesado en el trabajo con código abierto para el análisis de textos, las arquitecturas de bases de datos, las interfaces de usuarios y la administración de sistemas UNIX.



03-08-2011

En esta última entrega, prepárese para ver un sitio mod_perl completo (esta vez analizaremos las plantillas; el código base fue analizado en la Parte 4). Otra vez, les recomiendo especialmente leer el código fuente. Si bien el sitio es funcional, en esta serie hay muchos detalles que no se explican en su totalidad, debido a que espero que los comprenda o que aprenda aquello que no entendía. Su librería local o remota y sus motores de búsqueda son sus amigos.

¿Cómo sacar el mayor provecho de esta serie?

Esta serie requiere que los lectores tengan conocimientos básicos de HTTP y HTML y conocimientos intermedios de JavaScript y Perl (dentro de un proceso mod_perl en Apache). También resultará muy útil que tengan algunos conocimientos sobre bases de datos relacionales, almacenamiento en disco y redes. Dado que esta serie es bastante técnica, le recomiendo ver la sección Recursos si necesita ayuda con algunos de estos temas.

En particular, la configuración de un sitio mod_perl completo y el uso del Template Toolkit (conjunto de herramientas de plantilla) son temas muy amplios, por lo cual no se los explicará en esta serie. La mejor manera de aprender es responder a todas las preguntas y solucionar todos los obstáculos hasta que el sitio esté funcionando. Recuerde que se le ha presentado el motor, las ruedas, la carrocería, etc.—cargar combustible y poner en marcha el auto depende ahora de usted.

En esta serie, uso share.lifelogs.com como nombre de dominio. Recuerde adaptarlo a las necesidades de su propio entorno.

index.tmpl

Comenzaremos con las plantillas de arriba hacia abajo (policy.tmpl fue analizado en la Parte 4). Para ver la explicación de la sintaxis del Template Toolkit syntax, consulte la sección Recursos. Yo trataré de explicarle los bits que presentan dificultades.

La plantilla index.tmpl es una página HTML simple. Aquí, lo que se debe tener en cuenta es que todos los URI son relativos, por lo cual ésta y todas las demás plantillas funcionarán dentro de cualquier dominio.

Listado 1. La simple página HTML index.tmpl
<html>
  <head>
<title>Share Pictures</title>
</head>
<body>
<h1>Share Pictures</h1>

You can
<a href="/upload">upload or add images</a>
or
<a href="/browse">browse images and comments</a>.

<address>
Contact <a href="mailto:tzz@bu.edu">Ted Zlatanov</a> if you have lots
of money you're trying to get out of Nigeria.  The breath
is <strike>baited</strike>bated.
</address>
</body>
</html>

upload.tmpl

Ahora, estamos en condiciones de analizar lo importante. Los lenguajes JavaScript, HTML y Template Toolkit en un mismo lugar. Si esto no hace llorar a los diseñadores Web, no sé que lo hará.

Listado 2. Plantilla upload.tmpl, suficiente para hacer llorar de alegría a los diseñadores Web
<html> 
  <head>
    <title>Upload Page For [% username %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
    <script src="http://ajax.googleapis.com/ajax/libs/prototype/1.6.0.3/prototype.js"
                 type="text/javascript"></script>
  </head>

  <body> 
  <script language="JavaScript">
function OnSubmitForm()
{
 var form = $('uploader');
 var file = form['file'];
 var ct   = form['Content-Type'];
 var name = form['name'].value;

 if (!name || name.length < 1)
 {
  alert("Sorry, you can't upload without a name.");
  return false;
 }

 var filename = ''+$F(file);
 var f = filename.toLowerCase(); // always compare against the lowercase version

 if (!navigator['mimeTypes'])
 {
  alert("Sorry, your browser can't tell us what type of file you're uploading.");
  return false;
 }

 var type = $A(navigator.mimeTypes).detect(function(m)
 {
  // does any of the suffixes match?
  return m.type.length > 3 && m.type.match('/') &&
   $A(m.suffixes.split(',')).detect(function(suffix)
  {
    return f.match('\.' + suffix.toLowerCase() + '$');
  });
 });

 if (!type || !type['type'])
 {
  type = { type : prompt("Enter your own MIME type, we couldn't find one through
                          the browser", "image/jpeg") };
 }

 if (type && type['type'])
 {
  ct.value = type.type;

  // fix up the redirect if we're about to submit
  var sar  = form['success_action_redirect'];

  sar.value = sar.value + escape(name);
  return true;
 }

 alert("Sorry, we don't know the type for file " + filename);
 return false;
}
</script>
<h1>Hi, [% username %]</h1>
    <form id="uploader" action="https://images.share.lifelogs.com.s3.amazonaws.com/"
          method="post" enctype="multipart/form-data" onSubmit="return OnSubmitForm();">
      <input type="hidden" name="key" value="${filename}">
      <input type="hidden" name="AWSAccessKeyId" value="[% env.AWS_KEY %]"> 
      <input type="hidden" name="acl" value="public-read">
      <input type="hidden" name="success_action_redirect"
             value="http://share.lifelogs.com/s3uploaded?user=[% username %]&name=">
      <input type="hidden" name="policy" value="[% policy %]">
      <input type="hidden" name="Content-Type" value="image/jpeg">
      <input type="hidden" name="signature" value="[% signature %]">
      Select File to upload to S3:
      <input name="file" type="file"> 
      <br>
      Enter a Name:
      <input name="name" type="text"> 
      <br> 
      <input type="submit" value="Upload File to S3"> 
    </form> 
    <form id="adder" action="/urluploaded" method="post" enctype="multipart/form-data">
      <input type="hidden" name="user" value="[% username %]">
      Enter a URL:
      <input name="url" type="text"> 
      <br>
      Enter a Name:
      <input name="name" type="text"> 
      <br>
      <input type="submit" value="Add URL"> 
    </form> 
  </body>
</html>

La página de carga muestra dos diálogos de carga. Ambos agregan una imagen, pero el segundo es mucho más simple. En el segundo, el usuario completa el URL y el nombre de la imagen, y esos datos se POST ean a /urluploaded, que no es más que urluploaded.tmpl. Cuando se visualiza esa plantilla, el controlador de parámetros de imagen será invocado automáticamente. El nombre de usuario se obtiene del servidor, y es un parámetro POST oculto en el formulario.

El primer formulario es realmente complicado. Por fortuna, usted tiene la posibilidad de leer la Parte 2 de esta serie, donde se explica todo lo necesario sobre las cargas de S3, por lo cual ninguno de estos temas deberá ser una sorpresa para usted.

Los principales cambios en s3form.pl respecto de la Parte 2 (que, nuevamente, se incluyen en la sección Descargas) son:

  • success_action_redirect pasa el nombre de usuario y el nombre de la imagen como parámetros. La directiva se ajusta para que sólo una parte de esta cadena sea necesaria, hasta el nombre de usuario, sin incluir dicho nombre.
  • La directiva y la firma pasan desde el servidor.
  • El acceso y las claves secretas para AWS se pasan en el hash env desde el servidor.
  • La función OnSubmitForm requiere un nombre y lo agrega al campo del formulario success_action_redirect como un parámetro, con escape (observe que el nombre se exige antes de que se determine el tipo de MIME, pero se agrega al URL inmediatamente antes de que el formulario sea POST eado).
  • La función OnSubmitForm falla si no se puede encontrar el tipo de MIME, lo cual permite a los usuarios especificar el suyo propio.

s3uploaded.tmpl

En las cargas S3, se usa esta plantilla.

Listado 3. Plantilla s3uploaded.tmpl para cargas exitosas y no exitosas
[% success = params.result %]
<html> 
  <head>
    <title>[% IF success %]Successful[% ELSE %]Unsuccessful[% END %]
              Upload Page For [% params.user %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    [% IF success %]Congratulations[% ELSE %]Sorry[% END %], [% params.user %].
       You have [% IF success %]successfully[% ELSE %]unsuccessfully[% END %]
       uploaded [% params.key %] to S3 bucket [% params.bucket %]
       named [% params.name %].<p>
      (etag is [% params.etag %] but I doubt you care.)
    <p>
[% IF success %]
      <a href="http://[% params.bucket %].s3.amazonaws.com/[% params.key %]">
   Your new upload is probably here.  Let's see if it displays already.
   <img src="http://[% params.bucket %].s3.amazonaws.com/[% params.key %]">
      </a>
[% END %]
    <p>
      You can now go back to <a href="/upload">uploading</a> or
      <a href="/">the main page</a>.
  </body>
</html>

En algunas malas construcciones IF-ELSE del Template Toolkit y el parámetro result, la página maneja cargas exitosas y no exitosas. El éxito tiene relación con el agregado de SimpleDB; ya que si usted llega a este punto, la imagen siempre habrá sido agregada a S3. Queda como ejercicio para el lector el retiro de la imagen de S3 ante una falla de SimpleDB (ja, ja).


urluploaded.tmpl

Esta platilla se usa en un agregado de URL.

Listado 4. La plantilla urluploaded.tmpl, el sueño de la reutilización de códigos
[% success = params.result %]
<html> 
  <head>
    <title>[% IF success %]Successful[% ELSE %]Unsuccessful[% END %]
              URL add Page For [% params.user %]</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    [% IF success %]Congratulations[% ELSE %]Sorry[% END %], [% params.user %].
       You have [% IF success %]successfully[% ELSE %]unsuccessfully[% END %]
       added [% params.url %] named [% params.name %].<p>
    <p>
[% IF success %]
      <a href="[% params.url %]">
   The URL you added is, perhaps, visible here.
   <img src="[% params.url %]">
      </a>
[% END %]
    <p>
      You can now go back to <a href="/upload">uploading</a>
      or <a href="/">the main page</a>.
  </body>
</html>

Aparte del obviamente malo HTML, esto es similar a la s3uploaded.tmpl. La reutilización de código claramente no constituye un problema para el mundo ideal de este autor.


browse.tmpl

Esta plantilla explora imágenes y comentarios.

Listado 5. Plantilla browse.tmpl, imágenes y comentarios en una única toma
[% SET images = fimages() %]
[% SET comments = fcomments() %]
<html> 
  <head>
    <title>Browse</title> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 
  </head>
  <body> 
    <ul>
      [% FOR ik IN images.keys %]
      <li>
         [% SET image = images.$ik %]
         [% image.name %]<br>
         <img src="[% image.url %]"><br>
         [% IF image.bucket %](in S3)[% END %]<br>
         uploaded by [% image.user %]<br>
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="deleteimageid" value="[% ik %]">
           <input type="submit" value="Delete"> 
         </form> 
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="imageid" value="[% ik %]">
           Change Image Name:
           <input name="name" type="text" value="[% image.name|html %]"> 
           <input type="submit" value="Rename"> 
         </form>
         [% INCLUDE comments.tmpl ik=ik comments=comments %]
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="user" value="[% username %]">
           <input type="hidden" name="refimageid" value="[% ik %]">
           Enter a Comment (as user [% username %]):
           <input name="comment" type="text"> 
           <input type="submit" value="Comment"> 
         </form> 
         <form action="/browse" method="post" enctype="multipart/form-data">
           <input type="hidden" name="refimageid" value="[% ik %]">
           Enter Anonymous Comment:
           <input name="comment" type="text"> 
           <input type="submit" value="Comment"> 
         </form> 
      </li>
      [% END %]
    </ul>
  </body>
</html>

Con esto, usted obtiene todas las imágenes y todos los comentarios en una única toma. No es eficiente, y seguramente provocará fallas horribles en un sitio Web real cuando usted obtenga miles de imágenes y comentarios sobre ellas. Deberá configurar la paginación de las imágenes con el esquema de paginación NextToken de SimpleDB (que las funciones de utilidad de SimpleDB que aquí se presentan no usan eficientemente) o un esquema propio, de manera que usted sólo obtenga los comentarios de las imágenes que va a mostrar.

Es en verdad conveniente evitar la obtención de comentarios a menos que sean necesarios. Los pedidos a SimpleDB son caros. Es por ello que esta plantilla pasó los comentarios a la plantilla comments.tmpl cada una de las veces.

La plantilla utiliza un lazo FOR de Template Toolkit para iterar sobre las imágenes, no clasificadas (si usted necesita realizar una clasificación, es preferible que el código Perl la haga fuera del entorno del Template Toolkit). Aquí se necesita la clave de la imagen para seleccionar los comentarios que corresponden a la misma. Para cada una de las imágenes, usted mostrará el nombre, el URL, el estado S3 y el propietario de la misma. Luego, mostrará formularios para eliminar la imagen, cambiar su nombre o registrar un comentario de manera anónima o como un usuario. Esta tarea es bastante sencilla.

Por último (en realidad, en la mitad, pero no pensamos de manera lineal, ¿verdad?), la plantilla comments.tmpl tiene como INCLUDE (inclusión) determinados parámetros;—ik es la clave de imagen y comments es la lista de comentarios— lo cual significa que cualquier cosa que genere la plantilla será ubicado justo en el centro de nuestra lista de imagen para cada una de las imágenes.


comments.tmpl

Esta plantilla explora comentarios de manera recursiva para una imagen (encadenamiento descendente).

Listado 6. Encadenamiento descendente con la plantilla comments.tmpl
[% IF parent %]
 [% SET thread = comments.$ik.$parent %]
[% ELSE %]
 [% SET thread = comments.$ik.noparent %]
[% END %]

<ul>
[% FOR ck IN thread.keys %]
 [% SET comment = thread.$ck %]
  <li>[% comment.comment %] (by [% IF comment.user %][% comment.user %]
      [% ELSE %]Anonymous[% END %])<br>
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="deletecommentid" value="[% ck %]">
      <input type="submit" value="Delete"> 
    </form> 
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="commentid" value="[% ck %]">
      Edit Comment:
      <input name="comment" type="text" value="[% comment.comment|html %]"> 
      <input type="submit" value="Edit"> 
    </form>
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="user" value="[% username %]">
      <input type="hidden" name="refimageid" value="[% ik %]">
      <input type="hidden" name="refcommentid" value="[% ck %]">
      Enter a Comment (as user [% username %]):
      <input name="comment" type="text"> 
      <input type="submit" value="Comment"> 
    </form> 
    <form action="/browse" method="post" enctype="multipart/form-data">
      <input type="hidden" name="refimageid" value="[% ik %]">
      <input type="hidden" name="refcommentid" value="[% ck %]">
      Enter Anonymous Comment:
      <input name="comment" type="text"> 
      <input type="submit" value="Comment"> 
    </form> 
    [% INCLUDE comments.tmpl ik=ik comments=comments parent=ck %]
  </li>
[% END %]
</ul>

He reservado el código mejor y más desagradable para el final.

Con una clave de imagen determinada, esta plantilla busca todos los comentarios para esa imagen con un elemento primario que equivale a una clave de comentario específica.

Para cada comentario de interés (con o sin elemento primario, según el caso), la plantilla muestra dicho comentario seguido por formularios HTML para eliminarlo, editarlo o ingresar un nuevo comentario (de manera anónima o con nombre de usuario). Esto es casi lo mismo que sucede con los formularios de la plantilla browse.tmpl con la excepción de que incluyen un parámetro refcommentid.

Ahora bien, y esta es la parte más complicada u horrible, según sea usted un mero estudiante de Ciencias de Computación o un docente en esta materia, esta plantilla se vuelve a incluir en cada uno de los comentarios que encuentra, configurando el valor parent (elemento primario) al valor de la clave del comentario cada una de las veces. Por lo tanto, usted tiene una recursión en un pseudo-lenguaje (Template Toolkit) para generar recursión en un lenguaje de diagramación (HTML) que funciona en Perl.


Resumen

Hemos completado esta serie de cinco partes. Usted ha analizado las plantillas para un sitio mod_perl completo— usando los fragmentos que escribimos en las Partes 2 y 3 y el código que generamos en la Parte 4. Este sitio utiliza el Template Toolkit, S3, y SimpleDB para producir carga, búsqueda, edición y eliminación de imágenes, además del agregado (de manera anónima o no), la búsqueda (encadenada) y la eliminación de comentarios.


Descargas

DescripciónNombretamaño
SimpleDB utility functionssimpledb_utility.zip3KB
Sample script (from Part 2)s3form.zip2KB
Sample script (from Part 3)simple_go.zip4KB

Recursos

Aprender

  • Comience con "Cultured Perl: Perl y la nube Amazon, Parte 1" (developerWorks, marzo de 2009) para comprender las ventajas y desventajas de usar Amazon's S3 y SimpleDB para crear sitios Web. Luego, pase a la Parte 2 (abril de 2009) para cargar un archivo a S3 desde una página Web a través de un formulario HTML con el objetivo de minimizar la carga sobre del servidor, a la Parte 3 (junio de 2009) para cargar imágenes por medio de una lista de URLs en una tabla y gestionar imágenes y comentarios, y a la Parte 4 para recorrer el código base del sitio completo.
  • Los detalles de la oferta de servicios y los recursos para desarrolladores se encuentran en Amazon.com:
  • Obtenga más información sobre el fantástico JavaScript MimeType, una propiedad del objeto navegador, un objeto de alto nivel que es la representación del objeto del navegador Web del cliente o del programa navegador de Internet que se está usando.
  • mod_perl reúne todas las capacidades de Perl y el servidor HTTP de Apache.
  • Prototype es un marco JavaScript que hace que sea más fácil desarrollar aplicaciones Web dinámicas mediante un conjunto de herramientas para un desarrollo impulsado por la clase y la "mejor" biblioteca Ajax del mundo. Y aquí puede encontrar un excelente artículo sobre cómo usarlo: "Developer Notes for prototype.js" por Sergio Pereira.
  • Sí, Virginia, se trata de un servicio basado en la Web y, por lo tanto, puede dejar de funcionar momentáneamente; es muy importante tenerlo en cuenta.
  • La computación en nube con servicios Amazon es un tema candente. Esta serie se ocupa del acceso a los servicios usando Perl, pero para conocer más ampliamente las ofertas, recomendamos la lectura de la serie "Computación en nube con servicios web Amazon"(developerWorks, julio de 2008 – febrero de 2009).
  • En la zona Linux de developerWorks, podrá encontrar más recursos para los desarrolladores de Linux y leer nuestros artículos y tutoriales más populares.
  • Vea todos los consejos sobre Linux y tutoriales de Linux en developerWorks.
  • Manténgase actualizado con los eventos técnicos y Webcasts de developerWorks.

Obtener los productos y tecnologías

Comentar

  • Involúcrese en la comunidad My developerWorks; con su perfil personal y página de inicio personalizada, usted puede personalizar developerWorks según sus intereses e interactuar con otros usuarios de developerWorks.

Comentarios

developerWorks: Ingrese

Los campos obligatorios están marcados con un asterisco (*).


¿Necesita un IBM ID?
¿Olvidó su IBM ID?


¿Olvidó su Password?
Cambie su Password

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


La primera vez que inicie sesión en developerWorks, se creará un perfil para usted. La información en su propio perfil (nombre, país/región y nombre de la empresa) se muestra al público y acompañará a cualquier contenido que publique, a menos que opte por la opción de ocultar el nombre de su empresa. Puede actualizar su cuenta de IBM en cualquier momento.

Toda la información enviada es segura.

Elija su nombre para mostrar



La primera vez que inicia sesión en developerWorks se crea un perfil para usted, teniendo que elegir un nombre para mostrar en el mismo. Este nombre acompañará el contenido que usted publique en developerWorks.

Por favor elija un nombre de 3 - 31 caracteres. Su nombre de usuario debe ser único en la comunidad developerWorks y debe ser distinto a su dirección de email por motivos de privacidad.

Los campos obligatorios están marcados con un asterisco (*).

(Por favor elija un nombre de 3 - 31 caracteres.)

Al hacer clic en Enviar, usted está de acuerdo con los términos y condiciones de developerWorks.

 


Toda la información enviada es segura.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=90
Zone=SOA y servicios web
ArticleID=425264
ArticleTitle=Cultured Perl: Perl y la nube Amazon, Parte 5
publish-date=08032011