CS 214 Lab 9: Ruby


As a modern object-oriented language, Ruby supports the goals of modularity in multiple ways, including its class mechanism and its module mechanism. Today's exercise examines each of these.

Begin by making a ruby subdirectory within your labs/09 directory, and copying the file nameTester.rb from the labs/09/ruby/ directory into your subdirectory. Open it up using your favorite text editor and take a few moments to familiarize yourself with its contents, customize its documentation, etc. Then save and run it and verify that it works correctly.

Classes

In Ruby, instance variables and class variables are private by default. You access them through accessor methods of the same name and you have to explicitly specify any variables that are public. In this way, Ruby is similar to one of its parent languages Smalltalk. All instance variables are (by default) private and all methods are (by default) public.

Separate compilation is not (generally) an option for Ruby, because Ruby is an interpreted language, meaning it is compiled and run on the fly. However, as Ruby becomes more popular and programmers want better performance, people are developing Just-In-Time (JIT) compilers for Ruby. Since is ongoing research, we won't address it in this course.

So what does all of this mean for our Name class? Well, at the very least, we'd like this class to be reusable by other programs. To accomplish this in Ruby, there are a couple of different approaches:

1. The require Approach If we put the Name class into a separate file called Name.rb, we can then use the require method to import this code into our current program. The require method is used to load another file and execute all its statements, which will import all class and method definitions in the file. In addition to simply executing all of the statements in the file, the require method also keeps track of which files have been previously imported, and thus will not import the same file twice.

The require method takes the name of the file to require, as a string, as a single argument. This can either be a path to the file, such as ./lib/some_library.rb or a shortened name, such as some_library. If the argument is a path and complete filename, the require method will look there for the file. However, if the argument is a shortened name, the require method will search through a number of pre-defined directories on your system for that file. Using the shortened name is the most common way of using the require method.

Try using this approach for the Name class and driver program. Put the class in its own file called Name.rb and then import this file into the driver using the require method. Note that you can enclose the name of the file to require in either single quotes or double quotes.

2. The load Approach The load method works very similarly to the require method, in that it causes all of the statements in the file to be executed. There are few differences though:

Modules

(From the ruby-doc tutorial on modules) In Ruby, modules are a way of grouping together methods, classes and constants (like the C++ namespace and the Java package). One of the major benefits of a module is that it defines a namespace and thus can help prevent name clashes.

As you start to write bigger and bigger Ruby programs, you'll naturally find yourself producing chunks of reusable code -- libraries of related routines that are generally applicable. You'll want to break this code out into separate files so the contents can be shared among different Ruby programs.

Often this code will be organized into classes, so you'll probably stick a class (or a set of interrelated classes) into a file. This is the approach we will take for our Name class. There are also times when you want to group things together that don't naturally form a class. An initial approach might be to put all these things into a file and simply load that file into any program that needs it. This is the way the C language works. However, there's a problem. Say you write a set of trigonometry functions sin, cos, and so on. You stuff them all into a file, trig.rb, for future generations to enjoy. Meanwhile, Sally is working on a simulation of good and evil, and codes up a set of her own useful routines, including beGood and sin, and sticks them into action.rb. Joe, who wants to write a program to find out how many angels can dance on the head of a pin, needs to use both trig.rb and action.rb in his program. But both define a method called sin. Bad news.

This where where the module mechanism comes in. Modules define a namespace, a walled-off sandbox in which your methods and constants can play without having to worry about interfering with other methods and constants. The trig functions can go into one module:

module Trig
  PI = 3.141592654
  def Trig.sin(x)
   # ..
  end
  def Trig.cos(x)
   # ..
  end
end

and the good and bad action methods can go into another:

module Action
  VERY_BAD = 0
  BAD      = 1
  # ...
  def Action.sin(badness)
    # ...
  end
end

Module constants are named just like class constants, with an initial uppercase letter. The method definitions look similar, too: these module methods are defined just like class methods. If a third program wants to use these modules, it can simply import the correct files (using the Ruby require statement) and reference the qualified names:

require "trig"
require "action"

y = Trig.sin(Trig::PI/4)
wrongdoing = Action.sin(Action::VERY_BAD)

As with class methods, you call a module method by preceding its name with the module's name and a period, and you reference a class or constant using the module name and two colons.

Refactor your code to wrap the Name class in a module named Names, and then use it within your driver.

Testing. Test your changes and continue when your program works correctly.

Turn In: Use script to create a file in which you list each of your Ruby source files, and then run your test program.

That concludes the Ruby part of this lab.

If this was your last exercise, return to the lab 09 page and follow the "Turn In" instructions there.


Calvin > CS > 214 > Labs > 09 > Ruby


This page maintained by Joel Adams.