The difference between __getattribute__ and __getattr__

The other day I was teaching Python meta-programming to a workmate. I think it’s a good way to learn about high order functions since meta-programming makes extensive use of closures, function builders, decorators… I was trying to make a concept probe about a very, very generic REST connector. Here is my first (and wrong) attempt:

class RESTConn(object):

  def __init__(self, entry_point):
    self.entry_point = entry_point

  def method_builder(self, method_name):
    verb, _, collection = method_name.split('_', 2)
    def do_verb(payload=None, **kwargs):
      uri = self.make_uri(collection)
      querystring = self.make_querystring(kwargs)
      print verb.upper(), self.combine(uri, querystring)
      if payload:
        print payload

    return do_verb

  def make_uri(self, collection):
    return '/'.join([self.entry_point, collection])

  def make_querystring(self, kwargs):
    return '&'.join(['='.join(pair) for pair in kwargs.iteritems()])

  def combine(self, uri, querystring):
    if querystring:
      return '&'.join([uri, querystring])

    return uri

  def __getattribute__(self, name):
    if not hasattr(self, name):
      method = self.method_builder(name)
      setattr(self, name, method)

    return super(RESTConn, self).__getattribute__(name)

Try this example by instantiating a new connector and trying to call something like:

c = RESTConn('unoyunodiez.com')
c.get_from_articles()

The program falls into an infinite recursion and do nothing before crashing. Why?

There are three problems here. First and most important is using __getattribute__(), second is using hasattr() and third is accessing self.method_builder().

The object’s method __getattribute__() is used to retrieve an attribute from an instance. It captures every attempt to access an instance attribute by using dot notation or getattr() built-in function. Unless it was overridden, the former expression is translated into object.__getattribute__(self, ‘get_from_article’). The default implementations looks into the instance’s namespace, then looks into the class namespace, then into each base’s namespace and so on. Finally, if not found, the default implementation calls the fallback __getattr__() method of the instance and it raises an AttributeError exception as default implementation.

This is not a problem by itself but if you pay attention enough you’ll notice we are trying to create the new method only if the object does not have the method yet. It is semantically the same as overriding __getattr__() because it is called only when the attribute was not found. So, even if we cannot explain the infinite recursion error yet, we can fix the class by replacing:

  def __getattribute__(self, name):
    if not hasattr(self, name):
      method = self.method_builder(name)
      setattr(self, name, method)

    return super(RESTConn, self).__getattribute__(name)

with:

  def __getattr__(self, name):
    method = self.method_builder(name)
    setattr(self, name, method)
    return getattr(self, name)

So, the difference between __getattribute__() and __getattr__() is that the first one is called unconditionally when an attribute is being retrieved from an instance while the second is called only when the attribute was not found.

But, what about the infinite recursion? Why the first example was failing?

Seguir leyendo «The difference between __getattribute__ and __getattr__»

Evil side effects of changing arguments

I’ve just realized about a creepy fact about JavaScript. Suppose you have this code:

function f(a, b, c) {
  arguments[1] = 99;
  return [a, b, c];
}
f(1, 2, 3);

What do you think the result is?

[1, 2, 3]

You guess but NO. Right answer is:

[1, 99, 3]

Unless you’re in strict mode (and you should always be in strict mode), then result is [1, 2, 3].

In non strict mode, setting the n-th item of arguments alters the value of the n-th formal parameter in the function signature. A really evil and unexpected side effect but specified in http://ecma-international.org/ecma-262/5.1/#sec-10.6 (see Note 1).

So be careful and keep yourself in strict mode where things are less unpredictable!

function f(a, b, c) {
  'use strict';
  arguments[1] = 99;
  return [a, b, c];
}
f(1, 2, 3);

Django 1.5 in a nutshell II

The first part of this tutorial introduced some interesting aspects of Django 1.5. In this second part we are diving deeper into Django features but coding a little bit more, trying always to use the framework properly. Here is the index:

  1. Modifying the model.
  2. Improving posts administration.
  3. Adding commentaries.
  4. Tweaking the administrator to manage posts and commentaries.
  5. Adding a search function.

Seguir leyendo «Django 1.5 in a nutshell II»

Django 1.5 in a nutshell I

In 2011, I wrote a quick tutorial about Django 1.3. I was going to demonstrate how to write a blog in just one hour in a live code session. This time I’m in a mentoring program teaching Python and Django to a colleague so I decided to update that tutorial. Django is now in version 1.5 and you can find what is new in the release notes of the framework. Let’s code!

Seguir leyendo «Django 1.5 in a nutshell I»

Trabajando en FirefoxOS

Como os prometía, aquí tenéis la traducción al español del artículo que se publicaba ayer sobre cómo era trabajar con Mozilla en Firefox OS:

Escuchando las noticias del MWC 2013 y a la mayor parte de la prensa especializada te das cuenta de casi nada se ha dicho del enorme esfuerzo realizado por Telefónica I+D en materia de desarrollo en FirefoxOS (aunque se puede encontrar una importante mención por parte de Gary Kovacs en la rueda de prensa completa). Quizá Telefónica no tenga la mejor reputación en España pero puedo asegurar que la división de investigación y desarrollo, TID, está llena de personas de gran valor y que la compañía está haciendo lo mejor que puede por reclutar desarrolladores y managers con talento.

Quiero hablar de cómo es trabajar en FirefoxOS. Como probablemente sepas (o no), me trasladaron al proyecto OWD (Open Web Device) de Telefónica I+D en Mayo de 2012. OWD es el nombre de la iniciativa dentro de TID apoyando el desarrollo de FirefoxOS tanto en back-end como en front-end. Esto significa que hay código de los miembros de TID dentro de Gecko y de Gaia. Gecko es el navegador haciendo de sistema operativo mientras que Gaia es la capa de software con la que el usuario interactúa. Puedes ver el video de demostración del MWC 2013 si quieres conocer más sobre la plataforma.

Trabajar con Mozilla está siendo una experiencia muy enriquecedora permitiendo a miembros de ambas compañías abrir sus mentes a otras formas de trabajar, gestionar, culturas comerciales y motivaciones.

Se dice que los desarrolladores de Mozilla se encuentran entre los mejores hackers del mundo y os puedo asegurar que es mayoritariamente cierto no sólo porque cuenten entre sus filas con reconocidos profesionales sino por la dedicación y pasión que ponen en los retos que enfrentan. Y exactamente lo mismo puede decirse de los ingenieros de TID. Nunca estuve en un equipo tan virtuoso lleno de buenos desarrolladores y managers tan volcados. Este trabajo es mi primera aproximación a un proyecto tan grande con equipos diferenciados para desarollo, calidad y experiencia de usuario.

Sin embargo, también existen puntos de confrontación como es natural en cualquier relación.

Seguir leyendo «Trabajando en FirefoxOS»

Working on FirefoxOS

I was thinking about writing in English and expand the scope of the blog as it is widely read around the Spanish-speaking community. So this is my first English post but I’m planning to translate most visited posts as well. Anyway, I promise to publish another post with the translation of this 😉

Listening the news from MWC 2013 and most of the specialized press media, you realize that almost none is said about the big development effort made by Telefónica I+D on FirefoxOS (but can find an important mention from Gary Kovacs in the complete press conference). Maybe Telefónica does not have the best reputation in Spain, but I can ensure the research & development division, TID, is full of valuable people and the company is pushing hard to chase talented developers and managers out there.

I want to talk about how working on FirefoxOS is. As you probably know (or not), I was moved to OWD (Open Web Device) project in Telefónica I+D in May, 2012. OWD is the name of the initiative inside TID supporting FirefoxOS development both in back-end and also in front-end. This means there are code from TID members inside Gecko and Gaia. Gecko is the browser acting like the operating system while Gaia is the software layer the user will interact with. You can see the demo video of the MWC 2013 if you want to know more about the platform.

Working with Mozilla is being a very enriching experience allowing members from both companies to open their minds to other ways of work, management, company cultures and motivations.

Mozilla developers are said to be between the best hackers in the world and I can assure you it is mostly true; not only because they have recognized experts in their ranks but because the passion and dedication each of them put in the challenges they face. And exactly the same can be said about the engineers in TID. I’ve never been in such talented team plenty of good developers and willing managers. This job is my first approach to a big project with differentiated teams for Development, Quality Assurance and User eXperience and I’m proud to say I’m in this awesome team.

Although, there have been some confrontation points too as it is natural in any relationship.

Seguir leyendo «Working on FirefoxOS»

μTask

Bea's kanban in English
Bea’s kanban in English

Os presento μTask, una pequeña aplicación para gestionar tareas usando un panel kanban en fase alpha. Últimamente me ronda mucho por la cabeza las aplicaciones con modelos representables en texto plano. Como todo.txt, por ejemplo.

μTask es un pequeño parser de una notación de mi invención para gestionar tareas, pensada para interacturar con los comandos estándar cortar y pegar.

Tenéis un ejemplo de una tarea aquí mismo:

To do:
  Task 153: LMDL fixes

Y una escueta lista de características:

  • Tareas con identificador, descripción y detalles
  • Dos espacios para definir tareas: uno para reflejar el estado del kanban y otro para clasificar y detallar las tareas
  • Clasificaciones por temas y colores
  • Expresa el progreso de una tarea y su fecha límite

Podéis encontrar más información así como un lugar donde dejar vuestros comentarios en la página del proyecto:

http://unoyunodiez.com/proyectos/mtask/

El tutorial en GitHub:

https://github.com/lodr/mtask/#%CE%BCtask

ask.js

Bueno, hace tiempo que no hay actividad por el blog pero vuelvo para anunciar un nuevo proyecto que acabo de hacer entrar en fase alpha a la espera de que los usuarios me den algo de feedback.

Su nombre es ask.js y se trata de un módulo JavaScript que permite hacer búsquedas en un array de objetos utilizando la sintaxis de consultas de MongoDB.

No sé si conoceis un poco MongoDB pero se trata de una base de datos NoSQL orientada a documentos, documentos BSON, de hecho. BSON es una implementación binaria de JSON. Realizar búsquedas en Mongo se hace mediante una suerte de «objetos parciales» del tipo { name: 'Anne', 'address.city': 'Madrid', ...} aunque también admite restricciones complejas por campo como en { manufacturer: { $in: ['Ford', 'Munstang', 'Land Rover'] }, year: { $gte: 1990, $lt: 2000} }.

Una vez añadidos ask.js, puedes hacer ask.mongify() sobre un array para extender el mismo con un nuevo método find() que acepta, hasta el momento, las siguientes características de las búsquedas de MongoDB:

  • Restricciones por valor: { field: value }
  • Expresiones JS en cláusulas $where
  • Subobjetos, elementos parciales: { 'field.subfield': value } y $elemMatch
  • Comprobación de existencia $exists
  • Restricciones por expresión regular: { field: /value/i } y $regex
  • Operadores de comparación <, <=, >= y >
  • Operadores lógicos $or, $and y $nor
  • Módulo $mod
  • Comprobación de tipo $type
  • Operadores de inclusión $in, $nin y $all
  • Metaoperador $not
  • Comprobación de tamaño $size
  • Comprobación de desigualdad $ne

Además, es mi primer proyecto siguiendo TDD utilizando Jasmine como framework de testing. Algún día os hablaré de Jasmine y de cuánto me gusta su API.

Seguir leyendo «ask.js»

JavaScript no alfanumérico

Tengo una discusión con Adri acerca de si JavaScript es un buen lenguaje o no. En una de nuestras conversaciones critica el tipado débil de JS y enlaza el artículo Brainfuck beware: JavaScript is after you! de Patricio Palladino.

Os recomiendo encarecidamente su lectura. Es muy interesante.

Resumiendo, Patricio ha creado una herramienta con la que convertir un script JS a otro, equivalente, formado únicamente por símbolos +[](){}! ¿No me creeis? Probad a introducir esto es una consola JS de un navegador cualquiera (la de Firebug o la de Chrome servirán).

[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((+{}+[])[+!![]]+(![]+[])[!+[]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[]))+(+!![]+[])+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[+[]]+([][+[]]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()([][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(![]+[])[!+[]+!![]+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+([]+[][(![]+[])[!+[]+!![]+!![]]+({}+[])[+!![]]+(!![]+[])[+!![]]+(!![]+[])[+[]]][({}+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]]+(![]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+[]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(!![]+[])[+[]]+({}+[])[+!![]]+(!![]+[])[+!![]]]((!![]+[])[+!![]]+([][+[]]+[])[!+[]+!![]+!![]]+(!![]+[])[+[]]+([][+[]]+[])[+[]]+(!![]+[])[+!![]]+([][+[]]+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]+!![]+!![]]+(![]+[])[!+[]+!![]]+({}+[])[+!![]]+({}+[])[!+[]+!![]+!![]+!![]+!![]]+(+{}+[])[+!![]]+(!![]+[])[+[]]+([][+[]]+[])[!+[]+!![]+!![]+!![]+!![]]+({}+[])[+!![]]+([][+[]]+[])[+!![]])())[!+[]+!![]+!![]]+([][+[]]+[])[!+[]+!![]+!![]])()(({}+[])[+[]])[+[]]+(!+[]+!![]+[])+(!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])))()

Seguir leyendo «JavaScript no alfanumérico»

Un año en Telefónica I+D

Pues así de sencillo: un año en Telefónica I+D. Y cumpliendo los 6 años trabajando.

Y ha sido un año bastante intenso, vaya. Comencé programando en Python y he acabado con JavaScript. Pero no me quejo. Al contrario, estoy muy contento con mi proyecto actual: OWD y B2G (sí, sí, con Mozilla y Telefónica I+D. Ya le dedicaré un post sólo a él, lo prometo).

Durante este año he tratado infructiosamente de sacarme un máster (ojo, que aun queda septiembre, x) y me he independizado (dos veces que no es poco). He conocido a gente muy interesante en charlas y eventos y he tenido la oportunidad de trabajar en tareas realmente desafiantes que han mejorado mis conocimientos y expandido mis horizontes.   Me ha ido bien. De hecho, visto cómo está el país, me ha ido de p*t* madre. Pero mal de muchos, consuelo de bobos y más cuando uno de esos muchos también soy yo.

Política aparte. ¡Qué duro es cambiar! Un comentario de una compañera me indujo a la siguiente reflexión. Ella dijo que le daba pereza cambiar de proyecto porque había que demostrar lo que vales de nuevo. Ahora que me he cambiado puedo perfilar que realmente, te tienes que demostrar que vales. He pasado unos días muy intranquilo, porque el proyecto actual es inmenso, las reuniones son en inglés, recibo 30 correos diarios y casi me ahorco más de una vez con Git. Y encima me sentía un lastre para los compañeros que me proporcionaban ayuda preguntando todo el rato y discutiendo lo que no veía del todo claro o lo sobre lo que no estaba del todo de acuerdo

Pero después de superar la tarea entre manos, que ha sido dura y no en una semana especialmente tranquila, me doy cuenta de que sigo valiendo y eso me alegra y renueva mis energías. Luego insisto, estoy muy contento con el curro.

En fin, me permito felicitarme y desearme a mi mismo un mejor nuevo año. Para terminar, decir que además este proyecto está lleno de viajes chulos o al menos, oportunidades. Ahora tendremos una semana desarrollando con la gente de Mozilla codo con codo y para Agosto quizá un viaje a Brasil.