Articles / The Scalability of Ruby

The Scalability of Ruby

There are lots of reasons to like Ruby. It's a pure object oriented language. The syntax is elegant, and the use of blocks creates a novel feel. Another reason to like Ruby is its scalability. I don't mean scalability in the performance sense, but in regards to how you can code simple Ruby macros to solve small problems and also use Ruby in its object oriented form to support very large or complex applications.

The scalability of a language is a critical factor in understanding how broad of a range of applications it can support. How many times have you started with a ten line macro, only to end with a 15,000 line GUI application as the true requirements were uncovered? A scalable language reduces the barriers to scaling an application from 10 lines to 15,000 lines.

For this article, I will scale the solution of a simple problem to show the various forms of Ruby, from a simple scripting language to one which supports functions, libraries, and, finally, object oriented programming. For the problem, I'll use a simple implementation of the Unix "cat" command. "cat" outputs the contents of all of the files specified on the commandline to STDOUT.

Macros

First, here's a simple macro solution:


ARGV.each{ |fileName|
    inHandle = File::PrivoxyWindowOpen( fileName )
    inText = inHandle.read()
    print inText
}

This iterates through each item of the commandline argument array, opens the file, reads the contents, and prints it. A shortened form looks like this:


ARGV.each{ |fileName|
  print File::PrivoxyWindowOpen( fileName ).read()
}

What about errors? Ruby has easy exception handling built in; here's an example:


ARGV.each{ |fileName|
  begin
    print File::PrivoxyWindowOpen( fileName ).read()
  rescue
    print "Couldn't open file '" + fileName.to_s + "'\n"
  end
}

If any of the expressions with the begin block raise an exception, the block will be terminated and the rescue portion of the block will be called. In this case, we take any exception to mean that the file could not be opened for some reason.

Functions

The next example is a function-based solution:


def print_file( fileName )
  print File::PrivoxyWindowOpen( fileName ).read()
end

ARGV.each{ |fileName| print_file( fileName ); }

First, we define a function named "print_file" that takes one string, a filename, opens the file, reads the contents, and prints it. As above, we iterate through the commandline argument array, but in this case, we send the fileName string to the print_file function.

Libraries

I implement the library approach in this example using a static member function of a class:


class FilePrinter
  def FilePrinter::print_it( fileName )
    print File::PrivoxyWindowOpen( fileName ).read()
  end
end

ARGV.each{ |fileName|
  FilePrinter.print_it( fileName )
}

FilePrinter::print_it is really just the print_file function above, but it is wrapped in the FilePrinter class for some sense of namespace containment.

Objects

An object oriented approach is shown below (even though an OO solution to the "cat" problem is probably overkill...).


class FilePrinter < File
  def print_it()
    $stdout.print read()
  end
end

ARGV.each{ |fileName|
  FilePrinter.open( fileName ).print_it()
}

We extend File into FilePrinter and add the print_it method to it. As we go through each commandline argument, we create a FilePrinter object (instead of a File object) and send the print_it message to it.

Note the use of "$stdout.print" as opposed to just print. The File class supports a print method, but would print to the file instead of to STDOUT. We use the global $stdout to tell Ruby exactly where to print the contents of the file.

Another approach to the object oriented solution is to encapsulate a file within our object instead of extending the file object:


class FilePrinter
  def initialize( fileName )
    @fileName = fileName
    @text = ""
    read()
  end
  def print_it()
    print @text
  end
private
  def read()
    @text = File::PrivoxyWindowOpen( @fileName ).read()
  end
end

ARGV.each{ |fileName|
  FilePrinter.new( fileName ).print_it()
}

The "FilePrinter" class in this example derives directly from "Object" instead of File. It has two member variables, @fileName and @text. When the FilePrinter object is initialized, it sets the @fileName member variable and calls a private read method. Implicit support for access control is unique to Ruby when it is compared with languages like Python and Perl.

Conclusions

While there is no one programming language that can be used to implement every application, Ruby does have the flexibility to scale all the way from simple macros to a large and complex set of classes. This flexibility is one of the attributes that makes Ruby such a fun language to learn and to work in.

Recent comments

06 Dec 2002 18:51 Avatar bmelli

Re: Perl Examples

>
> My experience with perl is that people
> are tired of new releases breaking their
> code..
>


There are a few other issues with Perl besides that:
Whenever you start with a simple language and then
add a bunch of features not planned for at the begining
you end up with corner cases and gotchas.
I once wasted a log of time tracking a bug from a
customer script. Due to reference, a destructor for an
object was being called twice. Once by the user on the alias, and once by perl itself when the object got out of scope. That second call messed the Perl interpreter internal data structures and the script crashed much
later. (But since I built the Perl interpreter all the fingers
pointed to me :-)
You can argue that the programmer didn't know what he was doing, but I would argue that a languages that
do garbage collection for you but forces you to
understand how the underlying data structures work is
dangerous.
In that respect, Ruby is a much safer language and much
easier to learn: Everything is a reference to an object. Done.
I put Perl in the same basket as C++: A language that
has a lot of good features, but which also has too many
warts added to its core. Too many little things get
in the way. You have to watch context when using
things like arrays. You have to know that something is a hash, something is an array, something is a scalar...
Too many little rules.
In that respect, Ruby is much more elegant and simple to
learn, at least if you've done a lot of OO programming.
It's like Smalltalk without the weird environment.
Much better for rapid prototypes than something more rigid
like C++: You don't even have to stub a function. You
just call it, and if the object doesn't support it you get
an exception.

bruno.

03 Aug 2002 15:04 Avatar apag

Re: Perl

> The 7-line Ruby example is doing
> exception handling. You are not.
> Your Perl is not doing the same thing.


Wrong. &lt;> will handle errors automagically.


> % The code:
> % sub printfile
> % {
> % local @ARGV = @_;
> % print &lt;&gt;;
> % }
> % printfile @ARGV;
> Thanks for demonstrating that your Perl
> function is longer and far less obvious
> as to what it's doing. Any cretin can read
>
> print File::open( fileName ).read()
>
> and understand it is opening a file and
> reading it.


Maybe Juerd should have duplicated the Ruby version closer:
sub printfile {
open FH, "
print while &lt;FH&gt;;
}
printfile $_ for @ARGV;
which can still be written in a half dozen slightly different ways, each of which can be about twice as verbose as well as half as verbose as the given example, if you so chose. Now what was your point, again?


> % &quot;Libraries&quot; - There is no point in
> % having doing simple things in such a
> % complex way.
> % The code:
> % ...
> ... is hardly less complex, even
> ignoring the line noise with magic
> tokens that implements your functionality.


Are you intentionally missing the point? The "complexity" Juerd was referring to was in creating a library in the first place to solve such a simple problem, and not implying that the Ruby version was much more complex than the Perl one.


> % &quot;Objects&quot; - The author finally
> % discovered that some things are
> % overkill. Oh well, here goes:
> No, the author simply states that an OO
> version of cat is probably overkill,
> considering the size of the OO version
> compared to the initial macro that does
> the same thing.
> Read: this is an example of why object
> oriented design is not the be all and
> end all of programming.


Duh. That's exactly what Juerd was saying - the author finally discovered that using OO to write cat is indeed overkill considering the size of the OO version compared to the initial macro that does the same thing. As was using a function and then a library for this job.


> and your Perl version did not
> demonstrate inheritance (a very base OO
> principle) as the Ruby version did.


Obviously, since in Ruby everything is based on objects you cannot not use inheritance, so in which way did the sample code demonstrate a feature?

03 Aug 2002 14:37 Avatar apag

Re: How does any of this show Ruby is scalable?

> mostly because of better library and application support. And that's not fair, because libraries aren't the same as languages, but life isn't fair :)

Library and application support is merely a function of time of existance multiplied by community size. The first factor is unchangeable, but the latter depends on the language. I don't see the Python community being very large, so if Ruby really is all that it's made out to be, it shouldn't have trouble catching up to Python in the mid-term.

03 Aug 2002 14:30 Avatar apag

Re: Perl

> I can read the ruby code without any problems
> I can not read the perl code


That's because the Perl examples were written with deliberately little red tape. They can be written with as much red tape as the Ruby examples.

print &lt;>;

can also be written as

foreach my $filename (@ARGV) {
    open FH, "
    @lines = &lt;FH>;
    print @lines;
}

Just because the language allows you to use less red tape doesn't mean you have to forgo it. With most languages, however, you don't have the choice in the first place, and are stuck spelling the same mindnumbing things out over and over and over. Perl does not force anything upon the programmer, it lets them be as non-/verbose as desired. The quality and readability of Perl code depends on the programmer's skill.

That means Perl is a bad choice for newbies. It also means it's an excellent choice for seasoned programmers who know to write clean code without the need for a bondage and discipline language's confines.

03 Aug 2002 14:05 Avatar apag

Re: Perl
More red tape does not equal readability.

Screenshot

Project Spotlight

Kigo Video Converter Ultimate for Mac

A tool for converting and editing videos.

Screenshot

Project Spotlight

Kid3

An efficient tagger for MP3, Ogg/Vorbis, and FLAC files.