Why Ruby Is My Favorite Programming Language by Vincent Foley-Bourgon
Introduction ------------
Those who know me a little know that I enjoy scripting and programming a lot, and that I like to do these tasks in Ruby as much as possible. Some people wonder why Ruby? Wouldn't I be more "marketable" if I was a Java, VB or C# programmer? Wouldn't I be more hardcore if I coded in C or in C++? Sure, maybe I would be, but Ruby remains my favorite language for a multitude of reasons that I want to discuss here.
Scripts -------
When I learned Ruby for the first time, back in 2001, it was because I wanted to write scripts that were beginning to be too hard to do with bash scripting. I admit whenever I needed to use things like cut, sed or awk, I felt quite intimidated. I had heard a guy mention how much he liked this new scripting language called Ruby on now defunct LinuxNewbie.org. He seemed pretty excited about it, so I decided to investigate. I read a few testimonials of people who thought it was a very good, clear and easy language and how it helped them write scripts. Since that was my intent, I decided to give it a try. With the help of klamath from LNO, I slowly learned the language and I was really impressed by just how easy doing useful things was. The syntax was nice, my editor of choice at the time (vim) supported it nicely. Before long, I was writing semi-interesting scripts, little programs that made my life easier, I learned regular expressions with Ruby and it was all very fun. I also got two books, the first edition of the PickAxe and the Ruby Way to help me progress. Since then, I've added the second edition of the PickAxe to my
After leaving Ruby for about two years to investigate other languages (Python, O'Caml, Common Lisp, Smalltalk), I came back to the little japanese gem, because although the other languages were really interesting -- particularily Smalltalk -- I felt that Ruby was the best fit for me. So I started writing Ruby code again, and this time a lot of cool new stuff was available. First, the new version of Ruby (1.8) was out, Rails was just released, Gems was becoming more and more popular, the community had grown. So I was thrilled to be back.
I wrote a bunch of scripts for myself and for friends: scripts to create an RSS feed of my favorite web comics, a script for a friend to analyze his log files, one to make a nice HTML document out of a Ruby file (thanks to the syntax module by Jamis Buck, although I take credit for ripping the nice colors of inkpot, a vim color file, and bringing that to CSS), a small script to get the current song in Xmms to annoy people with the music I'm listening to on IRC.
ShortURL: The little train that could -------------------------------------
I've been chatting with my good buddy Joel for more than five years now, we started with email, went to ICQ, then IRC, then back to crypted emails, AIM, IRC again, silc, IRC again and Skype. A while back, Joe started being annoyed when I posted long links on IRC, because he uses a client (irssi) that can't click on the links, so he had to copy the long, long URL and he thought it was annoying and he kept telling me to give him tiny URLs. However, I was not thrilled at the idea of copying a link, going to tinyurl.com, pasting the link into the little box, pressing Enter, waiting for the new URL to be generated, copying the short URL and then pasting it on IRC. Since I was into a big scripting phase with Ruby, I decided I would make a script that I would call with the /EXEC command in my IRC client, so it would be fast for me and Joe wouldn't whine so much.
I quickly got a script together and it worked nicely. I decided to post it to comp.lang.ruby (http://rubyurl.com/9tg3D), thinking some people might find it useful. The first response came from James Britt who suggested that I change tinyurl.comw with rubyurl.com, a Rails-based URL shortening service. I quickly added rubyurl.com to my script, however I quickly realized that it would be very nasty to have more services, because there was no abstraction at all. Here's how the first version of rubyurl looked:
def self.rubyurl(url)
Net::HTTP.start("rubyurl.com", 80) { |http|
response = http.get("/rubyurl/create?rubyurl[website_url]=#{CGI.escape(url)}")
if response.code == "302"
body = response.read_body
regex = /<a href="(.+)">/
return regex.match(body)[1]
end
}
end
Not too cute, is it? Devin Mullens posted a method to abstract the process a bit, although the code was not much cleaner either:
def self.get_short_url(hostname, code, action, &block) # :yields: html_body
begin
Net::HTTP.start(hostname, 80) { |http|
response = http.instance_eval(action)
if response.code == code.to_s
block.call(response.read_body)
end
}
rescue SocketError, Net::HTTPExceptions => e
return nil
end
end
def self.rubyurl(url)
get_short_url("rubyurl.com", 302,
"get('/rubyurl/create?rubyurl[website_url]=#{CGI.escape(url)}')") { |body|
short_url = URI.extract(body)[0].gsub("rubyurl/show/", "")
return short_url
}
end
More abstract, but doesn't help maintenance much? I thought about this problem and I thought about how I could do it, and that's when I had the idea of a service class. With this new Service class, I could write rubyurl like so:
class Service
attr_accessor :port, :code, :method, :action, :field, :block
def initialize(hostname) # :yield: service
@hostname = hostname
@port = 80
@code = 200
@method = :post
@action = "/"
@field = "url"
@block = lambda { |body| }
if block_given?
yield self
end
end
def call(url)
Net::HTTP.start(@hostname, @port) { |http|
response = case @method
when :post: http.post(@action, "#@field=#{url}")
when :get: http.get("#@action?#@field=#{CGI.escape(url)}")
end
if response.code == @code.to_s
@block.call(response.read_body)
end
}
end
end
rubyurl = Service.new("rubyurl.com") { |s|
s.code = 302
s.method = :get
s.action = "/rubyurl/create"
s.field = "rubyurl[website_url]"
s.block = lambda { |body| URI.extract(body)[0].gsub("rubyurl/show/", "") }
}
This made adding new services much easier, because I had default values so I just need to to specify the things that are different from my original model. Some services are just one line long, the block. This made maintenance easy and addition of new services very fast. Usage is equally simple, take your object, invoke its call method with the URL you want to shorten and there you go.
With such a nice model, the number of URL shortening services started increasing quite rapidly: the latest version on RubyGems has 16, and my personnaly repository has 17 services, and the code with the comments is still under 200 lines.
I think the important thing that ShortURL has taught me about Ruby is that it's really appropriate for bottom-up development. I initially started this project just to make a friend shut up, but it has grown beyond what I expected when I wanted Joe to quit his whining.
A few other things made ShortURL easy to develop. Dynamic typing, so I did not have to convince the compiler I was right. Interactive development (irb) with a tight integration with Emacs meant that I just had to do a key combination (C-c C-v) in my shorturl.rb buffer to send the whole thing to the running irb process and I could start testing right away. The excellent Ruby libraries made my life easy too, the HTTP library was simple and powerful and it didn't take me long to figure it out and forget about it. Ruby blocks were also a big help: they're often mentionned as one of the reasons why Ruby is so fun and powerful and many people wonder why. Just look at the code above, and you have two examples of blocks: I use them to override the default Service attributes and I use them to specify how to retrieve the shortened URL in an HTML page.
I once wondered how ShortURL would look like if I'd written in in C#, however I stopped thinking about that when the words "static typing" popped into my mind ;)
Conclusion ----------
Give Ruby a try, I don't think you'll regret it. You can start with a small script to fix an itch you have, but you can also grow that script into a full-blown powerful program. Ruby is equally well suited for both small and large projects. And as is often said of Ruby: it's fun!! You want to do fun things, right?