Search code examples
rubyclassmoduleincludeextend

How to extend a class within a Ruby module


I've been using OilyPNG (a set of C extensions for working with PNG's) and I'd like to start adding some of my own methods. As such, I coded up a simple little C extension that creates the class Hola and includes the methods Hola.bonjour (which responds with a simple "hello") and Hola.wazzup(input) which spits the input right back.

OilyPNG is a module that contains a single class :Canvas with a bunch of methods :decode_png_image_pass, :from_data_url, :from_rgb_stream, etc.). I'd like to add my methods (bonjour and wazzup) to the Canvas so I can start using them. How do I do this?

It seems like this is a very basic Ruby question, but I can't seem to find it addressed anywhere. I've tried (among MANY other things) the following:

module ChunkyPNG
  class Canvas
    include Hola
  end
end

...but it returns the following:

Error: #TypeError: wrong argument type Class (expected Module)

All I want to do is something simple like creating a new Canvas object, then running my method, i.e. OilyPNG::Canvas.new.bonjour and I'm totally stuck.

UPDATE: The C code used to generate the extension

#include <ruby.h>

/* our new native method; it just returns the string "bonjour!" */
static VALUE hola_bonjour(VALUE self) {
    return rb_str_new2("bonjour, Zack!");
}

void Init_hola(void) {
    VALUE klass = rb_define_module("Hola"); /*DEFINED AS A MODULE*/

    rb_define_singleton_method(klass, "bonjour", hola_bonjour, 0);

}

UPDATE 2:

Finally got it working - using rb_define module in the C code, followed by defining the methods as rb_define_method (instead of as singletons). Then build the gem, install it, then require the .so file, then open up OilyPNG to include the Hola module, as follows:

module OilyPNG
  class Canvas < ChunkyPNG::Canvas
    def your_method
      puts "hello"
    end
      include Hola
  end
end

Then load a png file as a test object:

test = OilyPNG::Canvas.from_file("C:/code/testfiles/orange.png"); nil

(the nil is to prevent Sketchup from crashing when it tries to send the PNG object to the command line), and then testing the output of:

test.bonjour

And it works!


Solution

  • module OilyPNG
      class Canvas < ChunkyPNG::Canvas # You must match the superclass when opening class and adding more definitions
        def your_method
          puts "hello"
        end
        include Hola # this will "insert" the Hola module at this point in the code... not sure what Hola contains so, not sure if that works or not.
      end
    end
    

    Looking at the source for OilyPNG the above should work...

    Not sure why you got that error. Given this works here is why: You have to match the classes, super class when reopening it.

    Based on this article: http://www.rubyinside.com/how-to-create-a-ruby-extension-in-c-in-under-5-minutes-100.html by the great Peter Cooper, I believe you should be using rb_define_method, not rb_define_singleton_method

    If you are still having trouble, start with peter's example, then adapt it to your needs.