This post originated from an RSS feed registered with .NET Buzz
by Peter G Provost.
Original Post: Fun With Ruby
Feed Title: Peter Provost's Geek Noise
Feed URL: /error.aspx?aspxerrorpath=/Rss.aspx
Feed Description: Technology news, development articles, Microsoft .NET, and other stuff...
A few weeks ago Brad Wilson and I were invited to come down to Portland to speak and hang out at the Portland Code Camp. We pitched a few ideas to them including EntLib, CAB, Unit Testing/TDD, etc. All the usual suspects.
But Brad and I also thought it would be fun to talk about Ruby. If you’ve read this blog much, you know I’ve been in love with Ruby for a long time now, so this shouldn’t be a surprise.
Anyway, it turns out I can’t go which is a huge bummer. But Brad is going and he’s giving a talk introducing Ruby. Should be fun. (BTW, if you live in the Northwest, you should go. I would if I could, but I can’t.)
Today, Brad was asked by the organizers to translate this C# code into Ruby (for the T-Shirts or something):
using System;
namespace Community
{
class CodeCamp
{
private string city;
private string version;
public CodeCamp ( string city, string version )
{ this.city = city; this.version = version; }
public override string ToString()
{ return string.Format( "{0} Code Camp v{1}", city, version ); }
}
class ConsoleApp
{
[STAThread]
static void Main(string[] args)
{
CodeCamp pdx = new CodeCamp("Portland", "1.0");
Console.WriteLine( pdx );
}
}
}
Brad was nice enough to cc me on his answer, which was this:
module Community
class CodeCamp
def initialize(city, version)
@city = city
@version = version
end
def to_s
"#{@city} Code Camp v#{@version}"
end
end
end
puts Community::CodeCamp.new("Portland", "1.0")
Of course, Brad's answer is the obvious direct port of the provided C# code, but I'm not sure it is the "Ruby Way"...
My first answer was this:
puts "Portland Code Camp v1.0"
But I decided that probably wasn’t what they weren looking for. But I am reminded of the time I demoed Ruby to Ward Cunninham and he said, "Oh look. Hello World is the right number of lines."
But I digress...
Perhaps something that shows off Ruby method redefinition would be better?
class Float
alias old_to_s to_s
def to_s
return "Portland Code Camp v#{old_to_s}"
end
end
puts 1.0
I kinda like that one, but I don't like that the string "Portland" is embedded in my Float class. No to mention that all Floats now have this problem. Cool? Yes. The right answer? Probably not.
After playing around with some much nastier ideas, I decided that I wanted my puts line to look something like this:
And, of course, writing this in Ruby was really easy:
class String
def codeCamp(version)
puts "#{to_s} Code Camp v#{version}"
end
end
"Portland".codeCamp(1.0)
However, adding a codeCamp method to all strings in the system is kind of strange (at least as strange as redefining Float#to_s), so I went looking for something else...
And then I remembered that I could just use Ruby's ability to have one method answer all messages sent to the object (think .NET/Java transparent proxy kind of thing only easier):
class CodeCamp
def self.method_missing( name, *args )
puts "#{name.to_s} Code Camp #{args[0]}"
end
end
CodeCamp.Portland(1.0)
Now, that is getting good. But it also has some problems. It doesn't work well with cities like San Fransisco. You could make the city name CamelCase or use an underscore, and then it doesn't really print right.
I really wanted to use CamelCase (aka WikiWord formatting) to do it, but the regex got annoying and let's not forget that the underscore is very Ruby-ish, even if us .NET programmers find it annoying.
So I ended up with this, which I think is my final answer:
class CodeCamp
def self.method_missing( name, *args )
puts "#{name.to_s.sub("_", " ")} Code Camp #{args[0]}"
end
end
CodeCamp.Portland(1.0)
CodeCamp.San_Francisco(1.5)
CodeCamp.Boston(2.0)
Five lines of code. Not bad.
(Of course you could wrap it up in a Community module (namespace), but I just didn't see the point.)