Programando en Ruby

La Guía de los "Programadores Pragmáticos"

Ruby y la Web



Ruby no es un extraño en Internet. No solamente puedes escribir tus propios servidores SMTP, demonio FTP o servidor web con él, sino que puedes usarlo para tareas mas habituales como programas CGI o como un reemplazo de PHP.

Escritura de scripts CGI

Se puede utilizar Ruby para escribir scripts CGI fácilmente. Para hacer que un script Ruby genere una salida HTML, sólo se requiere:

#!/usr/bin/env ruby
print "HTTP/1.0 200 OK\r\n"
print "Content-type: text/html\r\n\r\n"
print "<html><body>Hola Mundo!</body></html>\r\n"

Se podrían usar las características de las expresiones regulares de Ruby para analizar las cadenas de búsqueda ingresadas, buscar las variables de entorno, verificar etiquetas, sustituir texto por plantillas, escapar los caracteres especiales, formatear HTML e imprimir todo.

O puedes usar la clase CGI.

La utilización de cgi.rb

La clase CGI brinda el soporte para escribir scripts CGI. Con ella, puedes manipular formularios, cookies, el entorno, mantener el estado de las sesiones, y demás. Está documentado de manera completa en la sección de referencias a partir de la página 497, pero haremos una breve introducción en ésta sección.

Codificando(?)

Al tratar con URLs y código HTML, debemos tener especial cuidado de codificar adecuadamente algunos caracteres. Por ejemplo, la barra (``/'') tiene un significado especial en una URL, de manera que debe ser ``escapada'' si no es parte del nombre de la ruta. Esto significa que cualquier ``/'' en la porción de búsqueda de la URL será traducido como la cadena ``%2F'' y deberá ser traducida nuevamente a ``/'' para poder ser utilizada. Los espacios y ampersand son también caracteres especiales. Para soportarlos, CGI provee las rutinas CGI.escape y CGI.unescape:

require 'cgi'
puts CGI.escape( "Nicholas Payton/Trumpet & Flugel Horn" )
produce el resultado:
Nicholas+Payton%2FTrumpet+%26+Flugel+Horn

De manera similar, deberías escapar los caracteres especiales HTML:

require 'cgi'
puts CGI.escapeHTML( '<a href="/mp3">Click aquí</a>' )
produce el resultado:
&lt;a href=&quot;/mp3&quot;&gt;Click aquí&lt;/a&gt;

Para hacerlo mas ameno, puedes decidir escapar sólo ciertos elementos dentro de una cadena:

require 'cgi'
puts CGI.escapeElement('<hr><a href="/mp3">Click aquí</a><br>','A')
produce el resultado:
<hr>&lt;a href=&quot;/mp3&quot;&gt;Click aquí&lt;/a&gt;<br>

Aquí solo la etiqueta ``A'' resulta escapada; el resto se dejan como están. Cada uno de éstos métodos tiene una versión de ``un-'' para recuperar la cadena original.

Formularios

Al utilizar la clase CGI tienes acceso a los parámetros de búsqueda de HTML de dos maneras. Supón que tenemos una URL /cgi-bin/lookup?player=Miles%20Davis&year=1958. Puedes acceder a los parámetros ``player'' y ``year'' usando CGI#[] directamente:

require 'cgi'
cgi = CGI.new
cgi['player'] » ["Miles Davis"]
cgi['year'] » ["1958"]

O puedes recuperar todos los parámetros como un Hash:

require 'cgi'
cgi = CGI.new
h = cgi.params
h['player'] » ["Miles Davis"]

Creación de formularios y HTML

CGI contiene bastantes métodos para crear HTML, un método por etiqueta. Para habilitarlos, deberás crear un objeto CGI invocándolo desde CGI.new, pasándole el nivel requerido de HTML. En éstos ejemplos, utilizaremos ``html3''.

Para facilitar el anidado de etiquetas, éstos métodos toman el contenido como bloques de código. El bloque de código deberá devolver un String, el cual será utilizado como el contenido para la etiqueta. En éste ejemplo, hemos añadido algunas líneas en blanco adicionales para acomodar la salida en la página.

require "cgi" 
cgi = CGI.new("html3")  # añade los métodos de generación de HTML 
cgi.out{
  cgi.html{
    cgi.head{ "\n"+cgi.title{"Esto es una Prueba"} } +
    cgi.body{ "\n"+
      cgi.form{"\n"+
        cgi.hr +
        cgi.h1 { "A Form: " } + "\n"+
        cgi.textarea("get_text") +"\n"+
        cgi.br +
        cgi.submit
      }
    }
  }
}
produce:
Content-Type: text/html
Content-Length: 302

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><HEAD> <TITLE>Esto es una Prueba</TITLE></HEAD><BODY> <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded"> <HR><H1>A Form: </H1> <TEXTAREA NAME="get_text" ROWS="10" COLS="70"></TEXTAREA> <BR><INPUT TYPE="submit"></FORM></BODY></HTML>

Éste código producirá un formulario HTML titulado ``Esto es una prueba'', seguido por una línea, un encabezado nivel uno, un área de prueba de ingreso de datos y finalmente un botón de aceptar. Al aceptar, obtendrás un parámetro CGI llamado``get_text'' conteniendo el texto que el usuario ingresó.

Cookies

Se puede almacenar cualquier cantidad de cosas interesantes en la máquina de un navegante incauto utilizando cookies. Se puede crear un objeto llamado cookie y guardar muchas cosas. Para enviarlo al navegador, pon un encabezado de ``cookie'' en la llamada a CGI#.

require "cgi"
cookie = CGI::Cookie.new("rubyweb", "CustID=123", "Part=ABC");
cgi = CGI.new("html3")
cgi.out( "cookie" => [cookie] ){
  cgi.html{
    "\ncódigo HTML aquí"
  }
}
produce:
Content-Type: text/html
Content-Length: 86
Set-Cookie: rubyweb=CustID%3D123&Part%3DABC; path=

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML> código HTML aquí</HTML>

La próxima vez que el usuario retorne a la página, puedes recuperar los valores de la cookie para CustID y Part, como se ve en la sig.salida HTML.

require "cgi"
cgi = CGI.new("html3")
cgi.out{
  cgi.html{
    cgi.pre{
      cookie = cgi.cookies["rubyweb"]
        "\nLas Cookies son\n" + cookie.value.join("\n")
    }
  }
}
produce:
Content-Type: text/html
Content-Length: 111

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML><PRE> Las Cookies son CustID=123 Part=ABC</PRE></HTML>

Sesiones

Las cookies necesitan algún trabajo adicional para volverse de utilidad. Lo que realmente deseamos es una sesión: un estado de persistencia para los navegantes Web. Las sesiones se manejan con CGI::Session (documentado desde la pág.504), que utiliza cookies pero brinda abstracciones de alto nivel.

require "cgi" require "cgi/session"

cgi = CGI.new("html3") sess = CGI::Session.new( cgi, "session_key" => "rubyweb",                           "session_id"  => "9650",                           "new_session" => true,                           "prefix" => "web-session.") sess["CustID"] = 123 sess["Part"] = "ABC"

cgi.out{   cgi.html{     "\nHTML content here"   } }

El ejemplo enviará una cookie al usuario llamado ``rubyweb'' con el valor 9650. Además, creará un archivo en $TMP/web-session.9650 con el par clave, valor para CustID y Part.

Al regresar el usuario, todo lo que necesitas es el parámetro que indique id de sesión. En éste ejemplo, sería rubyweb=9650. Con éste parámetro, podrás acceder a todos los datos guardados de la sesión.

require "cgi"
require "cgi/session"

cgi = CGI.new("html3") sess = CGI::Session.new( cgi, "session_key" => "rubyweb",                                "prefix" => "web-session.") cgi.out{   cgi.html{     "\nCustomer #{sess['CustID']} orders an #{sess['Part']}"   } }

Ruby embebido en HTML

Hasta aquí, hemos buscado la manera de utilizar Ruby para generar una salida HTML, pero démosle vuelta al asunto: podemos embeber ruby en un documento HTML.

Existe una cantidad de paquetes que permiten embeber sentencias Ruby en otro tipo de documentos, especialmente en una página HTML. Genéricamente, se lo conoce como ``eRuby''. Específicamente, existen diferentes implementaciones de eRuby, incluyendo eruby y erb. Durante el resto de ésta sección veremos eruby, de Shugo Maeda.

Embeber Ruby en HTML es un concepto potente; básicamente nos da una herramienta equivalente a ASP, JSP o PHP, pero con todo el poder de Ruby.

Utilización de eruby

eruby actúa como un sencillo y simple filtro. Cualquier texto en el archivo de entrada es introducido intacto, con excepción de:

Expresión Descripción
<% código ruby %> El código Ruby entre limitadores se reemplaza por su salida.
<%= expresión ruby %> La expresión Ruby entre limitadores se reemplaza por su valor.
<%# código ruby %> El código Ruby entre limitadores se ignora (de utilidad para pruebas).

Para invocar eruby:

eruby [
            opciones
            ] [
            documento
            ]

Si se omite documento, eruby leerá desde la entrada estándar. Las opciones de línea de comando de eruby se muestran en Tabla 14.1 en pag. 149.
Opciones de línea de comando para eruby
Opción Descripción
-d, --debug Asina a $DEBUG true.
-K kcode Especifica un sistema de codificado alternativo (ver pag. 137).
-M mode Especifica algun modo de ejecución:

f modo filtrado
c modo CGI (muestra los errores como HTML, asigna $SAFE=1)
n modo NPH-CGI (muestra encabezados HTTP extra, asigna $SAFE=1)
-n, --noheader Desactiva la salida de encabezados CGI.
-v, --verbose Habilita el modo explicativo.
--version Muestra información de la versión y termina.

Veamos algunos ejemplos. Correremos el ejecutable de eruby en la siguiente entrada.

This text is <% a = 100; puts "#{a}% Live!" %>

eruby sustituye la expresión delimitada y da como resultado

This text is 100% Live!

El uso de <%= form actúa como si hubieras impreso el valor de la expresión. Por ejemplo, en

<%a = 100%>This text is almost <%=a%> degrees! Cool!

reemplaza el =a con el valor de a.

This text is almost 100 degrees! Cool!

Y, por supuesto, puedes embeber Ruby en documentos de tipo mas complejo, como HTML.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html>
<head>
<title>eruby example</title>
</head>
<body>
<h1>Enumeration</h1>
<ul>
<%(1..10).each do|i|%>
  <li>number <%=i%></li>
<%end%>
</ul>
<h1>Environment variables</h1>
<table>
<%ENV.keys.sort.each do |key|%>
  <tr>
    <th><%=key%></th><td><%=ENV[key]%></td>
  </tr>
<%end%>
</table>
</body>
</html>

Instalación de eruby en Apache

Puedes configurar un servidor web Apache para comprender documentos embebidos con Ruby usando eRuby, al igual que con PHP. Debes crear los archivos embebidos con extensión ``.rhtml'' y configurar el servidor Web para ejecutar eruby con este tipo de documentos para producir la salida HTML deseada.

Para utilizar eruby con el servidor Apache deberás:

Eso es todo! Ahora puedes escribir documentos HTML que contengan Ruby embebido para generar formularios y contenido dinámico. Asegúrate de leer también la librería Ruby CGI,documentada a partir de la pag.497.

Mejorando el rendimiento

Puedes utilizar Ruby para escribir programas CGI para la Web, pero, como en la mayoría de los programas CGI, la configuración predeterminada debe lanzar una nueva copia de Ruby en cada acceso a la página cgi-bin. Esto es oneroso en términos de uso de recursos y puede ser dolorosamente lento para los navegantes. Apache resuelve éste problema, admitiendo módulos recargables.

Generalmente, éstos módulos son cargados dinámicamente y forman parte del proceso en ejecución de servidor Web; no es necesario lanzar otro intérprete una y otra vez al requerirse el servicio; el servidor Web, de éste modo, es el intérprete.

De ésta manera, llegamos a mod_ruby (disponible en los repositorios), un módulo Apache que enlaza un intérprete Ruby completo al servidor Web propiamente dicho. El archivo README incluido en mod_ruby brinda con lujo de detalles cómo compilar e instalarlo.

Una vez hecho, puedes ejecutar scripts Ruby tal como lo hicieras sin mod_ruby, excepto que ahora se ejecutarán más rápidamente.


Extraído del libro "Programming Ruby - The Pragmatic Programmer's Guide"
Copyright © 2000 Addison Wesley Longman, Inc. liberado bajo los términos de la Open Publication License V1.0.
La referencia está disponible en: download.