Tabulador en Mac OS X

21 de Septiembre de 2009 • 4 comentarios

Teclado y RatónParece una chorrada, pero todos los switchers tienen el mismo problema: no siempre pueden saltar de un botón a otro con la tecla ⇥ (TAB o tabulador). La (senzilla) solución es la que sigue:

Abrir Preferencias del Sistema (en el directorio Aplicaciones / Utilidades, o ⇧ + ⌘ + U), luego abrir Teclado Y Ratón y ahí modificar la opción Acceso total al teclado dejándola como: Todos los controles. (Probado en Leopard).

Teclado y Ratón / Todos los campos

21 de Septiembre de 2009 • 4 comentarios

Seed Data en Rails 2.3.4

Útil y breve screencast de Ryan Bates sobre una nueva característica introducida en la versión 2.3.4 de Ruby on Rails: Seed Data.

Nos explica como rellenar la base de datos con “valores iniciales”, muy útil para: crear un usuario administrador si no existe, poblar una tabla de [países, ciudades, categorías, ...].

14 de Septiembre de 2009 • 0 comentarios

Git: descentralizado y rápido

13 de Septiembre de 2009 • Un comentario

Cuando hace un año me puse a hacer una pequeña revisión (guiño involuntario..) de los sistemas de control de versiones existentes y populares, tardé muy poco en darme cuenta de cuál era el verdadero VCS que lo estaba petando.

Git es el nuevo dueño. Subversion llegó para comerse CVS. Git simplemente estando ahí, los ha devorado a todos. Por qué?

Resumiendo, porque es un sistema descentralizado. O puede serlo. O puede ser centralizado. Git es lo que tu quieras que sea. Un repositorio Git no está en un servidor donde tu haces commits y checkouts, aunque podría. Para aclarar un poco este lío, voy a intentar explicar el funcionamiento básico de Git, y porque se diferencia esencialmente de Subversion et al.

Un repositorio Git es un directorio situado en la raíz del directorio de trabajo (me estoy planteando dejar de traducir aleatoriamente y empezar a hablar de las cosas por su nombre: working directory). Éste contiene toda la información sobre los ficheros del repositorio, tanto en su versión actual como todo su historial. Cada revisión de un documento se almacena tal cual, como un objeto más, identificado por un nombre de 40 caracteres que se obtiene por una función de hash SHA1. Con lo de “tal cual” me refiero a que no se basa en diferenciales entre versiones (como hace Subversion), sino que cada versión de un fichero se almacena íntegramente. Sorprendentemente esto es eficiente en espacio y tiempo!

En resumen: cada proyecto en el que usemos Git tendrá un directorio en su raíz (por defecto llamado .git), que contendrá todo lo que haya pasado por el proyecto. Esto es la descentralización: no tenemos ninguna dependencia con un servidor. Podemos estar trabajando un mes offline, y luego sincronizar con el servidor.

Ah, pero hay servidor?

Puede haberlo. Aunque Git sea brutalmente útil en trabajos en solitario, “com més serem, més riurem”. Como he dicho, cada copia local de un repositorio Git es válida por si misma, y no necesita de ningún servidor, aunque Git nos ofrece la posibilidad de mandar mensajes entre copias del mismo repositorio y “sincronizarse” por decirlo de alguna forma. Así que si queremos, podemos sustituir fielmente la estructura de un servidor SVN con sus usuarios haciendo commits / checkouts a él, por un repositorio Git almacenado en un servidor y varias copias de éste que hagan push / pull cuando tengan cambios.

Prometedor, y aún no he nombrado la killer feature de Git: branch & merge. Cuántas veces has creado una rama (odio traducir! quiero decir branch, se llama branch, pero si no traduzco, parece que estemos en Puerto Rico (no offense!)) en un repositorio Subversion/CVS? Yo sólo un par de veces. Usando Git en una docena de proyectos, ya he creado (y luego fusionado casi todas ellas) unas 50 ramas (branches, sí). Precisamente debido a la descentralización de Git, puedes estar trabajando en un proyecto dónde hay un servidor, dónde mas gente participa, y querer implementar una nueva característica que va a afectar muchos ficheros a la vez (no siempre aislamos suficientemente el código..). Intentar hacerlo “de un sólo commit” (como diría el Sastrecillo Valiente), no es muy sensato. Así que hacemos un branch (me he hartado) en nuestro ordenador, empezamos a meter mano al código, y si al cabo de 40 commits no funciona nada, nos cargamos el branch. Si llegamos a buen puerto, en cambio, podemos hacer fusionar  el nuestro con el repositorio del servidor, y nuestros cambios ya estarán disponibles para el resto del equipo! Y lo mejor de todo, es muy fácil hacer un merge aún cuando otros han ido modificando el código mientras nosotros trabajábamos, haciendo un rebase y obteniendo nosotros los cambios del resto del equipo para mezclarlos con nuestro branch, para ver si son compatibles (si no lo son, solucionar los conflictos) y entonces fusionarnos sin colisiones. Easy as pie.

Aunque los detalles de branching & merging parezcan confusos (no es trivial explicar su funcionamiento en cuatro líneas), es muy sencillo y potente. Recomiendo las siguientes lecturas para profundizar en el tema, y los que no estéis usando Git hagáis el paso cuando no podáis aguantar más:

13 de Septiembre de 2009 • Un comentario

Creando una aplicación web con Ruby on Rails: Capítulo 2

24 de Marzo de 2009 • 4 comentarios

Seguimos donde lo dejamos, rápidamente nos dedicaremos a preparar el layout general de la página y empezaremos a poner Ajax por todas partes.

Se suele comentar que Rails y Ajax se llevan bien. Vamos a demostrarlo creando una pequeña aplicación pseudo GTD para mantener una lista de TODO’s.

En Rails un layout es una vista especial: es la parte común en un grupo de controladores/acciones. En muchas aplicaciones, con uno o dos layouts nos bastará, por ejemplo: uno para usuarios sin identificar y otro para los miembros, etc. En nuestra aplicación microtodo sólo necesitaremos uno. El generador scaffold nos generó el único fichero que podemos encontrar en app/views/layouts: todo_items.html.erb.

Muy típico de Rails: Convention over Configuration. Si no decimos nada, el controlador TodoItemsController buscará si existe un layout llamado todo_items, y si no lo encuentra después buscará uno llamado application. Podemos sobreescribir estas convenciones poniendo layout :nuevo_layout en el controlador, pero ahora no nos interesa.

Lo primero que haremos con este layout es cambiarle el nombre precisamente a application.html.erb, para que sirva para posibles nuevos controladores. Después le haremos un pequeño lavado de cara dejándolo así:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!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="en" lang="en">
<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>Micro Todo</title>
  <%= stylesheet_link_tag 'scaffold' %>
  <%= javascript_include_tag :defaults %>
</head>
<body>
 
<% flash.each do |key, msg| %>
  <%= content_tag :div, msg, :id => key %>
<% end %>
 
<%= yield %>
 
</body>
</html>

Es bastante sencillo: stylesheet_link_tag 'scaffold' usa un helper que nos ofrece Rails para insertar la etiqueta <link> y el fichero CSS en cuestión (scaffold.css). Usar este helper tiene algunas ventajas que ahora no estudiaremos, como por ejemplo cachear los ficheros de estilo. Lo mismo sirve para javascript_include_tag, aunque este usa la palabra defaults que se traduce en los ficheros necesarios para usar Prototype y Scriptaculous (Los usaremos para el Ajax).

Finalmente hay este bloque Ruby tan chulo y que puede parecer difícil de entender, aunque hace una cosa bien sencilla: Para cada flash[:algo] que exista, ponemos un <div id="algo">Con su contenido.</div>. Esto nos servirá tanto para flash[:error] como para flash[:notice], o cualquier otro que nos inventemos.

La última palabra Ruby del layout es la palabra mágica yield. Se sustituirá por el contenido de la vista de la acción que se esté ejecutando (index, edit, add, …). Espero no ir demasiado lento profundizando en los detalles, pero al ser un tutorial para principiantes… Pido paciencia a los iniciados que lo estén siguiendo.

Queremos tener el formulario para añadir tareas en la página principal, y que al pulsar el botón Add o la tecla Enter se añada a la lista la tarea mediante Ajax. Volvemos a modificar app/views/todo_items/index.html.erb para usar partials y añadir el formulario:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- index.html.erb -->
<h1>Things To Do</h1>
 
<div id="todos">
<%= render @todo_items %>
</div>
<br />
 
<% form_remote_for TodoItem.new do |f| -%>
  <%= f.text_field :task %>
  <%= f.submit "Add" %>
<% end -%>

Como podéis observar, hemos simplificado mucho el código que lista las tareas usando render @todo_items. Rails es muy listo, y observa que @todo_items es un array de todo_item, y piensa: Si me piden hacer un render de un todo_item, a lo mejor es que hay un partial para este… Y (oh! qué casualidad) justo acabo de crear un fichero llamado _todo_item.html.erb en app/views/todo_items/. Para que os funcione teneis que crearlo y dejarlo así:

1
2
3
4
5
6
7
<!-- _todo_item.html.erb -->
<% div_for todo_item do %>
  <%= h todo_item.task %>
  (<%= link_to 'Edit', edit_todo_item_path(todo_item) %>)
  <small>(<%= link_to 'Destroy', todo_item, :confirm => 'Are you sure?', :method => :delete %>)</small>
  <span><%= todo_item.completed_at %></span>
<% end %>

Seguramente habréis observado que he puesto form_remote_for en lugar de form_for, y aquí empieza el Ajax: Esto mandará el contenido del formulario en una llamada Ajax, y en lugar de refrescar el navegador ejecutará lo que el controlador le diga. Así que vamos al controlador y cambiamos un poco el método create.

42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def create
  @todo_item = TodoItem.new(params[:todo_item])
 
  respond_to do |format|
    if @todo_item.save
      flash[:notice] = 'Todo Item added!.'
      format.html { redirect_to(todo_items_url) }
      format.xml  { render :xml => @todo_item, :status => :created, :location => @todo_item }
      format.js
    else
      format.html { render :action => "new" }
      format.xml  { render :xml => @todo_item.errors, :status => :unprocessable_entity }
    end
  end
end

Hemos añadido la línia format.js en el bloque respond_to do |format|. Esto lo que hará es responder tamién al método create si se pide con Content-Type: text/javascript, que es lo que hará la llamada Ajax, pero responderá usando el view create.js.rjs. Hasta ahora todos los views tenian la doble extensión .html.erb, que significa que se trata de ficheros HTML procesados con ERB (Embedded Ruby). Ahora ofrecemos un fichero JavaScript usando RJS, un lenguaje muy simple para generar código JavaScript escribiendo Ruby. Este fichero estará obviamente en app/views/todo_items/ y será así:

1
2
3
4
5
# create.js.rjs
page.insert_html :bottom, :todos, :partial => 'todo_item', :object => @todo_item
page.visual_effect :highlight, "todo_item_#{@todo_item.id}"
page[:new_todo_item].reset
flash.discard

Resumiendo: La primera línea generará el partial con el objeto recién creado en el controlador y lo añadirá al final de <div id="todos">. La segunda hará un efecto de sobresalto en la tarea recién añadida, la tercera vaciará el formulario y con la última descartaremos el mensaje flash que se añade en el controlador. La magia de Ajax y Rails es que este código Ruby se convertirá en JavaScript, y gracias a form_remote_for se mandará de vuelta al cliente, donde se ejecutará dejándonos asombrados a nosotros mismos de lo que sómos capaces de hacer.

Ahora (sólo porque me conviene que se vean las cosas un poco mejor) añadiremos un poco de CSS al final de public/stylesheets/scaffold.css

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
.todo_item
{
  display: block;
  padding: 5px 0;
  background: #DADADA;
  margin: 10px;
}
 
.todo_item span
{
  font-size: 0.7em;
  color: #747474;
}
 
body
{
  margin: 0 auto;
  width: 60%;
}

Lo último que haremos en este capítulo es completar y descompletar las tareas mediante un checkbox que usando Ajax actualizará el estado de la tarea. Dado que estamos usando un controlador REST, tenemos que modificar el fichero config/routes.rb para permitir una nueva acción: toggle, que cambiará de completado a pendiente una tarea. Modificamos el fichero:

1
2
3
4
5
6
ActionController::Routing::Routes.draw do |map|
 
  map.resources :todo_items, :member => { :toggle => :put }
  map.root :todo_items
 
end

Ahora añadimos el método toggle en TodoItemsController:

87
88
89
90
def toggle
  @todo_item = TodoItem.find(params[:id])
  @todo_item.update_attribute :completed_at, (@todo_item.completed_at.nil? ? Time.now : nil)
end

Añadimos el checkbox al parcial _todo_item.html.erb

1
2
3
4
5
6
7
8
9
<!-- _todo_item.html.erb -->
<% div_for todo_item do %>
<%= check_box_tag :completed_at, "1", !todo_item.completed_at.nil?,
                  :onclick => remote_function(:url => toggle_todo_item_path(todo_item), :method => :put) %>
  <%= h todo_item.task %>
  (<%= link_to 'Edit', edit_todo_item_path(todo_item) %>)
  <small>(<%= link_to 'Destroy', todo_item, :confirm => 'Are you sure?', :method => :delete %>)</small>
  <span><%= todo_item.completed_at %></span>
<% end %>

Y sólo nos falta crear otro fichero RJS: toggle.js.rjs junto a las otras vistas de views/todo_items:

1
2
3
# toggle.js.rjs
page.replace "todo_item_#{@todo_item.id}", :partial => 'todo_item', :object => @todo_item
page.visual_effect :highlight, "todo_item_#{@todo_item.id}"

Esto es todo por hoy. Os podéis bajar el código de Github usando Git o directamente un fichero comprimido. Y para los más perezosos que tienen ganas de ver cómo funciona esto del Ajax pero no quieren probar el código o instalar Ruby on Rails, podéis ver este pequeño screencast de 30 segundos (sin audio):
Imagen del Screencast de Microtodo

Dejo para el próximo capítulo separar las tareas pendientes de las completadas, eliminarlas usando ajax, cambiarles el texto in-situ y reordenarlas! (Recordáis el campo position:integer de nuestra migración?).

24 de Marzo de 2009 • 4 comentarios

Creando una aplicación web con Ruby on Rails: Capítulo 1

22 de Marzo de 2009 • 14 comentarios

Se suele comentar que Rails y Ajax se llevan bien. Vamos a demostrarlo creando una pequeña aplicación pseudo GTD para mantener una lista de TODO’s. Aunque en el primer capítulo aún no se verá Ajax, aparecerá muy pronto. Fácilmente se puede extender para gestionar varias listas, pero interesa mantener el código simple.

Comprovamos las versiones de Ruby y Rails, y creamos la aplicación con el comando rails. El comando mate . abre el editor TextMate (sólo para Mac OS X). Aquí usad el equivalente para vuestro editor o simplemente abrid el directorio con vuestro navegador de ficheros.

~% ruby -v
ruby 1.8.6 (2008-03-03 patchlevel 114) [universal-darwin9.0]
~% rails -v
Rails 2.3.2
~% rails microtodo
      create
      create  app/controllers
      [...]
      create  log/test.log
~% cd microtodo
~/microtodo% mate .

Ahora toca respirar un poco y curiosear por el directorio que acabamos de crear y llenar. Podríamos ejecutar script/server y abrir con nuestro navegador http://0.0.0.0:3000/ para ver la página inicial de Rails, pero si habéis seguido la entrada anterior, esto no es necesario.

A continuación nos cargaremos esta página inicial (el objetivo es ver la lista de tareas por hacer cuando vayamos a la página principal) y usaremos el generador scaffold para las tareas. Crearemos el modelo TodoItem y la migración a la base de datos con tres campos: task: string (descripción de la tarea), position: integer (para poder reorganizar las tareas como nos parezca) y completed_at: timestamp (para saber si una tarea se ha completado y en qué instante se ha hecho). Una migración es un pequeño código ruby que se convertirá en sentencias SQL al ejecutar el comando rake db:migrate. Recomiendo echar un vistazo a la guía sobre migrations. El generador scaffold además crea el controlador TodoItemsController, las vistas para las acciones CRUD, un layout para el controlador y un fichero css básico.

~/microtodo% rm public/index.html
~/microtodo% script/generate scaffold TodoItem task:string position:integer completed_at:timestamp
      [...]
~/microtodo% rake db:migrate
(in /Users/jordi/microtodo)
==  CreateTodoItems: migrating ================================================
-- create_table(:todo_items)
   -> 0.0037s
==  CreateTodoItems: migrated (0.0041s) =======================================
 
~/microtodo%

Ahora es un buen momento para ejecutar script/server y jugar un poco con las tareas generadas por el scaffold. A continuación vamos a hacer que el controlador TodoItemsController responda a la página inicial modificando el fichero config/routes.rb y dejándolo así:

1
2
3
4
5
6
ActionController::Routing::Routes.draw do |map|
 
  map.resources :todo_items
  map.root :todo_items
 
end

Hoy acabaremos el capítulo uno limpiando un poco los views modificando los ficheros de app/views/todo_items. Primero borraremos el fichero show.html.erb puesto que no nos interesa una página para cada To Do Item, luego editaremos index.html.erb, edit.html.erb y new.html.erb y crearemos _form.html.erb con este contenido:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- index.html.erb -->
<h1>Things To Do</h1>
 
<% @todo_items.each do |todo_item| %>
  <% div_for todo_item do %>
    <%= h todo_item.task %>
    (<%= link_to 'Edit', edit_todo_item_path(todo_item) %>)
    <small>(<%= link_to 'Destroy', todo_item, :confirm => 'Are you sure?', :method => :delete %>)</small>
  <% end %>
<% end %>
 
<br />
 
<%= link_to 'Add To Do Item', new_todo_item_path %>

Aquí observamos el uso del iterador each sobre la variable @todo_items que se crea en el controlador. Se usa el clásico bloque de Ruby, y para cada todo_item usamos los helpers que Rails nos ofrece para definir un <div> que contiene la descripción de la tarea y enlaces para editarla y eliminarla. Sustituiremos estos enlaces por edición y eliminación in-situ usando Ajax, pero será en otro capítulo.

Vamos las vistas de las acciones edit y new, que son muy parecidas:

1
2
3
4
5
6
<!-- edit.html.erb -->
<h1>Editing To Do Item</h1>
 
<%= render :partial => "form", :locals => { :todo_item => @todo_item, :button_label => "Update Item" } %>
 
<%= link_to 'Back', todo_items_path %>
1
2
3
4
5
6
<!-- new.html.erb -->
<h1>New To Do Item</h1>
 
<%= render :partial => "form", :locals => { :todo_item => @todo_item, :button_label => "Add Item" } %>
 
<%= link_to 'Back', todo_items_path %>

Aquí, además de usar el helper link_to que ya hemos visto en la anterior vista, utilizamos render :partial, que es una herramienta muy potente que permite mantener el código mas limpio siguiendo fielmente el principio DRY. Más información en la guía Layout and Rendering.

Finalmente, la nueva vista que se usará en las acciones edit y new para mostrar el formulario. Tener este formulario duplicado era muy poco DRY, y puesto que usamos los helpers de Rails para crear los campos del formulario, si el objeto todo_item contiene información se mostrará en el campo creado por el helper.

1
2
3
4
5
6
7
8
9
10
11
<!-- _form.html.erb -->
<% form_for(todo_item) do |f| %>
  <%= f.error_messages %>
  <p>
    <%= f.label :task %><br />
    <%= f.text_field :task %>
  </p>
  <p>
    <%= f.submit button_label %>
  </p>
<% end %>

Casi estamos por hoy, pero si nos cargamos la vista para la acción show, tenemos que modificar el controlador para que nunca intente llevarnos a ver un To Do Item. Concretamente, el controlador creado por el scaffolding hace un redirect a la página /todo_items/ID después de crear o editar una tarea. Cambiaremos este comportamiento modificando las acciones create y update.

Modificamos las líneas 48 y 65 del fichero app/controllers/todo_items_controller.rb:

48
        format.html { redirect_to(@todo_item) }
65
        format.html { redirect_to(@todo_item) }

por

48
        format.html { redirect_to(todo_items_url) }
65
        format.html { redirect_to(todo_items_url) }

Hasta aquí en este primer capítulo. En el próximo haremos que toda la acción suceda en una página, insertando tareas desde la página principal y haciendo que una tarea se pueda completar con un checkbox, ambas acciones usando Ajax.

He colgado el proyecto en Github, por si en lugar de seguir los pasos queréis consultar directamente el resultado. Si no usáis Git, os podéis bajar un tarball también desde Github.

Responderé encantado cualquier duda que aparezca siguiendo este tutorial.

22 de Marzo de 2009 • 14 comentarios