Upload
phambao
View
229
Download
0
Embed Size (px)
Citation preview
SOLO PROGRAMADORES nº 153 54
REDES
http://digital.revistasprofesionales.com
Programación ágilcon Ruby on Rails
La escusa, los encuentros digitales
La aplicación que vamos a desarrollar a lo largo
del artículo sirve para gestionar encuentros digi-
tales. Como es sabido, los encuentros digitales se
organizan en sitios web, y permiten a los visitan-
tes formular preguntas que, en un día y hora
señalados, serán respondidas por un única perso-
na, habitualmente famosa. Finalizado el encuen-
tro, la lista de preguntas y respuestas queda dis-
ponible para su posterior lectura. Por regla gene-
ral 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?
El objetivo, la programación ágil
En la actualidad, conviven distintos paradigmas
del desarrollo de aplicaciones. Algunas veces, la
DABNE TECNOLOGÍAS DE LA INFORMACIÓN
Programación ágilcon Ruby on Rails
Conflicto de intereses en unescenario conocido: el clientepresiona para conseguir cambios enla aplicación que se estádesarrollando: desde su punto devista los cambios son aspectosobvios, poca cosa. El desarrolladorse resiste a aceptar esos cambios: estirar por la borda trabajo yarealizado. El comercial también seresiste: cambios significa mástiempo, y más tiempo significamenos dinero. Estas tensiones,inevitables, se atenúan sidisponemos de metodologías dedesarrollo menos rígidas y deestructuras de soporte para laprogramación más productivas.Ruby on Rails es una de esasestructuras de soporte.
Figura 1. La aplicación muestra la lista deencuentros, con la posibilidad de modificar oeliminar los encuentros creados, y de añadiruno nuevo.
Figura 2. La vista de un encuentro muestra laspreguntas y sus respuestas, si las hay, con laposiblidad de modificar o eliminar preguntas,y de añadir una nueva.
Figura 3. El añadido de una pregunta nomuestra una vista muy elaborada pero¿cuántos minutos hemos empleado paraconseguir que esta aplicación sea funcional?
REDES(ror) 21/9/07 14:26 Página 54
SOLO PROGRAMADORES nº 15355
REDES
http://digital.revistasprofesionales.com
Programación ágil con Ruby on Rails
especificación es lo más importante, y ceñir-
se a los requisitos es lo que conducirá al éxito
del proyecto. Si los planos son muy detalla-
dos, es posible seguirlos al milímetro. Pero
otras veces el usuario o promotor de la apli-
cació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 aplica-
ció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 adecua-
das 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) abri-
mos “InstantRails.exe”, menú principal (botón
“I”), opción “Rails Applications...”. Pulsamos en
el botón “Create New Rails App...” y en la con-
sola 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 aplica-
ción con Mongrel, desde InstantRails, menú
principal (botón “I”), “Rails Applications”,
Figura 4. Creación de la aplicaciónencuentrosdigitales.
Figura 5. Cambio de directorioencuentrosdigitales.
LISTADO 1 Creación de la aplicación
create create app/controllers create app/helpers create app/models create app/views/layouts create config/environments create components create db create doc create lib create lib/tasks create log create public/images create public/javascripts create public/stylesheets create script/performance create script/process create test/fixtures create test/functional create test/integration create test/mocks/development create test/mocks/test create test/unit create vendor create vendor/plugins create tmp/sessions create tmp/sockets create tmp/cache create tmp/pids create Rakefile create README create app/controllers/application.rbcreate app/helpers/application_helper.rbcreate test/test_helper.rb create config/database.yml create config/routes.rb create public/.htaccess create config/boot.rb create config/environment.rb create config/environments/production.rb create config/environments/development.rb create config/environments/test.rb create script/about create script/breakpointer create script/console create script/destroy create script/generate create script/performance/benchmarker create script/performance/profiler create script/process/reaper create script/process/spawner create script/process/inspector create script/runner create script/server create script/plugin create public/dispatch.rb create public/dispatch.cgi create public/dispatch.fcgi create public/404.html create public/500.html create public/index.html create public/favicon.ico create public/robots.txt create public/images/rails.png create public/javascripts/prototype.jscreate public/javascripts/effects.js create public/javascripts/dragdrop.js create public/javascripts/controls.js create public/javascripts/application.js create doc/README_FOR_APP create log/server.log create log/production.log create log/development.log create log/test.log
REDES(ror) 21/9/07 14:26 Página 55
SOLO PROGRAMADORES nº 153 56
REDES
http://digital.revistasprofesionales.com
“Manage Rails Applications...”, seleccionar
“encuentrosdigitales” y pulsar en “Start with
Mongrel” (véase figura 6). Se abrirá una con-
sola 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 direc-
ciones veremos una página de bienvenida de
Rails, donde nos explica cuáles son los
siguientes pasos que deberemos dar (véase
figura 7).
Segundo paso, crear la base de datos
Creamos una base de datos para el desarro-
llo 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”, ejecuta-
mos las sentencias que se muestran en el lis-
tado 3. (Es preciso estar en una consola de
Ruby, que se pude abir desde el menú prin-
cipal 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 incor-
pora el concepto de “entornos” para repre-
sentar 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 carga-
dos los últimos cambios realizados, evitando
tener que reiniciar la aplicación. En produc-
ción se cargan las clases una sola vez bus-
cando la eficiencia. En desarrollo la aplica-
ción va mucho más lenta pero es más cómo-
da para el programador. En el entorno de
pruebas se cargan todos los datos de prue-
bas en la base de datos para cada prueba
que se ejecuta, independizando así los resul-
tados 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 confi-
gurar 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 prue-
bas 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/encuentrosdigita-
les/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.
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:
LISTADO 2 Estructura dela 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
Figura 6. Inicio de la aplicación con elservidor web Mongrel.
Figura 7. La aplicación en acción.
REDES(ror) 21/9/07 14:26 Página 56
SOLO PROGRAMADORES nº 153
� El controlador es el componente que reci-
be 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 objeto-
relacional en Rails, la M en el patrón MVC. La
responsabilidad del modelo en el paradigma
MVC es ocuparse de la gestión del almace-
namiento de los datos de la aplicación. Sin
embargo, ActiveRecord es más que una sim-
ple librería para ejecutar queries SQL: auto-
máticamente mapea tablas de la base datos
con clases en la aplicación Rails, crea méto-
dos públicos para todos los campos de la
base de datos y añade muchos otros méto-
dos útiles para acceder los datos de la base
de datos.
Además, Rails hace suposiciones sobre los
nombres de las cosas. Por ejemplo, el nom-
bre 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 mode-
lo el controlador tendrá el nombre del
modelo pero en plural. (Un “scaffold” es un
“andamio” para nuestra aplicación. Es códi-
go 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 pode-
mos mantener un histórico de los cambios
que vamos haciendo en la base de datos y
podemos avanzar o retroceder a la versión
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 indepen-
dientemente o al crear cada modelo.
Tercer paso, crear el modelo
Empezamos con nuestra aplicación gene-
rando un modelo llamado “Encuentro” (en
una consola de Ruby, directorio “encuen-
trosdigitales”:
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
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.
Cuarto paso, crear la migración
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 crea-
mos una tabla llamada “encuentros” y aña-
dimos 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.
57
REDESProgramación ágil con Ruby on Rails
http://digital.revistasprofesionales.com
LISTADO 3 Creación de la bd de desarrollo
cd mysqlmysql -u rootCREATE DATABASE encuentrosdigitales_development CHARACTER SET utf8;GRANT ALL PRIVILEGES ON encuentrosdigitales_development.* TO usuario@localhost IDENTIFIED BY ‘solop’;exit
LISTADO 5 ruby script/generate model Encuentro
exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/encuentro.rb create test/unit/encuentro_test.rb create test/fixtures/encuentros.yml create db/migrate create db/migrate/001_create_encuentros.rb
LISTADO 4 database.yml
development: adapter: mysql database: encuentrosdigitales_development username: usuario password: solop host: localhostencoding: 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: solophost: localhostencoding: utf8
production: adapter: mysql database: encuentrosdigitales_production username: usuario password: solophost: localhostencoding: utf8
REDES(ror) 21/9/07 14:26 Página 57
SOLO PROGRAMADORES nº 153
Editamos el fichero “db/migrate/001_crea-
te_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 nues-
tra 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 opcio-
nes 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”):
rake db:migrate
La salida del comando se muestra en la
figura 8.
Rails usa el entorno de desarrollo por defec-
to, con lo que los cambios se han hecho en
nuestra base de datos de desarrollo. Si que-
remos 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 llama-
do “db/schema.rb”, con el mismo formato
que los ficheros de migración, que describe
el estado actual de la base de datos en su
totalidad. Los scripts de migración mantie-
nen 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 apli-
cació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ña-
diendo, 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 respon-
sabilidad al servidor web. Aplicando el princi-
pio de más convención y menos configura-
ción, Rails realiza una serie de mapeos conve-
cionales 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
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
58
REDES
http://digital.revistasprofesionales.com
LISTADO 6 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
Figura 8. Migración del modelo.
Figura 9. Creación del scaffold para el modelo.
REDES(ror) 21/9/07 14:26 Página 58
SOLO PROGRAMADORES nº 153
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
cosillas (véase listado 7). Así tenemos una plan-
tilla 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 correspondien-
te a la acción actual, que es lo que se encuen-
tra 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 depen-
de 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.
59
REDESProgramación ágil con Ruby on Rails
http://digital.revistasprofesionales.com
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>
LISTADO 8 public/stylesheets/scaffold.css
#cabecera { background-color: lightblue; padding: 20px;
}
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>
Figura 11. La aplicación, tal como la hacreado el scaffold.
REDES(ror) 21/9/07 14:26 Página 59
SOLO PROGRAMADORES nº 153
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” correspon-
dientes. En Internet hay multitud de iconos
gratis e incluso libres que podemos usar, y
además el lector puede encontrar estos fiche-
ros 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 tiem-
po 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
formulario es el mismo para las dos accio-
nes, 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 lla-
mada 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.
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 nues-
tro modelo no acepte campos vacíos, o que com-
pruebe la longitud de los textos.
En nuestro ejemplo queremos que todo encuen-
tro tenga un nombre y que ese nombre no exce-
da 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 expli-
ca qué ha pasado y nos vuelve a pedir los datos.
De vuelta al paso tres
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 lis-
tado 15.
Y al paso cuatro
Editamos la migración “db/migrate/002_cre-
ate_preguntas.rb”, añadiendo las líneas que
en el listado 16 se muestran en negrita. Cada
pregunta pertenece a un encuentro, por lo
60
REDES
http://digital.revistasprofesionales.com
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’ %>
LISTADO 13 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>
<!—[eoform:encuentro]—>
LISTADO 14 app/models/encuentro.rb
class Encuentro < ActiveRecord::Base validates_presence_of :nombre validates_length_of :nombre, :maximum => 255
end
LISTADO 12 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>
Figura 12. ruby script/generate model Pregunta.
LISTADO 10 app/views/encuentros/new.rhtml
<h1>Nuevo encuentro</h1>
<% form_tag :action => ‘create’ do %><%= render :partial => ‘form’ %><%= submit_tag “Crear” %>
<% end %>
<%= link_to ‘Volver’, :action => ‘list’ %>
REDES(ror) 21/9/07 14:26 Página 60
SOLO PROGRAMADORES nº 153
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/scaf-
fold.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 sobre-
escribimos perderíamos dichos cambios.
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
“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/pre-
guntas/_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 gene-
ra la lista de encuentros para el desplegable
la ponemos en “app/helpers/preguntas_hel-
per.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/hel-
pers”, donde podemos poner las funciones auxi-
liares 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 lista-
do 21. Después de cada pregunta ponemos
un icono con un enlace para editar la pre-
gunta y añadirle una respuesta y otro para
borrar la pregunta.
Para que al editar la respuesta y darle a guar-
dar vuelva a la pantalla anterior, modificamos
la redirección en el método “update” del con-
trolador (“app/controllers/preguntas_contro-
ller.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”, tam-
bié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/contro-
llers/preguntas_controller.rb), para que al pin-
char en “Añadir pregunta” aparezca seleccio-
61
REDESProgramación ágil con Ruby on Rails
http://digital.revistasprofesionales.com
Figura 13. Ejecución de la migración.
Figura 14. Creación de scaffold para el modelo.
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
LISTADO 15 ruby script/generate model Pregunta
exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/pregunta.rb create test/unit/pregunta_test.rb create test/fixtures/preguntas.yml exists db/migrate create db/migrate/002_create_preguntas.rb
REDES(ror) 21/9/07 14:26 Página 61
SOLO PROGRAMADORES nº 153
nado el encuentro desde el que hemos pin-
chado 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
correctamente.’
flash[:notice] = ‘Pregunta actualizada
correctamente.’
Conclusiones
En este momento del desarrollo de la apli-
cación, el lector puede manejar encuen-
tros digitales y, para cada encuentro, pre-
guntas. Es cierto que en vista de los
encuentros los mensajes se han puesto en
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 pulien-
do 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 aplica-
ción como mostrar la manera de desarro-
llarla, 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
62
REDES
http://digital.revistasprofesionales.com
LISTADO 18 app/models/pregunta.rbclass Pregunta < ActiveRecord::Base
belongs_to :encuentro validates_presence_of :pregunta , :encuentro_id
end
LISTADO 17 app/models/encuentro.rbclass Encuentro < ActiveRecord::Base
has_many :preguntas
validates_presence_of :nombre validates_length_of :nombre, :maximum => 255
end
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
LISTADO 20 app/helpers/preguntas_helper.rb
module PreguntasHelper def encuentros
Encuentro.find(:all).collect {|p| [ p.nombre, p.id ] } end
end
LISTADO 19 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]—>
LISTADO 21 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 %>
REDES(ror) 21/9/07 14:26 Página 62
SOLO PROGRAMADORES nº 153
y el presupuesto lo permiten. Desde la expe-
riencia de dabne.net, Ruby on Rails aporta la
posibilidad concreta y material de desarro-
llar 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 usua-
rios estamos implicados.
63
REDESProgramación ágil con Ruby on Rails
http://digital.revistasprofesionales.com
LISTADO 24 app/controllers/preguntas_controller.rb, createdef 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
LISTADO 23 app/controllers/preguntas_controller.rb, newdef new
@pregunta = Pregunta.new @pregunta.encuentro_id = params[:encuentro_id]
end
LISTADO 22 app/controllers/preguntas_controller.rb, destroydef destroy
p = Pregunta.find(params[:id]) e = p.encuentro p.destroy redirect_to :controller => ‘encuentros’, :action => ‘show’, :id => e
end
Conferencia Hispana Rails 2007Durante el próximo mes de
noviembre se celebrará en
Madrid la II Conferencia
Hispana Rails (www.confe-
renciarails.org/). De entre
las propuestas de ponencia
recibidas hasta el momento
destacamos estas:
� APIs de Identidad y RailsIntroducción en el mundo
de las APIs más populares
de la red como Flickr,
Last.fm/Audiscrobbler o
GoogleMaps.
� APIs Rest y proxificaciónIntroducción a las APIs
Rest y qué pueden aportar
a nuestras aplicaciones.
� BDD y rSpecAdemás del desarrollo
guiado por tests, existe otro
paradigma que va un paso
más allá de éste: el desarro-
llo orientado a comporta-
miento.
� Caché en RailsMecanismos para intro-
ducir caché en las diferen-
tes capas por las que
transcurre la resolución de
una petición.
� Camping, el microfra-meworkPara qué podríamos que-
rer usar Camping y cómo
hacerlo.
� Cómo programar unaaraña web con RailsTaller práctico en el que se
explicará cómo construir
desde cero una araña web.
� Flickr con RailsTaller práctico para cons-
truir una interfaz interac-
tiva con el servicio Flickr.
� Integración de Rails enel escritorio con SlingshotSlingshot es la alternativa
Open Source propuesta por
Joyent a los recientes desa-
rrollos de Microsoft y
Adobe para integrar aplica-
ciones web en el escritorio
usando Ruby on Rails.
� Inteligencia artificial apli-cada a la publicidad con RubyCómo utilizar la inteligen-
cia artificial en Ruby on
Rails para optimizar auto-
máticamente la publici-
dad de una aplicación.
� Introducción a RubyStackPresentación y demostra-
ción de RubyStack, una dis-
tribucion integrada Open
Source de Ruby on Rails.
� JRuby on Rails:Agilidad en la empresaJRuby on Rails proporciona
la agilidad de Rails sobre ser-
vidores y aplicaciones Java.
� La internacionaliza-ción sí es posibleAunque Rails no tiene sopor-
te incorporado para la inter-
nacionalización, hay muchas
opciones para abrir las puer-
tas de una aplicación a
audiencias más grandes.
� Más allá del testingVale. Tests. Sabes que los
necesitas, sabes que tienes
que hacerlos. Pero, ¿por
dónde empezar? ¿Qué tal
especificar el comporta-
miento de tu aplicación en
forma de tests?
� Phobos: scripting de ser-vidor para plataforma JavaPhobos es un en entorno
de aplicaciones ligero con
el rendimiento, escalabili-
dad y fiabilidad de la plata-
forma Java.
� Programa en Railscomo si jugases con LegoCómo funcionan los plug-
ins de Rails y cómo pode-
mos crear nuestros propios
plug-ins.
� Proyectos de bajo costecon limitaciones severasde tiempo y recursosExisten aplicaciones cuyo
desarrollo se plantea con una
limitación de tiempo y recur-
sos severa. Rails nos ayuda.
� Rails against the machineCómo crear entornos vir-
tuales clonables para el des-
pliegue de aplicaciones Rails
con Capistrano 2 y XEN.
� Rails desde el códigoBajaremos a las entrañas de
Rails y echaremos un vista-
zo a la implementacion del
framework.
� Rails para torpes como yoEs difícil transmitir qué es
Ruby on Rails y qué signi-
fica que nos guste y que
estemos felices por pro-
gramar en este lenguaje.
� Rails y Globalize: un trencon destinos internacionalesCómo Globalize puede
enseñar a tu aplicación Rails
monolingüe a aprender
idiomas.
� Seguridad web enaplicaciones RailsUn repaso a la seguridad
web y a algunas de las vul-
nerabilidades más comunes.
� Tractis, un enfoquetécnicoCon un enfoque lo más
técnico posible, comenta-
remos cómo trabajamos y
qué herramientas usamos.
� Unión de MXML conRailsSe tratará la unión del len-
guaje MXML usado por la
plataforma Flex con el fra-
mework Rails para conse-
guir una aplicación de las
que Adobe denomina RIA.
REDES(ror) 21/9/07 14:26 Página 63