Category Archives: Ruby

Create ruby/jruby bindings of C++/C with ffi

Let’s start with the basic. What are bindings? It’s a way to call low level libraries (normally in C or C++) from another language (high level one, like ruby, java or whatever). This simple step requires two important things: 1) first, to know how we should call the method itself and 2) second, how to map the type from the high level language to C primitives types. To accomplish this, we have what is called foreign function interface which trivialize this task.

In ruby/jruby world we are gonna need ffi gem (jruby also has a compatible ffi gem), besides that the most important part is to have a clear interface in C. You can do bindings for a C++ library if you create a C interface first (because the bindings between C++ and C are free, the compiler knows how to do it by itself). So let’s cut the crap and write some code

First our C++ library which can be this few lines or thousands:

class awesome_object {
  public:
    awesome_object(int a, int b) : a_(a), b_(b) {}
 
    int awesome_method() {
      return a_ + b_;
    }
 
  protected:
    int a_, b_;
}

Now the C bindings which will be our façade for our c++ library:

extern "C" {
  typedef awesome_object_p void *;
 
  awesome_object_p create_awesome_object(int a, int b) {
    return static_cast<void *>(new awesome_object(a, b));
  }
 
  void delete_pointer_to_awesome_object(awesome_object_p p) {
    delete static_cast<awesome_object *>(p);
  }
 
  int awesome_method(awesome_object_p p) {
    awesome_object o = *static_cast<awesome_object *>(p);
    return o.awesome_method();
  }
}

Now ruby code that use ffi gem to be able to call the methods defined in C:

require 'ffi'
 
module AwesomeObject
  include FFI
  extend FFI::Library
  ffi_lib "libawesome.so" # .dll if you are in windows, it doesn't matter the OS
  attach_function "delete_pointer_to_awesome_object", "delete_pointer_to_awesome_object", [:pointer], :void
  attach_function "awesome_method", "awesome_method", [:pointer], :int
  attach_function "create_awesome_object", "create_awesome_object", [:int, :int], :pointer
end

With this you can use your C++ library from ruby code (I’d recommend package everything as a gem) just like this:

class MyTest
  include AwesomeObject
  def test!
    object = create_awesome_object(11, 31)
    awesome_method object
    delete_pointer_to_awesome_object object
  end
end
MyTest.new.test!

So, if you have read until now, probably you just want something working, say no more. Download this tar.gz, and see all this by yourself. In the tar you have the code split into c/h cpp/hpp files as it should be, in the post I’ve put all together to simplify things. Just execute make test and if you have ruby, ffi gem and g++ installed on your system, you’ll see something like:

$ make test
g++ -g -Wall -fPIC -c awesome_object.cpp
g++ -g -Wall -fPIC -c awesome.c
g++ -g -Wall -fPIC -shared -o libawesome.so awesome.o awesome_object.o
ruby test.rb
42

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.

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.

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

Bot personal jabber para twitter, RTwittBot

Desde hace ya un tiempo llevo usando twitter, y no, no tiene ninguna utilidad, pero me gusta hablar solo (¡está loco!). Twitter se cae cada dos por tres, pero era algo que se puede soportar (así siempre tienes algo que decir… y de lo que quejarte que a todos nos gusta quejarnos, ¡quejicas!), pero desde Mayo desactivaron el servicio que tenían de mensajería instantánea (¡como el colacao!) por jabber.

Mediante ese servicio de IM jabber teníamos el bot de twitter al cual podíamos:

  • Mandar mensajes para postear en nuestro twitter
  • Recibir mensajes de nuestro timeline de la gente que seguíamos y teníamos marcada como follow

Es decir, ahorrarnos el tener que entrar a la puta página y tener que estar dándole a f5 todo el rato. Para mi esto es un requisito para usar twitter. Si no es por IM, no lo usaría.

Estuve un tiempo en jaiku (más que nada porque era el único servicio similar que tenía el IM activado, plurk, que es molón, molón; también lo desactivó) pero ahora se ha vuelto tonto y empieza a funcionar mal. Así que pensé un poquito y dije, ¡leches, pero si te puedes montar un bot jabber en 4 pipas!, dicho y hecho, volví a twitter creándome un bot jabber, en ruby, que hace justo la misma funcionalidad que el de twitter “oficial” (aunque es solo para una persona obviamente, :P).

Lo he llamado RTwittBot (feo de cojones, lo sé), el código está en github, para hacerlo funcionar solo necesitas tener ruby y algunas gemas que he indicado en el README en github. Naturalmente, necesitarás una cuenta jabber para que sea usada por el bot (por ejemplo cualquier cuenta que tengas de gmail sirve) aparte de la tuya propia que ya usas, todo esto se encuentra explicado en github.

Por ahora lo llevo usando unos días y es bastante estable, e incluso si ocurre algún fallo en el propio bot (twitter caído, bug en el código, etc…) el bot no debería caerse sino que cambia su disponibilidad a away e indica en su estado el porqué, para volver a cambiarlo cuando todo vuelve a la normalidad :).

Cualquier duda sobre su uso o lo que sea, deja un comentario ;).

Rails <2.1 con ruby 1.8.7, broken!

Es muy peligroso, de locos más bien (o aventureros), usar últimas versiones (aka debian testing/unstable) únicamente de ciertas “cosas” (llámese cosas a librerías, paquetes, lenguajes, etc…).

Me he encontrado con un problema curioso debido al uso del interprete de ruby en una de sus últimas versiones estables (1.8.7) con el framework rails en una versión que no es la última (2.0.2).

Habría que mencionar que rails 2.0.2 salió en diciembre del año pasado, mientras que ruby 1.8.7 es de apenas mes y medio por lo que es comprensible el fallo.

El problema está en que en ruby 1.8.7 se han añadido muchas cosas nuevas, como, por ejemplo, el método chars a la clase String:

$ ruby -v
ruby 1.8.7 (2008-05-31 patchlevel 0) [i486-linux]
$ irb
>> foo = "I love popcorns".chars
=> #<Enumerable::Enumerator:0xb7c0206c>
>> foo.first.class
=> String

Método inspirado en el activesupport de rails:

$ ruby -v
ruby 1.8.6 (2007-09-24 patchlevel 111) [i486-linux]
$ irb
>> "Hello world?".chars
NoMethodError: undefined method 'chars' for "Hello world?":String
$ cd /some/rails/project/ && script/console
Loading development environment (Rails 2.0.2)
>> "Hola hola".chars
=> #<ActiveSupport::Multibyte::Chars:0xb74d0050 @string="Hola hola">

Es decir, en rails se definía un método que devolvía un ActiveSupport::Multibyte::Chars, pero si ejecutamos en un interprete 1.8.7 nos encontraremos que al ejecutar el método chars de una String, nos devolverá un Enumerator por lo que ahí ya puede pasar de todo (mayormente porque son cosas diferentes y así mal asunto).

ActionView::TemplateError: undefined method '[]' for #<Enumerable::Enumerator:0xb6f3481c>

La solución, si queremos seguir usando rails 2.0.2, es realmente simple. Si no queremos pensar, podemos ver cómo lo han solucionado en el HEAD de rails. Y simplemente añadimos esta solución en algún initializer (por ejemplo en config/initializers/fixes.rb):

begin
 	String.class_eval { remove_method :chars }
rescue NameError
	# all Ok
end

De esta forma, nos eliminará el método chars de String y podemos usar el definido en ActiveSupport en la metaclase de String. Es decir, como si aquí no hubiera pasado nada.

Por cierto, hay otros temas similares en cuanto a compatibilidad con ruby 1.8.7, relacionados con String#start_with?/end_with? y Enumarable#group_by pero mucho más simples y no tan “graves”.