La Coctelera

Sacar los meses en los que hay chicha sin sufrir demasiado

No, no se trata de una dieta milagrosa. Se trata de una situación en la que me he encontrado más de una vez y que nunca resuelvo de la misma forma. Esta que he aplicado hoy me parece lo suficientemente buena como para ponerla por aquí y así no volver a darle más vueltas a esto si me toca hacerlo de nuevo.

Se trata del clásico "Archivo" que se muestra habitualmente en la columna de un blog de tal forma que muestre un enlace a todos los meses en los que existe algún contenido publicado. Algo tal que:

2010 Enero

2009 Diciembre, Octubre, Febrero

Y así sucesivamente. Los enlaces nos llevan a un listado con los contenidos que se publicaron dicho mes.

El problema es que si hay muchos contenidos hacerlo tirando del modelo en cuestión puede resultar una operación muy pesada.

Si por el contrario dejamos el trabajo duro de agrupar por año y mes al motor de BBDD la cosa se agiliza notablemente. En mi caso, detrás tengo MySQL y el siguiente helper me ha solucionado el problema:

 def archive
   yearandmonths = ActiveRecord::Base.connection.select_rows(<<QUERY)
     SELECT DISTINCT(DATE_FORMAT(published_at, "%Y-%c"))
     FROM highlights
     WHERE published_at IS NOT NULL
     ORDER BY published_at DESC
 QUERY
   yearandmonths.map do |yearandmonth|
     year, month = yearandmonth.first.split('-')
     Struct.new(:year, :month).new(year, month.to_i)
   end
 end

La tabla asociada a los contenidos que pretendo mostrar es "highlights" y el campo con la fecha de publicación "published_at". No costaría mucho que la función recibiese esto parametrizado pero en este caso sólo necesito el helper en esta parte de la aplicación.

Aquí asumo que cualquier contenido que tiene fecha de publicación ("published_at") está publicado (es decir, que no existe otro campo que determine si puede verse dicho contenido). Idealmente, para que la consulta fuese lo más rápida posible debería existir un índice en dicho campo.

Después, en la vista, uso el helper llevando su salida al "group_by" quedando la cosa tal que así:

 <% archive.group_by(&:year).each do |year, year_months| %>
 <strong><%= year %></strong>
 <% year_months.each do |m| %>
 <%= link_to t('date.month_names')[m.month],
                 highlights_path(:year => year, :month => m.month) %>
 <% end %>
 

Y eso es todo. Un posete, que ya iba tocando para que no se me quede el mes de febrero fuera del "archivo". :)

Directorio para ficheros temporales con Ruby

Aquí va uno curioso. El método de tmpdir de la clase Dir nos devuelve el directorio de ficheros temporales del sistema.

Pues bien dicho método no está dentro de la implementación principal de la clase, si no en otro fichero, tmpdir.rb, que tenemos que requerir si deseamos utilizarlo.

Unas órdenes desde la consola dicen más que mil palabras:


 irb(main):001:0> Dir.tmpdir
 NoMethodError: undefined method `tmpdir' for Dir:Class
         from (irb):1
 irb(main):002:0> require 'tmpdir'
 => true
 irb(main):003:0> Dir.tmpdir
 => "/tmp"
 irb(main):004:0>
 

No quiero meter ruido en Ruby-Talk con esta tontá, pero imagino que alguna razón habrá para que esto sea así.

Como borrar desde bash todos los ficheros cuyo nombre sigua un patrón

Si el patrón es, por ejemplo, ._* (todos los ficheros que empiecen por un punto seguido de un guión bajo), sería:

find . -name '._*' -delete

Si por alguna razón nos interesa hacerlo con xargs:

find . -name '._*' -print0 | xargs -0 rm

Con la opción -print0 del find le decimos que separe los ficheros con bytes nulos (null bytes). La opción -0 de xargs es su pareja de baile: hace que este espere dicha separación por byte nulo entre entradas. De esta forma los nombres de los archivos pueden tener nombres con espacios y cosas raras (¡hasta retornos de carro!).

Jugando con el MacBook de Pantulis nos hemos dado cuenta de que el find de BSD que trae su MacOS necesita la ruta en la que se quiere realizar la búsqueda. Sin embargo el find de las GNU findutils utiliza el directorio actual si no indicamos ruta.

Resumiendo, que en GNU/Linux podríamos ahorrarnos el puntico y dejarlo en:

find -name '._*' -delete

Eso sí, dada su potencia se recomienda evitar el consumo de alcohol y sustancias psicotrópicas antes de su utilización, no vaya a ser que al final se líe parda.

Créditos:

SegFault hizo aumentar notablemente el nivel de este post al comentarme la existencia de los parámetros -print0 y -0 del find y xargs respectivamente. ¡Gracias!

Formatear separación de decimales en Rails

Lo dicho, si me resuelve y puedo, lo posteo. En un lugar de la aplicación necesitábamos cambiar el punto de separación de los decimales por una coma.

El helper triunfador: number_with_delimiter(numero_con_decimales)

Por defecto dicha función utilizará como separador el valor que tengamos en config/locales/es.yml (o similar) dentro de la clave number.format.separator.

Si queremos utilizar otro separador puntualmente podemos pasarle el parámetro :separator. Un ejemplo desde la consola de Rails:


 >> helper.number_with_delimiter 34.76, :separator => "'"
 => "34'76"
 

Más info: NumberHelper's doc

Modificar un texto en archivos dispersos con bash y vim

Con la sana intención de retomar mi blog voy a comenzar a publicar cualquier cosa que me resulte útil en el día a día. Por ejemplo esta que acabo de utilizar.

Necesitaba modificar un texto que estaba en catorce ficheros dispersos por la geografía de mi disco duro.

Lo primero, localizar dichos ficheros y pasárselos a vim:


 grep -r 'el texto buscado' -l . | xargs vim
 

Una vez en vim, reemplazo el texto en todos los ficheros:


 bufdo! 1,$s/el texto buscado/el texto deseado/gc
 

La "c" final es para que nos pida confirmación antes de reemplazar, si lo tenemos claro la podemos quitar.

Por último, para revisar los cambios en cada fichero antes de guardarlos repetimos la siguiente secuencia para cada uno de ellos:


 :wn + enter +  enter
 

Si por el contrario queremos guardar todos los ficheros de golpe sin revisarlos:


 :wa
 

O :xa si queremos guardarlos y salir de vim. Ahora bien, tal y como comenta Porras más abajo, si desde el principio tenemos claro lo que queremos reemplazar y no nos preocupa revisar directamente con el editor las sustituciones, podemos utilizar sed desde la línea de comandos:


 grep -r 'el texto buscado' -l . | xargs sed -i 's/el texto buscado/el texto deseado/g'
 

Para terminar, no sé muy bien por qué motivo pierdo el eco de la terminal siempre que le paso a vim los ficheros que debe abrir por medio de xargs. Para resolverlo simplemente escribo con fe, por que no lo veo, lo siguiente:


 reset + enter
 

Y todo vuelve a estar como al principio pero, eso sí, con "el texto deseado".

Créditos:

  • el xargs me lo enseñó Javier Picado
  • el bufdo me lo presentó Daniel Blanco
  • el reset se lo vi a Blat
  • el sed y la opción -l de grep me lo comentó Porras

¡Gracias majos!

Habemus Iria!!!

Con tres kilos y seiscientos gramos repartidos a lo largo de cincuenta y un centímetros, el pasado miércoles a las doce y treinta y cinco de la mañana Iria se dejó abrazar por primera vez como si tal cosa.

Yo sin embargo no podía dejar de babear de nuevo ante semejante maravilla.

Más amor a dolor.

Mi "line & underline Cucumber day"

Es lo que tiene ser un early-adopter: has de volver a aprender una y otra vez lo que pensabas que ya sabías.

Resulta que estas navidades me actualice la versión de Cucumber y para mi sorpresa dos cosas que me parecían muy útiles ya no estaban ahí.

La primera fue la opción de línea de comandos --line que tenía cucumber. Con ésta podías lanzar un escenario concreto facilitando su línea, la cual te devolvía el propio cucumber en su salida estándar. Es decir, que ya no podía hacer:


 $ cucumber
 Característica: [...]
   
   Escenario: Saludo universal      # features/saludo.feature:7
   [...]
 $ cucumber features/saludo.feature --line 7
 optparse.rb:1443:in `complete': invalid option: --line (OptionParser::InvalidOption)
 

... para lanzar el escenario "Saludo universal". La aparente alternativa que tenía era utilizar la opción --scenario pasándole dicho literal ('Saludo universal') como valor. Pero me resultaba mucho más incómodo. Qué raro...

La segunda llegó poco después y estaba relacionada con la salida generada por cucumber al ejecutar las features. En la nueva versión en lugar de subrayar los parámetros de cada paso los resaltaba con el mismo color que el resto del texto, pero más brillante. Es decir, antes tenía:


   Dado que tenemos dos huertos cuyas áreas son "34 y 27"
 

Ahora me encontraba:


   Dado que tenemos dos huertos cuyas áreas son "34 y 27"
 

El problema que me generaba este cambio es que, al no poder destacarse los espacios en blanco, cuando tenía dos parámetros contiguos no sabía donde terminaba el primero y comenzaba el segundo. En el ejemplo anterior, no sabía si el primer parámetro era dos o dos huertos. Otra vez, qué raro... no era posible que la cosa estuviese yendo hacia atrás. Algo me estaba perdiendo.

Como estaba en plenas vacaciones decidí aparcar el asunto hasta que antes de ayer decidí investigar un poco más en el asunto.

Empecé por lo de la opción --line y la primera en la frente. Mandé un correo a la lista de usuarios RSpec (donde también están los de Cucumber) y dos personas (Matt Wynne y el propio Aslak que está al quite el tío) me dijeron lo que debería haber visto yo solito en la primera línea de la información de uso de cucumber:


 $ cucumber --help
 Usage: cucumber [options] [[FILE[:LINE[:LINE]*]] [...]
 

Es decir, ahora la línea (o líneas) se indica después del nombre del archivo precedida de dos puntos. Es decir, ahora podemos directamente copiar y pegar la salida de cucumber (en el ejemplo features/saludo.feature:7) y tirar millas. Muuucho mejor.

La segunda, la de los parámetros subrayados, por tratarse de algo que directamente consideraba un paso hacia atrás, decidí moverlo directamente como un ticket en Lighthouseapp.

En menos de una hora Aslak había respondido y cerrado el ticket:

There is no underline on Windows, so I decided to use a default formatting that works on across OSes. You can get underlines back by defining CUCUMBER_COLORS.

Y terminaba facilitándome el enlace a la página del wiki que también debería haberme leído antes de pedir por esta boquita.

Así es que he añadido la siguente línea en mi .bashrc y ya tengo lo que tenía:


 export CUCUMBER_COLORS="passed_param=green,bold,underline:failed_param=red,bold,underline:skipped_param=cyan,bold,underline"
 

Dos "tolaaaay!" en un mismo día. Tenía que contarlo.

Bryan Helmkamp <brynary/>

Mi intención inicial era contar más en detalle cómo echar a correr un navegador con nuestras features de Cucumber vía Webrat y Selenium. Después de la charla en la Conferencia Rails varias personas se han puesto en contacto con nosotros para contarnos sus dificultades a la hora de conseguir dicho objetivo.

Sin embargo en este caso, por suerte, no es tan fácil como escribir un post o dar una charla sobre el asunto. Ésta misma semana Bryan Helmkamp ha estado subiendo cambios a Webrat que harán que tanto parte de lo que contamos en la conferencia como todo lo que yo pretendía contar en este post sea, si no ruido, sí, al menos, algo totalmente prescindible en una primera toma de contacto.

Dichos cambios están siendo la solución "oficial" a dichos problemas, que son básicamente tres:
- Lanzar el servidor de Selenium-RC (o Selenium Grid)
- Lanzar la versión actual de nuestra aplicación (y pararla cuando terminen los tests)
- Embeber la sesión de Selenium dentro de la API de Webrat (para que la definición de un step sea independiente del tipo de sesión)

De momento no hay (o yo no he encontrado) nada de documentación, pero echando un ojillo al código he visto que ha cambiado la dependencia de la gema del cliente de Selenium (selenium-client en lugar de Selenium como hasta ahora) y que para lanzar el servidor de Selenium-RC lo busca en vendor/selenium-server.jar.

También he visto que, por ahora, la mayoría de las opciones (navegador que queremos lanzar, puerto para nuestra apliación, ubicación del servidor de Selenium, etc.) están metidas "a fuego" sin posibilidad de alterarlas desde fuera. Pero seguro que esto último no tarda en cambiar.

Al igual que el RSpec Story Runner fue un entorno crudo en el que muchas carencias eran resueltas por cada cual a su manera, me da la sensación de que Webrat con Selenium está justo cambiando de color y que en poco tiempo estará maduro y listo para ser disfrutado sin indigestiones.

Go Brynary, Go!

http://www.brynary.com