Extending ruby with mixed C/ruby classes?

I’d like to extend Ruby to directly interface with STAF
(http://staf.sf.net/), which I’m using to actually execute my tests,
and report on their status. STAF has a C API, and I think I
understand how to wrap it with Ruby, but I have a few questions on
that.

Just FYI, the only reference I’ve found so far is the online Pickaxe;
other pointers are welcome, so long as they are available online
(offline refs are welcome too, but I’d like to finish this today if
possible; it doesn’t seem to be THAT hard, and I’m getting excited
enough about Ruby that I want to start USING it, darn it!).

Also, I would like to define a few Ruby classes as well, to make
exception handling a little easier.

I’ve included a sort of psuedocode for how I’d write the whole thing
in ruby below. The issues I have are:

  1. I’m reading the Pickaxe example, and it seems to match my situation
    quit nicely-- STAF has a STAFHandle_t type that connects the client
    with the server, and is passed as a parameter to all other calls.
    So I want to wrap that up in a STAFHandle class (I’m naming the
    class that way to match the C++ and Java interfaces; I’m going to
    compose that into a STAFCommand class that operates like a File).

    So far, so good, but when I read the Pickaxe, it shows the CDPlayer
    class defining an ‘initialize’ method (good, I get that) and a
    ’new’ Singleton method. What?!? I’m perfectly willing to concede
    that I don’t understand Ruby’s concept of Singleton methods, but
    why doesn’t the example just create the CDJukebox * and call
    Data_Wrap_Struct() on it in the initialize() function? Why do it
    that way? And, of course, do I need to mirror that structure in my
    code?

  2. How do I structure my code so that I can keep STAFResult and
    STAFException as pure Ruby classes, and just create and/or
    raise them from the STAFHandle class? I saw all sorts of
    examples of how to create Strings, Arrays, and Hashes, in C code,
    but nothing about how to create some generic Ruby class in a C
    method.

    Assuming I have STAFHandle.c, STAFResult.rb and STAFException.rb
    in the same directory, how can I create a STAFResult object in
    STAFHandle.c? Am I correct in assuming that I’d use the same
    method to create a STAFException (or some appropriate subclass),
    and then call ‘rb_raise(cExceptionInstance, “Appropriate text”)’?

Thanks for your help-- I had initially thought Ruby a solution in
search of a problem, but all of a sudden, everything’s starting to
look OO again, and it’s a happy feeling. Any help you can give in
getting me out of this quandry is greatly appreciated.

-=Eric

---- file ‘STAFResult.rb’ -----
module STAF
class STAFResult
def initialize(rc, result)
@rc = rc;
@result = result
attr_reader :rc, :result
end
end
----- end ‘STAFResult.rb’ -----

---- file ‘STAFHandle.rb’ -----
require ‘stafresult’

module STAF
class STAFHandle
def initialize(name)
@handle = # do stuff to initialize “@handle” with 'name’
end

  def submit(machine, service, request)
    retval = # do stuff to submit a 'request' to 'service' on 'machine'
             # retval is a STAFResult
  end

  def STAFHandle.formatString(format, args...)
     # format a string here
  end

  def STAFHandle.wrapData(string)
     # quote the string in a way STAF likes
  end

end
end
----- end ‘STAFHandle.rb’ -----

---- file ‘STAFException.rb’ ----
module STAF
class STAFException < Exception
def initalize(rc,text,name=‘STAFException’)
super(text)
@rc = rc
@name = name
end

  def to_s
    puts "Caught STAFException"
    puts "Name      : #{@name}"
    puts "Location  : #{backtrace()[0]}"
    puts "Text      : #{message()}"
    puts "Error code: #{@rc}
  end

end

class STAFOutOfBoundsException < Exception
def initialize(rc, text)
super(rc, text, “STAFOutOfBoundsException”);
end
end

class STAFInvalidObjectException < Exception
def initialize(rc, text)
super(rc, text, “STAFInvalidObjectException”);
end
end

class STAFInvalidParmException < Exception
def initialize(rc, text)
super(rc, text, “STAFInvalidParmException”);
end
end

class STAFBaseOSErrorException < Exception
def initialize(rc, text)
super(rc, text, “STAFBaseOSErrorException”);
end
end
end
----- end ‘STAFException.rb’ ----

···


Come to think of it, there are already a million monkeys on a million
typewriters, and Usenet is NOTHING like Shakespeare.
– Blair Houghton

I’d like to extend Ruby to directly interface with STAF
(http://staf.sf.net/), which I’m using to actually execute my tests,
and report on their status. STAF has a C API, and I think I
understand how to wrap it with Ruby, but I have a few questions on
that.

I’ve never heard of STAF until now, but it looks intriguing. What would
STAF give you that Test::Unit would not?

Just FYI, the only reference I’ve found so far is the online Pickaxe;
other pointers are welcome, so long as they are available online
(offline refs are welcome too, but I’d like to finish this today if
possible; it doesn’t seem to be THAT hard, and I’m getting excited
enough about Ruby that I want to start USING it, darn it!).

The best source of information that I’ve found is the Ruby source
itself. I’m constantly finding new things that I didn’t know existed,
and discovering new functions and techniques that aren’t in the Pickaxe.

The second best source of information I know of is this list. Lots of
people really know what they are talking about and are willing and
capable of answering questions.

I wish there were a better reference for the C API, but I don’t know of
one. [ruby-talk:45142] seems to indicate that in the future rdoc will
be used to document the API.

Also, I would like to define a few Ruby classes as well, to make
exception handling a little easier.

I’ve included a sort of psuedocode for how I’d write the whole thing
in ruby below. The issues I have are:

  1. I’m reading the Pickaxe example, and it seems to match my situation
    quit nicely-- STAF has a STAFHandle_t type that connects the client
    with the server, and is passed as a parameter to all other calls.
    So I want to wrap that up in a STAFHandle class (I’m naming the
    class that way to match the C++ and Java interfaces; I’m going to
    compose that into a STAFCommand class that operates like a File).

    So far, so good, but when I read the Pickaxe, it shows the CDPlayer
    class defining an ‘initialize’ method (good, I get that) and a
    ‘new’ Singleton method. What?!? I’m perfectly willing to concede
    that I don’t understand Ruby’s concept of Singleton methods, but
    why doesn’t the example just create the CDJukebox * and call
    Data_Wrap_Struct() on it in the initialize() function? Why do it
    that way? And, of course, do I need to mirror that structure in my
    code?

initialize() doesn’t create a new object; it initializes an already
created object. The singleton method new creates the object and calls
initialize().

C++ works similarly; operator new creates new objects on the heap and
makes a call to the constructor; the constructor then initializes the
object.

  1. How do I structure my code so that I can keep STAFResult and
    STAFException as pure Ruby classes, and just create and/or
    raise them from the STAFHandle class? I saw all sorts of
    examples of how to create Strings, Arrays, and Hashes, in C code,
    but nothing about how to create some generic Ruby class in a C
    method.

The easiest solution is probably to call rb_require() to load your Ruby
code from Init_STAF():

static VALUE rb_eSTAFException;
static VALUE rb_cSTAFResult;

#ifdef __cplusplus
extern “C” {
#endif

void Init_STAF() {
/* … /
rb_require(“STAFException.rb”);
rb_require(“STAFResult.rb”);
rb_eSTAFException = rb_const_get(rb_cObject, rb_intern(“STAFException”);
rb_cSTAFResult = rb_const_get(rb_cObject, rb_intern(“STAFException”);
/
… */
}

#ifdef __cplusplus
}
#endif

You can then raise a STAFException with:

rb_raise(rb_eSTAFException, “your message here”);

Be sure not to call rb_raise if you have created any C++ objects on the
stack that still need to be destroyed! Those objects may or may not get
properly cleaned up! (to make a long story short… :slight_smile:

You can create a STAFResult object with:

VALUE args = {};
VALUE result = rb_class_new_instance(
sizeof(args)/sizeof(VALUE), /* argument count /
args, /
the arguments themselves */
rb_cSTAFResult);

which will create a new STAFResult object, call its initialize() method
with the arguments you passed in, and return the object back to you.

Assuming I have STAFHandle.c, STAFResult.rb and STAFException.rb
in the same directory, how can I create a STAFResult object in
STAFHandle.c? Am I correct in assuming that I’d use the same
method to create a STAFException (or some appropriate subclass),
and then call ‘rb_raise(cExceptionInstance, “Appropriate text”)’?

rb_raise takes a class as its first argument, not an instance. If you
want to raise an instance, then use rb_exc_raise.

Thanks for your help-- I had initially thought Ruby a solution in
search of a problem, but all of a sudden, everything’s starting to
look OO again, and it’s a happy feeling. Any help you can give in
getting me out of this quandry is greatly appreciated.

-=Eric

Paul

···

On Sat, Nov 09, 2002 at 03:33:37AM +0900, Eric Schwartz wrote:

In article etoznsk3oyw.fsf@narsil.emschwar,

I’d like to extend Ruby to directly interface with STAF
(http://staf.sf.net/), which I’m using to actually execute my tests,
and report on their status. STAF has a C API, and I think I
understand how to wrap it with Ruby, but I have a few questions on
that.

Oh, good. I was thinking about wrapping STAF so that we could use it from
Ruby - but I’m doing too many other things right now. Good to see that
someone else is working on it.

Just FYI, the only reference I’ve found so far is the online Pickaxe;
other pointers are welcome, so long as they are available online
(offline refs are welcome too, but I’d like to finish this today if
possible; it doesn’t seem to be THAT hard, and I’m getting excited
enough about Ruby that I want to start USING it, darn it!).

There’s also the README.ext file that comes with the Ruby source.

Also, I would like to define a few Ruby classes as well, to make
exception handling a little easier.

BTW: Have you looked at using SWIG (http://www.swig.org) to wrap the STAF
C library? I’ll bet you’ll find that the following will work pretty much
out-of-the-box with not much tweaking required:

//STAF.i
%module STAF
%{
#include “STAF.h” //I’m guessing that one exists
%}

%include “STAF.h”

Then run: swig -ruby STAF.i

and check the resulting STAF_wrap.c and use it to build the extension.

Phil

···

Eric Schwartz emschwar@fc.hp.com wrote:

In article 20021108141113.F1652@atdesk.com,

···

Paul Brannan pbrannan@atdesk.com wrote:

On Sat, Nov 09, 2002 at 03:33:37AM +0900, Eric Schwartz wrote:

I’d like to extend Ruby to directly interface with STAF
(http://staf.sf.net/), which I’m using to actually execute my tests,
and report on their status. STAF has a C API, and I think I
understand how to wrap it with Ruby, but I have a few questions on
that.

I’ve never heard of STAF until now, but it looks intriguing. What would
STAF give you that Test::Unit would not?

Test::Unit is good for, well, unit tests. Whereas STAF and tools like it
are intended primarily for acceptance testing. STAF also takes care of
distributing testcases to multiple machines.

Phil