Readers
of this blog know that I'm an avid C# .NET developer. However, one can hardly survive long in this business
without learning multiple approaches to programming. In the C# world, we have functional programming (lambda expressions, action and func delegates, etc.
), declarative programming (config files, fluent interfaces, etc.), procedural programming (gasp!) and elements of object oriented programming (though C# isn't a "pure" object oriented language - it's actually "class oriented", but that's a discussion for another blog post).
In an effort to expand my horizons even further, I've started investigating the world of Ruby. Ruby is a dynamically typed, interpreted language. Many people have heard of Ruby in the context of Ruby on Rails (RoR) though the language can be used for general purpose programming as well.
As a first project, I wanted to see just how "dynamic" Ruby really is. Well, it's not quite what I expected. I had this idea that objects in Ruby would work something like an ExpandoObject in C# 4.0. While Ruby objects don't work like this out of the box, it didn't take a lot of effort to accomplish this behavior in Ruby.
- class Anonymous
- def initialize(hash)
- hash.each do |key,value|
- self.class.class_eval do
-
- define_method("#{key}=") do |value|
- instance_variable_set("@#{key}", value)
- end
-
- define_method(key) do
- instance_variable_get("@#{key}")
- end
-
- end
- send("#{key}=", value)
- end
-
- end
- end
-
- d = Anonymous.new :foo => 'bar'
- puts d.foo
- d.foo = 'baz'
- puts d.foo
Let's walk through what is going on here in an effort to better understand how Ruby works. On line 1, I am declaring a class called "Anonymous" that I want to behave sort of like Anonymous types in .NET (but with both getter and setter behavior). Line 2 declares a constructor for this class which
receives one parameter called "hash". Skip down to line 21 and you can see a few things going on. First, a variable "d" is declared, and I am creating a new instance of the Anonymous type by calling Anonymous.new. The really interesting code happens at the end of this line where I am defining a ruby hash. Equivalent C# code would look something like this:
var d = new { foo = 'bar' };
However, in C# you run into a couple of issues. First, you cannot change the value of "foo" anymore. Second, you can't return this anonymous type from a method. Well, you could, but the caller would have no idea what the return type would be and couldn't work with your object without some nasty reflection. Such concerns don't even in matter in Ruby, however, since it is a dynamic, interpreted language.
On line 22 I'm simply writing the value of "foo" out to the console (puts in Ruby is the .NET equivalent of System.Console.WriteLine()). Notice that on line 23 I am able to change the value of "foo" from 'bar' to 'baz'. The class Anonymous has been constructed to automatically create a getter and setter for "foo". In declaring another Anonymous type, I can specify a different hash and attach a whole new set of properties to my object. In just 19 lines of code, I was able to create a class with the behavior of an Anonymous .NET type, but including setter behavior.
In a future post, I'll dive deeper into the implementation details of the Anonymous class. For now, I'll stop there and just ponder ways to use this functionality. I'm impressed with Ruby's ability to allow the expression of complex behavior with minimal code.
(thanks to emilio for helping me get this Ruby stuff working!)
Tags: ruby, c#, dynamic, static, anonymous