Favoritos de los expertos de tonicardenas en Genbetadev http://www.genbetadev.com/usuario/ seleccionado por los expertos de tonicardenas http://www.genbetadev.com <![CDATA[Optimizando al máximo: Bitvectores]]> http://www.genbetadev.com/lenguajes-de-programacion/optimizando-al-maximo-bitvectores http://www.genbetadev.com/lenguajes-de-programacion/optimizando-al-maximo-bitvectores Sat, 20 Aug 2011 13:47:56 +0000 seleccionado por los expertos de tonicardenas

optimizacion.jpg


Cuando programamos cualquier tipo de aplicación, es común la necesidad de almacenar estados o valores de tipo boleano para nuestras estructuras de datos. Si por ejemplo programamos en C99 podemos hacer uso del tipo _Bool que ocupa un byte de longitud, también ocupa un byte el tipo bool de C++.

Sin embargo, utilizar un byte para un valor que puede ser representado por un único bit es un gasto del 88% más de recursos de los estríctamente necesarios si partimos de la base de que un byte puede almacenar ocho bits y representar 256 valores diferentes.

Si en nuestra aplicación necesitamos guardar estados o flags sobre nuestras estructuras de datos, podemos utilizar un tipo de dato para almacenar estados mucho más eficiente que un array de elementos de un byte de longitud o que propiedades de tipo booleano. Podemos usar Bitvectores.

¿Qué es un Bitvector?

Un bitvector es una variable por lo general de 16 o 32 bits (dependiendo de las necesidades de la aplicación) que sirve para almacenar estados que pueden tener solo dos posibles valores, verdadero o falso. Un entero de 32 bits está compuesto precisamente por 32 bits diferentes que pueden ser usados de forma individual para definir si el bit en una determinada posición esta activado (su valor es uno) o desactivado (su valor es cero).

Así, con tan solo cuatro bytes de memoria podemos representar 32 posibles estados diferentes que de cualquier otro modo hubieran ocupado 32 bytes (256 bits) un 400% más. Hay que tener cuidado de no confundir los Bitvectores con los bit fields de las estructuras, pues son dos conceptos totalmente diferentes.

Entrando en detalles

Un numero sin signo de 32 bits puede representar desde el número 0 al 4294967295. Cada uno de esos números puede ser representado en base dos, por lo tanto, el número 0 representa todos los bits del entero sin signo a cero y el número 4294967295 los representa a uno:

BITS : 00000000000000000000000000000000  Decimal: 0
BITS : 11111111111111111111111111111111  Decimal: 4294967295
BITS : 00000000000000001111111111111111  Decimal: 65535
BITS : 00001100001000001111000011110111  Decimal: 203485431
Una vez entendido esto, podemos ver claramente como podemos utilizar una única variable de 32 bits (un entero sin signo) donde definamos que cada posición en base dos corresponde a un estado o flag en nuestras estructuras de datos. Si todos los estados están a true la variable tendrá un valor de 4294967295 y si están a false su valor será 0.

Pero operar con bitvectores es mucho más sencillo que eso, no es necesario recordar ningún valor decimal ya que podemos utilizar los operadores a nivel de bits para hacernos la vida mucho más sencilla. Utilizando los operadores a nivel de bits, podemos crear macros o funciones que nos ayuden a fijar los valores en nuestro vector de bits.

Implementación en C

Yo siempre he sido muy amante de las macros en C, así que voy a hacer una implementación de un BitVector y de las funciones necesarias para su manipulación y uso utilizando macros. Además, el resultado es portable a cualquier plataforma y sistema operativo bien sean sus enteros de 32 o 64 bits y su ordenamiento de bits big endian o little endian.

/* 
 * 32bit bitvector defines
 */
#define BV00        (1 &#60; &#60;  0)
#define BV01        (1 &#60;&#60;  1)
#define BV02        (1 &#60;&#60;  2)
#define BV03        (1 &#60;&#60;  3)
#define BV04        (1 &#60;&#60;  4)
#define BV05        (1 &#60;&#60;  5)
#define BV06        (1 &#60;&#60;  6)
#define BV07        (1 &#60;&#60;  7)
#define BV08        (1 &#60;&#60;  8)
#define BV09        (1 &#60;&#60;  9)
#define BV0A        (1 &#60;&#60; 10)
#define BV0B        (1 &#60;&#60; 11)
#define BV0C        (1 &#60;&#60; 12)
#define BV0D        (1 &#60;&#60; 13)
#define BV0E        (1 &#60;&#60; 14)
#define BV0F        (1 &#60;&#60; 15)
#define BV10        (1 &#60;&#60; 16)
#define BV11        (1 &#60;&#60; 17)
#define BV12        (1 &#60;&#60; 18)
#define BV13        (1 &#60;&#60; 19)
#define BV14        (1 &#60;&#60; 20)
#define BV15        (1 &#60;&#60; 21)
#define BV16        (1 &#60;&#60; 22)
#define BV17        (1 &#60;&#60; 23)
#define BV18        (1 &#60;&#60; 24)
#define BV19        (1 &#60;&#60; 25)
#define BV1A        (1 &#60;&#60; 26)
#define BV1B        (1 &#60;&#60; 27)
#define BV1C        (1 &#60;&#60; 28)
#define BV1D        (1 &#60;&#60; 29)
#define BV1E        (1 &#60;&#60; 30)
#define BV1F        (1 &#60;&#60; 31)
/*
 * BitVector Macros
 */
#define IS_SET(flag, bit)        ((flag) &#38; (bit))
#define SET_BIT(var, bit)        ((var) |= (bit))
#define REMOVE_BIT(var, bit)     ((var) &#38;= ~(bit))
#define TOGGLE_BIT(var, bit)	 ((var) ^= (bit))
En el ejemplo anterior hemos definido 32 posiciones de bitvector para el número entero sin signo 1. Después hemos definido unas macros con el objetivo de operar con estos bits:
  • IS_SET comprueba que el bitvector flag contiene el bit encendido o no utilizando el operador bit a bit AND
  • SET_BIT enciende el bit en el bitvector var utilizando el operador de asignación de bits OR
  • REMOVE_BIT apaga el bit en el bitvector var utilizando el operador de asignación AND y el NOT
  • TOGGLE_BIT conmuta entre encendido y apagado el bit en el bitvector var utilizando el operador de asignación XOR

¿Cómo se usa?

Vamos a ver un pequeño ejemplo de como se usaría en una aplicación:

/*
 * Bits para estados de la estructura trabajador
 */
#define TRFLAG_ENFERMO     BV00
#define TRFLAG_AUSENTE     BV01
#define TRFLAG_TEMPORAL    BV02
#define TRFLAG_INDEFINIDO  BV03
#define TRFLAG_DISCONTINUO BV04
#define TRFLAG_JUNIOR      BV05
#define TRFLAG_SENIOR      BV06
#define TRFLAG_PROJECTLEAD BV07
#define TRFLAG_MANAGER     BV08
#define TRFLAG_COACHER     BV09
#define TRFLAG_SCRUMMAST   BV0A
/*
 * Worker Data Struct
 */
struct worker_data
{
    char*      name;
    char        edad;
    int          flags;
}; 
struct worker_data w_data;
w_data.name = &#34;Perico&#34;;
w_data.edad = 26;
w_data.flags = 0; // Fijamos todos los bits a cero
SET_BIT(w_data.flags, TRFLAG_SENIOR);
SET_BIT(w_data.flags, TRFLAG_TEMPORAL);
SET_BIT(w_data.flags, TRFLAG_SCRUMMAST);
if (IS_SET(w_data.flags, TRFLAG_MANAGER) )
{
    printf(&#34;%s es un administrativo\n&#34;, w_data.name);
}
if (IS_SET(w_data.flags, TRFLAG_SCRUMMAST))
{
    printf(&#34;%s es Scrum Manager\n&#34;, w_data.name);
}
if (!IS_SET(w_data.flags, TRFLAG_INDEFINIDO))
{
    printf(&#34;%s aún no es indefinido\n&#34;, w_data.name);
}
Hemos definido varios bitvectores relacionados con los estados de un trabajador. Hemos definido una estructura muy simple y hemos creado una variable de ese nuevo tipo. Le hemos asignado un nombre, una edad y hemos fijado el vector de bits de los flags a cero. Después hemos ejecutado una serie de acciones sobre el bitvector, veamos el valor de la variable tanto en decimal como en binario conforme van añadiéndose bits encendidos al vector:
// w_data.flags = 0;
BITS : 00000000000000000000000000000000  Decimal: 0
// SET_BIT(w_data.flags, TRFLAG_SENIOR);
BITS : 00000000000000000000000001000000  Decimal: 64
// SET_BIT(w_data.flags, TRFLAG_TEMPORAL);
BITS : 00000000000000000000000001000100  Decimal: 68
// SET_BIT(w_data.flags, TRFLAG_SCRUMMAST)
BITS : 00000000000000000000000101000100  Decimal: 324
Puedes comprobar como se van encendiendo los bits correspondientes a las posiciones representadas por cada bitvector definido.

Bonus Track

Este tipo de optimización no está restringida solo al mundo de los lenguajes de tipado estático, en Python por ejemplo, también podemos hacer uso de esta optimización a nivel de bits de forma sencilla:

def IS_SET(flag, bit):
    return (flag &#38; (1 &#60; &#60; bit))
def SET_BIT(var, bit):
    return (var |= (1 &#60;&#60; bit))
def REMOVE_BIT(var, bit):
    return (var &#38;= (1 &#60;&#60; bit))
def TOGGLE_BIT(var, bit):
    return (var ^= (1 &#60;&#60; bit))
El anterior código en Python tiene el mismo efecto que lo expuesto anteriormente en C.

Conclusión

No solo optimizamos el espacio que nuestras estructuras de datos ocupan en memoria, las operaciones a nivel de bits son también más rápidas. La optimización no solo es cosa de los lenguajes de tipado estático y más bajo nivel, la optimización es una cuestión de ingeniería del software y buenas prácticas, algo, que muchos profesionales y fabricantes en la industria han olvidado por completo, sobre todo, en la del videojuego.



Más Información:

]]>
<![CDATA[Extendiendo la funcionalidad de las librerías básicas de Ruby con Facets]]> http://www.genbetadev.com/ruby/extendiendo-la-funcionalidad-de-las-librerias-basicas-de-ruby-con-facets http://www.genbetadev.com/ruby/extendiendo-la-funcionalidad-de-las-librerias-basicas-de-ruby-con-facets Wed, 13 Jul 2011 14:00:59 +0000 seleccionado por los expertos de tonicardenas Ruby Facets

Si bien las librerías que forman parte del core de Ruby son bastante completas, a menudo se echan en falta métodos para realizar operaciones con los objetos que no han sido tenidas en cuenta, y para las que deberemos bien recurrir a una gem externa, bien pasar a implementarlo nosotros mismos.

Una de las gems más conocidas en lo que a extensión de funcionalidad en las clases principales de Ruby se refiere es ActiveSupport, que nació como un componente más del framework Ruby on Rails, pero que hoy en día es utilizada ampliamente en multitud de gems y proyectos independientes de éste.

Sin embargo, también existe una colección mucho más amplia de extensiones a la librería de Ruby, y que es más desconocida. Su nombre es Ruby Facets, y aunque se trata de una gem bastante grande, no debe preocuparnos incorporarla a nuestro proyecto, pues está perfectamente modularizada, y podemos incluir únicamente las extensiones deseadas a nuestro software en Ruby, sin engordar inútilmente el espacio en memoria que ocupará nuestro proceso. De hecho, se pueden cargar las extensiones incluso método a método, o todas las extensiones disponibles para una clase determinada, con lo que podemos hilar tan fino como deseemos.

Para haceros una idea, en Facets nos encontraremos con métodos para realizar operaciones adicionales con un objeto de la clase Array, tales como hacer merges destructivos, calcular la entropía del mismo, calcular todas las permutaciones posibles de sus elementos, dividirlo en grupos de elementos según una expresión regular, y muchas más.

Dedicad unos minutos a navegar por su extensa documentación. Os garantizo que os encontraréis con muy gratas sorpresas.

Documentación | Facets Core, Facets More

Más información | Ruby Facets

]]>
<![CDATA[Los vídeos de las sesiones del Apple WWDC 2011 ya están disponibles para su descarga]]> http://www.genbetadev.com/formacion/los-videos-de-las-sesiones-del-apple-wwdc-2011-ya-estan-disponibles http://www.genbetadev.com/formacion/los-videos-de-las-sesiones-del-apple-wwdc-2011-ya-estan-disponibles Fri, 24 Jun 2011 13:57:46 +0000 seleccionado por los expertos de tonicardenas WWDC 2011

Si tienes cuenta de desarrollador de Apple (y si no, siempre estás a tiempo de creártela, puesto que es gratuita), ahora ya puedes aprovechar los ratos libres del verano para acceder a los vídeos de las sesiones que se celebraron durante el famoso evento para desarrolladores de la compañía, el Worldwide Developers Conference (WWDC), durante los días 6 a 10 de junio de este año 2011.

Los contenidos de las sesiones abarcan diversos temas respecto al desarrollo de aplicaciones para ordenadores de escritorio (centrándose en las nuevas características del próximo sistema operativo, OS X Lion), al desarrollo de software para la plataforma móvil iOS (iPhone, iPod Touch y iPad), y en menor medida, al desarrollo de aplicaciones web aprovechando las nuevas características de HTML5, efectos y animaciones por CSS, y otras tantas soportadas por el navegador Safari, tanto en su versión de escritorio como en su versión móvil.

Además de los vídeos, también pueden descargarse las diapositivas estáticas en PDF, para su impresión o consulta, si bien perderemos lógicamente las demos y las animaciones en las mismas dispuestas para mejorar la comprensión de los conceptos que en cada charla se tratan.

Sitio oficial | Vídeos de la Apple WWDC 2011

]]>
<![CDATA[Ruby Version Manager, o cómo tener múltiples intérpretes de Ruby en tu máquina al mismo tiempo]]> http://www.genbetadev.com/herramientas/ruby-version-manager-o-como-tener-multiples-interpretes-de-ruby-en-tu-maquina-al-mismo-tiempo http://www.genbetadev.com/herramientas/ruby-version-manager-o-como-tener-multiples-interpretes-de-ruby-en-tu-maquina-al-mismo-tiempo Thu, 16 Jun 2011 15:11:22 +0000 seleccionado por los expertos de tonicardenas RVM

Ruby es un lenguaje que ha madurado muchísimo en los últimos años. Al principio sólo existía un intérprete, el de Matz, y su rendimiento no era lo que se dice espectacular (el propio Matz ha admitido en varias ocasiones que él no es un experto en la construcción de este tipo de software, y que buena parte del código debería ser revisado y optimizado).

Hoy en día hay un buen puñado de intérpretes diferentes para este maravilloso lenguaje: Ruby 1.8, Ruby 1.9, JRuby, Rubinius, MacRuby, Maglev, Ruby Enterprise Edition, IronRuby… Algunos de ellos llevan usándose en entornos de producción desde hace algún tiempo.

¿Por qué es útil RVM?

Siempre es más que recomendable que utilices el mismo intérprete que estés usando en producción, para evitar sorpresas. El problema viene cuando trabajas en varios proyectos escritos en Ruby al mismo tiempo, y cada uno de ellos utiliza un intérprete diferente. O también, utilizan versiones de gems diferentes, y parece que la única solución sea andar desinstalando e instalando versiones de dichas gems cada vez que cambias de uno a otro, lo cual es evidentemente poco práctico y nada razonable.

Imagina la situación: Empiezas la jornada trabajando en el Proyecto 1, el cual lleva en desarrollo bastante tiempo y aún se basa en la versión 2.3 de Rails. Dado que esta versión aún tenía problemas de compatibilidad con la serie 1.9 de Ruby (esta versión del intérprete aún no era muy popular por aquel entonces, pues se encontraba en un estado poco maduro), el proyecto utiliza Ruby Enterprise Edition, una variante del intérprete 1.8 con varias mejoras de rendimiento (en especial, en el plano de la gestión de memoria). De modo que en tu máquina local instalas Ruby Enterprise Edition, Rails 2.3 y todas las gems de las que depende el proyecto.

Más tarde, te pones a trabajar en el Proyecto 2, que acaba de comenzar, y con el que os habéis decidido a usar Rails 3.1 (que para cuando finalice el proyecto ya estará maduro) y Ruby 1.9.2. Necesitas poder instalar este segundo intérprete sin que pise los ejecutables del primero, y cambiar tu PATH si es necesario para que localice los nuevos binarios. Cada vez que cambies del primer proyecto al segundo, has de actualizar tu PATH para asegurarte que estás usando los binarios correctos. Si has de actualizar a una versión menor del intérprete, tendrás que repetir estas operaciones, y reinstalar todas las gems.

Por último, llama un cliente al que le desarrollásteis una aplicación en Rails 2.2 con Ruby Enterprise Edition, y que necesita un pequeño cambio urgente. Como vuestro REE lo tenéis configurado para el Proyecto 1, tenéis instaladas las gemas de Rails 2.3, así como las versiones de otras gemas que funcionan bien para esa aplicación. Cuando os ponéis a instalar las dependencias de esta vieja aplicación, e intentáis arrancarla, empiezan a llover errores de dependencias entre versiones (se intenta cargar una versión específica de una gem, pero alguna otra librería la ha cargado ya en otra versión, de modo que cuando intenta leer la más vieja devuelve un error). Dado que Rails 2.x aún no utilizaba bundler para la gestión de dependencias, tenéis que arremangaros y empezar a desinstalar las versiones de gems conflictivas, a sabiendas que cuando volváis a volcar vuestra atención en el Proyecto 1 tendréis que deshacer estos pasos.

¿Te suenan algunas de estas situaciones? Si te dedicas a programar aplicaciones en Ruby desde hace tiempo, seguro que sí. Y por eso Ruby Version Manager viene a nuestro rescate. Gracias a esta herramienta, podemos tener tantos intérpretes de Ruby como queramos, incluso diferentes versiones menores de un mismo intérprete, y cambiar de uno a otro tan sólo ejecutando un comando. Además, podemos definir un gemset (un conjunto de gemas) para cada proyecto, de modo que cuando nos cambiemos a un gemset en particular, éste contenga exclusivamente las gemas que hacen falta para correr la aplicación, en sus versiones apropiadas, sin que afecten a las otras aplicaciones que podamos tener.

Vale, me has convencido. ¿Cómo lo instalo?

En primer lugar, has de tener un sistema que ejecute alguna variante de Unix. RVM no funciona en máquinas Windows, aunque sí que se comporta perfectamente bajo Linux o Mac OS X. La instalación es sencillísima, tan sólo has de tener instalada la herramienta curl (en Mac OS X viene instalada por defecto), así como Git, y ejecutar el siguiente comando:

  $ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)

Una vez instalado y actualizado tu PATH para incluir la ruta a los binarios de RVM, tal y como te recomienda el proceso final de instalación, sólo has de instalarte los intérpretes de Ruby que consideres oportunos. En Mac OS X, necesitarás tener instalado el paquete de Xcode SDK, que es donde viene el compilador de C. En Linux, deberás instalar estas dependencias usando el administrador de paquetes de tu distribución. Si utilizas Debian o derivados (como Ubuntu), bastará normalmente con que instales el metapaquete build-essentials. Si algún intérprete requiere que se cumplan dependencias adicionales, se te informará sobre ello.

Por ejemplo, podemos instalar el intérprete REE y el 1.9.2, requisitos de los ejemplos que mencioné antes:

  $ rvm install ree
  $ rvm install 1.9.2

Para cambiar de uno a otro una vez instalados, tan sólo hay que hacer:

  $ rvm ree

Cómo crear un gemset

Como mencionaba anteriormente, podemos crear conjuntos de gemas separadas para cada proyecto. Tan sólo hay que especificar un nombre para ese gemset, y utilizar las herramientas que rvm proporciona (puedes escribir “rvm gemset” para ver todos los comandos disponibles). Por ejemplo, podemos crearnos un gemset para el proyecto 1, y cambiarnos a él:

  $ rvm gemset create proyecto1
  $ rvm gemset use proyecto1

Es importante entender que el gemset se crea para el intérprete que estemos usando en ese momento. De modo que antes de crear un gemset, selecciona el intérprete adecuado.

Cómo automatizar el cambio de intérprete y gemset

No quisiera finalizar el artículo sin mencionaros una vía rápida para que, al cambiar a la carpeta de un proyecto, RVM os seleccione el intérprete y gemset adecuados de forma automática. Para ello, sólo tenéis que crear un fichero de nombre “.rvmrc” en la carpeta en cuestión, y éste contendrá la serie de comandos necesarios para realizar estas operaciones. Cuando entréis en dicha carpeta desde línea de comandos, RVM ejecutará dichas acciones, y todo estará listo para empezar a trabajar. Por ejemplo, podéis tener algo como esto (que no es más que un atajo a la selección del intérprete y del gemset, creando este último en el camino en caso de que no exista, todo en un único comando):

  rvm —create 1.9.2@proyecto2

Conclusiones

Espero haber esclarecido las ventajas principales de usar una herramienta como RVM en tu entorno de trabajo. Por supuesto, RVM posee muchas más funcionalidades, que te invito a que conozcas visitando la web oficial.

¿Usas ya RVM? ¿Qué otros trucos similares conoces para llevar un control de los requisitos de tus proyectos?

Más información | Ruby Version Manager

]]>
<![CDATA[La frikada de la semana: GitHub High Scores]]> http://www.genbetadev.com/desarrolladores/la-frikada-de-la-semana-github-high-scores http://www.genbetadev.com/desarrolladores/la-frikada-de-la-semana-github-high-scores Wed, 01 Jun 2011 05:20:56 +0000 seleccionado por los expertos de tonicardenas GitHub High Scores

Las semanas delante del teclado y la pantalla se pueden hacer muy duras mientras llega el anhelado verano. En Genbetadev lo sabemos y por eso, de vez en cuando, te traemos curiosidades (pijaditas, frikadas, tontás o como quieras llamarlas) para sacarte una sonrisa y que tu productividad decaiga durante unos minutos. Hoy os traemos GitHub High Scores, una web que no es más que un ranking de contribuidores a repositorios Git en GitHub (te recuerdo que mi compañero Carlos ha iniciado una serie de posts sobre GitHub y que incluso en Genbetadev hemos creado nuestra propia cuenta donde iremos subiendo nuestro código) en un formato realmente llamativo: la página de scores de una máquina recreativa de 8-bits ochentera. Es que lo retro está de moda.

Pues nada, entretente un rato metiendo repositorios a ver el ranking de sus contribuidores y, ya de paso, te reto a encontrar una frikada más gorda de entre las surgidas entre los últimos días. Venga, valiente, atrévete… ¿o es que acaso eres un gallina?

Vía | The Changelog
Fuente | GitHub High Scores

]]>
<![CDATA[Todas las APIs y herramientas de Google resumidas en una tabla periódica]]> http://www.genbetadev.com/herramientas/todas-las-apis-y-herramientas-de-google-resumidas-en-una-tabla-periodica http://www.genbetadev.com/herramientas/todas-las-apis-y-herramientas-de-google-resumidas-en-una-tabla-periodica Fri, 27 May 2011 17:00:03 +0000 seleccionado por los expertos de tonicardenas Tabla periodica APIs Google

¿Os ocurre también a vosotros que os perdéis entre todas las APIs y herramientas que ofrece Google? Pues bien, Google sabiendo que cada vez tienen más tecnologías que ofrecer a los desarrolladores creó hace tiempo una interesante tabla con todas ellas agrupadas en áreas de interes como si fueran los elementos de la tabla periódica.

Es interesante visitar de vez en cuando la tabla con las APIs de Google, ya que van incorporando nuevas APIs como la reciente Google Tasks o Google APIs Discovery Service lanzada después de la Google I/O de principios de mes.

Hay que echarle un buen rato para recorrer todo lo que ofrece, apabulla el dominio que posee Google pero nos puede ser de buena ayuda para descubrir algun servicio que nos facilite la vida y que no conociamos. Podemos encontrar desde Google Search, Android, Adsense, Chrome, Google Apps, GWT, App Engine, elementos sociales (Orkut o Social Graph Api), Google Tools, Google Maps o Google Mobile..

Sitio web | Google APIs

]]>
<![CDATA[Herramientas imprescindibles para un desarrollador de Ruby on Rails]]> http://www.genbetadev.com/ruby/herramientas-imprescindibles-para-un-desarrollador-de-ruby-on-rails http://www.genbetadev.com/ruby/herramientas-imprescindibles-para-un-desarrollador-de-ruby-on-rails Tue, 24 May 2011 08:00:54 +0000 seleccionado por los expertos de tonicardenas Herramientas Rails

Seguimos con los especiales acerca de cuáles son las herramientas imprescindibles a la hora de embarcarte en un proyecto con tu lenguaje y/o framework preferido. En esta ocasión, os voy a describir las que yo considero más importantes para desarrollar aplicaciones en Ruby on Rails. Aunque estoy seguro que hay muchas otras que se os ocurren, así que os invito a que comentéis vuestras preferencias.

El editor o IDE

¿Usar un editor ligero, o un IDE que lo abarque todo? El eterno dilema. Yo la verdad es que soy de los que prefieren un editor más o menos sencillo, pero que sea fácilmente extensible a través de plugins. Mi elección ha sido desde hace mucho tiempo TextMate, un editor del que ya os hablé en su día.

No obstante, existen un buen número de IDEs para programar con Ruby on Rails que funcionan realmente bien, y que se pueden convertir fácilmente en tu centro de mando de todo el proyecto, gracias a su integración con sistemas de control de versiones, visor de base de datos, etc.

Uno de los IDEs más conocidos es RubyMine. Si bien no es gratuito (salvo que sea usado para proyectos de software libre), su precio no es descomunal, partiendo de 58 euros la versión para un desarrollador individual. A cambio de este dinero, se nos ofrece un entorno con un editor que permite el autocompletado de código, herramientas de ayuda para la refactorización, análisis de código al vuelo, visor del esquema de los modelos, soporte para Bundler o RVM, depurador, soporte para diversos sistemas de control de versiones… En definitiva, toda una navaja suiza. Está disponible para Windows, Mac OS X y Linux.

El otro IDE popular para programar en Ruby es NetBeans, que aunque es más conocido como un entorno para Java, existen unos módulos oficiales que permiten utilizarlo para este otro lenguaje. De nuevo, nos ofrece herramientas de ayuda a la refactorización, inferencia de tipos, navegación rápida entre ficheros relacionados, parseo de código mientras se escribe para detectar errores de sintaxis, y un largo etcétera.

El intérprete

Lógicamente, necesitamos una versión del intérprete de Ruby para poder trabajar. En los proyectos nuevos, ya no tiene tanto sentido utilizar la versión 1.8.x del intérprete oficial, y lo recomendable suele ser trabajar directamente con la versión 1.9.2 del mismo. No obstante, además de los intérpretes oficiales, existen muchos otros igualmente válidos, algunos de los cuales bastante potentes, y siempre que las librerías y gems que vayamos a utilizar en nuestro proyecto sean compatibles con estos, no existe ninguna traba para usarlos.

El intérprete oficial (también llamado CRuby) se puede descargar en sus diferentes versiones desde la página oficial del lenguaje, aunque si utilizas Linux seguramente te sea más fácil utilizar los paquetes que te proporcione tu distribución. Algunos de estos intérpretes alternativos a CRuby son: JRuby, Rubinius, Ruby Enterprise Edition, y otros muchos que aún tienen algunos problemas de compatibilidad con librerías habituales.

Si tuviera que mencionar un favorito de entre los alternativos, ese sería JRuby, ya que no sólo su rendimiento es espectacular, sino que además permite la ejecución de aplicaciones Rails en entornos donde sólo tienen cabida servidores de aplicaciones en entornos Java como pudiera ser GlassFish. Esto permite que puedas colocar tu aplicación Rails en servidores de empresas que ya están siendo usados para servir otras aplicaciones escritas en Java sin tener que cambiar mucho el entorno del servidor.

Si además deseas poder trabajar con varios intérpretes simultáneamente desde tu máquina, te aconsejo que le eches un buen vistazo a Ruby Version Manager (RVM). Aunque tengo intención de dedicarle un artículo por separado en breve.

Software de virtualización

Aunque a menudo Rails se utiliza como backend web de ciertas aplicaciones, es más habitual que sea usado para programar una aplicación web completa, incluido el frontend. Y claro, cualquier aplicación web que se precie ha de probarse bien en diversos navegadores, no sólo el que tengas en tu máquina. Es por ello que el uso de un software de virtualización se vuelve imprescindible.

Yo trabajo con Mac, pero dado que he de probar el renderizado de las vistas en Internet Explorer, he de instalarme una máquina virtual para poder tener Windows en ella con este navegador, que no existe para otras plataformas. Es más, lo normal es tener más de una máquina virtual, cada una con una versión diferente del navegador, para realizar las pruebas con un entorno lo más fiel posible a aquel que utilizará el usuario. Si trabajas en Windows, necesitarás poder probarlo en Safari. Si trabajas en Linux, puede que necesites probar tu aplicación en los dos navegadores anteriormente mencionados. En definitiva, seguramente necesites un software de virtualización.

De entre los más famosos, se encuentra VirtualBox (que ahora pertenece a Oracle), el cual corre en todas las plataformas y es gratuito. Aunque también puedes usar Parallels, una de las opciones preferidas si trabajas con Mac, o VMWare, un clásico del mundo de la virtualización que no defrauda.

ImageMagick

Es muy habitual que tu aplicación web necesite poder manipular imágenes subidas por los usuarios. Y aquí la navaja suiza suele ser ImageMagick, un software que permite hacer recortes, giros, redimensionados, adiciones de texto superpuesto, y decenas de otros efectos, a imágenes que estén en casi cualquier formato. Si bien lo normal es utilizar una gema de Ruby que facilite estas operaciones utilizando un DSL sencillo, estas gemas suelen luego traducir las especificaciones de tamaños y cambios de proporción que hayamos escrito en comandos que utilizan las herramientas ImageMagick. Así que es casi obligatorio tenerlas instaladas.

Firebug

Aunque he de decir que últimamente utilizo más las herramientas para desarrolladores de Chrome, la herramienta favorita para obtener resultados similares es sin duda Firebug. Gracias a esta extensión para Firefox, podrás inspeccionar el DOM, realizar cambios en el mismo, echar un vistazo a las peticiones HTTP realizadas, depurar código Javascript, analizar el rendimiento de tu aplicación, y otras muchas actividades realmente útiles durante el desarrollo. Imprescindible.

Cliente de base de datos

Si bien podemos operar con la base de datos desde línea de comandos, probablemente existan muchas operaciones (especialmente de consulta) que sería más cómodo realizarlas desde una interfaz gráfica más amigable. Si trabajas con MySQL y estás en Mac, te recomiendo que descargues Sequel Pro. Es una aplicación muy cuidada, y con la que podrás manipular a tu antojo cualquier aspecto de tu base de datos. Si prefieres tener una solución web, que corre en todas las plataformas, entonces te recomiendo phpMyAdmin, desde el cual podrás igualmente hacer la mayoría de las operaciones entrando con cualquier navegador.

Servidor de integración continua

Es realmente sano hacer que los tests escritos para tu aplicación se corran de forma automática cada vez que subes algo al repositorio de control de versiones. Para este propósito, mi preferido es Integrity, un servidor muy ligero pero no por ello menos potente escrito en Sinatra.

Organizador de tareas

Es necesario que llevemos un control de las tareas que hemos realizado, estamos realizando o pensamos realizar, así como que marquemos unos objetivos a corto plazo y registremos cualquier tipo de notas relacionadas con las tareas o bugs que nos sirvan de base de conocimiento. Para todo esto, y para organizar el trabajo en equipo, lo mejor es utilizar un servicio online que encaje con vuestra forma de trabajar. En este caso, mis preferencias se decantan por Pivotal Tracker, un servicio web de gestión de tareas online que encaja perfectamente con la filosofía de trabajo SCRUM. Pero si prefieres un entorno más clásico de tickets, milestones y cosas así, o si sencillamente prefieres poder instalar la herramienta de gestión en tu servidor, Redmine es probablemente tu mejor opción. Y como está escrita en Ruby on Rails, siempre puedes extenderla fácilmente para ir adaptándola a tus necesidades.

Conclusión

Estas son algunas de las herramientas que utilizo a diario, y que se han convertido en parte imprescindible de mi trabajo. Espero que te resulten útiles para empezar. Y si ya trabajas con Ruby on Rails, esperamos que nos cuentes cuáles son tus preferidas.

]]>
<![CDATA[Novedades en la próxima versión de Ruby on Rails: 3.1 (I)]]> http://www.genbetadev.com/ruby/novedades-en-la-proxima-version-de-ruby-on-rails-31-i http://www.genbetadev.com/ruby/novedades-en-la-proxima-version-de-ruby-on-rails-31-i Thu, 19 May 2011 04:00:23 +0000 seleccionado por los expertos de tonicardenas Rails 3.1

La nueva versión de Ruby on Rails está cada vez más cerca, y son muchas las novedades que nos trae. Así que me he decidido a recopilarlas y a enumerar algunas de las que considero más interesantes para que os vayáis haciendo una idea.

Debido a que estas novedades son muy numerosas, y para no escribir un artículo excesivamente largo, trataré de recoger las que más han llamado mi atención en al menos un par de posts.

Si quieres ir disfrutando ya de la primera beta, así como probar algunos de los cambios que paso a comentaros a continuación, podéis instalarla utilizando Rubygems mediante el comando:

  $ gem install rails —pre

Os aconsejo utilizar Ruby Version Manager (rvm) para poder tener instaladas varios entornos, y así no comprometer vuestro trabajo diario con Rails. Al igual que las versiones anteriores más recientes de Rails, el framework es perfectamente compatible con la versión 1.9.2 de Ruby, con lo que recomiendo encarecidamente su uso (¿todavía no usas Ruby 1.9.2 en tu entorno de producción?):

  $ bash < <(curl -s https://rvm.beginrescueend.com/install/rvm)
  $ rvm install 1.9.2
  $ rvm 1.9.2@rails31 —create
  $ gem install rails —pre

jQuery pasa a ser la librería oficial

Pues sí, jQuery pasa a desbancar a Prototype y scriptaculous como las librerías de Javascript por defecto. He de decir que no me sorprende, pues la mayoría de los desarrolladores Rails que conozco usan ya esta librería para casi todos sus proyectos. Por supuesto, es muy sencillo usar otra si no te convence, pero ya sabemos que Rails se caracteriza por ser “opinionated software”, así que es normal que haya siempre una favorita.

Para mayor comodidad, la comunidad ha preparado un par de gemas que podemos añadir a nuestro fichero Gemfile, proveyendo así a nuestro proyecto de los ficheros necesarios correspondientes a estas librerías. En concreto, jquery-rails y prototype-rails. Es de esperar que aparezcan otras con el tiempo. Si quieres indicar la librería deseada desde el mismo momento en que creas el esqueleto de tu aplicación, puedes hacerlo utilizando el parámetro “-j”, seguido del nombre de la librería (“jquery” o “prototype”).

SASS incluido en Gemfile por defecto

SASS, o “Syntactically Awesome Stylesheets”, es una herramienta que merece ser nombrada en un artículo por separado. No obstante, resumiré que nos sirve para facilitar la escritura de nuestras hojas de estilo, permitiendo incluir en ellas reglas anidadas, variables, módulos que definen funciones que a su vez definen un conjunto de estilos, condiciones, bucles… En definitiva, nos permite eliminar código redundante en las CSS, simplificándolo bastante, de modo que sean más sencillas de mantener. SASS se encargará luego de convertir esos ficheros en hojas de estilo convencionales, que son las únicas que entiende el navegador.

Pues bien, en Rails 3.1 la gem para la integración de esta herramienta con nuestro proyecto viene incluida por defecto en el fichero Gemfile de dependencias.

Coffeescript de serie

Poco he de añadir a este hecho que ya fue comentado por mi compañero Toni el mes pasado. Pero consideré necesario incluirlo de nuevo en este artículo, para recordarlo. Si aún no has probado Coffeescript, te recomiendo enormemente que lo hagas, pues hará que tu código Javascript se reduzca a unas pocas líneas al mismo tiempo que disfrutarás de una sintaxis más parecida a Ruby para escribir código del frontend de tu aplicación.

Hojas de estilo y ficheros javascript fuera de public

Pues sí, a partir de ahora el lugar preferido para almacenar estos ficheros es en app/assets/stylesheets y app/assets/javascripts, respectivamente. Además, se utilizan nombres de fichero parecidos a las vistas, donde una primera extensión del fichero indica el formato de éste (ej: “js” o “css”), y una segunda extensión sirve para indicar el motor que se utilizará para generar este formato a partir del fichero original (por ejemplo, “coffee” para convertir un fichero escrito con Coffeescript en Javascript). En el directorio public sólo se almacenarían los ficheros ya procesados, los cuales además serían unidos para formar un único fichero Javascript y un único fichero CSS para toda la aplicación, gracias a Sprockets.

Preferencia por la sintaxis de Ruby 1.9

Los generadores de código de Rails 3.1 van a comenzar a usar sintaxis de Ruby 1.9 cuando estés usando esta versión del intérprete del lenguaje. Esto significa que los hashes por ejemplo van a empezar a escribirse de forma similar a los hashes escritos en JSON, que es la forma preferida de escribir un hash con Ruby 1.9:

  redirect_to posts_path, notice: “El post ha sido creado correctamente.”

Mejor salida para la ejecución de tests

Los resultados de la ejecución de los tests son ahora más claros con la inclusión de la gema turn, la cual utiliza un formato de salida a color para destacar mejor los tests que pasan y los tests fallidos, y en lugar de marcar simplemente los tests válidos como puntos y los tests erróneos o fallidos con los caracteres E y F, los describe cada uno en una línea utilizando el nombre del método definido en el fichero de tests (sustituyendo símbolos de subrayado por espacios), e incluyendo por cada uno de ellos el tiempo de ejecución.

Nuevos Rack en la pila de aplicaciones middleware por defecto

La utilización de aplicaciones Rack para tratar diferentes funcionalidades en cada petición se generaliza, con la inclusión de las siguientes al stack por defecto: Rack::Etag, Rack::ConditionalGet, Rack::Cache.

Soporte para streaming

Gracias a la inclusión de ActionController::Streaming, ahora se pueden responder a determinadas acciones mediante HTTP Streaming, si el servidor web lo soporta. Para activarlo en ciertas acciones de tus controladores, puedes incluir una llamada al método de clase “stream” con unos parámetros cuya sintaxis recuerda a la de los filtros de controladores. Por ejemplo:

  class PostsController < ActionController::Base
    stream :only => :index
  end

Definición de atributos accesibles según ciertas condiciones

¿Cansado de especificar tus propios métodos de actualización de atributos en tus modelos porque update_attributes no te basta, al querer diferentes comportamientos según el rol del usuario que esté realizando el cambio, o según se den ciertas condiciones? Con Rails 3.1, puedes proteger la actualización de diferentes atributos según el rol que se utilice durante dicha operación. Por ejemplo:

  class Post < ActiveRecord::Base
    attr_accessible :title, :published_at, :as => :admin
  end

Post.new(params[:post], :as => :admin)

Más información | The Changelogs for Rails 3.1 Beta 1

]]>
<![CDATA[Cinco gemas indispensables para programar aplicaciones con Ruby on Rails]]> http://www.genbetadev.com/ruby/cinco-gemas-indispensables-para-programar-aplicaciones-con-ruby-on-rails http://www.genbetadev.com/ruby/cinco-gemas-indispensables-para-programar-aplicaciones-con-ruby-on-rails Fri, 13 May 2011 09:37:10 +0000 seleccionado por los expertos de tonicardenas Especial Gemas Ruby

A día de hoy, existen numerosas gemas de Ruby para realizar tareas realmente dispares. Y es que el número de éstas ha aumentado considerablemente en los últimos años, proveyendo a los desarrolladores de todo un pequeño universo de librerías útiles con las que facilitarles el trabajo. En este breve especial, voy a tratar de describir algunas de las gemas que considero imprescindible conocer, pues cubren algunas de las funcionalidades más habituales que solemos encontrar en forma de requisitos en muchas aplicaciones, de modo que podamos integrarlas en nuestra aplicación escrita en Ruby on Rails para cumplir sobradamente estas necesidades.

Carrierwave

Esta gema escrita por Jonas Nicklas nos sirve para añadir a nuestra aplicación la posibilidad de almacenar y procesar ficheros subidos por nuestros usuarios. Si bien han existido numerosos plugins para realizar esta misma función anteriormente (attachment_fu, paperclip, y otros), esta gema utiliza una separación de código bastante limpia, guardando cada uploader como una clase propia donde podemos especificar reglas concretas para manipular esos ficheros que nos adjuntan.

Se integra con cualquier aplicación Rack, de modo que podemos utilizarla tanto con Ruby on Rails, como con cualquier otro framework basado en este adaptador (Sinatra, Merb, Camping…). Además, es capaz de utilizar proveedores de almacenamiento como Amazon S3, Google Storage, Rackspace Cloud Files, y otros, de modo que los ficheros entrantes, una vez procesados, puedan ser guardados utilizando alguno de estos servicios. Al tratarse de módulos separados, es fácil extender la gema para agregar cualquier otro servicio de almacenamiento.

También permite utilizar RMagick, ImageScience o MiniMagick cuando los ficheros a tratar sean imágenes, de modo que podamos hacer recortes, escalados, y cualquier otro efecto sobre la imagen que soporte la librería que hayamos elegido. Nuevamente, esta funcionalidad se agrega en forma de módulo, de modo que podemos crearnos el nuestro propio para realizar cualquier otro tipo de manipulación deseada y que no esté soportada (ej: extracción de texto de un PDF, parseo de un archivo separado por comas, etc).

Su código se puede encontrar en Github.

Kaminari

Gracias a esta gema de Akira Matsuda, la paginación de objetos se torna sencilla y elegante. Nuevamente, existen muchas otras opciones perfectamente válidas, siendo la más conocida will_paginate. Pero esta gema aprovecha la posibilidad de definir scopes en ORMs como ActiveRecord, Mongoid o Mongomapper, de modo que su código se torna más sencillo, y la personalización del aspecto del paginador es bastante más elegante. De hecho, esta personalización visual es extensible en forma de themes, de modo que ya empiezan a aparecer algunas gemas con dichos themes para adoptar el aspecto de paginadores como los de GitHub, Google, etc.

La paginación no se limita a instancias devueltas por nuestro ORM favorito, sino que podemos paginar también arrays utilizando un wrapper:

  Kaminari.paginate_array(my_array_object).page(params[:page]).per(10)

Su código se puede encontrar en Github.

OmniAuth

Gemas para tratar el problema de la autentificación de usuarios hay muchas, aunque yo sigo más la costumbre de crear mi propio sistema de registro y autentificación de usuarios, ya que es una tarea relativamente sencilla y que permite luego un mayor grado de personalización (tratar de adaptar el comportamiento de gemas como Devise se puede tornar complicado).

Pero empieza a ser muy habitual que necesitemos permitir la autentificación mediante cuentas de otras plataformas como Facebook, Twitter, Google Apps, y otros. Y aquí es donde viene a ayudarnos OmniAuth.

Con esta gema elaborada por los chicos de Intridea, y habitualmente agregando tan sólo un nuevo controlador por cada servicio para crear la sesión, así como la carga de un middleware, podemos habilitar la posibilidad de que el usuario se autentifique usando su cuenta en ese servicio, así como obtener toda clase de información relativa al usuario que se ha logueado (nombre y apellidos, nick, email, etc; los datos proporcionados dependen del servicio utilizado).

Así que ya no hay excusa para añadir esta posibilidad a tu aplicación Rails. El código de la gema se puede encontrar en GitHub.

Ancestry

¿Necesitas añadir una jerarquía a tus modelos? Esta gema viene a tu rescate. Los resultados son similares a otros plugins como acts_as_tree, pero gracias a la gema de Stefan Kroes, el almacenamiento de la jerarquía es bastante eficiente, puesto que se guarda en cada instancia en forma de ruta de ids desde el objeto padre hasta la propia instancia (por ejemplo, “12/83” indica que el objeto actual tiene como padre al objeto con id 83, que a su vez tiene como padre al objeto con id 12), de modo que utilizando búsquedas de texto en este path se pueden obtener fácilmente elementos como los objetos raíz (aquellos cuyo path no contiene nada), los hijos de un objeto (aquellos que tienen en el path la subcadena con el id del padre seguida de una barra), etc.

La cantidad de métodos para moverse a través del árbol, así como comprobadores de condiciones (si el nodo tiene hijos, si es el único hijo, etc) es verdaderamente amplia. La forma en que los objetos que quedan huérfanos son tratados es personalizable. En general, una gema muy sencilla de usar, y que proporciona la funcionalidad de organización de las instancias de tu modelo en forma de árbol con una solución elegante y eficiente.

Su código en GitHub.

aasm

Por último, no quisiera dejarme a una gema de Scott Barron que nos permitirá construir una máquina de estados en nuestro modelo, de modo que podamos definir en ellos un conjunto de estados y de transiciones permisibles entre los mismos. Así, podemos separar nuestras instancias en dichos estados, y asegurarnos de que cuando cambien de un estado a otro, se ejecuten determinadas acciones definidas en nuestro modelo. También nos permite impedir que un modelo cambie de un estado a otro si no le está permitido. Es ideal para controlar cosas como el workflow de un objeto que representa un documento, tarea, etc.

Su código en GitHub.

Conclusiones

En posteriores posts, os hablaré de algunas de mis gemas favoritas en otras tareas como el testeo unitario. Mientras tanto, os pregunto: ¿Cuáles son vuestras otras gemas favoritas?

]]>
<![CDATA[Una introducción a MongoDB]]> http://www.genbetadev.com/bases-de-datos/una-introduccion-a-mongodb http://www.genbetadev.com/bases-de-datos/una-introduccion-a-mongodb Tue, 10 May 2011 13:50:04 +0000 seleccionado por los expertos de tonicardenas MongoDB Logo

Hace ya unos días os hablaba de NoSQL y su conveniencia para ciertos proyectos. Hoy os voy a hablar de las características de un sistema de base de datos NoSQL en concreto, el cual he tenido ocasión de probar a fondo en un par de proyectos, y que sin duda se ha convertido en uno de mis favoritos: MongoDB.

Descripción y licencia

MongoDB es un sistema de base de datos multiplataforma orientado a documentos, de esquema libre. Como ya os expliqué, esto significa que cada entrada o registro puede tener un esquema de datos diferente, con atributos o “columnas” que no tienen por qué repetirse de un registro a otro. Está escrito en C++, lo que le confiere cierta cercanía al bare metal, o recursos de hardware de la máquina, de modo que es bastante rápido a la hora de ejecutar sus tareas. Además, está licenciado como GNU AGPL 3.0, de modo que se trata de un software de licencia libre. Funciona en sistemas operativos Windows, Linux, OS X y Solaris.

Las características que más destacaría de MongoDB son su velocidad y su rico pero sencillo sistema de consulta de los contenidos de la base de datos. Se podría decir que alcanza un balance perfecto entre rendimiento y funcionalidad, incorporando muchos de los tipos de consulta que utilizaríamos en nuestro sistema relacional preferido, pero sin sacrificar en rendimiento.

Terminología básica en MongoDB

En MongoDB, cada registro o conjunto de datos se denomina documento. Los documentos se pueden agrupar en colecciones, las cuales se podría decir que son el equivalente a las tablas en una base de datos relacional (sólo que las colecciones pueden almacenar documentos con muy diferentes formatos, en lugar de estar sometidos a un esquema fijo). Se pueden crear índices para algunos atributos de los documentos, de modo que MongoDB mantendrá una estructura interna eficiente para el acceso a la información por los contenidos de estos atributos.

Formato de los documentos e ideas para la organización de datos

Los distintos documentos se almacenan en formato BSON, o Binary JSON, que es una versión modificada de JSON que permite búsquedas rápidas de datos. Para hacernos una idea, BSON guarda de forma explícita las longitudes de los campos, los índices de los arrays, y demás información útil para el escaneo de datos. Es por esto que, en algunos casos, el mismo documento en BSON ocupa un poco más de espacio de lo que ocuparía de estar almacenado directamente en formato JSON. Pero una de las ideas claves en los sistemas NoSQL es que el almacenamiento es barato, y es mejor aprovecharlo si así se introduce un considerable incremento en la velocidad de localización de información dentro de un documento.

Sin embargo, en la práctica, nunca veremos el formato en que verdaderamente se almacenan los datos, y trabajaremos siempre sobre un documento en JSON tanto al almacenar como al consultar información. Un ejemplo de un documento en MongoDB podría ser perfectamente éste:

  {
    “_id”          : “4da2c0e2e999fb56bf000002”
    “title”        : “Una introducción a MongoDB”,
    “body”         : “Lorem ipsum dolor sit amet…”,
    “published_at” : “2011-05-09T18:17:07-07:00”,
    “author_info”  : {
                       “_id”  : “4dc8919331c0c00001000002”
                       “name” : “Carlos Paramio”
                     },
    “tags”         : [“MongoDB”, “NoSQL”, “Bases de datos”]
    “comments”     : [
                       {
                         “author_info” : { “name” : “Jorge Rubira”, “email” : “email1@example.com” },
                         “body”        : “Test”,
                         “created_at”  : “2011-05-10T10:14:01-07:00”
                       },
                       {
                         “author_info” : { “name” : “Txema Rodríguez”, “email” : “email2@example.com” },
                         “body”        : “Otro test”,
                         “created_at”  : “2011-05-10T10:14:09-07:00”
                       }
                     ]
    “liked_by”     : [“4d7cf768e999fb67c0000001”, “4da34c62ba875a19d4000001”]
  }

Como podemos adivinar, este documento pretende representar la manera en que podrían almacenarse los datos correspondientes a un post de un blog. Los atributos “_id” (o clave principal) pueden tener el formato que se desee, aunque MongoDB utiliza un valor parecido a un UUID en hexadecimal por defecto si no se ha especificado ninguno. A pesar de parecer un valor completamente aleatorio (aunque ya sabemos que la aleatoriedad real no existe en informática), utilizan como base una semilla basada en la MAC de la interfaz de red de la máquina (y otros detalles de la misma) para evitar que dos máquinas diferentes puedan generar el mismo valor para la clave de un documento. Y los primeros bytes corresponden a una marca de tiempo, de modo que las claves se ordenan de forma natural por orden de creación (o casi, pues está claro que las distintas máquinas corriendo MongoDB deben tener la fecha y hora sincronizadas) sin tener que mirar cuál fue el último valor usado. Una solución inteligente, a mi modo de ver bastante más eficiente que un campo autonumérico, en especial para evitar que una máquina bloquee la inserción de registros en una colección sólo para asegurarse que no se dan condiciones de carrera al intentar dos máquinas diferentes escribir un documento con el mismo valor para “_id”. Por cierto, este atributo “_id” es el único obligatorio para un documento.

Las etiquetas y los comentarios están en el propio documento que representa al post, en lugar de guardarlos en colecciones separadas y utilizar claves foráneas para referenciar a los mismos. Sin embargo, en el atributo “liked_by” sí que guardamos una relación de claves, que corresponden a los usuarios que han marcado el post como que les ha gustado. Utilizar una forma u otra dependerá de las necesidades de acceso a estos datos En este caso, por ejemplo, sabemos que no vamos a pintar información sobre los usuarios que han marcado un post con un “me gusta”, pero sí queremos ver cuántos lo han marcado así, o si el usuario actual ya lo ha marcado o no, con lo que almacenar únicamente las claves de esos usuarios y guardar su información personal detallada en otra colección es lo más conveniente.

Por supuesto, no es necesario pedir a MongoDB que nos devuelva todo el documento cada vez que lo consultamos. Si por ejemplo vamos a pintar únicamente un listado de posts recientes, seguramente sea suficiente obtener el atributo “title”, con los documentos ordenados por “published_at”. Así, ahorramos ancho de banda entre el motor de base de datos y la aplicación, al mismo tiempo que memoria dado que no hay que instanciar todo el documento. Además, si tenemos muchos miles de visitantes, el atributo “liked_by” podría llegar a crecer bastante. Aunque el tamaño de un documento de MongoDB puede llegar hasta los 16 Megabytes, con lo que podemos almacenar bastante información dentro de un único documento sin necesidad de utilizar referencias, si así lo necesitamos. En caso de que tuviéramos que almacenar mucho más, habría que optar por utilizar otro esquema.

A veces, toca desnormalizar para poder tener a mano la información necesaria a la hora de pintar un post. Es por eso que en el atributo “author_info” he utilizado una versión intermedia: Si bien tenemos la clave principal del usuario que ha escrito este post, como es habitual que pintemos el nombre del autor, he almacenado también dicho nombre en el documento que representa al post, para que no sea necesario realizar una segunda consulta a la colección “usuarios”. Estas desnormalizaciones dependen nuevamente del uso que se den a los datos. En este caso, tengo claro que el nombre de un usuario no va a cambiar demasiado, así que recorrer todos los posts para cambiar este valor en caso de que el usuario realice esta operación, si bien es una modificación que podría llevar un tiempo considerable para ejecutarse, no es una operación habitual frente a la consulta del nombre del autor, y por tanto compensa. Incluso podríamos llegar a tratar el post como algo más permanente, de modo que aunque un usuario cambiara su nombre a posteriori, el nombre utilizado para firmar los posts anteriores no varíe, o sencillamente los posts puedan firmarse con diferentes pseudónimos.

Como creo que habrá quedado patente a estas alturas, el modelado del esquema de datos con MongoDB depende más de la forma en que consultaremos o actualizaremos los datos que de las limitaciones del propio sistema.

Cómo consultar los datos

Sin entrar demasiado en detalles acerca de todas las posibilidades que MongoDB nos ofrece para consultar los datos almacenados, sí quisiera nombrar algunas de ellas, para hacer notar que no estamos frente a un sistema simple de almacenamiento de pares clave-valor.

En primer lugar, MongoDB nos permite utilizar funciones Map y Reduce escritas en Javascript para seleccionar los atributos que nos interesan de los datos, y agregarlos (unificarlos, simplificarlos) en la manera deseada, respectivamente. Esto es algo habitual en muchos sistemas NoSQL, y en algunos casos es incluso la única forma posible de consultar datos. Claro está que muchas veces necesitamos algo bastante más sencillo que ésto.

En MongoDB se pueden utilizar consultas al valor de un atributo específico. Por ejemplo, podemos capturar el post que tiene un determinado título:

  db.posts.find({‘title’ : ‘Una introducción a MongoDB’})

El valor a consultar puede estar anidado en un tipo de datos más completo en el atributo del documento (por ejemplo, como valor de un hash asociado al atributo, o como el valor de uno de los ítems de un array). Se utiliza un punto como separador de los nombres de las claves de los diferentes hashes que hay que recorrer hasta llegar al valor deseado. Por ejemplo, la siguiente consulta devolvería todos los posts escritos por un determinado autor:

  db.posts.find({‘author_info._id’ : ‘4da2c0e2e999fb56bf000002’})

Y esta otra los posts etiquetados con MongoDB:

  db.posts.find({‘tags’ : ‘MongoDB’})

El hash utilizado como conjunto de condiciones que deben cumplir los documentos a devolver puede incluir operadores de muy diversos tipos, no sólo comparadores del valor absoluto buscado. Algunos de ellos son:

  • $all : Para indicar que el array almacenado como valor del atributo debe tener los mismos elementos que el proporcionado en la condición.
  • $exists : Para comprobar que el atributo existe en el documento.
  • $mod : Para comprobar el resto de una división del valor del atributo por un número.
  • $ne : Para indicar que el valor no puede ser el proporcionado.
  • $in : Para indicar que el valor debe estar entre alguno de los proporcionados.
  • $nin : Contrario de $in.
  • $or : Para indicar que se debe cumplir al menos una condición de entre un grupo de condiciones.
  • $nor : Contrario de $or.
  • $size : Para indicar el número de elementos que debe haber en el array almacenado como valor.
  • $type : Para comprobar el tipo del valor almacenado (número, cadena…)
  • Expresiones regulares : El valor debe concordar con la expresión regular indicada.

Y muchos, muchos más. Los resultados se pueden agrupar, ordenar, contar, paginar, y otras tantas operaciones comunes sin necesidad de recurrir al farragoso Map / Reduce. Y siempre que los atributos consultados tengan definidos un índice, la velocidad de las consultas es espectacular.

Bueno, por ahora creo que es suficiente como para que sirva de introducción a este fabuloso motor de base de datos. En futuros artículos procuraré hablaros de sus otras características a la hora de escalarlo para su uso en múltiples máquinas, así como otros mecanismos interesantes de MongoDB como los índices geoespaciales. Si queréis experimentar con él, y trabajáis habitualmente con Ruby, tal vez os interese echar un vistazo a un Object Document Mapper para este lenguaje que escribí para un proyecto, y que facilita la interacción con el motor desde tus modelos: MongoODM.

Más información | MongoDB, BSON, MongoODM

]]>