Invoking Ruby in C# / .NET

In my previous post, .NET the Ruby Way, I looked at the newly released Ruby.NET compiler developed at Queensland University. I managed to do some interop by compiling Ruby executables (.rb's) into .exe's, and also did some basic fiddling with instance variables through the .dll option, but I was ultimately unable to access my Ruby methods from within the dll. A little disappointed, I fired off an email to Dr. Wayne Kelly asking him to shed some light on the issue, and sure enough, a few days later I received a very thorough reply - with a little luck too, I discovered his message in my junk folder. (Ah, the perils of spam filters!)

Accessing Ruby methods from .NET

Wayne pointed out that the interop features between C# and Ruby are not yet implemented, and hence the current method is a little convoluted. Nonetheless, if your fingers are itching to tap into some Ruby power from your .NET environment, this should get you up and running. For the sake of an example, let's assume you have the following Ruby class:

class Foo
  def bar(a)
    puts a
  end
end

Granted, it's a trivial example, but it's the process that counts here. For example, let's assume that we want to invoke in C# the equivalent of:

require 'Foo.rb'

f = Foo.new
f.bar('Hello World')

To accomplish this, first we compile our class into a dll: RubyCompiler.exe --dll Foo.rb. Next, we add Foo.dll and QUT.Runtime.dll as references into our C# project, and now we're ready to start interfacing with our freshly minted Ruby dll:

class Program {
  static void Main(string[] args) {
    Ruby.Inits.rb_call_inits();
    Ruby.Class current_class = Ruby.Inits.rb_cObject;
    Ruby.Frame caller = new CallerFrame();

    Foo FooProgram = new Foo("Foo.dll");
    FooProgram.Init(current_class, caller);

    object FooClass = Ruby.Eval.get_const(current_class, "Foo", caller);
    object f = Ruby.Eval.CallPublic(FooClass, caller, "new", null);

    Ruby.Eval.CallPublic(f, caller, "bar", null, new Ruby.String("Hello world"));
  }
}

class CallerFrame : Ruby.Frame {
  public CallerFrame(): base(null) {  }
  protected override string file() { return "Main.cs"; }
  public override string methodName() { return "Main"; }
  public override Ruby.Class[] nesting() { return new Ruby.Class[0]; }
}

I imagine that future releases of Ruby.NET will provide a cleaner interface for this task, but in the meantime this should get you off the ground. Again, all credits, and big thanks to Dr. Wayne Kelly for providing the solution.

Ilya GrigorikIlya Grigorik is a web ecosystem engineer, author of High Performance Browser Networking (O'Reilly), and Principal Engineer at Shopify — follow on Twitter.