IronRuby for Rubyists
IronRuby is Microsoft's implementation of the Ruby language we all know and love with the added bonus of interoperability with the .NET framework — the Iron in the name is actually an acronym for 'Implementation running on .NET'. It's supported by the .NET Common Language Runtime as well as, albeit unofficially, the Mono project. You'd be forgiven for harbouring some question in your mind about running a dynamic language such as Ruby atop the CLR - that's where the DLR (Dynamic Language Runtime) comes in. The DLR is Microsoft's way of providing dynamic language capability on top of the CLR.
Both IronRuby and the DLR are, as part of Microsoft's commitment to open source software, available as part of the Microsoft Public License on GitHub and CodePlex respectively.
IronRuby is designed to be the Ruby implementation of choice on Windows and as such boasts solid compatability and performance. IronRuby, at the time of writing, currently hits about an 86% pass rate against RubySpec. For comparison, the MRI interpreter passes about 98%. It also benchmarks considerably (more than 4x) faster than MRI 1.8, as Antonia Cangiano demonstrates with his suite of Ruby benchmarks.
Besides being reasonably compatible and performant, IronRuby's real strength is it's interoperability with the .NET standard library and .NET assemblies in general. With no more than a call to 'require', you can begin using your .NET classes and the framework at large in your Ruby code — 'automatically' providing IronRuby with substantial functionality over the standard Ruby library. require will happily take, as a string, any valid .NET assembly name. Just to illustrate how seamless this exchange between Ruby and .NET is, let's create a new Windows Form from within Ruby.
require 'System.Windows.Forms' System::Windows::Forms::Form.new.show
Run that through ir, the IronRuby interpreter, and you should have upon your screen a standard Windows form! It'll be empty and boring, but what do you expect for just 2 lines? The beauty of this is that it requires notably less ceremony than it's C# or VB counterpart. As you might imagine, you could programmatically add some controls to this new Form:
require 'System.Windows.Forms' form = System::Windows::Forms::Form.new lbl = System::Windows::Forms::Label.new lbl.text = "foo" form.controls.add(lbl) form.show
It can often be difficult to compare between code examples using a static language and a dynamic language, but in C# the above would look something like this:
using System; using System.Windows.Forms; namespace MyWinFormsApplication { static class Program { static void Main() { Form form = new Form(); Label lbl = new Label(); lbl.Text = "foo"; form.Controls.Add(lbl); form.Show(); } } }
Those that have a bit of experience in working with the .NET Framework will be aware that objects in your C# or VB applications, once compiled, exist specifically as CLR objects — that is, objects represented in a way that is accessible to anything that wants to play nice with .NET CLI (Common Language Infrastructure). This allows you to address things like methods and classes written in C# from VB, C++ or even PowerShell (and vice-versa). .NET code is compiled not into native code, but into an intermediate language imaginatively called CIL (Common Intermediate Language). Unfortunately, CIL is statically typed and as I alluded to in my introduction, this is where the DLR comes in to enable interoperability between the CLR and dynamic languages such as Ruby.
Methods that exist in .NET assemblies are available in IronRuby in two forms: familiar ruby lowercase form (controls.add) and traditional C# camelcase form (ToString). Since Ruby has no concept of properties in the way most .NET languages do (known as fields to the CLR), properties are implemented in IronRuby as accessor and mutator methods (foo.bar() and foo.bar=(x)). Let's assume I have MyClassLibrary.dll which was, at some point, the following C# namespace and class Person:
namespace MyClassLibrary { public class Person { public string Name { get; set; } public string Introduce() { return String.Format("Hi, I'm {0}", Name); } } }
This is also completely accessible in IronRuby.
require 'MyClassLibrary.dll' me = MyClassLibrary::Person.new me # => MyClassLibrary.Person me.name = "Edd" me.name #=> 'Edd' me.introduce # => 'Hi, I'm Edd'
This pure interoperability presents reusability of already existing code in IronRuby projects as well as some interesting metaprogramming possibilities.
Note: Watch your casing! If you have a .NET assembly of your own you want to require in your IronRuby, ensure that your .NET code is using proper Upper CamelCase for names of namespaces and classes. .NET classes can directly translate into Ruby classes and .NET namespaces are represented as Ruby modules — both of these are always named using Upper CamelCase in Ruby. If your source names in your .NET code do not match this convention, they cannot be included in your Ruby code.
Metaprogramming with IronRuby
The art and science of metaprogramming — especially in Ruby, where it's an absolute joy — is something that could very easily span an entire article. As you would hope, IronRuby code is fully able to manipulate itself allowing you to bend your classes to your whim just as you would expect with a good dynamic language. Let's demonstrate this with our Person class from the above example using Ruby's trusty class_eval to let us shout.
require 'MyClassLibrary.dll' me = MyClassLibrary::Person.new me.name = "Edd" MyClassLibrary::Person.class_eval do define_method(:shout) do |text| "#{name} shouts: #{text.upcase}!" end end puts me.shout("Hello, world") #=> "Edd shouts: HELLO, WORLD!"
As with prior examples, feed this into ir, your IronRuby interpreter. In case you needed any more proof that IronRuby is in fact a viable implementation of the language and is capable of doing everything you'd expect, let's chow down and dynamically extend our Person with a typical Rails-esque acts as mixin. Remember we're manipulating a class originally written in C# here!
require 'MyClassLibrary.dll' me = MyClassLibrary::Person.new module ActsAsHungry def self.included(base) puts "I'm hungry..." end def tummy_contents @tummy_contents ||= [] end def eat(foodstuff) tummy_contents << foodstuff puts "*Burp*" end end MyClassLibrary::Person.class_eval do include ActsAsHungry end me.eat("cheeseburger")
Riding the irails?
So let's get to the point. I think it's a solid bet to make that a large proportion of Ruby programmers are familiar with the Rails framework - perhaps it's even safe to assume that most were first led to the Ruby language by the siren song of the Rails framework itself.
Long story short, IronRuby is compatible enough to run your Rails app. Perhaps an abrupt statement like that is a bit of an anticlimax for an article with such a title but I hope, given that knowledge, your mind has started churning out uses for this .NET interoperability in your Rails applications. Some things of note though:
- IronRuby has some of it's own scripts that take over the jobs of irb, gem, rake and indeed rails — named iirb, igem, irake and irails respectively. This is to distinguish itself from the regular Ruby you probably have installed alongside it.
- However, the IronRuby site disclaims, as of 1.0 these scripts will be deprecated in favour of calling ir with the -S <task> flag.
- With it's separation from other Ruby installations, IronRuby does not share RubyGems with them (as can be reflected in calling igem list) — although it can.
- SQLite is quickly becoming the prefered database for development apps. Running under IronRuby, the sqlite3-ruby gem won't do. Be sure to install the sqlite3-ironruby gem.
Note: The MSI IronRuby installer will extract IronRuby to "C:/Program Files/IronRuby x.x" — if you installed IronRuby this way, I advise moving this installation to a directory that does not involve any spaces (and update your PATH appropriately) to avoid any problems with scripts that do not appropriately handle path names.
As stated above, IronRuby is currently very compatible with Ruby 1.8.6. There should be very little problem running an already existing Rails app with IronRuby (run your script/server script with ir — see for yourself).
Since you're probably deploying to Windows, IronRuby takes advantage of it's .NET compatibility to provide SQL Server support (along with Windows integrated security) through the ironruby-sqlserver gem — which requires a different DBI library than the one distributed with Rails, namely ironruby-dbi, but ironruby-sqlserver should take care of that for you. Configuration for this adapter in your database.yml looks like this:
development: mode: ADONET adapter: sqlserver host: HOSTNAME\INSTANCE database: application_development integrated_security: true
Very little beyond this is required to take advantage of IronRuby in your Ruby on Rails applications. Running these applications on a production environment is a huge topic with so many different options — primarily deploying to IIS or Windows Azure, a new cloud platform on which to run your web applications and web services that's made for running anything on.NET.
Hopefully by now you have been made aware of the great advantages that arise from combining a fluid, dynamic language such as Ruby with the .NET framework and are wondering; "can I write an ASP.NET MVC application in IronRuby?" or "how well does IronRuby play with other technologies such as Silverlight or its parent WPF for outside-the-browser applications?". For answers to these questions or just more information to calcify your understanding of IronRuby, look out for any future articles right here or check out the many resources on the IronRuby website.