Category Archives: Programación

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 :).

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

Visualización de la documentación de todas las gemas instaladas

La documentación de las gemas de ruby se genera a partir del código fuente mediante rdoc. Después de eso puedes optar por ver individualmente cada una o usar gem server.

Para poder mejorar todo esto podemos usar par de útiles herramientas: (1) la plantilla para rdoc, hanna; y (2)bdoc como alternativa a gem server para visualizar la documentación.

Hanna es una plantilla para rdoc que mejora notablemente el formato por defecto. Podemos ver la diferencia fácilmente, por ejemplo, en la documentación de rspec con el formato típico vs la documentación hecha con hanna. La instalación es simple:

$ sudo gem sources -a http://gems.github.com
$ sudo gem install mislav-hanna

Para que a partir de ahora se generar la documentación con esta plantilla, puede añadir a tu .gemrc la siguiente línea:

rdoc: --inline-source --line-numbers --template=hanna

Y para convertir la documentación de todas tus gemas instaladas, puedes hacer algo como esto:

$ sudo gem list | awk '{print $1}' | xargs sudo hanna --gems

Por otro lado, gracias a bdoc podremos navegar fácilmente entre todas las documentaciones.

$ sudo gem install manalang-bdoc
$ bdoc

bdoc
Ejecutando bdoc se nos abrirá en nuestro navegador por defecto listo para poder leer la documentación y poder movernos fácilmente entre las diferentes gemas (y versiones). Muy útil.

Librería de logging para C++, boost::logging

En C++ tienes diversas librerías de logging, la mayoría son clones de log4j realmente, u otras con un estilo diferente bastante interesante; pero como suele ser costumbre en el mundo C++, tienes una alternativa relacionada con las librerías boost (en realidad boost::logging no es de boost oficialmente, pero es más que probable que en el futuro lo sea) que suele ser la ganadora por méritos propios.

Estoy hablando de la implementación de John Torjo. Muy flexible a la hora de configurarla unido a un uso trivial de la misma (como tiene que ser, tampoco es que, la labor de logging, sea algo muy complejo que digamos…). Para conocer todos los detalles puedes leerte la extensa y útil documentación, aunque de primeras puede ser un tanto compleja debido a nuevos conceptos que se usan a diestro y siniestro.

Principalmente debemos de conocer dos cosas:

  • Qué tipo de filtro queremos: el cual será el encargado decidir si un mensaje se escribe o no, dependiendo tanto de si el logging está activado, como si cumplimos la restricción de nivel (debug, warn, error, etc…)
  • Qué tipo de log queremos: ¿cómo debe de ser el formato de salida?, ¿cuál es la salida/s? ¿cómo será su comportamiento?.

Una vez definido esto, podemos escribirnos un par de ficheros en los cuales definiremos el log que podrá ser usado desde cualquier parte de la aplicación, incluyendo el header, y siempre y cuando se haya inicializado previamente.

#ifndef __LOGGING_HPP__
#define __LOGGING_HPP__
 
#include <boost/logging/format_fwd.hpp>
 
using namespace boost::logging;
using namespace boost::logging::scenario::usage;
 
typedef use<
    filter_::change::single_thread, // how often does the filter change?
    filter_::level::no_levels,      // does the filter use levels?
    logger_::change::single_thread, // how often does the logger change?
    logger_::favor::correctness     // what does the logger favor?
  > finder;
 
BOOST_DECLARE_LOG_FILTER( g_log_filter, finder::filter )
BOOST_DECLARE_LOG( g_log, finder::logger )
 
#define L_ BOOST_LOG_USE_LOG_IF_FILTER( g_log(), g_log_filter()->is_enabled() )
 
void initialize_logs();
 
#endif // __LOGGING_HPP__

Aquí acabamos de declarar un log, sin niveles, para una aplicación simple sin usar un thread separado para el logging. Para ver otras alternativas puedes echarle un ojo a los namespaces boost::logging::scenario::usage::filter_ y boost::logging::scenario::usage::logger_.

Finalmente solo nos quedaría inicializar el log (o logs, recuerda que nada te impide tener tantos como quieras):

#include "logging.hpp"
#include <boost/logging/format_ts.hpp>
#include <boost/thread/xtime.hpp>
 
BOOST_DEFINE_LOG_FILTER( g_log_filter, finder::filter )
 
BOOST_DEFINE_LOG( g_log, finder::logger )
 
void initialize_logs()
{
  g_log()->writer().add_formatter( formatter::idx(), "[%] "  );
  g_log()->writer().add_formatter( formatter::time("$hh:$mm.$ss ") );
  g_log()->writer().add_formatter( formatter::append_newline() );
 
  typedef detail::flag<destination::file_settings> flag;
  destination::file_settings file_settings;
  file_settings.initial_overwrite = flag::t<bool>( &file_settings, true );
  g_log()->writer().add_destination(
      destination::file( "app_debug.txt", file_settings )
    );
  g_log()->writer().add_destination(
      destination::cerr
    );
 
  g_log()->turn_cache_off(); // for showing output immediately
  g_log()->mark_as_initialized();
}

Aquí tenemos que hacer la definición del log y del filter de la misma forma que hemos hecho su declaración anteriormente. Definimos el formato de salida como más nos guste y finalmente solo nos queda indicar dónde se deben de escribir los mensajes. Se puede tener varios destinos, aunque básicamente podemos usar desde ficheros hasta cualquier stream, pasando por las salidas estándar (fíjate en los typedef de boost::logging:destination).

Una cosa importante que merece la pena mencionar es la llamada al método turn_cache_off(), especialmente importante si usas una salida a consola, pues por defecto el logging se cachea y solo se escribe al destino cada cierto tiempo, por lo que si monitorizas el log en tiempo real es crucial desactivar esta caché.

De esta forma, ya tendremos en cualquier parte de nuestro código donde incluyamos la declaración una macro L_ (o como la hayamos definido) lista para ser usada:

L_ << "Here we are";
 
unsigned int n = 42;
L_ << "The meaning of life is " << n;
 
L_ << boost::format( "We can even use the awesome %s library versión %d.%d.%d" ) 
    % "boost::format"
    % ( BOOST_VERSION / 100000 )
    % ( ( BOOST_VERSION / 100 ) % 1000 )
    % ( BOOST_VERSION % 100 );

How to change a light bulb?

– How many prolog programmers does it take to change a light bulb?
– No

That’s because:

?- change_bulb(prolog_programmer, X)
no.

Obviously.

Rails 2.2 & Inflector & Dependencies

Si has actualizado a rails 2.2 (o superior) desde una versión previa puedes encontrarte con un par de errores típicos con una solución muy simple:

`const_missing’: uninitialized constant Rails::Plugin::Dependencies (NameError)

Es debido a que Dependencies ahora es ActiveSupport::Dependencies.

`load_missing_constant’: uninitialized constant Inflector (NameError)

Es debido a que Inflector ahora es ActiveSupport::Inflector. Por lo que ahora puedes usar las inflections tal que así:

ActiveSupport::Inflector.inflections do |inflect|  
  ...
end