Adventures in Ruby MVVM – Bootstrapping Ruby

More Adventures in MVVM Shout it

In this post, I want to discuss how I am loading the ViewModels into the View.  When I write my Views, I like to use the tools I have available to me; Visual Studio and/or Expression Blend.  This means that I want a Visual Studio project and I want to be able to use these tools to create new views quickly.  The XAML files that define my views will still be backed by the obligatory auto-generated C# code, but this is where I want my C# code to end (for these experiments).  I don’t want to write an more C# code than that (with one exception).

My ViewModels, however, will be written in Ruby.  I will use RSpec to specify these ViewModels and I will use an editor other than Visual Studio to edit the code; mostly because Visual Studio does not have any tooling support for Ruby!  At some point, my View world needs to converge with my ViewModel world. 

To support this, I have written the only C# code that I intend to write in these experiments.  It is a bootstrapper, if you will, to load Ruby ViewModels.  It looks like this:

public class ViewModelLocator : DynamicObject
{
    private readonly ScriptEngine engine;

    public ViewModelLocator()
    {
        engine = IronRuby.Ruby.CreateEngine();
        engine.Runtime.LoadAssembly(typeof(INotifyPropertyChanged).Assembly);
        engine.Runtime.LoadAssembly(typeof(ICommand).Assembly);

        LoadAllViewModels();
    }

    private void LoadAllViewModels()
    {
        Directory
            .EnumerateFiles("ViewModels", "*.rb").ToList()
            .ForEach(file => engine.ExecuteFile(file));
    }

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (base.TryGetMember(binder, out result))
            return true;

        result = FindViewModel(binder.Name);

        return result != null;
    }

    private object FindViewModel(string name)
    {
        return engine.Execute(name + ".new");
    }
}

This ViewModelLocator class exists for one reason: to generate Ruby ViewModels.  It creates a Ruby script engine and pre-loads all the ViewModels when it is constructed.  It then sits around, waiting to create new ViewModels for the View:

<StackPanel Orientation="Horizontal" DataContext="{Binding PersonViewModel, Source={StaticResource VMLocator}}">
    <TextBlock Text="{Binding first}" Margin="5,0" />
    <TextBlock Text="{Binding last}" Margin="5,0" />
</StackPanel>

In the XAML above, I am setting the DataContext of the StackPanel to create a new PersonViewModel (defined in Ruby). The binding references a ViewModelLocator as its source that was defined in App.xaml:

<Application.Resources>
    <IronRubyMVVM:ViewModelLocator x:Key="VMLocator" />
</Application.Resources>

A dead-simple ViewModel for illustration is defined in Ruby:

require File.dirname(__FILE__)  + "/../RubyVM/ViewModelSupport"

class PersonViewModel
    include ViewModelSupport

    declare_notifiable :first, :last

    def initialize
        @first = "Brian"
        @last = "Genisio"
    end
end

You can see all the code in action from the snapshot of my project from which I wrote this post:  I will be working on this project quite a bit after this post, so if you want to see what I have been working on, the code is always available.

Tags: ,

2 Responses to “Adventures in Ruby MVVM – Bootstrapping Ruby”

  1. Did you opt for C#-backed Views because you could not get 100% pure IronRuby Views to work?

    In my IronPython/Silverlight experiments I didn’t even get as far as Views since I could not get ValueConverters to work…

  2. admin says:

    I opted for C#-based Views because I want to use Expression Blend to say “New User Control” and there is now way that I know of to have it generate views with Ruby code-behind.

    But, for my experiments to be completely successful, in my mind, I will need to write zero lines of C# code-behind in my views (except for my bootstrapper). By using the MVVM pattern, I believe that I can achieve this… but we’ll see :)

    I haven’t even tried this in Silverlight yet, but I suspect my only modification to what I am doing will be to bind using the index operator: “{Binding [first]}” and “{Binding [last]}”.

    I’ll blog about it more when I get there :)

    Speaking of that, I’ll play with Ruby value converters and see if I can get it to work. I am guessing that your stumbling block with that was in creating the resource? Not sure how to do that yet :)

Leave a Reply