Ruby Comes to the .NET Platform

Ruby Comes to the .NET Platform

icrosoft’s IronRuby project brings a powerful and fun dynamic language to the Windows platform. The Ruby programming language is a modern, object-oriented scripting language, with a syntax inspired by languages such as Perl and Smalltalk. It was conceived by Yukihiro Matsumoto (aka “Matz”). In his words, Matz wanted a language that was “more powerful than Perl and more object-oriented than Python” (read more from Matz). The language is designed to feel natural; something Matz calls the “principle of least surprise.” Version 1.0 of the language was released in 1996.

For several years, Ruby existed as a little-known scripting language that excelled in the tasks for which it was originally designed: manipulating data and environment with the least amount of effort. When I first started toying with it a few years ago, I was looking for something to replace batch files for automating some of my more common administrative tasks.

Ruby’s popularity really began to pick up when a small software company from Chicago, Illinois, named 37signals released a Web application framework named Rails. This new framework combined proven patterns like Model-View-Controller and ActiveRecord with new ideas like “convention over configuration.” The end result is a framework that achieves a lot with almost no code.

RubyCLR and IronRuby
In early 2006, John Lam went public with a project named RubyCLR, which worked as a bridge between Ruby and .NET. It allowed the user to have direct access to the richness of the .NET platform from Ruby, and even expose Ruby objects to the CLR. This project was very ambitious, but it wasn’t about putting Ruby on .NET so much as it was about letting the two worlds talk to each other. You still needed the Ruby runtime to be installed on your machine.

The RubyCLR project was a tremendous first step toward understanding how to get Ruby and .NET to live together in harmony. John’s work did not go unnoticed, and at the end of 2006, he announced on his weblog that he would be joining Microsoft’s newly formed Dynamic Language Runtime (DLR) team. Just a few months before John’s announcement, Microsoft released version 1.0 of IronPython, a new implementation of the Python language on the .NET Framework. The Dynamic Language Runtime work takes the work done on IronPython and builds a runtime on top of the .NET Framework that allows dynamic languages to be brought into .NET with much less effort.

John and his team announced IronRuby at the MIX conference in 2007. Perhaps more surprising than the revelation of the IronRuby project itself was that it would be the first truly open source .NET language from Microsoft. Not only would the source code be available, but outside contributions from the community are also accepted.

IronRuby is still a work in progress. Although drops have occasionally been available, usually in the context of another project (such as the recently released Silverlight 2.0 Beta 2), those following the project are keeping up with the source tree and participating in the mailing lists.

Why Learn Ruby?
One of my favorite books, The Pragmatic Programmer: From Journeyman to Master (Andrew Hunt and David Thomas, Addison-Wesley Professional), encourages you to learn a new language every year. For me, the language that most changed my world-view when I learned it was Ruby.

Ruby is a fully object-oriented language in the Smalltalk sense. That means everything you interact with in the system is an object, including direct values like numbers. Even classes, which form the templates from which new object instances are created, are themselves objects in the system.

Because Ruby is a dynamic language, you’ll find that types become much less important. When a method takes an object as a parameter, you don’t need to specify what type that object needs to be. In fact, there is no compiler, so you might not discover until runtime that the object you passed to a method wasn’t suitable for the requirements of that method.

If you’re like I was a few years ago, you might find this concept a little unsettling. Aren’t compilers here to provide help to you, so you can know these things as soon as possible before they cause runtime errors? The answer is, of course, yes: compilers do provide better feedback, sooner. So why should you throw them away and use a language like Ruby?

As it turns out, compilers are a very seductive form of straitjacket. The ability of compilers to tell you about mismatched types necessarily causes a form of type-rigidity. When you write a method, you may wish to say “objects here must be able to do foo and bar” and then invent an interface called IFooBar. That seems like a neat solution for describing the requirements?but it fails you when you want to use someone else’s classes (especially those from the framework) that were written before IFooBar existed.

Author’s Note: Although IronRuby isn’t quite ready for mainstream use, you can use the standard version of Ruby for your explorations. If you want to follow along with the examples, you can download the Ruby installer for Windows.

Ruby by Example
The best way to learn a language like Ruby is by exploring it interactively. Many dynamic languages have interactive prompts known as a Read-Execute-Print Loop (REPL). The REPL program for Ruby is called irb (which standards for “interactive Ruby”).

When you execute the irb program, you’ll see an irb prompt:

   C:UsersBrad> irb   irb(main):001:0>

You type commands into the irb prompt and the Ruby interpreter evaluates them, with the results printed to your screen. REPLs like irb are an excellent way to learn a language: one statement at a time.

As an introduction to irb, tell Ruby to add two numbers. At the irb prompt, type 5 + 2 and press Enter:

   irb(main):001:0> 5 + 2    => 7

The irb(main):001:0> part is the prompt that irb gave you. When you typed 5 + 2 and pressed Enter, irb printed the result to you as => 7. The => is your hint that irb is showing you the result of the evaluation.

If Ruby believes you haven’t finished your expression, it will allow you to continue. For example, if you were to type 5 + 2 + and press enter, Ruby realizes that you’re still missing part of the expression and lets you continue typing on the next line:

   irb(main):002:0> 5 + 2 +   irb(main):003:0* 13   => 20

The second prompt line ends with “*” instead of “>” to let you know that you’re still finishing a previous statement.

Editor’s Note: This article was first published in the September/October 2008 issue of CoDe Magazine, and is reprinted here by permission.

Basic Types
A programming language wouldn’t really be worth much if it couldn’t manipulate numbers. Simple arithmetic is no trouble for Ruby:

   irb(main):004:0> 3 + 4   => 7   irb(main):005:0> 3 * 4   => 12   irb(main):006:0> 3 - 4   => -1   irb(main):007:0> 3 / 4   => 0   irb(main):008:0> 3.0 / 4.0   => 0.75   irb(main):009:0> 0xF   => 15   irb(main):010:0> 0x3 * 0xA   => 30

As you can see, Ruby has support for integer and floating point types, and even accepts the common format for hexadecimal integers, although the result of 0x3 * 0xA was printed in decimal (30 rather than 0x1E).

As with .NET, numbers are actually objects, so you can call methods on them:

   irb(main):011:0> 14.to_s   => "14"

Don’t try that with C++.

To to_s method converts an object into a string, so calling 14.to_s returns the string “14.” As with .NET’s ToString() method, the to_s method is actually a method on Object, so you can convert everything in Ruby to a string.

Ruby’s strings have the full complement of operations that you might expect:

   irb(main):012:0> "hello" + "there"   => "hellothere"   irb(main):013:0> "Reader".length   => 6   irb(main):014:0> "Reader".reverse   => "redaeR"   irb(main):015:0> "reader".capitalize   => "Reader"   irb(main):016:0> "Reader".include?("foo")   => false   irb(main):017:0> "Reader".include?("ade")   => true   irb(main):018:0> "  Reader  ".strip   => "Reader"   irb(main):019:0> "Reader".gsub("e", "f")   => "Rfadfr"   irb(main):020:0> "Reader".delete("ea")   => "Rdr"   irb(main):021:0> "a"  true

Several string operations are available that you’re probably not familiar with. For example, the following code tests whether a string lies between two other strings, alphabetically:

   irb(main):022:0> "Bob".between? "Adam", "Chris"   => true

The multiplication operator causes a string to repeat a specified number of times:

   irb(main):023:0> "hi" * 5   => "hihihihihi"

The crypt method provides a salted one-way hash of the given string, which is useful for storing sensitive data like passwords:

   irb(main):024:0> "Reader".crypt("ab")   => "abofgDjq6JNJo"

Ruby doesn’t have a built-in character type. It represents characters as numeric values. You can represent character constants with the ? syntax. You can use the chr method to convert a number into the string equivalent of its character:

   irb(main):025:0> "Reader"[2]   => 97   irb(main):026:0> ?a   => 97   irb(main):027:0> 97.chr   => "a"

It’s not really helpful to just perform operations unless you can store the results for later use:

   irb(main):028:0> x = 42   => 42

Strings have a special syntax that allows embedded evaluation. This evaluation isn’t limited to just simple variable replacement, either: it’s a full evaluation:

   irb(main):029:0> "The answer is #{x}!"   => "The answer is 42!"   irb(main):030:0> "The answer is #{6 * 7}!"   => "The answer is 42!"

You can avoid the evaluation by surrounding your string with single quotes instead of double quotes:

   irb(main):031:0> 'The answer is #{x}!'   => "The answer is #{x}!"

Arrays in Ruby work much like the ArrayList class from .NET 1.0. They are variable-sized arrays that can store any type of data, indexed starting with 0:

   irb(main):032:0> a = ["hello", 42, "world"]   => ["hello", 42, "world"]   irb(main):033:0> a  ["hello", 42, "world", 37.5]   irb(main):034:0> a[0]   => "hello"   irb(main):035:0> a[6] = 'hi' * 2   => "hihi"   irb(main):036:0> a   => ["hello", 42, "world", 37.5, nil, nil, "hihi"]   irb(main):037:0> a[99]   => nil

The preceding code shows how to use the push operator () to add items to the end of the array, and the index operator ([]) to both get and set values. When you add an item past the end of the array, Ruby fills in the “holes” in the array with nil values. When you attempt to access values beyond the end of the array, Ruby returns nil rather than throwing an exception.

You can slice off part of the array by using a ranged index. You can also combine this with the ability to use negative indices to access the array from the back rather than the front (where -1 is the last item, -2 is the second to last item, etc.). Although you can’t use reverse ranges to get reversed slices, you can use a forward range, and then call the reverse method afterward:

   irb(main):038:0> a[-1]   => "hihi"   irb(main):039:0> a[1..3]   => [42, "world", 37.5]   irb(main):040:0> a[2..-2]   => ["world", 37.5, nil, nil]   irb(main):041:0> a[-4..-1]   => [37.5, nil, nil, "hihi"]   irb(main):042:0> a[-1..-4]          # Won't work   => []   irb(main):043:0> a[-4..-1].reverse  # Works fine   => ["hihi", nil, nil, 37.5]

Like strings, you’ll find several unique and useful methods available to arrays:

   irb(main):044:0> a   => ["hello", 42, "world", 37.5, nil, nil, "hihi"]   irb(main):045:0> a.compact   => ["hello", 42, "world", 37.5, "hihi"]   irb(main):046:0> a.join   => "hello42world37.5hihi"   irb(main):047:0> [10, 75, 6, 29].sort   => [6, 10, 29, 75]   irb(main):048:0> [[1, 2, 3], [4, 5, 6]]   => [[1, 2, 3], [4, 5, 6]]   irb(main):049:0> [[1, 2, 3], [4, 5, 6]].flatten   => [1, 2, 3, 4, 5, 6]

The final core data structure in Ruby is the Hash, similar to .NET 1.0’s Hashtable. It is an associative array, where the indices (keys) can be any kind of value, and the data they point to (values) can also be any kind of data. In practice, most Hashes use symbols for keys (see the following section).

You declare hashes using the {} syntax, and you declare initialization values in “key => value” form. You can use the index operators to both get and set values in the hash:

   irb(main):050:0> h = {:foo=>'bar', :baz=>'biff'}   => {:foo=>"bar", :baz=>"biff"}   irb(main):051:0> h[:foo]   => "bar"   irb(main):052:0> h[:unknown]   => nil   irb(main):053:0> h[:baz] = "new"   => "new"   => {:foo=>"bar", :baz=>"new"}   irb(main):054:0> h.entries   => [[:foo, "bar"], [:baz, "new"]]

Variable (and method) names in Ruby start with a lowercase letter, and can contain letters, numbers, and underscores.

Local variables have no prefix. Instance variables are prefixed with @. Global variables are prefixed with $.

You do not need to declare variables before you use them. Uninitialized variables have the value nil.

There are several pre-defined variables:

  • nil is an object that represents nothing (comparable to null in .NET, except that nil is an instance of the NilClass class).
  • true and false are instances of the TrueClass and FalseClass.
  • self, when used from a method, is a pointer to the object instance from which the method was called. When it is used from within a class, it refers to an instance of the class object itself.
  • __FILE__ and __LINE__ return the currently executing file and the line number in that file.

Ruby has a special kind of string known as a Symbol. Because strings can be changed in Ruby, using them as hash keys can be slow at best and unpredictable at worst.

The names of symbols follow the same rules as variables, except that they start with a colon (:). You can’t change the value of a symbol, and two symbols with the same name have the same identity, which makes them excellent hash keys; instead of comparing the value of a variable length string, a lookup requires only comparisons against integer values.

Everything in Ruby is an object, and objects are all instances of classes. To discover what class something is, call the class method on it:

   5.class   => Fixnum   (2 ** 96).class   => Bignum   7.5.class   => Float   (1..10).class   => Range   "foo".class   => String   /^foo[a-e]$/.class   => Regexp   :foo.class   => Symbol   [].class   => Array   {}.class   => Hash

Blocks and Closures
Although there were things like event handlers in .NET 1.x, you were forced to define full methods to attach to events when you wanted them handled. This led to the creation of a lot of methods that really only existed because the framework demanded it.

.NET 2.0 introduced the concept of anonymous delegates. They function in much the same way as blocks do in Ruby:

   irb(main):001:0> h = {:foo=>'bar', :hi=>'there'}   => {:foo=>"bar", :hi=>"there"}   irb(main):002:0> h.each_key {|k| puts k}   foo   hi   => {:foo=>"bar", :hi=>"there"}   irb(main):003:0> h.each {|k,v| puts "#{k}: #{v}"}   foo: bar   hi: there   => {:foo=>"bar", :hi=>"there"}

As you can see, the syntax for blocks in Ruby is quite terse: Use curly braces to open and close the block, and use the |x,y| syntax to designate the variables passed to the block.

Blocks in Ruby also act like closures, just as anonymous delegates do in .NET 2.0. This means they have access to values in their enclosing scope, even after that scope has exited. Here’s a simple closure that multiplies together several values:

   irb(main):004:0> n = [5, 6, 10]   => [5, 6, 10]   irb(main):005:0> t = 1   => 1   irb(main):006:0> n.each { |i| t *= i }   => [5, 6, 10]   irb(main):007:0> t   => 300

You can even store references to blocks to be used later:

   irb(main):008:0> t = 1   => 1   irb(main):009:0> f = lambda { |i| t *= i }   => #<0x0818a7b0>&f

















Share the Post: