Ruby: First Impressions

Well, it’s effectively killed Perl. Ruby, that is. That was my first reaction.

This past weekend, I had some small digital housekeeping work to do involving some manipulations in a MySQL database. Mundane stuff: get records from database, perform some string manipulations, insert new records. Historically, my language of choice for this type of work has been Perl. Since it is now 2005, I guess that means that I have about 10 years of experience using Perl, so it’s fair to say that I can slice and dice with it pretty well. I don’t have to think too hard.

Since I had some spare time on my hands and because my last conversation with Pate about Ruby has been echoing in my head, I figured that it was about time to give it a whirl. A new language obviously has an associated ramp-up cost, but Ruby’s is pretty easy, especially if you’re fluent in both Perl and Java – it really felt like I was halfway between as I went about my little task.

I made it interesting by applying some of the patterns and more traditional OO stuff that I am accustomed to in order to see how they would fit in to the Ruby world, so it involved using the Ruby MySQL support, some of my own classes and a bunch of the built in classes that Ruby comes with.

Their module documentation is pretty good, but they also have a nice tutorial online that you can use to get your feet wet. Good documentation doesn’t mean having lots of documentation: it means having the right amount of well-written tutorials and reference guides that are easy to understand. My experience was pretty good in this regard.

Some of the nice rubyisms are nothing more than syntactic sugar… cute, but not necessary. Specifically, there are shortcut mechanisms for generating accessor and mutator methods (in Java, we call these getters and setters):


class Song
attr_reader :name, :artist, :duration
end

This creates getters on the Song class for the name, artist and duration fields. There are shortcuts for setters as well as both getters and setters.

Or this one, that iterates through a series of numbers from 0 to n:


for i in 0...@recurs.length
r = @recurs[i]
eulexin
...
end

If you’re from the Kingdom of Perl, you already knew what this meant. But also kind of pythonesque, too, without semicolons at the end of each line.

Like I said: cute. But not going to make me switch.

More familiarity: Ruby’s begin/rescue/ensure syntax is similar to Java’s try/catch/finally construct, so it was easy to make sure my database connection was cleaned up properly if there was a problem executing. Syntactically, it just seemed like Perl without the ‘$’ char in front of scalar $variables.

Now, when you’re writing code, you need something to write it in. If I’m hacking Perl or just doing some quick job, then I’ll use emacs or vi. However, this is just an editor. My weapon of choice for my Java code is Eclipse. Let me clarify this, in case anybody has misunderstood my position on Eclipse: Eclipse is a Big Deal. Those who feel that you can productively write Java code using a text editor and javac are dreaming. It just ain’t so. Now, the cool thing about Eclipse is that it’s fairly generic and supports other languages (E-P-I-C provides Perl support, but I haven’t tried it). And, yes, there is support for Ruby… but it’s not great. You basically get syntax highlighting and the ability to run Ruby scripts right within Eclipse and watch the output on the console.

There’s no refactoring. There’s no autocomplete. There’s precious little in the way of parse errors, warnings or other guidance. I can’t walk the call hierarchy. I certainly can’t use FindBugs. Maybe someday there will be all of this, but it’s not there right now. Years ago, when I started doing more Java, I was sorely missing Perl’s CPAN. In case you don’t know, it’s a tool for managing your perl installation and performing module installations and also for discovering new modules. So if you’re working on a project and want to work with, say, sending email or something, you can use cpan to find and install modules that might be helpful to you. My affinity to CPAN pales in comparison to my affinity to Eclipse.

So while I can’t write Ruby code in Eclipse using all the bells and whistles, Ruby does have a CPAN analog called gems, which I didn’t use, but it was nice to see it.

Why did I say that Ruby has basically killed Perl? It’s because the Perl community has completely failed to articulate a way to build web applications. I guess I should count myself among those who have failed in this regard, because I didn’t come up anything either 😉 Yes, there’s mod_perl, but that’s just basically giving you the ability to write Apache modules in Perl instead of C. Want something more advanced? Well, Mason sits on top of mod_perl… so does Embperl and most of the other usable options. The problem with mod_perl is that it exists between whatever Apache is doing and whatever Perl is doing: it’s dependent on stability in both. Perl is in the process of a huge transition from Perl 5 to Perl 6. Apache successfully transitioned from 1.3 to 2.0, but mod_perl lagged behind for so long that people had plenty of time to consider all the potential threading problems that were going to occur. I’m not sure where that debate ended up. I tuned out.

The big buzz surrounding Ruby these days is Ruby on Rails; some people are making noise about the runtime performance as well as the ease with which web apps can be built. It’s conceptually similar to Struts and Hibernate, but it’s definitely nowhere near as mature. In any event, this is the Ruby community’s effort and it’s a great one. I wish the same could have been said for Perl.

In spite of all the Ruby joy that I’m spouting, I’m not yet convinced of Ruby’s place in enterprise or distributed environments yet. The only two current options in the enterprise space are .NET and J2EE. Sun did an absolutely horrible job describing Java to the world because they used the same word (Java) to describe three things: the platform, the language and the virtual machine. Microsoft was smarter about it, so they have .NET, C# and the CLR. Now, if you decide that C# or JavaTheLanguage aren’t your cup of tea, you can still write code in any language that compiles to bytecode or IL. Say, using Python. Or Scheme. I’m fairly sure that there’s no such ability in the Ruby world. And that’s a big deal. If you’re going to make a 15 year investment in a platform and commit hundreds of developers to it, you’re going to look for that level of flexibility and abstraction.

Furthermore, the big enterprise stuff doesn’t exist yet in Rubyland: distributed transactions with XA? Message queues with a unified interface? Concurrent programming libraries? The list here could go on and on.

Time will cure these shortcomings.

So Ruby’s good. I’m into it. My first experience was good, which means that I’ll certainly have a second experience.

14 thoughts on “Ruby: First Impressions”

  1. You can find better Ruby documentation, including API docs for the Ruby 1.8.2 core and standard libraries, at htpp://www.ruby-doc.org

  2. btw, the proper way to iterate an array in Ruby is with #each or #each_with_index

    [1,2,3,4].each_with_index do |element, index|
    puts ‘holy smokes’ unless element – 1 == index
    end

    # should print nothing.

  3. Phil, is it possible that you are in a state of denial? The “syntactic sugar” may be the cure for Perl induced hypoglycaemia! And Java has too many high density carbs, can you really stand using the decorator pattern to open a file? But I can’t really judge, I’ve been suckered in by all the OS X eye-candy and use 4D cause its just so darn easy to get a cross platform UI. Think of Ruby more in terms recreation.

  4. Mel, design patterns are langugage independent constructs. But, yes, if my job was to write code that opened files, I wouldn’t use java.

    Me, why is your approach to iteration the proper way? If you’re iterating through an array of integer values, I think my approach works fine. Am I missing something?

  5. If you ever need to write a C extension, you’ll appreciate Ruby even more when compared to the mess that is XS.

  6. Philip, Me’s approach for iteration is more correct when talking in terms of Ruby. In the ruby community there seems to be a preferred ‘ruby way’ to do basic tasks do things. As you mentioned in your blog/article, it is more of syntactic sugar. Your example gets the job done, but it also takes way to many characters. Imagine you had: ‘

    for i in 0…@recurs.length
    r = @recurs[i]

    end

    Now see the ruby way:

    @recurs.each { |r| … }

    or

    @recurs.each do |r| … end

    If you need multiline support you can do that:

    @recurs.each { |r|

    }

    or

    @recurs.each do |r|

    end

    It saves you typing and reduces two unnecessary lines to 1 line. I prefer the ruby way in this case.

  7. You have missed some ruby features in your evaluation that may have lead you to some erroneous conclusions:

    – jruby (like jython)
    – ruby’s builtin concurrent programming libraries
    – the ruby queue library (http://www.linuxjournal.com/article/7922)

    For me, what ruby has over perl is the ease for OOP. I’m highly surprised that you didn’t mention that. The syntax is ‘cute’, as you say, but the ability to easily create/extend classes and the like really is more important IMHO.

    Perhaps with a more in-depth analyis of ruby, the real advantages it has will emerge. ‘Ruby first impressions’ is a good title for your entry for that reason, just be wary of what conclusions you arrive at with only a ‘first impression’. You really do take your conclusions a little too far for so little experience.

  8. Why use “each” for iterators? Because you don’t need the index, so fewer useless variables littering your code.

    list = [“dog”, “cat”, “weasel”, “fish”, “canary”, “monkey”]
    list.sort.each do
    |animal|
    puts “would you like to pet my #{animal}?”
    end

    The only variable there is “animal”, there is no “i”, this makes it much easier to have nested looping constructs, and to copy and paste these loops around all over the place. If you have to use “i” in your loops, when you stick it into another loop, you have to remember to change that “i” to a “j”.

    The bit between “do” and “end” is a block, and they’re also really useful for doing things like connecting to databases. Rather than knowing about “begin”, “rescue”, “ensure”, “end”, you can let that be handled inside the DB layer:

    db.connect(foo, bar, baz) do
    |handle|
    handle.do_something()
    end

    If do_something() makes the database throw an exception, the db wrapper can clean it all up and you just get to “end” cleanly.

  9. Dan,

    The only conclusion that I’ve made about Ruby so far is this one:

    So Ruby’s good. I’m into it. My first experience was good, which means that I’ll certainly have a second experience.

    Zdennis,

    Thanks for your interesting thoughts about list iteration. What I’m seeing is again reminiscent of one of the differences between Perl and Java: the size of the language. Java is actually a much smaller language than Perl is, precisely because it doesn’t have built-in support for things like iteration… all of Java’s cool features are in classes that exist outside of java.lang (like java.util’s collections, iterators, etc.) while Perl has all of this stuff baked into the language as builtin functions (keys, each, etc.). Notice again how I’m not drawing any conclusions about Ruby but rather am making a general comparision of the design of the language.

  10. Gotcha Philip. Glad to hear it.

    Hope I didn’t scare you off with the…intensity…of my first response. I saw these lines:

    > I’m fairly sure that there’s no such ability in the Ruby world.
    > …the big enterprise stuff doesn’t exist yet in Rubyland:

    and had to speak up. Guess they weren’t really ‘conclusions’ per say.

    Good luck, as you’ve probably noticed, there’s quite a community out there to support (chastize? 🙂 you in your ruby adventures.

    Dan

  11. two tiny little differences in the exception handling beetween java and ruby.
    First, in ruby you have a “rescue modifier”, wich means that where in java you’d do:

    Foo f=null;
    try{
    f= someDangerousCall();
    } catch (SomeException e){
    f= someThingElse;
    }

    in ruby you’d do:
    f= dangerous_call() rescue something_else

    The other nice thing is that, when you’re writing methods, the method block acts as an enclosing try/catch, so you would write:
    def foo
    bla bla bla
    rescue
    yuk yuk
    end

    where in java you’d write:
    Foo foober( ) {
    try {
    bla bla bla
    } catch(Excep e){
    yuk yuk
    }
    return something
    }

    and since the ruby stile is to write many little methods, you almost never use the begin/end thing

  12. Another way to iterate:

    for i in [1, 2, 3]
    # do something
    end

    or for a hash:

    for (key, value) in {:one => 1, :two => 2}
    # …
    end

    or:

    {:one => 1, :two => 2}.each do |(key, value)|
    # …
    end

    I think most ruby programmers will do this:

    list = [?dog?, “cat?, “weasel?, “fish?, “canary?, “monkey?]
    puts list.sort.map{|animal| “would you like to pet my #{animal}??)

    instead of:

    list = [?dog?, “cat?, “weasel?, “fish?, “canary?, “monkey?]
    list.sort.each do |animal|
    puts “would you like to pet my #{animal}??
    end

Comments are closed.