Hosting IronRuby in a C# application

After writing the post Hosting IronPython in a C# application I decided to see how easy it was to host IronRuby in place of IronPython.

Before we go any further let’s setup a project. Create a console application (mine’s just named ConsoleApplication) and then, using NuGet, download/reference IronRuby. The version I have is 1.1.3.

Allowing our scripts to use our assembly types

Let’s look at some code

public class Application
{
   public string Name { get { return "MyApp"; } }
}

class Program
{
   static void Main(string[] args)
   {
      string code = "app = ConsoleApplication::Application.new\n" + 
                    "puts app.Name";

      ScriptEngine engine = Ruby.CreateEngine();
      ScriptScope scope = engine.CreateScope();

      engine.Execute("require 'mscorlib'", scope);
      engine.Execute("require 'ConsoleApplication'", scope);

      ScriptSource source = engine.CreateScriptSourceFromString(code, 
                     SourceCodeKind.Statements);
      source.Execute(scope);
   }
}

Here, as per the IronPython post, we create some scripting code, obviously this time in Ruby. Next we create an instance of the Ruby engine and create the scope object. We then execute a couple of commands to “require” a couple of assemblies, including our ConsoleApplication assembly. The next two lines will also be familiar if you’ve looked at the IronPython post, the first of the two creates a ScriptSource object from the supplied Ruby code and the next line executes the code.

All looks good, except I was getting the following exception

An unhandled exception of type ‘System.MissingMethodException’ occurred in Microsoft.Dynamic.dll

Additional information: Method not found: ‘Microsoft.Scripting.Actions.Calls.OverloadInfo[] Microsoft.Scripting.Actions.Calls.ReflectionOverloadInfo.CreateArray(System.Reflection.MemberInfo[])’.

I found the following post IronRuby and the Dreaded Method not found error which demonstrated a solution to the problem.

If we insert the following line of code, after the last engine.Execute line

engine.Execute("class System::Object\n\tdef initialize\n\tend\nend", scope);

or in Ruby code we could have written

class System::Object
   def initialize
   end
end

this will solve the problem – I don’t have any explanation for this, at this time, but it does work.

What if we already have an instance of an object in our hosting application that we want to make available to IronRuby ?

As per the IronPython example, we can simply set a variable up on the scope object and access the variable from our script, as per

string code = "puts host.Name";

ScriptEngine engine = Ruby.CreateEngine();
ScriptScope scope = engine.CreateScope();

scope.SetVariable("host", new Application());

engine.Execute("require 'mscorlib'", scope);
engine.Execute("require 'ConsoleApplication1'", scope);

ScriptSource source = engine.CreateScriptSourceFromString(code,        
          SourceCodeKind.Statements);
source.Execute(scope);


Using a dynamic to create our variables

Even cooler than creating the variables using SetVariable on the scope object, if we change our scope code to look like the following

dynamic scope = engine.CreateScope();

we can add the variables directly to the scope dynamic variable, for example

scope.host = new Application();