Troubleshooters.Com, Code Corner and Ruby Revival Present

Ruby the Right Way
Copyright (C) 2005 by Steve Litt
Note: All materials in Ruby Revival are provided AS IS. By reading the materials in Ruby Revival you are agreeing to assume all risks involved in the use of the materials, and you are agreeing to absolve the authors, owners, and anyone else involved with Python Patrol of any responsibility for the outcome of any use of these materials, even in the case of errors and/or omissions in the materials. If you do not agree to this, you must not read these materials.
To the 99.9% of you honest readers who take responsibility for your own actions, I'm truly sorry it is necessary to subject all readers to the above disclaimer.


CONTENTS

About Ruby the Right Way

Rubyesque ways of doing things fall into two catagories:
  1. Syntax optimizations
  2. Style differences (Ruby culture)
Most of the syntax optimizations help you code quickly and accurately. Use them. Ruby iterators are often easier than for or while loops. A single Ruby attr_accessor line can replace 10 subroutines in certain cases. Use this stuff. Almost always.

ALMOST Always?

Yes, almost always. Every language gives you syntax capabilities you might not want to use, usually for readability's sake or for the sake of program-as-documentation. So as not to pick on Ruby, let's take this line of C code:
for(p = buf; (*p = ((p == buf) || *(p-1) == ' ' ? toupper(*p) : *p)) != '\0'; p++);
Isn't it nice that C enables you to put so much power in a single line of code? Quick: What does the preceding line of code do?

Now let's look at the same functionality, written for readability:
for(p = buf; *p != '\0'; p++) {
	if(p == buf)
		*p = toupper(*p);
	else if(*(p-1) == ' ')
		*p = toupper(*p);
}
It converts a string to title case. In the latter's for loop, the first part does nothing but initialization, the middle part does nothing but comparison, the last part does nothing but incrementation. The actual data processing is done in the block associated with the for loop, and it's plain to see that the first line of the block uppercases the first character of the string, and the second line of the block changes to uppercase any character preceded by a space.

Compare that with the former's for loop, where all data processing is included in the comparison section of the for loop. The former uses less characters, less lines, and on an ancient non-optimizing compiler is slower, but on modern optimizing compilers runs at the same speed (the proof).

I would NEVER code the one liner. I'm not a genius, and don't instantly understand it. I'm sure the average maintenance programmer following me wouldn't understand it. And yet, a sizeable contingent of the C community thinks it's really kewl to code like that. Many imply that if you don't do the one liner loops, you're not really a C programmer -- you're really a Pascal speaking quiche eater.

Whatever.

Personally, I like my code to be readable. Beyond that, I like my code to serve as documentation. I'll take the 4 line algorithm every time.

A Ruby Example

Ruby has something called "parallel assignment", by which you can assign one comma delimited list to another, with corresponding variables on the left side getting corresponding values from the right. The following example uses parallel assignment to perform the initialization, and in addition adds one attribute, linewidth, that is not initialized during instantiation. Here's the code:

#!/usr/bin/ruby

class Rectangle
def initialize(y, x, height, width, linecolor, fillcolor)
@y, @x, @height, @width, @linecolor, @fillcolor = y, x, height, width, linecolor, fillcolor
end
attr_accessor :y, :x, :height, :width, :linecolor, :fillcolor, :linewidth
end

rect = Rectangle.new(10, 20, 50, 60, "blue", "yellow")
rect.linewidth = 4
puts rect.x.to_s + ", " + rect.fillcolor + ", " + rect.linewidth.to_s

Being a big fan of code-as-documentation, I prefer to code it without the parallel assignment:

#!/usr/bin/ruby

class Rectangle
def initialize(y, x, height, width, linecolor, fillcolor)
@y = y
@x = x
@height = height
@width = width
@linecolor = linecolor
@fillcolor = fillcolor
@linewidth = 1
end
attr_accessor :y, :x, :height, :width, :linecolor, :fillcolor, :linewidth
end

rect = Rectangle.new(10, 20, 50, 60, "blue", "yellow")
rect.linewidth = 4
puts rect.x.to_s + ", " + rect.fillcolor + ", " + rect.linewidth.to_s

My method uses more lines of code, but, at least to me, I see at a glance what the instance variables are. Another thing I did was instantiated @linewidth to a sane default, right in the initializer. Even if I preferred @linewidth to be nil, I'd assign it to nil in the initializer so a future programmer (maybe even me) could see at a glance that it was there.

Yes, I know that instance variables don't need to be declared until use. Yes, I know that I can see evidence of @linewidth in the attr_accessor line, but I still feel it's better documentation to list and initialize it in the initialize() routine.

Parallel assignment is certainly not the readability gaffe that the earlier described C one liner was, but in my opinion it should be used sparingly.

Now that I've described a Rubyism that I don't like, let me show you all the Rubyism's I do:

Underscores, Not Camel Case


After doing some Java programming, I got in the habit of naming variables mySpecialVariable. If you're going to be showing off your Ruby code in a Ruby community, or collaborating on a Ruby project, mySpecialVariable will be disparaged as "Camel Case", named after the fact that it has humps. Ruby programmers name it like this:  my_special_variable.

I asked around the Ruby list, and it seems like the main reason is that separation by underscore is much more readable for non-English speakers. Many Ruby users are non-English speakers. A lot are Japanese. Ruby originator Yukihiro Matsumoto is Japanese. So although the computer understands mySpecialVariable as well as my_special_variable, the Ruby community prefers the latter.


Troubleshooters.ComCode Corner * Linux Library