Hosting Ruby Parts in MEF
MEF is fascinating because of the way some initial assumptions led to a different flavor of composition technology from the ones we've seen for .NET to date.
In my opinion, the most exciting ‘parameter variation’ in the design process for MEF was the idea that ‘parts’ should support the full range of .NET languages. The recent popularity of IronPython and IronRuby, and the inclusion of the DLR in .NET 4.0, meant that dynamic language support got first-class consideration.
As readers of my other blog may know, I’m a fan of Ruby in particular. For that reason I’m going to spend some time exploring how Ruby can fit into a MEF application.
I'm by no means a Ruby expert. Let me know if you seem me off course!
The Role of Parts
MEF’s unit of composition is the ComposablePart, or just ‘part’. Each part is self-contained and opaque to the outside world. The MEF CompositionContainer wires parts together according to the primitive Exports that each part exposes and consumes.
The independence of the part implementation behind these abstractions makes it possible to build a part just about any way you please.
The goal of the MEF/Ruby integration is to allow parts to be written in Ruby.
C# and the Attributed Part
In the default programming model, parts are defined in C# as classes, with attributes to describe their exports and imports:
This example is from the MEF Programming Guide, which describes these and some other interesting export and import capabilities (like method exports, which we'll look at later in this series.)
We call this the attributed programming model because MEF is agnostic about how parts are defined. A good example of an alternative programming model for an earlier version of MEF is Jason Olson's fluent version.
Translating Part Definitions into Ruby
I'm going to assume that if you are interested enough to read this far, you probably have at least a basic familiarity with the Ruby language, or the motivation to learn it.
Just as there are multiple ways of defining MEF parts in C#, there are endless ways that MEF parts could be defined in Ruby. There are two obvious ways that I can see Ruby interacting with other .NET languages via MEF:
- Ruby snippets fulfil a specific role in a C# application (e.g. as command macros); or,
- Composite applications are built using a mixture of Ruby and C#
Ruby Parts as 'Macros' (1)
The first approach would point towards supporting a minimal subset of MEF in order to supply Ruby snippets, probably as functions, to C# components:
For this imaginary example, a MEF integration would expose all similar functions from a script as Action<Document> delegates that could be imported by other parts. Export metadata could be used in order to describe the command as "delete selection" and this would be sufficient for including the command into user-defined toolbar buttons.
A simple technique like this is appealing for the first kind of Ruby/MEF integration, and I'm sure it is going to be implemented widely (I've already seen something similar done in Python.)
Full-Fledged Ruby Parts (2)
The second approach - building composite applications - requires that Ruby parts support most of the key MEF programming model features. It makes more sense in this case to use Ruby classes as the primary definition of a Part, and to require that the Ruby programmer has some knowledge of MEF itself.
This is the approach that I'm going to take in this series of posts.
Defining Ruby Parts
One of the wonderful things about Ruby is its flexibility. It is entirely possible to take a syntax-first approach and to work back from there towards an implementation.
My 'ideal' translation of the C# sample above would look something like:
It should be possible to interchange the Ruby Configuration part for the C# one in this example without changing any further code in the application. This is a really exciting possibility enabled by the DLR and MEF, and we'll try to push our implementation as far as possible in that direction.
I'm going to attempt to avoid a 'Part' base class, and instead regard any class that contains export statements as representing a part.
One thing this seems to imply is that the helper methods will be on Kernel. This might not be a good thing (it's like adding extension methods to System.Object in C#) so the base class might appear in the future.
There are three kinds of exports we need to account for: exporting the entire 'part object', exporting a property value, and exporting a method. All three of these are going to appear in the body of our Ruby class definition.
We'll need to use separate methods for each export type, so that we can differentiate between them. This is especially relevant in the case of properties/methods, which are the same under the hood in Ruby, but which need to be exported differently. A method export will be a delegate type, while a property export will be a value that is the result of invoking the property. Using export_attr and export_method would help to disambiguate this case clearly.
Imports are a bit simpler. I think that a single mechanism for importing values will suffice, and some hash parameters will allow the value to be imported into an attribute, an attribute writer (property) or perhaps via an optional block.
Where to Next?
There's no source code with this article today. I have a few unit tests working but things are a bit haphazard and not packaged up to distribute. I'm still experimenting with syntax so expect some significant changes of direction :)
I'll try to keep installments in this series short - perhaps a single unit test each. We'll work from there towards an example application that pulls everything together.