Portada SoloP 153

Anuncio
REDES(ror)
21/9/07
14:26
Página 54
REDES
Programación ágil
con Ruby on Rails
DABNE TECNOLOGÍAS DE LA INFORMACIÓN
Conflicto de intereses en un
escenario conocido: el cliente
presiona para conseguir cambios en
la aplicación que se está
desarrollando: desde su punto de
vista los cambios son aspectos
obvios, poca cosa. El desarrollador
se resiste a aceptar esos cambios: es
tirar por la borda trabajo ya
realizado. El comercial también se
resiste: cambios significa más
tiempo, y más tiempo significa
menos dinero. Estas tensiones,
inevitables, se atenúan si
disponemos de metodologías de
desarrollo menos rígidas y de
estructuras de soporte para la
programación más productivas.
Ruby on Rails es una de esas
estructuras de soporte.
La escusa, los encuentros digitales
La aplicación que vamos a desarrollar a lo largo
del artículo sirve para gestionar encuentros digitales. Como es sabido, los encuentros digitales se
organizan en sitios web, y permiten a los visitantes formular preguntas que, en un día y hora
señalados, serán respondidas por un única persona, habitualmente famosa. Finalizado el encuentro, la lista de preguntas y respuestas queda disponible para su posterior lectura. Por regla general las preguntas son filtradas o moderadas, según
su pertinencia, y solo pueden ser respondidas por
el famoso/a en cuestión.
Pero nuestra aplicación será mucho más sencilla:
cualquier usuario podrá crear, modificar o elimar
encuentros y preguntas (véanse las figuras 1, 2 y
3). Entonces ¿qué sentido tiene un artículo que
ilustre el desarrollo de una aplicación que no tiene
nada del otro mundo?
SOLO PROGRAMADORES nº 153
54
Figura 1. La aplicación muestra la lista de
encuentros, con la posibilidad de modificar o
eliminar los encuentros creados, y de añadir
uno nuevo.
Figura 2. La vista de un encuentro muestra las
preguntas y sus respuestas, si las hay, con la
posiblidad de modificar o eliminar preguntas,
y de añadir una nueva.
Figura 3. El añadido de una pregunta no
muestra una vista muy elaborada pero
¿cuántos minutos hemos empleado para
conseguir que esta aplicación sea funcional?
El objetivo, la programación ágil
En la actualidad, conviven distintos paradigmas
del desarrollo de aplicaciones. Algunas veces, la
http://digital.revistasprofesionales.com
REDES(ror)
21/9/07
14:26
Página 55
REDES
Programación ágil con Ruby on Rails
especificación es lo más importante, y ceñirse a los requisitos es lo que conducirá al éxito
del proyecto. Si los planos son muy detallados, es posible seguirlos al milímetro. Pero
otras veces el usuario o promotor de la aplicación solo tiene una idea vaga de lo que
quiere. Parte de nuestro trabajo consistirá en
mostrarle posibilidades, opciones. En este
caso, estamos ante un proceso imprevisible,
que exige empezar a construir sin tener los
planos. ¿Qué hacer? Las métodologías ágiles
vienen en nuestra ayuda. En lugar de hacer
reuniones para hablar de cómo será la aplicación, dediquemos ese tiempo a construir un
prototipo funcional que podamos mostrar en
acción al cliente, y sobre el que plantear
modificaciones y mejoras. En principio, como
idea, esto está muy bien. Pero ¿cómo hacerlo
sin que el presupuesto se dispare?
Ciertamente, uno de los inconvenientes de
las metodologías ágiles es que son caras. El
otro es que el cliente debe implicarse como
parte del equipo de desarrollo. Por eso, las
métodologías ágiles no siempre son adecuadas pero, incluso cuando lo son, para que
sean operativas es necesario disponer de
estructuras de soporte a la programación.
Ruby on Rails es una de esas estructuras de
soporte.
El instrumento, Ruby on Rails
Ruby on Rails (RoR) es una estructura de
soporte (framework) para la programación
web desarrollada en lenguaje Ruby. Ruby es
un lenguaje de script, multiplataforma,
orientado a objetos. Y todo ello es software
libre. Ruby fue diseñado para un desarrollo
rápido y sencillo: sencillo por fuera pero
complejo por dentro. El lector encontrará
en la edición 149 de Sólo Programadores
un artículo de introducción a Ruby on Rails,
con indicaciones para instalarlo. El artículo
puede consultarse en la web de Dabne:
http://www.dabne.net/article104.html.
Damos por supuesto que el lector ya tiene
instalado Ruby on Rails, por lo que nos
ponemos manos a la obra en la creación
de la aplicación. No es preciso conocer
Ruby para seguir el artículo. Más bien al
revés, si Ruby on Rails convence, Ruby ya
se aprenderá.
Primer paso, crear la aplicación
Para crear la aplicación, (véase figura 4) abrimos “InstantRails.exe”, menú principal (botón
http://digital.revistasprofesionales.com
LISTADO 1
Figura 4. Creación de la aplicación
encuentrosdigitales.
Figura 5. Cambio de directorio
encuentrosdigitales.
“I”), opción “Rails Applications...”. Pulsamos en
el botón “Create New Rails App...” y en la consola que se abre ejecutamos:
rails encuentrosdigitales
El listado 1 muestra la lista de directorios
que se generan al crear la aplicación.
Entramos en el directorio recien creado
(véase figura 5):
cd encuentrosdigitales/
Ahí podemos ver la estructura de directorios
y ficheros creada por Rails para nuestra
nueva aplicación, tal como se muestra en el
listado 2.
Nota: Si por algún motivo hemos cerrado
InstanRails y queremos retomar el trabajo, al
volver a abrir InstantRails se debe pulsar en
el menú principal (icono “I”), opción “Rails
Applications” y luego “Open Ruby Console
Window”.
Con esto tenemos el esqueleto de nuestra
aplicación Rails, en el que todo tiene un sitio
predefinido. Al principio esta estructuración
puede parecer un engorro, pero luego se
agradece el no tener que pensar dónde hay
que poner cada fichero. El código de nuestra
aplicación irá dentro de “app”, los ficheros
estáticos (css, imágenes, javascripts, etc.)
irán en “public”, la configuración de la base
de datos en “config”, etc.
Rails viene con un servidor web incorporado
que podemos usar mientras desarrollamos.
Se trata de Mongrel. Para arrancar la aplicación con Mongrel, desde InstantRails, menú
principal (botón “I”), “Rails Applications”,
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
create
Creación de
la aplicación
app/controllers
app/helpers
app/models
app/views/layouts
config/environments
components
db
doc
lib
lib/tasks
log
public/images
public/javascripts
public/stylesheets
script/performance
script/process
test/fixtures
test/functional
test/integration
test/mocks/development
test/mocks/test
test/unit
vendor
vendor/plugins
tmp/sessions
tmp/sockets
tmp/cache
tmp/pids
Rakefile
README
app/controllers/application.rb
app/helpers/application_helper.rb
test/test_helper.rb
config/database.yml
config/routes.rb
public/.htaccess
config/boot.rb
config/environment.rb
config/environments/production.rb
config/environments/development.rb
config/environments/test.rb
script/about
script/breakpointer
script/console
script/destroy
script/generate
script/performance/benchmarker
script/performance/profiler
script/process/reaper
script/process/spawner
script/process/inspector
script/runner
script/server
script/plugin
public/dispatch.rb
public/dispatch.cgi
public/dispatch.fcgi
public/404.html
public/500.html
public/index.html
public/favicon.ico
public/robots.txt
public/images/rails.png
public/javascripts/prototype.js
public/javascripts/effects.js
public/javascripts/dragdrop.js
public/javascripts/controls.js
public/javascripts/application.js
doc/README_FOR_APP
log/server.log
log/production.log
log/development.log
log/test.log
55
SOLO PROGRAMADORES nº 153
REDES(ror)
21/9/07
14:26
Página 56
REDES
LISTADO 2
.
|—
|—
|—
|
|
|
|
|
|
|
|—
|—
|
|
|
|
|
|
|
|
|—
|—
|
|—
|
|—
|
|
|
|
|—
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|—
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|—
|
|
|
|
|
|
|
|
|—
|
|
|
|
`—
Estructura de
la aplicación
README
Rakefile
app
|— controllers
|
`— application.rb
|— helpers
|
`— application_helper.rb
|— models
`— views
`— layouts
components
config
|— boot.rb
|— database.yml
|— environment.rb
|— environments
|
|— development.rb
|
|— production.rb
|
`— test.rb
`— routes.rb
db
doc
`— README_FOR_APP
lib
`— tasks
log
|— development.log
|— production.log
|— server.log
`— test.log
public
|— 404.html
|— 500.html
|— dispatch.cgi
|— dispatch.fcgi
|— dispatch.rb
|— favicon.ico
|— images
|
`— rails.png
|— index.html
|— javascripts
|
|— application.js
|
|— controls.js
|
|— dragdrop.js
|
|— effects.js
|
`— prototype.js
|— robots.txt
`— stylesheets
script
|— about
|— breakpointer
|— console
|— destroy
|— generate
|— performance
|
|— benchmarker
|
`— profiler
|— plugin
|— process
|
|— inspector
|
|— reaper
|
`— spawner
|— runner
`— server
test
|— fixtures
|— functional
|— integration
|— mocks
|
|— development
|
`— test
|— test_helper.rb
`— unit
tmp
|— cache
|— pids
|— sessions
`— sockets
vendor
`— plugins
SOLO PROGRAMADORES nº 153
56
Figura 6. Inicio de la aplicación con el
servidor web Mongrel.
Figura 7. La aplicación en acción.
“Manage Rails Applications...”, seleccionar
“encuentrosdigitales” y pulsar en “Start with
Mongrel” (véase figura 6). Se abrirá una consola donde aparecerán los logs y el mensaje
de que pulsando Ctrl+C se cierra Mongrel.
Ahora, si vamos al navegador y tecleamos
http://localhost:3000/ en la barra de direcciones veremos una página de bienvenida de
Rails, donde nos explica cuáles son los
siguientes pasos que deberemos dar (véase
figura 7).
ción va mucho más lenta pero es más cómoda para el programador. En el entorno de
pruebas se cargan todos los datos de pruebas en la base de datos para cada prueba
que se ejecuta, independizando así los resultados de unas y otras. Por este motivo hay
que tener una base de datos aparte para
pruebas, puesto que si no perderíamos todos
los datos que tuviesemos en la base de datos
cada vez que ejecutásemos las pruebas.
Cuando Rails genera un nuevo proyecto crea
un fichero llamado “database.yml” en el
directorio “config” con secciones para configurar cada una de las tres bases de datos.
Como Rails destruye todos los datos en la
base de datos de pruebas cada vez que se
ejecuta un test, hay que tener cuidado de no
poner la misma configuración en la de pruebas que en las otras.
Editamos el fichero “config/database.yml”
(véase listado 4) y modificamos los datos de
conexión a las bases de datos que acabamos
de crear. (El directorio debe ser
“InstantRails/rails_apps/encuentrosdigitales/config/”). Además, añadimos una línea
para indicarle a Rails que el set de caracteres
de la base de datos es UTF8. Dado que por
defecto los ficheros que genera Rails están
en utf8 esto es lo más recomendable.
Nota sobre la edición de ficheros: Como es
natural, los ficheros se pueden editar con
cualquier editor, pero es muy recomendable
que se guarden con la codificación UTF-8. El
bloc de notas de Windows tiene una opción
para ello.
Segundo paso, crear la base de datos
Creamos una base de datos para el desarrollo y un usuario asociado para acceder a ella.
En este caso usaremos MySQL pero podemos
usar otras como SQLite o Postgres. El set de
caracteres de la base de datos será utf8. Para
ello, en el directorio “InstantRails”, ejecutamos las sentencias que se muestran en el listado 3. (Es preciso estar en una consola de
Ruby, que se pude abir desde el menú principal de InstantRails, opción “Rails
Applications”, “Open Ruby Console Window”.
Observemos que el directorio actual será
“InstantRails/rails_app”, por lo que habrá
que moverse al directorio superior).
En realidad deberíamos crear tres bases de
datos, una para cada entorno de ejecución
de Rails: desarrollo (development), pruebas
(test) y producción (production). Rails incorpora el concepto de “entornos” para representar las etapas del ciclo de vida de una
aplicación. El entorno se especifica en la
variable de entorno “RAILS_ENV”. Cada
entorno tiene su propia base de datos para
no interferir con los demás.
En desarrollo se recargan todas las clases
cada vez que se produce una llamada a una
acción, con lo que siempre se tienen cargados los últimos cambios realizados, evitando
tener que reiniciar la aplicación. En producción se cargan las clases una sola vez buscando la eficiencia. En desarrollo la aplica-
Más convención y menos configuración
Ruby on Rails está construido siguiendo el
patrón Modelo-Vista-Controlador (MVC).
MVC es un patrón de diseño usado para
separar el modelo de datos de la aplicación,
la interfaz de usuario y la lógica de control
en tres capas diferentes con unas mínimas
dependencias entre ellas:
http://digital.revistasprofesionales.com
REDES(ror)
21/9/07
14:26
Página 57
REDES
Programación ágil con Ruby on Rails
LISTADO 3
Creación de la bd de desarrollo
cd mysql
mysql -u root
CREATE DATABASE encuentrosdigitales_development CHARACTER SET utf8;
GRANT ALL PRIVILEGES ON encuentrosdigitales_development.* TO usuario@localhost IDENTIFIED BY ‘solop’;
exit
El controlador es el componente que recibe la petición del navegador y ejecuta la
acción especificada por el usuario.
El modelo es la capa de datos que se usa,
generalmente desde el controlador, para
leer, añadir, modificar o borrar datos
almacenados, por ejemplo, en una base
de datos relacional.
La vista es la representación de los datos
que el usuario ve en su pantalla.
ActiveRecord es el sistema de mapeo objetorelacional en Rails, la M en el patrón MVC. La
responsabilidad del modelo en el paradigma
MVC es ocuparse de la gestión del almacenamiento de los datos de la aplicación. Sin
embargo, ActiveRecord es más que una simple librería para ejecutar queries SQL: automáticamente mapea tablas de la base datos
con clases en la aplicación Rails, crea métodos públicos para todos los campos de la
base de datos y añade muchos otros métodos útiles para acceder los datos de la base
de datos.
Además, Rails hace suposiciones sobre los
nombres de las cosas. Por ejemplo, el nombre del modelo es singular y la tabla que
contiene los datos de ese modelo tiene el
mismo nombre pero en plural. Si por alguna
razón no nos gusta que sea así es fácil
decirle a Rails que lo haga de otra forma. Si
creamos un “scaffold” a partir de un modelo el controlador tendrá el nombre del
modelo pero en plural. (Un “scaffold” es un
“andamio” para nuestra aplicación. Es código generado para realizar las operaciones
básicas, como crear, editar y borrar registros,
con nuestros modelos.
Podríamos crear las tablas de nuestra base
de datos directamente con comandos SQL,
pero Rails tiene un sistema muy bueno, e
independiente del gestor de base de datos
que usemos, para definir la estructura de
nuestra base de datos, escribiéndola en Ruby
en los ficheros de “migraciones” y haciendo
que un adaptador la convierta a la sintaxis
adecuada para nuestro gestor de base de
datos. Además, de esta forma también podemos mantener un histórico de los cambios
que vamos haciendo en la base de datos y
podemos avanzar o retroceder a la versión
http://digital.revistasprofesionales.com
LISTADO 4
database.yml
development:
adapter: mysql
database: encuentrosdigitales_development
username: usuario
password: solop
host: localhost
encoding: utf8
# Warning: The database defined as ‘test’ will be erased and
# re-generated from your development database when you run ‘rake’.
# Do not set this db to the same as development or production.
test:
adapter: mysql
database: encuentrosdigitales_test
username: usuario
password: solop
host: localhost
encoding: utf8
production:
adapter: mysql
database: encuentrosdigitales_production
username: usuario
password: solop
host: localhost
encoding: utf8
LISTADO 5
exists
exists
exists
create
create
create
create
create
ruby script/generate model Encuentro
app/models/
test/unit/
test/fixtures/
app/models/encuentro.rb
test/unit/encuentro_test.rb
test/fixtures/encuentros.yml
db/migrate
db/migrate/001_create_encuentros.rb
que queramos fácilmente. También facilita el
despliegue en múltiples servidores de bases
de datos, si la aplicación llega a tener esas
dimensiones.
Las migraciones se pueden generar independientemente o al crear cada modelo.
campos que va a tener nuestra tabla en la
base de datos. El número 001 al principio del
nombre del fichero indica que es la primera
migración de esta aplicación.
Tercer paso, crear el modelo
Una migración tiene que implementar los
métodos “self.up” y “self.down”, que son
creados automáticamente al generar la
migración. El código que hay en “self.up” es
lo que se ejecuta al migrar la base de datos
a una versión superior, y el código en
“self.down” cuando migramos a una versión
inferior. En nuestra primera migración creamos una tabla llamada “encuentros” y añadimos columnas/campos para el nombre y
la descripción del encuentro. En “self.down”
simplemente borramos la tabla creada, para
el caso de que queramos volver a la versión
0, en la que la base de datos está vacía.
Empezamos con nuestra aplicación generando un modelo llamado “Encuentro” (en
una consola de Ruby, directorio “encuentrosdigitales”:
ruby script/generate model Encuentro
La salida será como se muestra en el listado 5.
La salida del comando de generación del
modelo será como se muestra en el listado 5.
Como podemos ver en la última línea, Rails
crea automáticamente una migración para
este modelo. Ahí es donde definimos los
Cuarto paso, crear la migración
57
SOLO PROGRAMADORES nº 153
REDES(ror)
21/9/07
14:26
Página 58
REDES
LISTADO 6
Editamos el fichero “db/migrate/001_create_encuentros.rb” y añadimos los campos
que se muestran en el listado 6. Si ponemos
las columnas “created_at” y “updated_at”
con tipo “datetime” o “timestamp” en nuestra tabla, Rails se encargará de rellenarlas
automáticamente con los valores correctos.
Todas la tablas usadas por ActiveRecord
(excepto las tablas join) tienen que tener un
campo de clave primaria llamado id. No lo
especificamos en el código de la migración
porque Rails lo crea automáticamente a
menos que le indiquemos lo contrario (con
la opción “:id => false” al crear la tabla).
Para ver una descripción completa de las opciones disponibles en las migraciones, se puede
recurrir a la documentación disponible en
http://api.rubyonrails.org/classes/ActiveRecord/
Migration.html.
Ahora ejecutamos la tarea de migración de
la base de datos, para que Rails se encargue
de crear los campos que hemos especificado
(en el directorio “encuentros digitales”):
001_create_encuentros.rb
class CreateEncuentros < ActiveRecord::Migration
def self.up
create_table :encuentros do |t|
t.column :nombre, :string
t.column :descripcion, :text
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
end
def self.down
drop_table :encuentros
end
end
rake db:migrate
La salida del comando se muestra en la
figura 8.
Rails usa el entorno de desarrollo por defecto, con lo que los cambios se han hecho en
nuestra base de datos de desarrollo. Si queremos que se produzcan en otra base de
datos podemos especificar el entorno usando
la variable RAILS_ENV en el comando rake:
rake db:migrate RAILS_ENV=production
Rails también ha creado automáticamente
una tabla llamada “schema_info”. Esta tabla
tiene sólo un campo, “version”, cuyo valor
indica cuál es el número de migración
actual. Los scripts de migración usan este
dato para saber qué migraciones tienen que
ejecutar para poner todo al día.
Además, la migración crea un fichero llamado “db/schema.rb”, con el mismo formato
Figura 9. Creación del scaffold para el modelo.
que los ficheros de migración, que describe
el estado actual de la base de datos en su
totalidad. Los scripts de migración mantienen este fichero al día, con lo que no se
debería editar a mano.
Quinto paso, crear el scaffold
Como se ha mencionado anteriormente, un
“scaffold” es un “andamiaje” de nuestra aplicación. Es código generado para realizar las
operaciones básicas (crear, editar y borrar
registros) con nuestros modelos.
Generamos un “scaffold” para este modelo:
ruby script/generate scaffold Encuentro
La salida del comando puede verse en la
figura 9.
Ahora editamos el fichero “config/routes.rb”
(directorio “encuentrosdigitales”) para que por
defecto vaya al controlador encuentros, añadiendo, como se muestra en la figura 10, la
siguiente línea (alerta porque antes de la coma
hay dos comillas simples y no una doble):
map.connect ‘’, :controller => “encuentros”
El fichero “routes.rb” contiene las reglas para
el manejo de las URL, quitándole esta responsabilidad al servidor web. Aplicando el principio de más convención y menos configuración, Rails realiza una serie de mapeos convecionales entre la vista y el controlador. Lo que
queremos conseguir con la línea anterior es
que cualquier petición de nuestra aplicación
se dirija al controlador “encuentros”.
Sexto paso, personalizar la vista
Figura 8. Migración del modelo.
SOLO PROGRAMADORES nº 153
58
Eliminamos el fichero public/index.html, que es
donde está la página de bienvenida a Rails que
veíamos antes, y vamos a http://localhost:3000
http://digital.revistasprofesionales.com
REDES(ror)
21/9/07
14:26
Página 59
REDES
Programación ágil con Ruby on Rails
Figura 11. La aplicación, tal como la ha
creado el scaffold.
LISTADO 8
Figura 10. Asignando el control encuentros con edit, desde una consola, a la antigua usanza.
LISTADO 7
app/views/layout/encuentros.rhtml
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml” xml:lang=”es” lang=”es”>
<head>
<meta http-equiv=”content-type” content=”text/html;charset=UTF-8” />
<title>Encuentros digitales</title>
<%= stylesheet_link_tag ‘scaffold’ %>
</head>
<body>
<div id=”cabecera”>
<h1><%= link_to “Encuentros digitales”, :controller => ‘encuentros’ %></h1>
</div>
<p style=”color: green”><%= flash[:notice] %></p>
<!—Content start—>
<%= yield %>
<!—Content end—>
<div id=”pie”>
</div>
</body>
</html>
en el navegador. Vemos la aplicación básica
que ha creado el scaffold (véase figura 11). Lo
que genera el scaffold es el punto de partida.
Ahora habrá que irlo modificando hasta que
nuestra aplicación tenga el aspecto deseado.
Los “layouts” se usan en Rails para poner en un
solo directorio el contenido común a todas las
vistas, como son la cabecera de la página y el pie.
Editamos “app/views/layout/encuentros.rhtml”
para añadirle una cabecera y algunas otras
LISTADO 9
public/stylesheets/
scaffold.css
#cabecera {
background-color: lightblue;
padding: 20px;
}
cosillas (véase listado 7). Así tenemos una plantilla general para todas las acciones, en la que
ponemos el código común a todas las vistas.
Luego con la llamada a “yield” incluimos el
contenido concreto de la vista correspondiente a la acción actual, que es lo que se encuentra en cada uno de los ficheros rhtml que hay
en “app/views/encuentros/”.
Como queremos que esta plantilla sea para toda
la aplicación, no sólo para este controlador
renombramos el fichero “encuentros.rhtml”
como “application.rhtml”. Así se aplicará a todos
los controladores que no tengan su “layout”
específico (un fichero con nombre
“modelo_en_plural.rhtml” en el directorio
“app/views/layouts/”).
Añadimos estilos para la cabecera en
“public/stylesheets/scaffold.css”, como se
muestra en el listado 8. Esta parte ya depende de las habilidades gráficas de cada uno y
no entra dentro del objetivo de este artículo,
así que lo dejaremos tirando a feote.
Cambiamos la vista del listado de encuentros,
que está en “app/views/encuentros/list.rhtml”,
para que se vea más clara, como se muestra
en el listado 9.
app/views/encuentros/list.rhtml
<% for encuentro in @encuentros %>
<p>
<%= link_to encuentro.nombre, :action => ‘show’, :id => encuentro %>
<%= link_to image_tag(‘edit.gif’), :action => ‘edit’, :id => encuentro %>
<%= link_to image_tag(‘delete.gif’), { :action => ‘destroy’, :id => encuentro }, :confirm => ‘¿Seguro?’, :method => :post %>
<br>
<%= encuentro.descripcion %>
</p>
<% end %>
<%= link_to ‘Anterior’, { :page => @encuentro_pages.current.previous } if @encuentro_pages.current.previous %>
<%= link_to ‘Siguiente’, { :page => @encuentro_pages.current.next } if @encuentro_pages.current.next %>
<p>
<%= link_to ‘Nuevo encuentro’, :action => ‘new’ %>
</p>
http://digital.revistasprofesionales.com
59
SOLO PROGRAMADORES nº 153
REDES(ror)
21/9/07
14:26
Página 60
REDES
LISTADO 10
Como se puede ver hemos puesto imágenes
en lugar de textos en los enlaces para las
acciones de editar y borrar, con lo que ahora
hay que poner en el directorio “public/images”
los ficheros “edit.gif” y “delete.gif” correspondientes. En Internet hay multitud de iconos
gratis e incluso libres que podemos usar, y
además el lector puede encontrar estos ficheros en el cd-rom.
Ahora vamos a editar la vista de creación de
nuevo encuentro, para quitarle al formulario los
campos correspondientes a las marcas de tiempo de creación y actualización, ya que Rails se
encarga de rellenarlos automáticamente.
Editamos “app/views/encuentros/new.rhtml”
y “app/views/encuentros/edit.rhtml” para
poner los textos en castellano, como se
muestra en los listados 10 y 11.
Como podemos observar, los campos del
formulario no están en ninguno de estos
ficheros y en su lugar tenemos una línea:
<%= render :partial => ‘form’ %>
Esto lo que hace es incluir el contenido del
fichero “_form.rhtml” en ese punto, de
forma que se pueden reutilizar trozos de
código en las vistas. En este caso, ya que el
LISTADO 12
<h1>Nuevo encuentro</h1>
<% form_tag :action => ‘create’ do %>
<%= render :partial => ‘form’ %>
<%= submit_tag “Crear” %>
<% end %>
<%= link_to ‘Volver’, :action => ‘list’ %>
LISTADO 11
app/views/encuentros/edit.rhtml
<h1>Edición del encuentro</h1>
<% form_tag :action => ‘update’, :id => @encuentro do %>
<%= render :partial => ‘form’ %>
<%= submit_tag ‘Editar’ %>
<% end %>
<%= link_to ‘Mostrat’, :action => ‘show’, :id => @encuentro %> |
<%= link_to ‘Volver’, :action => ‘list’ %>
formulario es el mismo para las dos acciones, está contenido en un “partial” que se
incluye desde sus vistas. Obsérvese que el
nombre de fichero de un “partial” lleva un
guión bajo delante, mientras que en la llamada desde la vista sólo se pone el nombre,
sin extensión y sin guión.
Del
fichero
“app/views/encuentros/
_form.rhtml” borramos las líneas que se
muestran en el listado 12 y lo dejamos
como se muestra en el listado 13.
app/views/encuentros/_form.rhtml, borrar
<p><label for=”encuentro_created_at”>Created at</label><br/>
<%= datetime_select ‘encuentro’, ‘created_at’ %></p>
<p><label for=”encuentro_updated_at”>Updated at</label><br/>
<%= datetime_select ‘encuentro’, ‘updated_at’ %></p>
LISTADO 13
app/views/encuentros/new.rhtml
app/views/encuentros/_form.rhtml, final
<%= error_messages_for ‘encuentro’ %>
<!—[form:encuentro]—>
<p><label for=”encuentro_nombre”>Nombre</label><br/>
<%= text_field ‘encuentro’, ‘nombre’, :size => “70” %></p>
<p><label for=”encuentro_descripcion”>Descripción</label><br/>
<%= text_area ‘encuentro’, ‘descripcion’, :cols => “80”, :rows => “5” %></p>
Séptimo paso, pulir
Con esto ya podemos crear, modificar y borrar
encuentros, pero quedan algunas cosas por pulir.
Por ejemplo, podríamos crear un encuentro con
todos los campos vacíos (menos el “id” que crea
automáticamente Rails), pero eso es algo que no
queremos. Para evitar eso tenemos algo muy
potente en los modelos, que son las validaciones.
Con un par de líneas podemos hacer que nuestro modelo no acepte campos vacíos, o que compruebe la longitud de los textos.
En nuestro ejemplo queremos que todo encuentro tenga un nombre y que ese nombre no exceda de los 255 caracteres, que es el tamaño por
defecto del string que hemos puesto antes al
definir los campos de la tabla “encuentros” en la
migración. Eso lo conseguimos añadiendo en
“app/models/encuentro.rb” las dos líneas que, en
el listado 14, se muestran en negrita.
Ahora, si intentamos crear un encuentro con el
nombre vacío nos saldrá un error que nos explica qué ha pasado y nos vuelve a pedir los datos.
De vuelta al paso tres
<!—[eoform:encuentro]—>
LISTADO 14
app/models/encuentro.rb
class Encuentro < ActiveRecord::Base
validates_presence_of :nombre
validates_length_of :nombre, :maximum => 255
end
Nuestra aplicación tiene encuentros pero no
tiene preguntas. Creamos el modelo
“Pregunta” (véase figura 12):
ruby script/generate model Pregunta
La salida del comando se muestra en el listado 15.
Y al paso cuatro
Figura 12. ruby script/generate model Pregunta.
SOLO PROGRAMADORES nº 153
60
Editamos la migración “db/migrate/002_create_preguntas.rb”, añadiendo las líneas que
en el listado 16 se muestran en negrita. Cada
pregunta pertenece a un encuentro, por lo
http://digital.revistasprofesionales.com
REDES(ror)
21/9/07
14:26
Página 61
REDES
Programación ágil con Ruby on Rails
LISTADO 15
exists
exists
exists
create
create
create
exists
create
ruby script/generate model Pregunta
app/models/
test/unit/
test/fixtures/
app/models/pregunta.rb
test/unit/pregunta_test.rb
test/fixtures/preguntas.yml
db/migrate
db/migrate/002_create_preguntas.rb
LISTADO 16
db/migrate/002_create_preguntas.rb
class CreatePreguntas < ActiveRecord::Migration
def self.up
create_table :preguntas do |t|
t.column :pregunta, :text
t.column :respuesta, :text
t.column :encuentro_id, :integer
t.column :created_at, :datetime
t.column :updated_at, :datetime
end
end
def self.down
drop_table :preguntas
end
end
Figura 13. Ejecución de la migración.
que ponemos una referencia al encuentro
correspondiente.
Ejecutamos la migración (véase figura 13):
rake db:migrate
Y al paso cinco
Creamos el “scaffold”, respondiendo “n” a la
pregunta “overwrite public/stylesheets/scaffold.css? [Ynaqd]” (véase figura 14):
ruby script/generate scaffold Pregunta
Cuando pregunte si sobreescribir el fichero
“public/stylesheets/scaffold.css” decimos que
no porque lo hemos modificado antes para
incluir los estilos de la cabecera y si lo sobreescribimos perderíamos dichos cambios.
“app/models/pregunta.rb”, como se muestra
en las líneas en negrita de los listados 17 y
18. De esta manera, nos aseguramos de que
se rellene el campo pregunta y el encuentro
al que pertenece la pregunta.
Después de todo esto, si vamos a
http://localhost:3000/preguntas y creamos
una nueva pregunta vemos que no hay
forma de relacionarla con un encuentro.
Para eso editamos el fichero “app/views/preguntas/_form.rhtml” y añadimos el campo al
formulario, agregando las dos líneas que en
el listado 19 se muestran en negrita.
Como en las vistas no debe ir nada de la
lógica de la aplicación, la función que genera la lista de encuentros para el desplegable
la ponemos en “app/helpers/preguntas_helper.rb”, como se muestra en el listado 20.
Al generar el “scaffold” de un modelo se crea un
fichero llamado “modelo_helper.rb” en “app/helpers”, donde podemos poner las funciones auxiliares que necesitemos usar en nuestras vistas. Si
hace falta que una de estas funciones o “helpers”
sea accesible desde las vistas de varios modelos
hay que ponerla en “application_helper.rb”.
Los helpers son útiles para mantener las vistas
limpias y fáciles de leer. Las vistas no deberían
contener lógica compleja. Si la hay, ésta se
debería refactorizar y moverla a un helper. Los
métodos de la clase “helper” se pueden llamar
desde la vista.
Ahora vamos a modificar la vista
“app/views/encuentros/show.rhtml” para
que se vean todas las preguntas asociadas a
ese encuentro, como se muestra en el listado 21. Después de cada pregunta ponemos
un icono con un enlace para editar la pregunta y añadirle una respuesta y otro para
borrar la pregunta.
Para que al editar la respuesta y darle a guardar vuelva a la pantalla anterior, modificamos
la redirección en el método “update” del controlador (“app/controllers/preguntas_controller.rb), sustituyendo la línea “redirect_to” por la
que se muestra en negrita en el listado 22. En
el mismo fichero, en el método “destroy”, también sustituimos la línea “redirect_to” por la
que en el listado 23 se muestra en negrita. Para
no perder el encuentro al que pertenecía la
pregunta lo guardamos en una variable “e”.
Siguiendo en el mismo fichero (app/controllers/preguntas_controller.rb), para que al pinchar en “Añadir pregunta” aparezca seleccio-
Saltamos el paso seis
También se ha generado un “layout” para
este controlador, pero como queremos que
use el general tenemos que borrar el fichero
“app/views/layouts/preguntas.rhtml”.
Y seguimos por el paso siete, pulir
Añadimos las relaciones y las validaciones a
los modelos en “app/models/encuentro.rb” y
http://digital.revistasprofesionales.com
Figura 14. Creación de scaffold para el modelo.
61
SOLO PROGRAMADORES nº 153
REDES(ror)
21/9/07
14:26
Página 62
REDES
LISTADO 17
app/models/encuentro.rb
class Encuentro < ActiveRecord::Base
has_many :preguntas
validates_presence_of :nombre
validates_length_of :nombre, :maximum => 255
end
LISTADO 18
app/models/pregunta.rb
class Pregunta < ActiveRecord::Base
belongs_to :encuentro
validates_presence_of :pregunta , :encuentro_id
end
nado el encuentro desde el que hemos pinchado añadimos un parámetro “:encuentro_id
=> @encuentro” al enlace y en el método
“new” lo asignamos al objeto pregunta, como
se muestra en la línea en negrita del listado 23.
De nuevo en el mismo fichero, para que al
crear la pregunta vuelva a la pantalla del
encuentro, no a la lista general de todas las
preguntas modificamos la redirección en el
método create, como como se muestra en la
línea en negrita del listado 24.
Ya que estamos editando el controlador
podemos aprovechar y cambiar los textos en
inglés por unos en castellano:
flash[:notice] = ‘Pregunta creada
tros digitales y, para cada encuentro, preguntas. Es cierto que en vista de los
encuentros los mensajes se han puesto en
LISTADO 19
español y se han eliminado los campos
“Created at” y “Updated at”, cosa que no se
ha hecho con las preguntas, así que esta
sería una buena manera de seguir puliendo la aplicación.
Por motivos de espacio y de simplicidad se
ha omitido la validación de usuarios, pero el
objetivo no es tanto desarrollar una aplicación como mostrar la manera de desarrollarla, al estilo Ruby on Rails. Con RoR, en
cuestión de minutos disponemos de una
aplicación operativa que empezamos a pulir,
y que vamos mejorando mientras el tiempo
app/views/preguntas/_form.rhtml
<%= error_messages_for ‘pregunta’ %>
<!—[form:pregunta]—>
<p><label for=”pregunta_encuentro_id”>Encuentro</label><br/>
<%= select ‘pregunta’, ‘encuentro_id’, encuentros %></p>
<p><label for=”pregunta_pregunta”>Pregunta</label><br/>
<%= text_area ‘pregunta’, ‘pregunta’, :cols => “80”, :rows => “5” %></p>
<p><label for=”pregunta_respuesta”>Respuesta</label><br/>
<%= text_area ‘pregunta’, ‘respuesta’, :cols => “80”, :rows => “5” %></p>
<!—[eoform:pregunta]—>
correctamente.’
flash[:notice] = ‘Pregunta actualizada
LISTADO 20
correctamente.’
Conclusiones
En este momento del desarrollo de la aplicación, el lector puede manejar encuen-
LISTADO 21
app/helpers/preguntas_helper.rb
module PreguntasHelper
def encuentros
Encuentro.find(:all).collect {|p| [ p.nombre, p.id ] }
end
end
app/views/encuentros/show.rhtml
<h2><%=h @encuentro.nombre %></h2>
<p><%=h @encuentro.descripcion %></p>
<h3>Preguntas: </h3>
<ul>
<% for p in @encuentro.preguntas %>
<li>
<strong><%= p.pregunta %></strong>
<%= link_to image_tag(‘edit.gif’), :controller => ‘preguntas’, :action => ‘edit’, :id => p %>
<%= link_to image_tag(‘delete.gif’), {:controller => ‘preguntas’, :action => ‘destroy’, :id => p }, :confirm =>
‘¿Seguro?’, :method => :post %>
<br>
<%= p.respuesta %>
</li>
<% end %>
</ul>
<%= link_to “Añadir una pregunta”, :controller => ‘preguntas’, :action => ‘new’, :encuentro_id => @encuentro %>
LISTADO 22
app/controllers/preguntas_controller.rb, update
def update
@pregunta = Pregunta.find(params[:id])
if @pregunta.update_attributes(params[:pregunta])
flash[:notice] = ‘Pregunta actualizada correctamente.’
redirect_to :controller => ‘encuentros’, :action => ‘show’, :id => @pregunta.encuentro
else
render :action => ‘edit’
end
end
SOLO PROGRAMADORES nº 153
62
http://digital.revistasprofesionales.com
REDES(ror)
21/9/07
14:26
Página 63
REDES
Programación ágil con Ruby on Rails
LISTADO 22
y el presupuesto lo permiten. Desde la experiencia de dabne.net, Ruby on Rails aporta la
posibilidad concreta y material de desarrollar según metodologías ágiles que integran
los cambios en la especificación no como un
escollo o un paso atrás sino como algo
inherente al proceso de comunicación en el
que comerciales, técnicos, clientes y usuarios estamos implicados.
app/controllers/preguntas_controller.rb, destroy
def destroy
p = Pregunta.find(params[:id])
e = p.encuentro
p.destroy
redirect_to :controller => ‘encuentros’, :action => ‘show’, :id => e
end
LISTADO 23
app/controllers/preguntas_controller.rb, new
def new
@pregunta = Pregunta.new
@pregunta.encuentro_id = params[:encuentro_id]
end
LISTADO 24
app/controllers/preguntas_controller.rb, create
def create
@pregunta = Pregunta.new(params[:pregunta])
if @pregunta.save
flash[:notice] = ‘Pregunta was successfully created.’
redirect_to :controller => ‘encuentros’, :action => ‘show’, :id => @pregunta.encuentro
else
render :action => ‘new’
end
end
Conferencia Hispana Rails 2007
Durante el próximo mes de
noviembre se celebrará en
Madrid la II Conferencia
Hispana Rails (www.conferenciarails.org/). De entre
las propuestas de ponencia
recibidas hasta el momento
destacamos estas:
APIs de Identidad y Rails
Introducción en el mundo
de las APIs más populares
de la red como Flickr,
Last.fm/Audiscrobbler o
GoogleMaps.
APIs Rest y proxificación
Introducción a las APIs
Rest y qué pueden aportar
a nuestras aplicaciones.
BDD y rSpec
Además del desarrollo
guiado por tests, existe otro
paradigma que va un paso
más allá de éste: el desarrollo orientado a comportamiento.
Caché en Rails
Mecanismos para introducir caché en las diferentes capas por las que
transcurre la resolución de
una petición.
Camping, el microframework
Para qué podríamos querer usar Camping y cómo
hacerlo.
Cómo programar una
araña web con Rails
Taller práctico en el que se
explicará cómo construir
desde cero una araña web.
Flickr con Rails
Taller práctico para construir una interfaz interactiva con el servicio Flickr.
Integración de Rails en
el escritorio con Slingshot
Slingshot es la alternativa
Open Source propuesta por
Joyent a los recientes desarrollos de Microsoft y
Adobe para integrar aplicaciones web en el escritorio
usando Ruby on Rails.
Inteligencia artificial aplicada a la publicidad con Ruby
Cómo utilizar la inteligencia artificial en Ruby on
Rails para optimizar automáticamente la publicidad de una aplicación.
Introducción a RubyStack
Presentación y demostración de RubyStack, una dis-
http://digital.revistasprofesionales.com
tribucion integrada Open
Source de Ruby on Rails.
JRuby
on Rails:
Agilidad en la empresa
JRuby on Rails proporciona
la agilidad de Rails sobre servidores y aplicaciones Java.
La internacionalización sí es posible
Aunque Rails no tiene soporte incorporado para la internacionalización, hay muchas
opciones para abrir las puertas de una aplicación a
audiencias más grandes.
Más allá del testing
Vale. Tests. Sabes que los
necesitas, sabes que tienes
que hacerlos. Pero, ¿por
dónde empezar? ¿Qué tal
especificar el comportamiento de tu aplicación en
forma de tests?
Phobos: scripting de servidor para plataforma Java
Phobos es un en entorno
de aplicaciones ligero con
el rendimiento, escalabili-
dad y fiabilidad de la plataforma Java.
Programa en Rails
como si jugases con Lego
Cómo funcionan los plugins de Rails y cómo podemos crear nuestros propios
plug-ins.
Proyectos de bajo coste
con limitaciones severas
de tiempo y recursos
Existen aplicaciones cuyo
desarrollo se plantea con una
limitación de tiempo y recursos severa. Rails nos ayuda.
Rails against the machine
Cómo crear entornos virtuales clonables para el despliegue de aplicaciones Rails
con Capistrano 2 y XEN.
Rails desde el código
Bajaremos a las entrañas de
Rails y echaremos un vistazo a la implementacion del
framework.
Rails para torpes como yo
Es difícil transmitir qué es
Ruby on Rails y qué signi63
fica que nos guste y que
estemos felices por programar en este lenguaje.
Rails y Globalize: un tren
con destinos internacionales
Cómo Globalize puede
enseñar a tu aplicación Rails
monolingüe a aprender
idiomas.
Seguridad web en
aplicaciones Rails
Un repaso a la seguridad
web y a algunas de las vulnerabilidades más comunes.
Tractis, un enfoque
técnico
Con un enfoque lo más
técnico posible, comentaremos cómo trabajamos y
qué herramientas usamos.
Unión de MXML con
Rails
Se tratará la unión del lenguaje MXML usado por la
plataforma Flex con el framework Rails para conseguir una aplicación de las
que Adobe denomina RIA.
SOLO PROGRAMADORES nº 153
Descargar