Since the last installment in this little series, I've started to consider how Ruby/C# hybrid MEF applications might look.

The result is yet another component-based calculator:

image

Besides the Radiohead arithmetic, there is one reason to get excited...

image

Ruby parts! (I bet you hadn't guessed.)

The Ruby-based export and import features are heading towards a fairly natural syntax. IOperation, for example, is a regular .NET interface type:

image 

Let's take a look at how the Ruby implementation is woven into the app.

Configuration

This is still a work-in-progress, so configuration is basic. All of the Ruby parts are loaded from a single file that is fed into the RubyCatalog:

image

The calculator_ops.rb file contains part definitions like Multiply from above.

An additional catalog adds all of the C# parts to the composition as well:

image

Main Window

The main window is a typical WPF Window that Imports a list of operations:

image

Because the MainWindow is instantiated and composed by MEF, all known exporters of IOperation will be provided, regardless of the language they're implemented in.

Bi-Directional Composition

You'll notice that the MainWindow class implements the IErrorLog contract. This allows messages from the parts to be presented to the user:

image

Parts that wish to access the IErrorLog service from Ruby can import it:

image

The integration (and IronRuby in general) treats interface contracts as Ruby Module objects, so the IErrorLog used by the Divide part could be implemented by a Ruby object, although I haven't tested that case.

Fanatics take note: I did attempt to use IronRuby's case-transforming features to allow Symbol and Apply() to be specified in their natural Ruby forms (symbol and apply()) but had no success. Hopefully I'll resolve this in a future version.

Monkeypatching the Export Type

I can't say whether this will turn out to be a good move or not, but right now it seems reasonable.

Notice this method call:

image

In order to support MEF's lazy-instantiation feature, the @error_log attribute needs to be of type System::ComponentModel::Composition::Export, which will supply the actual instance when it is required through the get_exported_object() method. Calling @error_log.get_exported_object.add_message felt decidedly unnatural, so I've added method forwarding to the Ruby version of the Export class:

image

I've had to do some type aliasing to disambiguate Export from Export<T>, but otherwise this is straightforward. Rubyists, please weigh in and let me know if this implementation is less-than-desirable :)

Source Code

Once again you can download the full source code for this article from SkyDrive.