Apuntes prestados http://nando.lacoctelera.net En el parque somos mayoría es-es Tecnología vuelo libre código libre the-shaker v0.1. More on http://www.the-shaker.com Luis Villa (aka <maguisso/>, aka <grancomo/>, aka <dispersiontotal/>) http://nando.lacoctelera.net/post/2011/11/02/luis-villa-aka-aka-aka 2011-11-02T10:46:03+00:00

"El testing es para cobardes"

Hace una semana Luis vino hasta mi puesto y me dijo que se iba. Mentiría si dijese que me sorprendió la noticia. No es que me la esperase de él concretamente, pero es que a estas alturas del partido ya me la espero de cualquiera.

Mientras subíamos por las escaleras hacia la cocina le dije que para mí nos dejaba un "mítico", a lo que como no podía ser de otra forma me respondíó:

"¿Mítico? Los mitos suenan a gente que se muere y [...]"

Charlando en la cocina sobre los principios, sobre los éxitos y los fracasos en el camino, recordé que este blog surgió como un experimento en el que las personas podían tener su post. Y en estos días me he dado cuenta de que no puedo dejar de retomar aquella costumbre para dedicarle uno a él, al gran "Luis Bill" :)

Embajador de La Ruby Room por donde quiera que fuese, ha sido desde el principio (aunque le llevó un tiempo entrar "en plantilla", su apoyo y su Dispersión Total estuvieron en The Cocktail desde que yo recuerdo) una cucharilla que nos agitaba a todos para que nos sintiésemos parte del mismo cóctel.

Mi Motivación de Base estos días está un poquito tocada, pero tal y como intentaba contar en mi pasada charla en el Wild Congress, esa es, en el fondo, muy buena señal. Es señal de que he tenido la suerte de trabajar y disfrutar de un Gran Compañero.

]]>
http://nando.lacoctelera.net/post/2011/11/02/luis-villa-aka-aka-aka#comentarios
Parámetros opcionales en una definición de paso de Cucumber http://nando.lacoctelera.net/post/2011/09/21/parametros-opcionales-una-definicion-paso-cucumber 2011-09-21T11:52:37+00:00 Hoy un amigo me ha preguntado cómo podía completar una definición de paso (step definition) ya existente con una finalización opcional. Aunque la solución es simple cuando se conoce, si buscas no es fácil encontrarla en la red.

La clave está en conocer que las expresiones regulares tienen un tipo de agrupación que agrupa sin capturar: son los llamados "grouping-only parentheses".

En la expresión regular de una step definition cualquier parte entre paréntesis pasa a ser un parámetro del método que define el paso. Usando los paréntesis grouping-only evitamos este comportamiento, y si al final de ellos colocamos una interrogación (?) indicando que dicha agrupación es opcional, que puede aparecer o no, lo tenemos casi todo para tener un parámetro opcional.

¿Qué nos falta? Pues nos falta simplemente tener capturas dentro de esa sólo-agrupación-opcional.

El ejemplo, que siempre ayuda. Tenemos la siguiente definición de paso:


 Dado /^que creo un post cuyo título es (.+)$/ do |title|
   [...]
 end
 

Y queremos que, opcionalmente podamos indicar también los tags que asociamos al post, completando la frase con algo como "... tageado con 'cucumber optional parameters'".


 Dado /^que creo un post cuyo título es (.+)(?: tageado con (.+))?$/ do |title, tags|
   [...]
 end
 

Si en la feature sólo indicamos el título del post la regexp macheará y tendremos un nulo en el parámetro tags.

Esta técnica es perfecta si los parámetros opcionales están al final de la frase. La cosa cambia si están al principio o entre medias, ya que esa situación tendremos que añadir lógica dentro del paso para descubrir qué parámetros son los que nos han llegado, si es que es posible averiguarlo.

Los grouping-only sirven también para hacer los pasos más abiertos, posibilitando expresarlos de distintas formas igualmente válidas. Si en el ejemplo anterior queremos aceptar también "artículo" además de "post" podríamos escribir la definición de esta forma:


 Dado /^que creo un (?:artículo|post) cuyo título es (.+)(?: tageado con (.+))?$/ do |title, tags|
   [...]
 end
 

Desde hace mucho tiempo he querido escribir un post sobre dos features de las expresiones regulares que me permitieron disfrutar como un enano del desarrollo de MundoPepino. Una de ellas es esta que he contado.

La otra es el "negative lookahead". A ver si encuentro un caso de uso chulo para dedicarle post y termino de cumplir mi deseo.

]]>
http://nando.lacoctelera.net/post/2011/09/21/parametros-opcionales-una-definicion-paso-cucumber#comentarios
NULL de SQL y las búsquedas "por exclusión" http://nando.lacoctelera.net/post/2011/05/31/null-sql-y-busquedas-por-exclusion 2011-05-31T15:26:43+00:00 Hay un comportamiento de SQL al que no termino de acostumbrarme. No sé si se puede etiquetar como gotcha, pero lo que está claro es que si andas un poco despistado puede llegar a confundir bastante.

Por definición en SQL toda comparación con un nulo evalua a falso.

Lo habitual cuando hacemos búsquedas es que sean por inclusión, es decir, buscando registros que son iguales, parecidos, mayores o menores que un patrón dado. En estas situaciones que la comparación de nuestro patrón con nulo evalue a falso no crea ninguna confusión: si el valor es nulo entonces no es igual, ni parecido, ni mayor, ni menor que mi patrón.

La confusión se genera cuando lo que buscamos es algo distinto de un patrón, es decir algo que no es igual que nuestro patrón (campo <> patron) o que no se parece a nuestro patrón (campo not like patron). Aquí en principio podríamos pensar que un nulo no coincide con nuestro patrón, y que por lo tanto debería formar parte de los resultados obtenidos. Pero no, no es así, porque toda comparación con un nulo evalua a falso.

Con un ejemplo rápido se ve claro. Pongamos que no tenemos claro el color de la oveja Ovi:


 mysql> select * from sheeps;
 +----+-------+-------+
 | id | name  | color |
 +----+-------+-------+
 | 1  | Molly | black |
 | 2  | Dolly | white |
 | 3  | Ovi   | NULL  |
 +----+-------+-------+
 3 rows in set (0.00 sec)
 
 mysql> select * from sheeps where color <> 'black';
 +----+-------+-------+
 | id | name  | color |
 +----+-------+-------+
 | 2  | Dolly | white |
 +----+-------+-------+
 1 row in set (0.00 sec)
 

Cuando recuperamos todas las ovejas que no son negras Ovi no está entre ellas. Y lo mismo nos ocurre cuando queremos recuperar todas las ovejas cuyo color no comienza por white:


 mysql> select * from sheeps where color not like 'white%';
 +----+-------+-------+
 | id | name  | color |
 +----+-------+-------+
 | 1  | Molly | black |
 +----+-------+-------+
 1 row in set (0.01 sec)
 

Si queremos que aparezca Ovi tenemos que abrir la condición con un or color is null:


 mysql> select * from sheeps where color not like 'white%' or color is null;
 +----+-------+-------+
 | id | name  | color |
 +----+-------+-------+
 | 1  | Molly | black |
 | 3  | Ovi   | NULL  |
 +----+-------+-------+
 1 row in set (0.01 sec)
 

A ver si con suerte, gracias a este post, no lo vuelvo a olvidar :)

Moraleja:
Por omisión las migraciones de Rails generan campos cuyo valor por defecto es nulo. En algunas ocasiones puede resultar interesante impedir que un campo acepte nulos (con la opción :null => false).

]]>
http://nando.lacoctelera.net/post/2011/05/31/null-sql-y-busquedas-por-exclusion#comentarios
Truncado por palabras en ruby utilizando una expresión regular http://nando.lacoctelera.net/post/2011/04/27/truncado-palabras-ruby-utilizando-expresion-regular 2011-04-27T21:54:19+00:00 Buscando como truncar un texto por palabras me he encontrado con este post de Paul Sturgess en el que como en casi todo buen post lo mejor está en los comentarios. En estos se propone y desarrolla una expresión regular que finalmente queda así:


 texto.gsub(/^(.{100}[\w.]*)(.*)/m) {$2.empty? ? $1 : $1 + '…'}
 

Esto, en teoría, nos cortaría el texto en el final de la palabra que se encuentre a partir del carácter número 100.

Sin embargo, casualidades de la vida, en el ejemplo que yo estaba probando esta expresión regular me cortaba la palabra "infomática" por la "á", dejándome "[...] no es que la informá...". El problema de siempre...

Fácil de resolver por otro lado, cambiando el "[\w.]*" por "[^\s]*" (literalmente "todo lo que no sea un espacio") que se aproxima mejor a lo que se pretende:


 texto.gsub(/^(.{100}[^\s]*)(.*)/m) {$2.empty? ? $1 : $1 + '…'}
 
]]>
http://nando.lacoctelera.net/post/2011/04/27/truncado-palabras-ruby-utilizando-expresion-regular#comentarios
Búsqueda "aproximada" de un texto en Javascript http://nando.lacoctelera.net/post/2011/03/25/busqueda-aproximada-un-texto-javascript 2011-03-25T12:33:29+00:00 Hoy me ha tocado bregar con algo que no tenía nada claro como resolver. Al final me ha resultado más sencillo de lo que pensaba, pero lo voy a dejar aquí en un post por si me toca volver a hacer algo similar.

Se trata de, teniendo ya en el cliente un conjunto más o menos grande de elementos (como opciones de una lista de selección, por ejemplo), cuando en un campo de texto se teclee los elementos de dicho conjunto deben reducirse sólo a aquellos que contienen el texto tecleado.

Además, no deberán tenerse en cuenta mayúsculas/minúsculas y tampoco, y aquí es donde viene la gracia del post, las siempre delicadas tildes en las vocales.

Seguro que existen mejores formas de resolverlo, pero como primera "aproximación" esto es lo que he hecho:

       var re_text = text.toLowerCase().
         replace(/[aáäâà]/g,'[aáäâà]').
         replace(/[eéëêè]/g,'[eéëêè]').
         replace(/[iíïîì]/g,'[iíïîì]').
         replace(/[oóöôò]/g,'[oóöôò]').
         replace(/[uúüûù]/g,'[uúüûù]');
       
       var re = new RegExp(re_text, 'i');
 
       collection.each(function(elem){
         if(re.test(elem)) alert(elem+' is in the house!');
       });
 
]]>
http://nando.lacoctelera.net/post/2011/03/25/busqueda-aproximada-un-texto-javascript#comentarios
Sacar los meses en los que hay chicha sin sufrir demasiado http://nando.lacoctelera.net/post/2010/02/23/sacar-meses-los-hay-chicha-sin-sufrir-demasiado 2010-02-23T21:33:21+00:00 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". :)

]]>
http://nando.lacoctelera.net/post/2010/02/23/sacar-meses-los-hay-chicha-sin-sufrir-demasiado#comentarios
Directorio para ficheros temporales con Ruby http://nando.lacoctelera.net/post/2010/01/14/directorio-ficheros-temporales-con-ruby 2010-01-14T15:30:45+00:00 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í.

]]>
http://nando.lacoctelera.net/post/2010/01/14/directorio-ficheros-temporales-con-ruby#comentarios
Como borrar desde bash todos los ficheros cuyo nombre sigua un patrón http://nando.lacoctelera.net/post/2010/01/12/como-borrar-todos-ficheros-cuyo-nombre-sigua-patron 2010-01-12T20:52:35+00:00 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!

]]>
http://nando.lacoctelera.net/post/2010/01/12/como-borrar-todos-ficheros-cuyo-nombre-sigua-patron#comentarios
Formatear separación de decimales en Rails http://nando.lacoctelera.net/post/2009/12/24/formatear-separacion-decimales-rails 2009-12-24T13:44:43+00:00 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

]]>
http://nando.lacoctelera.net/post/2009/12/24/formatear-separacion-decimales-rails#comentarios
Modificar un texto en archivos dispersos con bash y vim http://nando.lacoctelera.net/post/2009/12/24/editar-todos-archivos-los-aparezca-texto 2009-12-24T11:02:55+00:00 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!

]]>
http://nando.lacoctelera.net/post/2009/12/24/editar-todos-archivos-los-aparezca-texto#comentarios