Aug 3 2010

"Anonymous Types" in Ruby

Category: Software DevelopmentJeff @ 06:42

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.

  1. class Anonymous
  2. def initialize(hash)
  3. hash.each do |key,value|
  4. self.class.class_eval do
  5. define_method("#{key}=") do |value|
  6. instance_variable_set("@#{key}", value)
  7. end
  8. define_method(key) do
  9. instance_variable_get("@#{key}")
  10. end
  11. end
  12. send("#{key}=", value)
  13. end
  14. end
  15. end
  16.  
  17. d = Anonymous.new :foo => 'bar'
  18. puts d.foo
  19. d.foo = 'baz'
  20. 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: , , , ,

Comments

1.
Rob Fahrni United States says:

Thanks for the Ruby lesson Jeff.

Ruby and Python have intrigued me for quite a while, I have books on the subject but have never taken the time to read them. The curly brace languages always suck me in! C/C++/C#/Objective-C, I just can't leave them alone.

Embracing something like Ruby would probably make my life easier! I guess I just like to punish myself.

Comments are closed