IronRuby – A Wrapper to Execute ruby code using C#

It’s been quite a while since I posted! So, lets have some fun. In this post I am going to present to you something experimental (not path breaking by any means!) I was doing with IronRuby. I wanted to create a wrapper for IronRuby, so that I don’t have to have a lot of repetitive code to execute some ruby code within .net using the new DLR. Before getting to that, let me give a quick outlook of executing ruby code in C#. The .net world has an equivalent to ruby called as “IronRuby”. Before getting started, we need the references for IronRuby. Download them from the IronRuby site (here) and add references to the following dll’s in your project. In this example I am using the 1.1.3 (download link) version of IronRuby in this post.

  • IronRuby.dll
  • IronRuby.Libraries.dll
  • Microsoft.Scripting.dll

After this step you are ready to go the ironruby way! IronRuby, being a dynamic language uses the DLR (Dynamic Language Runtime). In order to execute ruby code in .net, you first need to create a runtime object using the CreateRuntime method in the Ruby class. Using this runtime, you then have to create a ruby engine using the GetEngine method of the ScriptEngine. Note that you have to pass the engine type "rb".

Note: Similarly if you have references to IronPython (.net equivalent of python), you can follow similar steps to create an engine for python. So in this case GetEngine‘s parameter would be "py" to create a IronPython script engine.

Once you create the ScriptEngine object, you are ready to execute any ruby script and in case they have classes, you can even instantiate an instance in .net and call their methods. Following is the fully working code that executes some ruby code to just print some text and also to register a class with the DLR. In order to get familiar with the DLR an important keyword to understand is the dynamic keyword. Let’s understand this keyword before I get in to the remaining details

public static void Main(string[] args)
{
    Console.Write("Loading sample.rb...\n\n");

    ScriptRuntime rb = Ruby.CreateRuntime();
    ScriptEngine engine = rb.GetEngine("rb");
    engine.Execute("puts 'This is a test'");

    var rbPerson = engine.Execute(@"class Person
				    def greet()
					puts 'Hello world!'
				    end
				    end");

    object persInst = engine.Runtime.Globals.GetVariable("Person");
    dynamic person = engine.Operations.CreateInstance(persInst);
    person.greet();
}

.Net framework 4 introduced the dynamic keyword. Most important thing about dynamic is that is bypasses type checking. It has other purposes too, but we are mainly concerned about the uses in the perspective of dynamic languages. In this case, I use dynamic variables to hold instances of classes created. Thus, as there is no type checking, intellisense wouldn’t work with the variables of type dynamic.

Now, let me dwelve more in to the above sample. Lines 5 & 6, create a ruby runtime object and then creates a ruby engine using theis runtime object. After this point, the engine created can be used to execute ruby code. Line 7′s results can be immediately seen as it’s a call to the put method of ruby which prints the string passed. So line 7 would just print the message. But lines 9 – 13 are interesting. These lines execute the code that defines a class. So this wouldn’t produce any output. In order to use/create an instance of Person, first you need to create a reference to the person variable (Person is also a variable) using the GetVariable method of the Globals object in the engine’s runtime. Once you have a reference to this variable, an “instance” of Person can be created, using the CreateInstance method of the Operations object, again belonging to the engine object we created earlier.

Following is the output when the above code is ran.

Loading sample.rb...

This is a test
Hello world!
Press any key to continue . . .

Now, lets get to the most important part of this post. As I said before, I created a wrapper around the concept I explained. Whenever I want to create an instance (or add a script), I don’t want to repeat the steps listed earlier. Because, whenever an instance is required, if it doesn’t exist, we have to to execute the ruby script.

Taking care of this also would require managing a set of variables. To free myself from all these nuances, I created the following wrapper. I am just going to explain the most important parts. This wrapper contains the RubyExecutor class, which is responsible for managing the adding and the executing of the ruby scripts. This class inherits from the ExecutorBase class which takes on the entire load of taking care of stuff. It contains 2 abstract methods CreateRuntime and CreateEngine which the RubyExecutor overrides to create the ruby runtime and the engine (It will be evident why these methods are abstract in the following sections). Also there is a virual method called Init which need not overridden in the derived class. Apart from this a variable isInitialize is used to keep track of the state of the object. In this case it stores whether the object has been initialized. When a user instantiates an instance of RubyExecutor the base class’s ExecuteAll method is called. This executes all the scripts added using the AddScript and AddFile methods and then sets the isInitialized variable to true. After this point, the RubyExecutor instance can still be used to add scripts or files, but only the new ones will be executed by the engine.

public abstract class ExecutorBase
{
	private bool isInitialized = false;

	protected ScriptRuntime scriptRuntime;
	protected ScriptEngine scriptEngine;
	protected List<DynamicLanguageScript> scriptList;

	protected abstract void CreateRuntime();
	protected abstract void CreateEngine();

	public void Init()
    {
	    ExecuteAll();
    }

	public ExecutorBase()
	{
	    InitiateSequence();
	}

	private void InitiateSequence()
	{
	    scriptList = new List<DynamicLanguageScript>();
	    CreateRuntime();
	    CreateEngine();
	}

	public void AddScript(string scriptContents)
	{
	    scriptList.Add(new DynamicLanguageScript { CodeText = scriptContents, Executed = false });

	    if (isInitialized)
		    ExecuteRecent();
	}

	public void AddFile(string scriptFile)
	{
	    scriptList.Add(new DynamicLanguageScript { CodeText = ReadFile(scriptFile), Executed = false });

	    if (isInitialized)
		    ExecuteRecent();
	}

	protected void ExecuteAll()
	{
	    scriptList.ForEach(s => { scriptEngine.Execute(s.CodeText); s.Executed = true; });
	    isInitialized = true;
	}

	protected void ExecuteRecent()
	{
	    scriptList.Where(s => !s.Executed).ToList().ForEach(s => { scriptEngine.Execute(s.CodeText); s.Executed = true; });
	}

	public object GetInstance(string instanceName)
	{
	    if (!isInitialized)
		    throw new NotSupportedException("Executor not initialized. Call \"Init()\" to initialize");

	    dynamic instanceVariable;
	    var instanceVariableResult = scriptEngine.Runtime.Globals.TryGetVariable(instanceName, out instanceVariable);            

	    if (!instanceVariableResult && instanceVariable == null)
		    throw new InvalidOperationException(string.Format("Unable to find {0}", instanceName));

	    dynamic instance = scriptEngine.Operations.CreateInstance(instanceVariable);
	    return instance;
	}

	public IEnumerable<string> GetInstanceNames()
	{
	    return scriptEngine.Runtime.Globals.GetVariableNames();
	}

	public bool CanInstantiate(string className)
	{
	    return scriptEngine.Runtime.Globals.ContainsVariable(className);
	}

	private string ReadFile(string fileName)
	{
	    if (File.Exists(fileName))
	    {
		    return File.ReadAllText(fileName);
	    }
	    throw new FileNotFoundException(string.Format("{0} was not found", fileName));
	}
}

public class RubyExecutor : ExecutorBase
{
	protected override void CreateRuntime()
	{
	    scriptRuntime = Ruby.CreateRuntime();
	}

	protected override void CreateEngine()
	{
	    scriptEngine = scriptRuntime.GetEngine("rb");
	}
}

public class DynamicLanguageScript
{
	public string CodeText { get; set; }
	public bool Executed { get; set; }
}

Line 7 and lines 56 – 69 is of prime importance in this wrapper. Line 7 declares a list of DynamicLanguageScript which is used to hold a reference to any file or script added and also whether or not it was executed. Lines 59 to 69 is the most important method for this wrapper GetInstance. Once the wrapper is initialized, users can call this method to get an instance of any class defined in the scripts/files added. So, let’s dig in to this method in detail.

public object GetInstance(string instanceName)
{
    if (!isInitialized)
	    throw new NotSupportedException("Executor not initialized. Call \"Init()\" to initialize");

    dynamic instanceVariable;
    var instanceVariableResult = scriptEngine.Runtime.Globals.TryGetVariable(instanceName, out instanceVariable); 

    if (!instanceVariableResult && instanceVariable == null)
	    throw new InvalidOperationException(string.Format("Unable to find {0}", instanceName));

    dynamic instance = scriptEngine.Operations.CreateInstance(instanceVariable);
    return instance;
}

In the above code snippet, when the method is called, I first check if the wrapper is initialized. Without initialization, the scripts are not executed and so I just throw and exception. If initialized, I use the dynamic keyword to declare a placeholder for the instance of the ruby object that would be created. Then, using the script engine’s Runtime method, I access the Globals instance and call the TryVariable method. This method looks at the scripts executed and tries to find a class definition. If available, I use the CreateInstance method defined within the Operations object (using the script engine) to create an instance of the name of the variable passed and return it. Enough said, let’s see an example of how this wrapper could be used.

To begin with, create an instance of the ruby executor (rExecutor). The AddScript method is then called to add a script which has the definition for a class named Person. Then I call the Init method to initialize the wrapper. From this point, the wrapper can be used to create instances of Person. To do this, I use the GetInstance method in line 12. Now, using this instance I can call the methods for the instance, for example the say method, which would just print the message “Hello”. Following this in the snippet below, there are more examples of how this wrapper could be used.

public static void Main(string[] args)
{
    Console.Write("Creating an instance of ruby executor...");
    RubyExecutor rExecutor = new RubyExecutor();
    Console.Write("done");
    Console.WriteLine();

    rExecutor.AddScript(@"class Person
			    def say
				puts ""Hello""
			    end
			   end");
    rExecutor.Init();

    dynamic inst = rExecutor.GetInstance("Person");
    inst.say();

    Console.WriteLine();

    foreach(var name in rExecutor.GetInstanceNames())
	Console.WriteLine(name);

    Console.WriteLine();

    if (rExecutor.CanInstantiate("Person"))
    {
	   dynamic inst2 = rExecutor.GetInstance("Person");
	   inst2.say();
    }

    Console.WriteLine();

    if (rExecutor.CanInstantiate("Person2"))
    {
	   dynamic inst2 = rExecutor.GetInstance("Person2");
	   inst2.say();
    }
    else
    {
	   Console.WriteLine("Can't instantiate \"Person2\"");
    }

    rExecutor.AddScript(@"class Testclass
			    def run
				puts ""The sum of 1 & 2 is #{1+2}""
				p = Person.new
				p.say
			    end
			   end");
    dynamic instTest = rExecutor.GetInstance("Testclass");
    instTest.run();
}

The output produced when running the above snippet is given below:

Creating an instance of ruby executor...done
Hello

Person

Hello

Can't instantiate "Person2"
The sum of 1 & 2 is 3
Hello
Press any key to continue . . .

To follow up, you could also create a PythonExecutor for dealing python scripts. It would work similar to the ways of the RubyExecutor. But, in this post I am not going to discuss about python, because it’s not really my forte!

public class PythonExecutor : ExecutorBase
{
	protected override void CreateRuntime()
	{
	    scriptRuntime = Python.CreateRuntime();
	}

	protected override void CreateEngine()
	{
	    scriptEngine = scriptRuntime.GetEngine("py");
	}
}

Downloads – RubyExecutor.cs and RubyExecutorVerifier.cs (rename to .cs)

Hope this post was interesting!

Share