After Justin James got his feet wet with IronRuby, he was eager to use it in a real-world scenario. Find out what he thinks of using IronRuby to write a WP7 app.

A Windows Phone 7 application I was working on kept sitting on the shelf because I was too overwhelmed with a couple of contracts to finish it. Then several weeks ago I won a free Samsung Focus in a Microsoft-sponsored contest, and the application (currently called Lifting Calculator) went from a hobby to a valuable tool. It also seemed like the perfect opportunity for me to try out IronRuby in the Windows Phone 7 environment.

Download IronRuby

I had IronRuby 1.1.1 installed, but I learned that IronRuby 1.1.2 was released in early February, and it includes Windows Phone 7 support. You need to download the binaries package (the MSI version is not fine -- I learned that the hard way). Also, while you are in the directory, look at each DLL's properties and click the Unblock button.

Write a Windows Phone 7 app in IronRuby

I read Shay Friedman's MSDN article about writing Windows Phone 7 apps in IronRuby, and then I tweaked his directions to fit my needs. One reason for the tweaks is that I already completed quite a bit of work on my application, and I wanted to see how to use IronRuby for one piece of my app first. Another issue was that the article's instructions were not entirely correct; I'm sure that this was primarily due to IronRuby changing since the article was written.

Here are the steps that I took:

I added a reference to the IronRuby binaries. I added all of the Windows Phone 7 binaries from the package. I prepped my XAML and attached code behind for the functionality I wanted. (No, I haven't moved to MVVM yet.) In the C# code that I wanted to call Ruby from, I added the following using statements: using System.Reflection; using System.IO; using Microsoft.Scripting.Hosting; using IronRuby; I created my Ruby file that contained the functionality I wanted. The Ruby script should manipulate global variables that will be passed into it by the Silverlight host, and return an output value. I set the Build Action and Copy to Output Directory of the new Ruby file as Content and Copy if newer, respectively. I added the code needed to call the IronRuby script, and received its output into a variable. I had to cast it to make sure I could use it nicely after I got it back.

For the full details, see(the C# code calling the IronRuby script) and(the IronRuby script). To get an idea of what this app does, this functionality is to determine how many weight plates of what size to put onto each side of a barbell to load it to the desired weight. This is a big help in the gym once you get past a certain point, and you end up taking forever to do the math and end up improperly loading the barbell. (This is part of a bigger project that I am working on, and I hope to release it in the near future.)

In Shay's article, he passes in the entire application to the script; for my discrete functionality, that is overkill. Instead, I pass in an object that represents my input parameters and an object that will hold my output. After I run the script, I use the output information to put the results on the screen. If I really wanted to be picky, I could bind the controls to this object.

private void ShowBarbellLoadout(int barbellWeight, int desiredLoad)

{

var resourceStream = Application.GetResourceStream(new Uri("BarbellLoader.rb", UriKind.Relative));

var dataFile = new StreamReader(resourceStream.Stream);

var code = dataFile.ReadToEnd();

var engine = Ruby.CreateEngine();

engine.Runtime.Globals.SetVariable("BarbellWeight", barbellWeight);

engine.Runtime.Globals.SetVariable("DesiredLoad", desiredLoad);

var loadoutResults = (IronRuby.Builtins.Hash)engine.Execute(code);

var results = new List<BarbellLoadout>

{

{new BarbellLoadout{ PlateSize = 45, PlateCount = int.Parse(loadoutResults["45"].ToString()) }},

{new BarbellLoadout{ PlateSize = 25, PlateCount = int.Parse(loadoutResults["25"].ToString()) }},

{new BarbellLoadout{ PlateSize = 10, PlateCount = int.Parse(loadoutResults["10"].ToString()) }},

{new BarbellLoadout{ PlateSize = 5, PlateCount = int.Parse(loadoutResults["5"].ToString()) }},

{new BarbellLoadout{ PlateSize = 2.5M, PlateCount = int.Parse(loadoutResults["2.5"].ToString()) }}

};

loadingChart.ItemsSource = results; mainPivotControl.SelectedItem = barbellLoading; }

currentTotal = DesiredLoad.to_i - BarbellWeight.to_i

output = {} output["45"] = (currentTotal / 90).truncate currentTotal -= output["45"] * 90 output["25"] = (currentTotal / 50).truncate currentTotal -= output["25"] * 50

output["10"] = (currentTotal / 20).truncate currentTotal -= output["10"] * 20

output["5"] = (currentTotal / 10).truncate currentTotal -= output["5"] * 10

output["2.5"] = (currentTotal / 5).truncate currentTotal -= output["2.5"] * 5

return output

I learned a very important lesson: All global variables set from the outside must have a capitalized variable name; otherwise, the variables will not work. Even more frustrating, in my script I prefaced the global variables with a dollar sign, and they didn't balk -- the variables auto initialized to the defaults, giving me no output. It took me a couple of hours to really get things working.

One alternative to my technique is to use the dynamic functions of C# 4. For example, a Ruby script could create classes, and you could use dynamic to instantiate them and run their methods from C#. I didn't try this directly, but dynamic worked when I gave it a dry run in plain C#, and I have no reason to believe that it wouldn't work to call IronRuby code.

Conclusion

This novel approach is not terribly practical, unless you prefer Ruby or have a deep investment in it. The big issue is that the already lean debugging options are nonexistent in the hosted DLR scenario. You can't step into the Ruby code; for me to debug it, I copied it into another IronRuby project and set up global variables to replicate what my C# code was setting so I could step into the code. This might be enough for a Ruby pro, but as a Ruby novice, it's not what I need. All the same, using IronRuby in Windows Phone 7 apps is of interest.

If you want more Ruby on Windows Phone 7, check out iron7, a Ruby interpreter for WP7.

J.Ja