Due to a project currently ending at work (hope to be ble to give more details soon, since it’s going open source) I’ve been looking for ways to run Ruby code out of C#.
The reason is clear: I can come up with a GUI in C# much faster than I would with say Fox. Nothing against Fox, it’s more my own inability to think in layout terms that makes me so unproductive.
Anyway I am not in the position to argue GUI toolkits as I have used them very sparingly over the years.
Now what I didn’t want to do is write the rest of the logic (which is mostly XML parsing and collection manipulation) in C#. Although C# collections are a bit better than Java’s comparing the same code with Ruby makes me cringe. Quick example:
Iterate over a list and pick only the objects that match a criterion.
List<SomeObject> result = new List<SomeObject>();
foreach(SomeObject o in List<SomeObject> list)
{
if (criterion)
{
result.Add(o);
}
}
while in Ruby:
result=list.collect{|o| o if criterion}.compact
Come to think of it, when coding in C# I am grateful for Intellisense because it saves me typing and provides documentation. In Ruby I would never think to use it.
Back to our subject though: The decision was made to make the GUI in C# and write the rest of the logic in Ruby. Now the next step was to find out how to call that ruby code out of C#.
The best solution would ofcourse be IronRuby (the Iron prefix seems to have stuck with Microsoft – at TechEd everyone is refering to the Ruby implementation in this way). I’ ve seen some impressive things done with the DLR and if IronRuby gets up-to-speed soon enough C#3.0 is in serious trouble (John Lam comments:” C#3.0 local inference typing looks a lot like Ruby blocks “)
Since IronRuby is far from ready we are left with less than optimal options:
SaphireSteel is a Visual Studio plugin. They offer a widget, the RubyConnector that allows you to call Ruby code from C#.
It’s not exactly programming against your Ruby objects, but it’s a way.
One disadvantage is that you need to have the interpreter installed which makes it dificult to distribute and install easily.
Instaling an extra interpreter is one step too many for most people – and yes you can go to the trouble of bundling everything with the app, but then you have to account for the users that do have Ruby installed etc. etc.
You can also build a Ruby app with a RESTfull interface and use HTTP to communicate with the GUI – this is definitely an entertaining idea, but then, why not make it browser based and be done with it.
OK, I admit it, I was just having fun when I thought of this one, although the new REST capabilities of WCF in .NET 3.5 make this pretty easy.
The less desirable, but easier to deploy solution is to make the Ruby logic a CLI app, call that from the GUI with the proper parameters and log or display the console output.
For ease of deployment use rubyscript2exe and just ship the ruby executable.
The GUI responsiveness suffers with the overhead of calling an external process and this is compounded by the unpacking rubyscript2exe has to do.
System.Diagnostics.Process lets you call an external process from C# and redirect stdin, stdout and stderr, so it is easy to capture the output from Ruby. Doing it asynchronously allows you to improve on the GUI responsiveness.
Actually, you need to do it asynchronously if you have a process that generates any decent amount of output:
If you redirect a process’ output in System.Diagnostics.Process and the buffer gets full, Process hangs till you read the output stream. This was a rather unpleasant discovery (I still haven’t found any mention of it in the docs).
Also something to note is that in order to receive any redirected output events you need to call BeginOutputReadLine/BeginErrorReadLine and at the end call the appropriate CancelXXXXRead methods. If you forget the Cancel methods you won’t be able to reuse the Process object.