RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


Ruby Comes to the .NET Platform : Page 5

Find out why .NET programmers may want to learn and use Ruby, and discover the core syntax of the language.

Reflection is the process of discovering information about objects at run time. You can discover the methods available for a class by calling the methods method. The base class for everything in Ruby is Object, so here's a look at some of the methods available to every object in Ruby:

   irb(main):001:0> o = Object.new
   => #<Object:0x3f8feb4>
   irb(main):002:0> o.methods
   => ["inspect", "taguri", "clone", "public_methods"
   , "taguri=", "display", "instance_variable_defined
   ?", "equal?", "freeze", "methods", "respond_to?",
   ...many more methods listed...
The result of calling methods is an array of strings containing each method name available on that object. You can think of a class much like a Hash; calling methods is like getting the keys to that hash table.

Knowing the methods wouldn't be interesting unless you could do something with them, of course. To call the method, you call the send method. The following operations are equivalent:

   irb(main):003:0> o.inspect
   => "#<Object:0x3f8feb4>"
   irb(main):004:0> o.send "inspect"
   => "#<Object:0x3f8feb4>"
Ruby gives you an opportunity to handle unknown methods by defining the method_missing method on your class:

   irb(main):139:0> class Object
   irb(main):140:1>   def method_missing(*args)
   irb(main):142:2>     puts args
   irb(main):143:2>   end
   irb(main):144:1> end
   => nil
   irb(main):145:0> o.foobar 1, 2, 3
   => nil
As you can see, the arguments passed to method_missing include the requested method as well as all the arguments passed to that method. A better definition would probably be:

   def method_missing(method, *args)
Although Ruby does not have properties, you can use the fact that method invocation does not (usually) require parenthesis to simulate properties. You also need to leverage the fact that Ruby treats methods that end with = specially, making them act like setters.

You might define a person class as such:

   irb(main):001:0> class Person
   irb(main):002:1>   def age
   irb(main):003:2>     @age
   irb(main):004:2>   end
   irb(main):005:1>   def age=(value)
   irb(main):006:2>     @age = value
   irb(main):007:2>   end
   irb(main):008:1> end
   => nil
Now you can use instances of Person, and treat age like a property of the person:

   irb(main):009:0> p = Person.new
   => #<Person:0x89c5678>
   irb(main):010:0> p.age
   => nil
   irb(main):011:0> p.age = 42
   => 42
   irb(main):012:0> p.age
   => 42
If you wanted age to default to something other than nil, you could set it in an initialize method.

This code feels very boilerplate. If this were a language like C#, you'd probably be tempted to use things like snippets in Visual Studio or even static code generation to automatically generate the reader and writer of a property.

With Ruby, though, you can use meta-programming to create these things with just a small amount of effort. Ideally, you'd like to be able to write something like:

   class Person
     prop :age
You'll define a class (static) method on Object so that it's available to use when you're defining your class. You'll also use a method you haven't seen yet, the class_eval method:

   irb(main):001:0> class Object
   irb(main):002:1>   def self.prop *names
   irb(main):003:2>     names.each { |name|
   irb(main):004:3*       self.class_eval "
   irb(main):005:3"         def #{name}
   irb(main):006:3"           @#{name}
   irb(main):007:3"         end"
   irb(main):008:3>       self.class_eval "
   irb(main):009:3"         def #{name}=(value)
   irb(main):010:3"           @#{name} = value
   irb(main):011:3"         end"
   irb(main):012:3>     }
   irb(main):013:2>     nil
   irb(main):014:2>   end
   irb(main):015:1> end
   => nil
The class_eval method as used above is what ends up creating the methods. It evaluates the string just as if you'd done that in the context of the class itself. So you can write your methods just as if you'd been writing them inside the class all along.

Each name passed to the prop method adds two methods to the new class: a getter and a setter. The end result string replaces #{name} with the name that you passed to the prop.

Now you can use prop in your class definition:

   irb(main):016:0> class Person
   irb(main):017:1>   prop :age, :name
   irb(main):019:1*   def initialize(age, name)
   irb(main):020:2>     @age = age
   irb(main):021:2>     @name = name
   irb(main):022:2>   end
   irb(main):023:1> end
   => nil
   irb(main):024:0> p = Person.new(36, "Brad")
   => #<Person:0x89c0768 @age=36, @name="Brad">
   irb(main):025:0> p.age
   => 36
   irb(main):026:0> p.name
   => "Brad"
With these kinds of facilities at your disposal, you can quickly create classes at a much higher level, using these kinds of meta-programming tricks to do much of the work for you, without needing to rely on editor snippets or compiler-time code generation.

Next Steps
This article really just scratched the surface of the facilities available in Ruby. Learning Ruby today will help you prepare for when Ruby is available on .NET and in Silverlight. Having such a powerful dynamic language will be a welcome addition to your programmer's tool belt, but more importantly, it will help you start to think about problems and solutions in a new way.

To continue your Ruby education, I strongly recommend Programming Ruby: The Pragmatic Programmer's Guide by David Thomas and Andrew Hunt (Addison-Wesley Professional), sometimes called "The Pickaxe" for its cover picture.

Brad Wilson is a software developer on the ASP.NET team and has worked at Microsoft more than three years. He has been a professional developer for more than 15 years. He is one of the co-creators of xUnit.net, a test-driven development framework for .NET 2.0.
Email AuthorEmail Author
Close Icon
Thanks for your registration, follow us on our social networks to keep up-to-date