Professional Documents
Culture Documents
con Rails
Han habido varias deman da s extravagantes hechas acerca de Rails. Por ejemplo, un
articulo publicado en OnLAMP.com 1 deman da n d o que “puedes desarrollar una aplicación
web por lo menos diez veces mas rápido con Rails que con un tipico framework Java...” El
artículo luego muestra como instalar Rails y Ruby en una PC y contruir una aplicación
funcionan d o con “scaffold” virtualmen te sin código.
Mientras que esto es impresiona nt e, desarrolladores web “reales” saben que esto es humo
y espejos. aplicaciónes ‘reales’ no son tan simples como eso. ¿Qué es realmen te lo que
esta pasan do debajo de la superficie? ¿Qué tan dificil es construir aplicaciónes web
‘reales’?
Aqui es donde la vida se vuelve un poco difícil. Rails tiene muy buena documen t ación
on - line, de hecho, posiblement e esta demasiado bien docume n t a d o para principiantes,
con mas de 30.000 palabras de docume n t ación on- line en el formato de manual de
referencia. Lo que esta faltando en un road ma p (railmap?? - mapa del camino) apunta n d o
a las páginas claves que necesitas saber para comen za r a desarrollar con Rails.
Este documen t o está para llenar ese vacio. Asume que ya tienes Ruby y Rails en
funciona mient o en una PC (si no has llegado tan lejos, ve devuelta y sigue el articulo de
Curt). Esto te lleva al final de ‘Dia 1 con Rails’.
‘Dia 2 con Rails’ comienza posicionan d o se detras del humo y los espejos. Lo lleva a traves
del código ‘scaffold’. Nuevas características son remarcadas en negrita, explicadas en el
texto, y seguidas por una referencia a la documen t acio de Rails o de Ruby donde puedes
aprender más.
‘Dia 3 con Rails’ toma scaffold y comien za a construir algo reconocible como una
aplicación “real”. Todo el tiempo, esta contruyen d o su propia caja de herra mient as con
Rails. Lo mas importa nt e de todo, deberia también sentirse comodo con la
documen t ación en linea para que pueda continuar con la exploración usted solo.
‘Dia 4 con Rails’ agrega otra tabla y trata con algunas de las complejidades de mantene r
la integridad referencial. Al final, tendra una aplicación en funciona mient o, suficientes
herra mient as como para comenza r, y el conocimient o de donde buscar mas información.
Diez veces mas rápido? Después de cuatro dias con Rails, juzguelo por usted mismo!
Página 1
Reconocimiento s : m u c h as gracias a la ge n te en el canal irc 2 y e n la lista d e correo 3 . El
regist ro d e arc hivos e n linea fue u n a invaluable asiste ncia m ie n t ras a p re n dia Rails y
Ruby.
Versión: 2.3 usand o la versión 0.12.1 de Rails – vea http: / / r ails.ho melin ux.org para la
última versión y para bajar una copia del futuro código. Document o escrito y pdf
generado con OpenOffice.org 'Writer'.
Página 2
Día 1 con Rails
La aplicación ‘Lista de Tareas’
Este d oc u m e n t o sig ue la co n s t r ucción d e u n a si m ple a plicació n d e “lista d e ta reas” – el
ti p o d e cosa q ue tienes e n t u PDA, co n u n a lista d e ite m s, agr u p a d o s e n categorias, con
n o ta s o pcio nales (para ver u n a a delan t o d e co m o se va a ver, vea Ilustración 5: La p a nt alla
de ‘Lista de T areas’ en la p ági na 2 3 ).
Ejecutar rails ToDo crea un nuevo directorio ToDo\ y lo puebla con una serie de archivos y
subdirectorios, los mas importan t es de estos son los siguientes:
app
contiene el nucleo de la aplicación, dividida en los subdirectorios modelos (model),
vistas(view), controladores(controller), y 'ayudantes'(helper)
config
contiene el archivo database.yml que provee detalles de la base de datos a utilizar
con la aplicación
log
registros especificos de la aplicación. Nota: development.log mantiene una traza de
cada acción que Rails realiza – muy util para rastrear errores, pero no necesita
ser purgado regularmente!
public
el directorio disponible para Apache, que incluye imagenes, javascripts, y
subdirectorios para stylesheets
Página 3
Cambiar a fastcgi
A menos que sea paciente (o que tenga una PC potente) deberia habilitar fastcgi para esta
aplicación.
public\.htaccess
# Para mejor desempeño reemplaze el despachador con el de fastcgi
RewriteRule ^(.*)$ dispatch.fcgi [QSA,L]
Versiones de Rails
Para el m o m e n t o q ue leas este d oc u m e n t o, Rails p r o b able me n te h aya ava n z a d o varias
versió nes. Si inte n t as avan za r a t raves d e este d oc u m e n t o, cheq uea la versión ins tala da en
t u PC:
W:\ToDo>gem list --local
Si s o n dis tin ta s a las versió ne s lista da s abajo, e n t o nces yo fue rte m e n te aco n sejaria q u e
baje las versió nes u s a d a s en “Cuat ro dias co n Rails”, eje m plo:
W:\ToDo>gem install rails --versión 0.12.1
config\database.yml (fragmento)
development:
adapter: mysql
database: todos
host: localhost
username: foo
password: bar
Página 4
Crear la tabla Categorias
La tabla categories (categorias) es utilizada en los ejemplos siguientes. Es una simple lista
de categorias que seran usadas para agrupar los items en la Lista de Tareas.
Definición MySQL
Tabla Categories
CREATE TABLE `categories` (
`id` smallint(5) unsigned NOT NULL auto_increment,
`category` varchar(20) NOT NULL default '',
`created_on` timestamp(14) NOT NULL,
`updated_on` timestamp(14) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `category_key` (`category`)
) TYPE=MyISAM COMMENT='List of categories';
• Guiones bajos en el nombre de los campos seran cambiados por espacios por Rails
para obtener nombres ‘amigables al humano’
• tener cuidado con la mezcla de mayusculas y minisculas en el nombre del campo –
algunas partes del código de Rails son sensibles a mayusculas y minisculas.
• Cada tabla deberia tener una clave primaria llamada ‘id ’ - en MySQL es mas fácil tener
este campo como un numeric auto_increment
• enlaces a otras tablas deberian seguir la misma convencion de nombre ‘_id’
• Rails auto matica men t e manten d r a los campos llamados created_at/created_on o
updated_at/updated_on, entonces es una buena idea agregarlos
Document ación: ActiveRecord::Timesta m p
• Tip útil: si esta construyen d o un sistema multiusuario (no es relevante aqui), Rails
también utilizara el bloqueo optimista si agrega un campo llamado lock_versión
(integer default 0). Todo lo que necesita recordar es incluir lock_versión como un
campo oculto en sus formularios de actualización.
Document ación: ActiveRecord::Locking
Modelo de Datos
Genera un archivo vacio:
W:\ToDo>
q ue crea u n archivo category.rb, y d o s arc hivos d e p r u eba category_controller_test.rb y
categories.yml. Agregare m o s algu na s en t ra d a s e n el m o d elo d e d a t o s e n u n mi n u t o –
d ejelo vacio p o r el m o m e n t o.
Scaffold
El controlador es el corazón de la aplicación Rails.
Ejecutar el script generador del controlador
W:\ToDo>ruby script/generate controller category
exists app/controllers/
Página 5
exists app/helpers/
create app/views/category
exists test/functional/
create app/controllers/category_controller.rb
create test/functional/category_controller_test.rb
create app/helpers/category_helper.rb
W:\ToDo>
Realzar el Modelo
El Modelo es donde toda las reglas relacionadas con datos son guarda das, incluida la
validación y integridad relacional. Esto significa que puede definir una sola regla, y Rails
automáticame n t e la aplicará donde sea que el dato es accedido.
Página 6
Crear reglas de validación de datos
Rails otorga mucho manejo de errores gratis (casi). Para demost rar esto, agregue algunas
reglas de validación al modelo de categoría vacio:
app\models\category.rb
class Category < ActiveRecord::Base
validates_length_of :category, :within => 1..20
validates_uniqueness_of :category, :message => "already exists"
end
Página 7
Para p r o ba r es to, a h o ra inte n t a agregar u n regist ro d u plica d o d e n u evo. Esta ve z, Rails
m a n eja el erro r e n ve z d e “ro m p e r se” - vea abajo. El estilo es ta u n p oco en s u cara – n o
es la in terfa z d e u s u a rio m a s s u til. De t o d a s for m a s, q ue es pera si es gratis?
Página 8
Dia 2 con Rails
Para progresar mas alla de este punto, necesita mo s ver que esta pasando detras de la
escena. Durante el dia 2, trabajare m o s sistematica me n t e a traves del código scaffold
generado por Rails, decifrando que significa todo. Con la acción scaffold, Rails genera
todo el código que necesita dinámica me n t e. Ejecutando scaffold como un script , podemo s
obtener todo el código escrito al disco donde lo pode mo s investigar y luego comen za r a
modificarlo para ajustarse a nuestros requeri mient os.
Ejecutar el script generate scaffold
W:\ToDo>ruby script/generate scaffold category
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
skip app/models/category.rb
skip test/unit/category_test.rb
skip test/fixtures/categories.yml
exists app/controllers/
exists app/helpers/
create app/views/categories
exists test/functional/
create app/controllers/categories_controller.rb
create test/functional/categories_controller_test.rb
create app/helpers/categories_helper.rb
create app/views/layouts/categories.rhtml
create public/stylesheets/scaffold.css
create app/views/categories/list.rhtml
create app/views/categories/show.rhtml
create app/views/categories/new.rhtml
create app/views/categories/edit.rhtml
create app/views/categories/_form.rhtml
W:\ToDo>
Este script genera un rango de archivos necesarios para crear una aplicación completa,
incluyendo un controlador, vistas, layouts, e incluso hojas de estilo (css).
def list
Página 9
@category_pages, @categories = paginate :category, :per_page => 10
end
def show
@category = Category.find(@params[:id])
end
def new
@category = Category.new
end
def create
@category = Category.new(@params[:category])
if @category.save
flash['notice'] = 'Category was successfully created.'
redirect_to :action => 'list'
else
render_action 'new'
end
end
def edit
@category = Category.find(@params[:id])
end
def update
@category = Category.find(@params[:id])
if @category.update_attributes(@params[:category])
flash['notice'] = 'Category was successfully updated.'
redirect_to :action => 'show', :id => @category
else
render_action 'edit'
end
end
def destroy
Category.find(@params[:id]).destroy
redirect_to :action => 'list'
end
end
Cuando el usuario de la aplicación Rails selecciona una acción – ejemplo: ‘Show’ (mostrar)
– el controlador ejecutará cualquier código de la sección apropiada – ‘def show’ - y luego
por defecto renderizara una plantilla con el mismo nomb re - ‘show.rthml’. Este
comport a mi en t o por defecto puede ser sobreescrito:
Página 10
Document ación: ActiveRecord::Base
La Vista
Las vistas s o n d o n d e se d efine la interfa z d el u s ua rio. Rails p u e d e re n de ri za r la p ági na
HTML fi nal p rese n ta da al u s u a rio d e s de t res co m p o n e n te s:
Layout Template - Plantilla Partial - Parcial
e n app\views\layouts\ e n app\views\<controller>\ e n app\views\<controller>\
p o r d efecto: application.rhtml p o r d efecto: <action>.rhtml p o r d efecto _<partial>.rhtml
o <controller>.rhtml
Layout
Convención de nombres en Rails: si hay una plantilla en app\views\layouts\ con el mismo
nombre que el controlador actual entonces sera utilizada auto máticame n t e como layout
del controlador a menos que explícitamen te se indique lo contrario.
</body>
</html>
Esto es en su mayoria HTML, mas algunos bits de código Ruby embebido entre etiquetas
<% %>. Este layout sera llamado por el proceso de renderi za d o sin considerar la acción que
se este ejecutan d o. Contiene etiquetas estan da r HTML – las
<html><head>...</head><body>...</body></html> que aparecen en todas las páginas.
Las partes de Ruby en negrita son traducidas a HTML durante el proceso de renderizad o
Página 11
de Rails como se muest ra a continuación:
Template (plantilla)
Convención de nombres de Rails: las plantillas estan guardad as en
app\views\categories\‘acción’.rhtml.
Partial (parcial)
Convención de nombres de Rails: un parcial ‘foo’ estara en el archivo
Página 12
app\views\‘action’\_foo.rhtml (note el guion bajo inicial).
<!--[form:category]-->
<p><label for="category_category">Category</label><br/>
<%= text_field 'category', 'category' %></p>
Página 13
<body>
<h1>New category</h1>
<!--[form:category]-->
<p><label for="category_category">Category</label><br/>
<input id="category_category" name="category[category]" size="30" type="text" value=""
/></p>
<a href="/categories/list">Back</a>
</body>
</html>
<table>
<tr>
<% for column in Category.content_columns %>
<th><%= column.human_name %></th>
<% end %>
</tr>
Página 14
<%= link_to "Previous page", { :page => @category_pages.current.previous } if
@category_pages.current.previous %>
<%= link_to "Next page", { :page => @category_pages.current.next } if
@category_pages.current.next %>
<br />
ActionController::Pagination::Paginator
y @category_pages.current.previous d ev uelve u n n u evo objeto Página re p rese n t a d o la
p ágina a n terio r a la act ual, o n ulo si esta es la p ri m e ra p ági na.
ActionController::Pagination::Paginator::Page
4 Por ejemplo, piense que pasaria si un usuario tipea “< / t a ble >” como una categoria.
Página 15
Ento nces, si h ay u n a p ágina a n te rior a la cual n avegar, este co nt r uc to r m o s t ra rá u n link;
si n o h ay ni ng u n a, el link es s u p ri mi d o.
El có digo genera d o p a ra la p ágina n se vera asi:
<a href="/categories/list?page=[n-1]">Previous page</a>
<a href="/categories/list?page=[n+1]">Next page</a>
El controlador
En la vista ‘List’, esperaria que los registros sean mostrad o s en orden alfabético. Esto
requiere un cambio menor al controlador:
app\controllers\categories_controller.rb (fragmento)
def list
@category_pages, @categories = paginate :category,
:per_page => 10, :order_by => 'category'
end
La Vista (View)
Página 16
</head>
<body>
<h1><%=@heading %></h1>
<% if @flash["notice"] %>
<span class="notice">
<%=h @flash["notice"] %>
</span>
<% end %>
<%= @content_for_layout %>
</body>
</html>
Un simple agregado a la hoja de estilo hace que el mensaje flash sea mas visible:
public\stylesheets\scaffold.css (fragmento)
.notice {
color: red;
}
Página 17
• págination_links crea u n a ba r ra lin k HTML bá sica p a ra u n p agi na d o r d a d o
ActionView::Helpers::PaginationHelper
y algunos cambios menores a las dos plantillas (note en particular el uso de @heading)::
app\views\categories\Edit.rhtml
<% @heading = "Edit Category" %>
<%= start_form_tag :action => 'update', :id => @category %>
<%= render_partial "form" %>
<hr />
<%= submit_tag "Save" %>
<%= end_form_tag %>
<%= link_to 'Back', :action => 'list' %>
app\views\categories\New.rhtml
<% @heading = "New Category" %>
<%= start_form_tag :action => 'create' %>
<%= render_partial "form" %>
<hr />
<%= submit_tag "Save" %>
<%= end_form_tag %>
<%= link_to 'Back', :action => 'list' %>
Página 18
Día 3 con Rails
Ahora es tiempo de empezar con el corazón de la aplicación. La tabla Items contiene la
lista de “cosas para hacer”. Cada item puede pertenecer a una de las categorias creadas
en el Dia 2. Un item opcionalmen t e puede tener un Nota, guardad a en una tabla separada,
que veremos mañana. Cada tabla tiene una clave primaria ‘id’, que también es usada para
guardar links entre las tablas.
La tabla ‘Items’
Definición MySQL de la tabla
Los ca m p o s en la t abla Ite m s so n los sig uie n tes:
• d o ne – 1 sig nifica q ue el ite m d e la lista ha si d o finali za d o 5
• p rio rity – 1 (Alta p rio ri da d) a 5 (baja p riori da d)
• d e scri p tion – texto libre in dican d o cual es la ta rea
• d u e_da te – in dica cua n d o se d e be ria reali za r la ta rea
• category_id – u n link a la Categoria a la q ue este ite m p e r te nece (‘id’ en la t abla d e
Catego rias)
• n o te_id – u n link a u n a n o ta o pcio nal ex plican d o es te ite m (‘id’ en la ta bla d e n o t as)
• p rivate – 1 sig nifica q ue el ite m d e la lista est a clasifica d o co m o ‘Priva do’
Tabla Items
CREATE TABLE items (
id smallint(5) unsigned NOT NULL auto_increment,
done tinyint(1) unsigned NOT NULL default '0',
priority tinyint(1) unsigned NOT NULL default '3',
description varchar(40) NOT NULL default '',
due_date date default NULL,
category_id smallint(5) unsigned NOT NULL default '0',
note_id smallint(5) unsigned default NULL,
private tinyint(3) unsigned NOT NULL default '0',
created_on timestamp(14) NOT NULL,
updated_on timestamp(14) NOT NULL,
PRIMARY KEY (id)
) TYPE=MyISAM COMMENT='List of items to be done';
El Modelo
Co m o a n tes, Rails p u e de genera r u n arc hivo d e m o d elo vacio:
W:\ToDo>ruby script/generate model item
exists app/models/
exists test/unit/
exists test/fixtures/
Página 19
create app/models/item.rb
create test/unit/item_test.rb
create test/fixtures/items.yml
W:\ToDo>
q ue p o d e m o s p o blar co n:
app\models\item.rb
class Item < ActiveRecord::Base
belongs_to :category
validates_associated :category
validates_format_of :done_before_type_cast, :with => /[01]/, :message=>"must be 0 or
1"
validates_inclusion_of :priority, :in=>1..5, :message=>"must be between 1 (high) and
5 (low)"
validates_presence_of :description
validates_length_of :description, :maximum=>40
validates_format_of :private_before_type_cast, :with => /[01]/, :message=>"must be 0
or 1"
end
La tabla de ‘Notas’
Esta t a bla co n tiene u n solo ca m p o d e t ext o libre p a ra con te ner infor m ación f ut u r a p a ra
u n ite m d e la lista d e t a reas en p a r ticular. Este d a t o p o d ria, p o r s u p u es t o, h a ber si d o
con te ni d o en u n ca m p o d e la t abla Ite m s, d e t o d a s for m a s, si lo h ace d e es ta for m a
a p re n d e ra m u c h o m a s acerca d e Rails : - )
Página 20
id smallint(6) NOT NULL auto_increment,
more_notes text NOT NULL,
created_on timestamp(14) NOT NULL,
updated_on timestamp(14) NOT NULL,
PRIMARY KEY (id)
) TYPE=MyISAM COMMENT='Additional optional information for to-dos';
El modelo
Genere el archivo de modelos vacio, pero no contiene nada nuevo:
app\models\note.rb
class Note < ActiveRecord::Base
validates_presence_of :more_notes
end
Esto se lee: antes de borrar un registro Item, encontrar el registro en Notas cuyo id sea
igual al valor de Nota_id en el registro Item que se esta a punto de eliminar, y eliminarlo
primero. A menos que no exista uno :- )
Página 21
Mas de Scaffold
Generemo s algo mas de código scaffold. Haremos esto para la tabla Items y la tabla
Notas. Todavía no estamos listos para trabajar sobre Notas, pero tener en su lugar a
scaffold significa que podemo s referirnos hoy al código de las Notas sin generar muchos
errores. Es como construir una casa – scaffold permite contruir una pared a la vez sin
que todo se derru m be alrededor de sus ojos.
El @heading en la plantilla es ahora utilizado para las etiquetas <title> como también para
<h1>. He renom bra d o public/stylesheets/scaffold.css a todo.css para mantener una
prolijidad, y también he jugado general ment e con los colores, bordes de las tablas, para
Página 22
dar un estilo mas lindo. También he agregado un poco de Javascript para
automáticame n t e posicionar el cursor en el primer campo de entrada en el navegador
para que el usuario comienze a tipear.
8 Es asombros o lo que un par de lineas en la hoja de estilos pueden hacer para cambiar la
apariencia de la pantalla, por supues to ademas de la colección de íconos...
Página 23
app\views\items\list.rhtml
<% @heading = "To Do List" %>
<%= start_form_tag :action => 'new' %>
<table>
<tr>
<th><%= link_to_image "done", {:action => "purge_completed"}, :confirm => "Are you
sure you want to permanently delete all completed To Dos?" %></th>
<th><%= link_to_image "priority",{:action => "list_by_priority"}, "alt" => "Sort
by Priority" %></th>
<th><%= link_to_image "description",{:action => "list_by_description"}, "alt" =>
"Sort by Description" %></th>
<th><%= link_to_image "due_date", {:action => "list"}, "alt" => "Sort by Due Date"
%></th>
<th><%= link_to_image "category", {:action => "list_by_category"}, "alt" => "Sort
by Category" %></th>
<th><%= show_image "note" %></th>
<th><%= show_image "private" %></th>
<th> </th>
<th> </th>
</tr>
<%= render_collection_of_partials "list_stripes", @items %>
</table>
<hr />
<%= submit_tag "New To Do..." %>
<%= submit_tag "Categories...", {:type => 'button', :onClick=>"parent.location='" +
url_for( :controller => 'categories', :action => 'list' ) + "'" } %>
<%= end_form_tag %>
<%= "Page: " + pagination_links(@item_pages, :params => { :action => @params["action"]
|| "index" }) + "<hr />" if @item_pages.page_count>1 %>
Página 24
end
def list_by_priority
@item_pages, @items = paginate :item,
:per_page => 10, :order_by => 'priority,due_date'
render_action 'list'
end
Agregar el Helper
Los encabezad o s para las columnas Nota y Privado son imagenes, pero no son
clickeables. He decidido escribir un pequeño método show_image(name) para solo most rar
la imagen:
app\helpers\application_helper.rb
module ApplicationHelper
def self.append_features(controller)
controller.ancestors.include?(ActionController::Base) ?
controller.add_template_helper(self) : super
end
def show_image(src)
img_options = { "src" => src.include?("/") ? src : "/images/#{src}" }
img_options["src"] = img_options["src"] + ".png" unless
img_options["src"].include?(".")
img_options["border"] = "0"
tag("img", img_options)
end
end
Página 25
<%= render_partial "list_stripes", item %>
<% end %>
El resto de la linea es en realidad una criptica expresión if then else que sacrifica ser
legible por ser breve: if la expresión antes de la marca de pregunta es verdadera,
devuelve el valor antes de los dos puntos; sino devuelve el valor después de los dos puntos.
Ruby Document ación: Expressions
Nota: el mismo constructor if then else es usado para mostrar el ícono ‘tilde’ si
Página 26
list_stripes["done"]es igual a 1, de otra forma muestra un caracter de espacio en blanco
HTML:
La pla n tilla es m í ni m a:
app\views\items\new.rhtml
<% @heading = "New To Do" %>
<%= error_messages_for 'item' %>
<%= start_form_tag :action => 'create' %>
<table>
<%= render_partial "form" %>
</table>
<hr />
<%= submit_tag "Save" %>
<%= submit_tag "Cancel", {:type => 'button', :onClick=>"parent.location='" + url_for(
:action => 'list' ) + "'" } %>
<%= end_form_tag %>
Página 27
y el t rabajo real es reali za d o en el p a rcial, q u e p u e d e ser co m p a r ti d o con la acción ‘Edit’:
app\views\items\_form.rhtml
<tr>
<td><b>Descripción: </b></td>
<td><%= text_field "item", "description", "size" => 40, "maxlength" => 40
%></td>
</tr>
<tr>
<td><b>Finalizar: </b></td>
<td><%= date_select "item", "due_date", :use_month_numbers => true %></td>
</tr>
<tr>
<td><b>Categoria: </b></td>
<td><select id="item_category_id" name="item[category_id]">
<%= options_from_collection_for_select @categories, "id", "category",
@item.category_id %>
</select>
</td>
</tr>
<tr>
<td><b>Prioridad: </b></td>
<% @item.priority = 3 %>
<td><%= select "item","priority",[1,2,3,4,5] %></td>
</tr>
<tr>
<td><b>Privado? </b></td>
<td><%= check_box "item","private" %></td>
</tr>
<tr>
<td><b>Completado? </b></td>
<td><%= check_box "item", "done" %></td>
</tr>
Página 28
Crear una lista desplegable desde una tabla de busqueda
Este es ot ro eje m plo d e Rails resolvien d o u n p r o ble m a d e co dificación diario d e u n a
fo r m a ext re m a d a m e n te econ ó mica. En es te eje m plo:
options_from_collection_for_select @categories, "id", "category", @item.category_id
Note q u e las cajas d e s plegables d e be n t raer los d a t o s d e algu n lugar – q ue sig nifica u n
agrega d o al con t r ola do r:
app\controllers\items_controller.rb (fragmento)
def new
@categories = Category.find_all
@item = Item.new
end
def edit
@categories = Category.find_all
@item = Item.find(@params[:id])
end
Toques finales
Modificar la hoja de estilos
Hast a es te p u n t o, la p a n t alla d e ‘Lista d e Tareas’ d e be ria f u ncio na r, y t a m bién d e beria
fu ncio nar el b o t ó n ‘New To Do’. Para p r o d ucir las p a n t allas m o s t ra d a s aq ui, t a m bié n hice
los sig uien te s ca m bios a la h oja d e es tilos:
public\stylesheets\ToDo.css
body { background-color: #c6c3c6; color: #333; }
.notice {
color: red;
background-color: white;
}
Página 29
h1 {
font-family: verdana, arial, helvetica, sans-serif;
font-size: 14pt;
font-weight: bold;
}
table {
background-color:#e7e7e7;
border: outset 1px;
border-collapse: separate;
border-spacing: 1px;
}
10 Pero no como los autores de los textos de secundaria, yo si muestro la respues t a en el Dia 4 :- ) -
vea app \ views \ite m s \ e dit.rht ml en la página 31
Página 30
Dia 4 con Rails
Las pantallas ‘Notas’
Enlazando ‘Notas’ con ‘Editar Tarea’
Aunque el código scaffold de las Notas le da todas las fácilidades, no quere mos que el
usuario invoque cualquiera de estas directa me n t e. En cambio, si un item no tiene
asociada una nota, queremo s poder crear una nota haciendo click en el ícono de Nota en
la pantalla de Edicion de tareas:
Si ya existe una nota, queremo s poder editarla o borrarla haciendo click en el ícono
apropiado en la pantalla de Edicion de tareas:
Página 31
<%= render_partial "form" %>
<tr>
<td><b>Notes: </b></td>
<% if @item.note_id.nil? %>
<td>None</td>
<td><%= link_to_image "note", :controller => "notes", :action => "new", :id =>
@item.id %></td>
<% else %>
<td><%=h @item.note.more_notes %></td>
<td><%= link_to_image "edit_button", :controller => "notes", :action => "edit",
:id => @item.note_id %></td>
<td><%= link_to_image "delete_button", {:controller => "notes", :action =>
"destroy", :id => @item.note_id }, :confirm => "Are you sure you want to delete this
note?" %></td>
<% end %>
</tr>
</table>
<hr />
<%= submit_tag "Save" %>
<%= submit_tag "Cancel", {:type => 'button', :onClick=>"parent.location='" + url_for(
:action => 'list' ) + "'" } %>
<%= end_form_tag %>
y el parcial:
app\views\notes\_form.rhtml
<table>
<tr>
<td><label for="note_more_notes">More notes</label></td>
<td><%= text_area 'note', 'more_notes' %></td>
</tr>
</table>
Una vez que la acción update o destroy de la tabla Notas es completada, queremos regresar
a la pantalla de ‘Lista de Tareas’:
app\controllers\notes_controller.rb (fragmento)
def update
@note = Note.find(@params[:id])
if @note.update_attributes(@params[:note])
flash['notice'] = 'Note was successfully updated.'
redirect_to :controller => 'items', :action => 'list'
else
render_action 'edit'
end
end
def destroy
Note.find(@params[:id]).destroy
redirect_to :controller => 'items', :action => 'list'
end
Recuer de las reglas d e integ ri da d refere ncial q ue ya h a n si d o crea da s se asegu rara n d e
Página 32
q ue cua n d o u n a n o t a es eli mina da, cualq uier refere ncia a ella sera re m ovida d el Ite m
t a m bié n (vea Integridad Refere ncial en la p ágina 2 1 ).
El método create trae los datos de la variable de sesión de nuevo y lo usa para encontrar
el registro en la tabla de Items. Luego actualiza el campo note_id en la tabla Item con el id
del registro que se acaba de crear en la tabla de Notas, y retorna al controlador Items de
nuevo:
app\controllers\notes_controller.rb (fragmento)
def create
@note = Note.new(@params[:note])
if @note.save
flash['notice'] = 'Note was successfully created.'
@item = Item.find(@session[:item_id])
@item.update_attribute(:note_id, @note.id)
redirect_to :controller => 'items', :action => 'list'
else
render_action 'new'
end
end
Página 33
Cambiar la pantalla de ‘Categorias’
No hay demasiado por hacer en el sistema ahora, a parte de acomo da r las plantillas
creadas en los dias anteriores para que tengan el mismo estilo que los botónes de
navegación:
app\views\categories\list.rhtml
<% @heading = "Categories" %>
<form action="/categories/new" method="post">
<table>
<tr>
<th>Category</th>
<th>Created</th>
<th>Updated</th>
</tr>
<% for category in @categories %>
<tr>
<td><%=h category["category"] %></td>
<td><%= category["created_on"].strftime("%I:%M %p %d-%b-%y") %></td>
<td><%= category["updated_on"].strftime("%I:%M %p %d-%b-%y") %></td>
<td><%= link_to_image 'edit', { :action => 'edit', :id => category.id } %></td>
<td><%= link_to_image 'delete', { :action => 'destroy', :id => category.id },
:confirm => 'Are you sure you want to delete this category?' %></td>
</tr>
<% end %>
</table>
<hr />
<input type="submit" value="New Category..." />
<input type="button" value="To Dos" onClick="parent.location='<%= url_for(
:controller => 'items', :action => 'list' ) %>'">
</form>
app\views\categories\new.rhtml
<% @heading = "Add new Category" %>
<%= error_messages_for 'category' %>
<%= start_form_tag :action => 'create' %>
<%= render_partial "form" %>
<hr />
<input type="submit" value="Save" />
<input type="button" value="Cancel" onClick="parent.location='<%= url_for( :action
=> 'list' ) %>'">
<%= end_form_tag %>
app\views\categories\edit.rhtml
<% @heading = "Rename Category" %>
<%= error_messages_for 'category' %>
<%= start_form_tag :action => 'update', :id => @category %>
<%= render_partial "form" %>
<hr />
<input type="submit" value="Update" />
<input type="button" value="Cancel" onClick="parent.location='<%= url_for( :action
=> 'list' ) %>'">
<%= end_form_tag %>
Página 34
Nueva
Tarea
Nueva
Categoria
Lista
Tareas Nueva
Nota
Lista
Categorias
Editar
Editar Not a
Tarea
Editar
Categoria
Y finalmente
Espero q ue e nc ue n t re es te d oc u m e n t o ú til – sie m p re m e p o n e co n te n t o recibir
co me n t arios, b ue n o s o m alos, a j p mcc@users.so u rceforge.ne t.
Co me n ta rios d e la versió n en es pa ñ ol a e m m a n u eln _at_ g m ail.co m
Página 35
Apéndice – Cambios posteriores
Des p ues d e escribir ‘Cuat ro Dias’, recibi m u c h o s co me n t a rio s q ue ay u d a ro n a m ej ora r la
calida d d el d oc u m e n t o. Una p reg u n t a a pa recio re pe tí da me n te - “co m o act uali zo m a s d e
u n regist ro d e s de la m i s m a p a n t alla” - en to nces a q ui h ay a pé n dice cub rien d o esta s
Pregu n t a s Frecue n te s. No es el conce p t o m a s fácil d e d o mi na r, y es u n area q u e es pe ra ria
q ue a pa re zca n m a s “ayu da n te s” (hel per s) e n el f ut u r o.
Actualizaciones Multiples
En la captura de pantalla de abajo, el usuario puede tildar / d e s tildar varias “Tareas”
usando las cajas de verificación en la columna de la izquierda, y luego presionar
“Guardar” para guardar los resultados en la base de datos.
View
Rails soporta actualizaciones multiples con otra convención de nombres, que es agregar
el id del registro que se esta editan do al nombre entre corchetes [ ]. Esto permite
seleccionar un registro en particular desde multiples registros en la pantalla.
Trabajemos hacia atras desde el HTML que estamos generan do. Esto es lo que se ve para
el registro con id = 6:
Página 37
<%=hidden_field_tag("item["+list_stripes.id.to_s+"][done]","0") %>
</td>
Controlador
Lo q ue re t o r na d el co nt rola d o r cua n d o se p re sio na el b o t ó n “Guar d ar” es el siguien te
h a s h:
params: {
:controller=>"items",
:item=> {
"6"=>{"done"=>"0"},
... etc...
"5"=>{"done"=>"1"}
},
:action=>"updater"
}
Esta m o s interesa d o s en la p a r t e d e :item. Por eje m plo, la linea e n negrita significa “el
regist ro co n el id = 6 tiene el valor d el ca m p o done en 0”. Des de aq ui, es u n t rabajo
ba s ta n te si m ple el d e act uali za r la t abla d e Items:
app\controller\items_controller (fragmento)
def updater
@params[:item].each { |item_id, attr|
item = Item.find(item_id)
item.update_attribute(:done,attr[:done])
}
redirect_to :action => 'list'
end
each coloca “6” e n la variable item_id, y “do ne” = > “0” e n attr.
Ruby Document ación: class Array
Página 38
es solo act u ali za r si ‘done’ h a ca m bia do, p e ro est o significa te ner q ue escribir m a s có digo
:-(
app\controller\items_controller (fragmento)
def updater
@params[:item].each { |item_id, contents|
item = Item.find(item_id)
if item.done != contents[:done].to_i
item.update_attribute(:done,contents[:done])
end
}
redirect_to :action => 'list'
end
Si alguien tiene u n a m ej o r s ol ución, p o r favor h aga m elo saber. Dejo este có digo co m o
eje m plo d e q u e si t o d o falla, Rails n o lo d ejara t raba d o p e ro le p e r mi tira o r de n ar con este
viejo có digo!
Página 39
Indice de Terminos de Rails y Ruby usados en este
Documento
A new (nuevo) ........................................................10
action_na me .......................................................12 O
B options_fro m_collection_for_select ............29
before_type_cast ...............................................20 P
belongs_to (pertenece a).................................20 paginate ...............................................................14
C págination_links ...............................................18
check_box ............................................................29 Partial o Parcial .................................................11
check_box_tag ...................................................38 previous ...............................................................15
confirm .........................................................15, 24 R
content_colum n s ..............................................15 redirect_to ...........................................................10
content_for_layout ...........................................12 render_collection_of_partials .......................26
created_at ..............................................................5 render_partial .............................................12, 25
created_on ......................................................5, 13 render_te m plate ................................................10
current .................................................................15 rescue ...................................................................28
D S
date_select ..........................................................28 save (guardar) ....................................................10
destroy (destruir) ..............................................10 select .....................................................................29
destroy_all ..........................................................24 start_form_tag ...................................................12
developme n t.log ....................................3, 10, 39 strftime ................................................................17
E stylesheet_link_tag ...........................................12
end_for m_tag .....................................................12 submit_tag ..........................................................12
error_messages_for .........................................13 T
F Template o Plantilla .........................................11
find (encontrar) .................................................10 text_field .............................................................13
find_all (encontrar todos) ..............................10 U
Flash ......................................................................16 update_attribut e ...............................................38
H update_attribut es (actualizar atribut os) ...10
h .............................................................................15 updated_at ............................................................5
helper (ayudante) ..............................................12 updated_on ....................................................5, 13
hidden_field_tag ...............................................38 url_for ..................................................................25
HTML....................................................................15 V
human_att ribu te_na m e ...................................15 validates_associated (validar asociaciones) ...
human_na m e .....................................................15 20
I validates_form at_of .........................................20
id ..............................................................................5 validates_inclusion_of .....................................20
Integridad Referencial .....................................21 validates_lengt h_of .....................................7, 20
L validates_presence_of .....................................20
Layout ...................................................................11 validates_uniq ue ness_of ...................................7
link_to ..................................................................12 variable de sesión .............................................33
link_to_image .....................................................24 .
lock_versión ..........................................................5 .each ......................................................................38
N
Página 41