Sinatra

Publicado en

Sinatra es un DSL (Domain Specific Language) para la creación rápida de aplicaciones web en Ruby con un mínimo esfuerzo. Sinatra se destaca por su simplicidad y ligereza, además de contar también con libertad de elección ya que no impone el uso de patrones ni librerías específicas.

En este tutorial veremos un ejemplo sencillo de cómo hacer una aplicación web con Sinatra tipo CRUD (create, read, update, delete). Utilizaremos Ruby 1.9.2, Datamapper como ORM y PostgreSQL como base de datos.

Modelo

Iniciamos creando un archivo llamado sinatra_test.rb. Este será el archivo que contenga la lógica de nuestra aplicación. Lo primero que vamos a incluir es el siguiente código:

require 'sinatra'
require 'data_mapper'

configure do
 DataMapper.setup(:default, 'postgres://localhost/development')
 DataMapper.setup(:default, {
   :adapter => 'postgres',
   :host => 'localhost',
   :username => 'sysdba' ,
   :password => 's3cret',
   :database => 'development'})
end

Lo primero que hace el código es indicar que utilizaremos las “gemas” sinatra y data_mapper. Luego, configuramos DataMapper para conectarse a nuestra base de datos llamada ‘development’, con el username ‘sysdba’ y password ‘s3cret’.

A continuación definimos el modelo de nuestra aplicación.

class Linea
 include DataMapper::Resource
 property :cve_linea, String, :length => 4, :required => true, :key => true
 property :linea, String, :length => 30, :required => true
end

Nuestro modelo consta tan sólo de una clase Linea, con dos propiedades: cve_linea y linea. Sinatra se encargará de crear la tabla correspondiente en la base de datos. Para pedir que Sinatra borre las tablas que existan en la base de datos (si es que existen) y cree nuevas a partir de nuestros modelos agregamos:

DataMapper.auto_migrate!

Si por el contrario, las tablas ya existen y no queremos borrarlas sino actualizarlas con la nueva definición de los modelos, usaríamos DataMapper.auto_upgrade!

Ruteo

Sinatra usa rutas (routes). Una ruta es un método HTTP (get, post, etc.) junto con un patrón que representa uno o más URLs. Cada ruta se asocia con un bloque.

Definamos la ruta para la página principal de nuestra aplicación.

get '/' do
  erb :index
end

El código anterior indica que cuando el usuario entre al URL ‘/’ mediante el método get, queremos que se procese una vista llamada index utilizando el lenguaje de
plantillas erb. Vale la pena mencionar que Sinatra soporta diferentes lenguajes de plantillas como erb, haml y builder, entre otras.

De forma similar, al recibir una petición get en ‘/linea’ mandamos invocar la vista reg_linea.

get '/linea' do
  erb :reg_linea
end

Ahora definimos un bloque que atiende la petición ‘/listalineas’ que, como se podrán imaginar, nos da un listado de líneas. Crearemos una variable @lineas donde pondremos todos los registros del modelo Linea (que se mapea a la tabla lineas en la BD) e invocamos la vista lista_lineas para que despliegue el listado.

get '/listalineas' do
  @lineas = Linea.all :order => :cve_linea
  erb :lista_lineas
end

Para crear o actualizar una línea que tengamos almacenada, enviaremos peticiones tipo POST a ‘/linea’. El siguiente bloque se encarga de manejar estas peticiones.

post '/linea' do
  if !campo_vacio(params[:cve_linea]) and !campo_vacio(params[:linea])
    @linea = Linea.new(:cve_linea => params[:cve_linea].upcase, :linea => params[:linea].upcase)
    if @linea.save
      redirect "/linea/#{@linea.cve_linea}"
    else
      redirect '/linea'
    end
  else
    redirect '/campo_vacio'
  end
end

Lo primero que hacemos es validar que los parámetros cve_linea y linea no estén vacíos. Si la validación es exitosa creamos una nueva instancia de Linea, mapeando el valor de sus propiedades con los parámetros recibidos en la petición. Como se darán cuenta, estamos convirtiendo los valores a mayúsculas antes de asignarlos. Posteriormente intentamos guardar nuestro registro en la base de datos (save). Si tenemos éxito, redireccionamos a la ruta para consultar la línea en cuestión, y si no lo tenemos redireccionamos a la ruta ‘/linea’ (estas redirecciones se hacen con el método get).

El siguiente bloque de código atiende la ruta para mostrar el detalle de una línea específica.

get '/linea/:cve_linea' do
  @linea = Linea.get(params[:cve_linea])
  if @linea
    erb :cons_linea
  else
    redirect '/linea'
  end
end

En esta ruta lo único que hacemos es buscar el registro correspondiente a la clave de línea que recibimos como parámetro y lo guardamos en la variable @linea. Si el registro fue localizado,
llamamos a la vista cons_linea que se encarga de presentar el detalle.

Para mantener breve el tutorial, no se incluye aquí el código para definir las rutas para editar y borrar registros, pero imagino que a estas alturas ya tienen una buena idea de como sería. Al final del artículo puedes consultar como obtener el código fuente completo de esta aplicación, donde vienen todas las rutas.

Vistas

En el directorio en donde guardamos el archivo sinatra_test.rb crearemos un directorio llamado ‘views’ donde pondremos los archivos que definen las vistas de la aplicación. La primer vista que crearemos es la plantilla base en la que irán embedidas las otras vistas. El archivo de dicha vista se debe llamar layout.erb (la extensión .erb, es porque estamos utilizando el lenguaje de plantillas erb). El contenido de layout.erb es el siguiente:

<html>
 <head>
   <title>Ejemplo Sinatra</title>
   <meta httpequiv="ContentType" content="text/html; charset=utf8" />
 </head>
 <body>
   <%= yield %>
 </body>
</html>

Como se puede apreciar, es código HTML común y corriente, con una sola línea de código en Ruby (<%= yield %>). Cada que se manda a llamar a una vista con erb, se utiliza esta plantilla y el sistema sustituye la línea <%= yield %> por el código de la vista correspondiente.

Nuestra vista index puede ser algo tan sencillo como:

<h1>Menú Principal</h1>
 <p><a href="/linea">Registro de líneas</a></p>
 <p><a href="/listalineas">Listado de líneas</a></p>

La vista reg_linea con la forma de captura para registrar líneas sería:

<form action="/linea" method="post">
 <label>Clave de línea </label>
 <input type="text" name="cve_linea" id="cve_linea">
 <br/>
 <label>Línea </label>
 <input type="text" name="linea" id="linea">
 <br/>
 <input type="submit" value="Guardar"/>
</form>

A continuación definimos la vista cons_linea que muestra el detalle de una línea. Para ello recurrimos a la variable @linea que declaramos al definir la ruta get '/linea/:cve_linea'.  Dicha variable contiene el registro correspondiente a la clave de línea que se recibió en el parámetro :cve_linea

<label>Clave: </label><%= @linea.cve_linea %><br/>
<label>Línea: </label><%= @linea.linea %>
<p><a href="/linea">Registrar una línea</a></p>
<p><a href="/">Menú Principal</a></p>

Ahora veamos el código de la vista que muestra el listado de líneas. Recordemos que al recibir las peticiones a esta ruta generamos una variable @lineas con el contenido de todas las lineas en la base de datos.

<p>Líneas registradas: <%= @lineas.length %></p>
<table>
<% @lineas.each do |linea| %>
 <tr>
 <td><%= linea.cve_linea %></td><td><%= linea.linea %></td>
 <td><a href="/editlinea/<%= linea.cve_linea %>">[editar]</a></td>
 <td><a href="/borralinea/<%= linea.cve_linea %>">[borrar]</a></td>
 </tr>
<% end %>
</table>
<p><a href="/">Menú Principal</a></p>

Por razones de espacio, no incluyo aquí el código del resto de las vistas, pero el código completo incluyendo las rutas y funcionalidad para editar y borrar, así como el uso de una hoja de estilos personalizable, se puede descargar en: http://salomonrt.wordpress.com/2011/09/28/instalar-sinatra-postgresql-datamapper-en-debian-squeeze/

Ejecutar la aplicación

Para ejecutar nuestra aplicación, vamos al directorio en donde tenemos el archivo sinatra_test.rb y en la línea de comando tecleamos:

ruby sinatra_test.rb

Una vez que este corriendo nuestra aplicación, podremos accederla con un navegador en la dirección: http://localhost:4567 (por default Sinatra escucha en el puerto 4567).

Bio

Salomón Rincón es Director General de Top Systems y catedrático en la Universidad Popular Autónoma del Estado de Puebla (UPAEP). srincon@topsystems.com.mx