How to know, in ruby, which methods have been added and by whom?

If you are not very careful, monkeypatching could be very harmful. One thing to remember is that you should never override a method to add funcionality, for those kind of thinks you must use alias chain method pattern, a safer way of doing that.

For the rest of the monkeypatching, i.e. add new methods, you could debug them really easy with something like this:

class Class
   def method_added(method_name)
      puts "#{method_name} added to #{self}, callstack:"
      puts caller.map{|line| "\t#{line}" }.join("\n")
   end
end

You can always add more code to filter by class or by method’s name. Let’s see an example:

$ more example.rb 
require 'date'
require 'time'
 
class Class
   def method_added(method_name)
      return if %w(method_added).include? method_name.to_s
      puts "#{method_name} added to #{self}, callstack:"
      puts caller.map{|line| "\t#{line}" }.join("\n")
   end
end
 
class Time
   def to_date
      Date.ordinal self.year, self.yday
   end
end
 
class Date
   def to_time
      Time.parse self.to_s
   end
end
 
raise "to_date not working" unless
   Time.now.to_date == Date.today
raise "to time not working" unless
   Time.now.to_date.to_time == Date.today.to_time

The output will be:

$ ruby example.rb 
to_date added to Time, callstack:
	example.rb:13
to_time added to Date, callstack:
	example.rb:19

Nice, isn’t it?. Remember to be carefull with your monkeypatching, with great power comes great responsibility, it’s just a tool, neither magic nor the panacea.

Comandos Windows para linuxeros: kill -9, ps, route, servicios

Si por cuestiones del destino tienes la mala suerte de tener que lidiar con este sistema operativo, he aquí unas pequeñas ayudas para típicas tareas a realizar, traduciendo desde comandos *nix.

  • Matar un proceso
    • Linux: kill -9 PID
    • Windows: taskkill /f /pid PID
  • Mirar procesos y filtrarlos
    • Linux: ps aux | grep PID
    • Windows: tasklist /fi "PID eq $PID" | filter $PID
  • Establecer la ruta por defecto
    • Linux: route add default gw IP
    • Windows: route change 0.0.0.0 mask 0.0.0.0 IP
  • Iniciar/parar servicios
    • Linux: /etc/init.d/foobar start|stop
    • Windows: net start|stop foobar

Espero que nunca tengas que usarlos, por el bien de tu salud mental. Si llega a ser el caso, y no hay forma de salir del atolladero sin matar gente, te recomiendo usar alguna que otra ayuda en este hostil, adverso y aciago entorno.

Hola qué tal

Cuando no apetece escribir, simplemente no apetece escribir, qué le vamos a hacer. Después de mucho tiempo creo que toca ya volver a dar mal por estos lares. Van ya cerca de 5 años con el blog, y aunque últimamente las redes sociales y el microblogging han quitado muchos usuarios a los blogs (posiblemente todos aquellos que creaban un blog y a los cuatro post lo dejaban abandonado), a la blogosfera la veo mejor que nunca.

De nuevo, como otras muchas veces, tengo el dilema sobre qué idioma elegir, español o inglés. Mientras que con el primero puedo conseguir, a veces, no cometer faltas ortográficas y crear frases coherentes y comprensibles por otros seres humanos, el segundo, por suerte o desgracia, es el idioma neutral y empleado principalmente en esto del interné, nuevas tecnologías y especialmente en el desarrollo de software. Posiblemente iré decidiéndolo post a post, intercalando, para dar emoción al asunto.

Y tú, ¿has dejado de escribir en tu blog? Él no lo haría.

A new gem: rubygems is it jruby?

A few days ago after seeing this tweet, I’ve decided to migrate the gem rubygems-isit19 to jruby. Thanks to this, when you install another gem you’ll be able to read about compability of this gem with jruby, the information is fetched from isitjruby.com website.

Once you have installed the gem you will have two main features, it’s easier if I just show you. First the messages when you install gems:

$ jgem install daemons
 
daemons 1.0.10 probably might not work, blaxter says 1.0 fails
Update http://isitjruby.com/daemons with your experiences!
 
Successfully installed daemons-1.0.10
1 gem installed
$ jgem install cucumber
 
cucumber 0.3.97 might work, 100% say 0.3.11.3 works on jruby
Update http://isitjruby.com/cucumber with your experiences!
 
Successfully installed cucumber-0.3.97
1 gem installed
$ jgem install faker
 
faker 0.3.1 is 100% verified jruby
Update http://isitjruby.com/faker with your experiences!
 
Successfully installed faker-0.3.1
1 gem installed

And also we have an explicit gem command:

$ jgem isitjruby faker
faker 0.3.1:    http://isitjruby.com/faker
    Blaxter says 0.3.1 works on GNU/Linux
        it works perfectly, and it passes all test.
$ jgem isitjruby daemons
daemons 1.0.10:    http://isitjruby.com/daemons
    blaxter says 1.0 fails on GNU/Linux
        Daemons use fork, jruby doesn't support fork, so it doesn't work and
        never will

And that’s it. The code is on github be free of fork me or open any issues. The important thing is populate with relevant information isitjruby.com for benefit of all jruby community. Oh I forgot to tell you how to install it, very easy, I’ve uploaded the gem to gemcutter so you can install it with something like

$ jgem install rubygems-isitjruby -s http://gemcutter.org

It’s also in github, but I think (and I hope) gemcutter will be the next rubyforge, so better gemcutter :).

Backup incremental de tu base de datos con Git

Una forma realmente interesante de realizar backups de tus bases de datos (por ser trivial y muy potente) es usando Git. El proceso es simple y se basa en la realización de los dumps de la base de datos de forma que cada fila de las tablas sea un insert aislado, de esa forma en cada commit solo estaremos salvando las diferencias respecto al último estado (tanto deletes como inserts, como updates).

En el caso concreto de MySQL inicialmente haríamos algo como esto:

$ mkdir mydatabase && cd mydatabase
$ mysqldump --skip-extended-insert > data.sql
$ git init
$ git add data.sql
$ git commit -m "Initial dump of mydatabase"

A partir de entonces podemos automatizar el proceso con un script tan simple como este:

$ cat /usr/bin/git_backup_database
#!/bin/sh
BACKUP_DIRECTORY=$1
DATABASE=$2
 
cd $BACKUP_DIRECTORY && \
mysqldump --skip-extended-insert $DATABASE > data.sql && \
git commit -am "Updating dump `date +%Y_%m_%d__%H_%M`"

Según el volumen de consultas que tenga tu base de datos, te será interesante ponerlo en cron con una frecuencia determina u otra. Adicionalmente sería recomendable ejecutar $ git gc para optimizar el repositorio. Por ejemplo, dos veces al día y una vez a la semana mantenimiento:

0 0 * * * /usr/bin/git_backup_database /path/to/mydatabase mydatabase
0 12 * * * /usr/bin/git_backup_database  /path/to/mydatabase mydatabase
0 1 * * 1 cd /path/to/mydatabase && git gc

Además desde otro equipo nadie te impide hacer un $ git clone ssh://equipo:path/to/mydatabase y tener todo el historial de la base de datos en un plis (bueno eso es relativo, que ocupará lo suyo…) o incluso programar un $ git pull para tener varios backups en distintas maquinas. En definitiva, se abren un sinfín de opciones :).

C++, cadenas multilínea de forma clara

Según el estándar c++98 (sección 2.13.4) las cadenas literales tienen como propiedad que durante el análisis léxico del código fuente (la primera fase que realiza el compilador, convirtiendo texto de entrada a tokens y símbolos) se concatenarán cadenas adyacentes.

In translation phase 6 (2.1), adjacent narrow string literals are concatenated and adjacent wide string literals are concatenated.

Esto viene a decir que podemos partir nuestras cadenas en cualquier momento sin poner ningún signo de concatenación entre ellas, de esta forma:

const char s1* = "dum" "de" "dum";
const char s2* = "dum"
                 "de"
                 "dum"
;

Lo cual viene realmente bien para formar código bien indentado y formateado, pues aunque también podemos usar “\” para saltar de línea, el texto a continuación no puede estar indentado pues significaría incluir esos espacios/tabuladores en la cadena que estamos definiendo. Y lo realmente interesante es que todo esto está en la propia definición del lenguaje, no es ninguna ayuda concreta del compilador, a los ojos del programa todas las siguientes definiciones son idénticas:

#include <string>
#include <iostream>
 
const char * cmp( const char *s1, const char *s2 );
 
int main()
{
 
    const char * s1 = "My spoon is too big"
                      "My spoon is TOO BIG"
                      "I AM A BANANA";
    const char * s2 = "My spoon is too big\
My spoon is TOO BIG\
I AM A BANANA";
    const char * s3 = ( std::string("My spoon is too big") +
                        std::string("My spoon is TOO BIG") +
                        std::string("I AM A BANANA") ).c_str();
 
    std::cout << "s1 and s2 are " << cmp( s1, s2 ) << std::endl;
    std::cout << "s1 and s3 are " << cmp( s1, s3 ) << std::endl;
    std::cout << "s2 and s3 are " << cmp( s2, s3 ) << std::endl;
 
    return 0;
}
 
 
const char * cmp( const char *s1, const char *s2 );
{
    while( *s1 && *s2 && *s1++ == *s2++ );
    if ( ( *s1 != *s2 ) || ( *s1 || *s2 ) )
        return "different";
    return "equal";
}
$ c++ const_char_declaration_sample.cpp && ./a.out
s1 and s2 are equal
s1 and s3 are equal
s2 and s3 are equal