Note: For a more complete Ruby tutorial, download
Huw's free eBook (and source code), The
Little Book Of Ruby, from SapphireSteel Software. |
See also: Part Two of this series
You will be reading a great deal about
Ruby programming in Bitwise in the coming months. We
feel that Ruby is a language with huge potential. In
fact, in our opinion, it is one of the most exciting
languages to emerge in a long time. It has a simple
but flexible syntax; it can be used for creating standalone
or web applications; it combines the power of ‘scripting’ languages
such as Perl or Python with the elegance of a pure OOP
language such as Smalltalk. And, as an added bonus, it
doesn’t look like C!
With any new language
you have to start somewhere and ‘Hello
world’ is as good a place as any. Here is our first
Ruby program running inside the Ruby In Steel environment
for Visual Studio
Currently much of the excitement surrounding Ruby can
be attributed to a web development framework called Rails – giving
a combination which is popularly known as ‘Ruby
On Rails’. While Rails
is an impressive framework, it is not the be-all and
end-all of Ruby. Indeed, if you decide to leap right
into Rails development without first mastering Ruby,
you may find that you end up with an application
that you don’t even understand. Moreover, Rails
can be a good deal trickier to install than Ruby alone
(it needs a database such as MySQL plus a web server;
and it doesn’t work as seamlessly as one might
wish with Apache); what's more, online hosting of Rails
applications is currently offered by very few hosting
companies.
We shall certainly be looking at Rails later on. However,
in this series we shall be dealing exclusively with the
Ruby language itself and its standard libraries.
Install Ruby…
You can download Ruby – the interpreter itself
and its class library - from the Ruby site at: http://www.ruby-lang.org/.
For Windows users there is a one-click Ruby installer
with additional tools such as the FreeRIDE and SciTE
code editors and documentation at: http://rubyinstaller.rubyforge.org/wiki/wiki.pl
The SciTE editor includes a split-pane view so
that you can see your code on the left and run
your program on the right |
The first thing you have to know about the Ruby language
is that it’s easy to use. To prove this, let’s
look at the code of the traditional ‘Hello world’ program.
Here it is:
puts 'hello world'
That is it in its entirety. One method, puts,
and one string, ‘hello world’. No headers
or class definitions, no import sections or main functions.
This really is as simple as it gets. Download
our source files. Then load up the code, 1helloworld.rb,
and run it.
RUNNING RUBY PROGRAMS
It is often useful to keep a Command window open
in the source directory containing your Ruby
program files. Assuming that the Ruby interpreter
is correctly pathed on your system, you will
then be able to run programme by entering ruby <program
name> like this:
ruby 1helloworld.rb
Optionally, you may want to download the free
Ruby IDE, FreeRIDE.
This will automatically open a Ruby command window
when you run a program; note, however, it may also
shut down this window (somewhat too rapidly) if
a syntax error occurs.
Most of the screenshots in this article show
an early beta of Ruby In Steel 'Personal
Edition' – a
free Ruby IDE for Visual Studio 2005. The final
version of Ruby In Steel PE 1.0 was released after
this article was published. A commercial edition
of Ruby In Steel ('Developer Edition') has also
been released, which includes the ultra-fast 'Cylon'
debugger and powerful analytical IntelliSense capabilities.
To download, evaluate or buy Ruby In Steel,
go to the SapphireSteel Software site: http://www.sapphiresteel.com |
Having ‘put’ a string to the output (here,
our command window), the obvious next step is to ‘get’ a
string. As you might guess, the Ruby method for this
is gets. I’ve written a program that prompts
the user for his or her name – let’s suppose
it is “Fred” - and then displays a greeting: “Hello
Fred”. Load and run 2helloname.rb to try this out.
Here is the code:
print('Enter your name: ' )
name = gets()
puts( "Hello #{name}" )
While this is still very simple, there are a few important
details that need to be explained. First, notice that
I’ve used print rather than puts to display the
prompt. This is because puts adds a linefeed at the end
whereas print does not; in the present case I want the
cursor to remain on the same line as the prompt.
On the next line I use gets() to
read in a string when the user presses Enter. This string
is assigned to the variable, name.
Notice that I have not pre-declared this variable, nor
have I specified its type. In Ruby you can create variables
as and when you need them and Ruby ‘infers’ their
types. In the present case I have assigned a string to
name so Ruby knows that the type of the name variable
must be a string.
Note: Ruby is case
sensitive. A variable called
myvar is different from one called myVar. A variable
such as name in our sample project must begin with
a lowercase character (if it begins with an uppercase
character Ruby will treat it as a constant). |
Incidentally, the braces following gets() are optional
as are the braces enclosing the strings after print and
puts; the code would run just the same if you removed
the braces. However, Ruby is increasingly moving towards
the use of braces – particularly when passing arguments
to methods - and, in some cases, the interpreter will
warn you if you omit them.
The last line in our sample code is rather interesting:
puts( "Hello #{name}" )
Here the name variable
is embedded into the string itself. This is done by placing
the variable name between two curly braces preceded by
a hash (‘pound’)
character #{}. This
kind of ‘embedded’ evaluation
only works with strings delimited by double quotes. If
you were to try this with a string delimited by single
quotes, the variable would not be evaluated and the string ‘Hello
#{name}’ would be displayed exactly as entered.
It isn’t only variables which can be embedded
in double-quoted strings. You can also embed non-printing
characters such as newlines “\n” and tabs “\t”.
You can even embed bits of program code and mathematical
expressions. Let’s assume that you have a method
called showname, which returns the string ‘Fred’.
The following string would, in the process of evaluation,
call the showname method and, as a result, it would display
the string “Hello Fred”:
puts( "Hello #{showname}" )
See if you can figure out what would be displayed by
the following:
puts( "\n\t#{(1+2) * 3}" )
Now run the 3string_eval.rb program to see if you were
right.
Numbers are just as easy to use as strings. For example,
let’s suppose you want to calculate the selling
price or ‘grand total’ of some item based
on its ex-tax value or ‘subtotal’. To do
this you would need to multiply the subtotal by the applicable
tax rate and add the result to the value of the subtotal.
Assuming the subtotal to be $100 and the tax rate to
be 17.5%, this Ruby program (4calctax.rb) does the calculation
and displays the result:
subtotal = 100.00
taxrate = 0.175
tax = subtotal * taxrate
puts "Tax on $#{subtotal} is $#{tax}, so grand total
is $#{subtotal+tax}"
Obviously, this program would be more useful if it could
perform calculations on varying subtotals. Here is a
simple version of a Tax Calculator:
taxrate = 0.175
print "Enter price (ex tax): "
s = gets
subtotal = s.to_f
tax = subtotal * taxrate
puts "vat on $#{subtotal} is $#{tax}, so grand total
is $#{subtotal+tax}"
Here to_f is a method
of the String class. It attempts to convert the string,
s, to a floating point number. For example, the string “145.45” would
be converted to the floating point number, 145.45. If
the string cannot be converted, 0.0 is returned. So,
for instance, “Hello
world”.to_f would return 0.0.
Note: ‘Method’ is the object oriented
term for a procedure, function or subroutine which
is defined inside a class. In OOP terminology, you
don’t call functions and pass arguments to
them; you send messages to methods. The idea is that
an object – such as a string – has various
ways (i.e. ‘methods’) of responding to
messages that are sent to it. |
The problem with the simple tax calculator code shown
above is that it accepts minus subtotals and calculates
minus tax on them – a situation upon which the
Government is unlikely to look favourably! I therefore
need to check for minus figures and, when found, set
them to zero. This is my new code, which you will find
in 5taxcalculator.rb:
taxrate = 0.175
print "Enter price (ex tax): "
s = gets
subtotal = s.to_f
if (subtotal < 0.0) then
subtotal = 0.0
end
tax = subtotal * taxrate
puts "vat on $#{subtotal} is $#{tax}, so grand total
is $#{subtotal+tax}"
The Ruby if test is similar to an if test in other programming
languages. Note, however, that the braces are once again
optional, as is the then. However, if you were to write
the following, with no line break after the test condition,
the then would be obligatory:
if (subtotal < 0.0) then subtotal = 0.0
Putting everything on one line like this adds nothing
to the clarity of the code, which is why I prefer to
avoid it. My long familiarity with Pascal instinctively
makes me want to add a then after
the if condition,
however; but, as this really is not required, you may
look upon this as a wilful eccentricity of mine. The
end keyword that terminates the if block is not optional.
Forget to add it and your code will not run.
Instead of going through all the rest of Ruby’s
syntax - its types, loops, modules and so on (but fear
not, we’ll come back to those later) - let’s
wrap up for now with a look at how to create classes
and objects. It may seem no big deal to say that Ruby
is object oriented. Aren’t all languages, these
days? Well, up to a point. Most modern OOP languages
(Java, C++, C#, Object Pascal and so on) have a greater
or lesser degree of OOP features. Ruby, on the other
hand, is obsessively Object Oriented. In fact, unless
you program in Smalltalk, it is likely to be the most
object oriented language you have ever used. Every chunk
of data – from simple things like numbers and strings
to complicated things like modules and applications – is
an object. And almost everything you do with an object
is done by a method. Even operators such as plus + and
minus – are methods. Consider the following:
x = 1 + 2
Here + is a method
of the Fixnum (Integer) object, 1. The value 2 is sent
to this method and the result, 3, is returned and assigned
to the object, x.
Incidentally, the operator, =,
is one of the rare exceptions to the rule that “everything
you do with an object is done by a method”. The
assignment operator is a special built-in thingummy (not
the formal terminology, you understand) and is not a
method of anything.
Now let’s see how to create objects of our own.
As in most other OOP languages, a Ruby object is defined
by a class. The class is like a blueprint from which
individual objects are constructed. For example, this
class defines a dog:
class Dog
def set_name( aName )
@myname = aName
end
end
Note that the class definition begins with the keyword
class (all lower
case) and the name of the class itself, which must begin
with an uppercase letter. The class contains a method set_name.
This takes an incoming argument,
aName. The body of
the method assigns the value of aName to
a variable called @myname.
Variables beginning with the @ character
are ‘instance variables’ – that
means that they belong to individuals objects – or ‘instances’ of
the class. It is not necessary to pre-declare instance
variables.
I can create instances of the Dog class by calling the
new method. Here I am creating two dog objects (note
that while class names begin uppercase letters, object
names begin with lowercase letters):
mydog = Dog.new
yourdog = Dog.new
At the moment, these two dogs have no names. So the
next thing I do is call the set_name method to give them
names:
mydog.set_name( 'Fido' )
yourdog.set_name( 'Bonzo' )
Having given each dog a name, I need to have some way
to find out its name later on. How shall I do this? I
can’t poke about inside an object to get at the
@name variable, since the internal details of each object
are known only to the object itself. This is a fundamental
principle of ‘pure’ object orientation: the
internal representation of each object is private. There
are precisely defined ways into each object (for example,
the method set_name) and precisely defined ways out (logically,
this might be another method called get_name). Only the
object itself can mess around with its internal state.
The outside world cannot. This is called ‘encapsulation’.
Note: In Ruby, encapsulation is not quite as rigorously
enforced as it initially appears. There are some
very dirty tricks that you can do to mess around
inside an object. For the sake of clarity (and to
make sure you don’t have nightmares), we shall,
for now, silently pass over these ‘features’ of
the language… |
OK, so as we need each dog to know its own name, let’s
give it a get_name method:
def get_name
return
@myname
end
The return keyword here is optional. Ruby methods will
always return the last expression evaluated. For the
sake of clarity (and to avoid unexpected results from
methods of more complexity than this one!) we shall make
a habit of explicitly returning any values which we plan
to use. Finally, let’s give the dog some behaviour
by asking it to talk. Here is the finished class definition:
class Dog
def set_name( aName )
@myname
= aName
end
def get_name
return
@myname
end
def talk
return 'woof!'
end
end
Now, we can create a dog, name it, display its name
and ask it to talk like this:
mydog = Dog.new
mydog.set_name( 'Fido' )
puts(mydog.get_name)
puts(mydog.talk)
I’ve written an expanded version of this code
in the 6dogs.rb program. This also contains a Cat class
which is similar to the Dog class apart from the fact
that its talk method returns a miaow instead of a woof.
Here I am talking to my dogs and cats using the Steel
IDE
This example, incidentally, is based on a classic Smalltalk
demo program which illustrates how the same ‘message’ (such
as talk) can be sent to different objects (such as cats
and dogs), and each different object responds differently
to the same message with its own special method (here
the talk method). The ability to have different classes
containing methods with the same name goes by the fancy
Object Orientated name of polymorphism – a term
which, once remembered, can safely be forgotten…
When you run this program, the code is executed in sequence.
The code of the classes themselves is not executed until
instances of those classes (objects) are created by the
code at the bottom of the file. In this series, I will
frequently mix class definitions with ‘free standing’ code
which executes when the program is run. This may not
be the way you would want to write a major application
but for just ‘trying things out’ it is extremely
convenient.
Note: If Ruby is an Object
Orientated language, you may think it odd that
we can enter ‘free
floating’ methods into a code file. In fact,
it turns out that when you run a program, Ruby creates
a main object and any code that appears inside a
code unit is, in fact, running inside the main object.
You can easily verify this. Create a new source code
file, add the code below then run it to view the
output:
puts self
puts self.class |
One obvious defect of this program is that the two classes,
Cat and Dog, are highly repetitious. It would make more
sense to have one class, Animal, which has get_name and
set_name methods and two descendent classes, Cat and
Dog, which contain only the behaviour – woofing or miaowing – specific to that species of animal.
We’ll be finding out how to do this next month.
Finally, let’s look at another example of a user-defined
class. Load up 7treasure.rb. This is an adventure game
in the making. It contains two classes, Thing and Treasure.
The Thing class is very similar to the Cat and Dog classes
from the last program – well, apart from the fact
that it doesn’t woof or miaow, of course. The Treasure
class has a few interesting extras, however. First of
all, it hasn’t got get_name and set_name methods.
Instead, it contains a method named initialize which
takes two arguments whose values are assigned to the
@name and @description variables.
I've overriden the Treasure class's to_s method so that
it returns a nicely formatted string description
When a class contains a method named initialize this
will be automatically called when a new object is created
using the new method. It is a good idea to use an initialize method to set the values of an object’s instance
variables. This has two clear benefits over setting each
instance variable using methods such set_name. First
of all, a complex class may contain numerous instance
variables and you can set the values of all of them with
the single initialize method rather than with many separate ‘set’ methods;
secondly, if the variables are all automatically initialised
at the time of object creation, you will never end up
with an ‘empty’ variable (like the nil value
returned when we tried to display the name of someotherdog in the 6dogs.rb program).
Finally, I have created a method called to_s which is
intended to return a string representation of a Treasure
object. The method name, to_s, is not arbitrary. The
same method name is used throughout the standard Ruby
object hierarchy. In fact, the to_s method is defined
for the Object class itself which is the ultimate ancestor
of all other classes in the Ruby library. By redefining
the to_s method, I have added new behaviour which is
more appropriate to the Treasure class than the default
method. In other words, I have ‘overridden’ its
to_s method. To see how to_s can be used with a variety
of objects and to test how a Treasure object would be
converted to a string in the absence of an overridden
to_s method, try out the 8to_s.rb program.
While the 7treasure.rb program may
lay the foundations for a game containing a variety of
different types of object, its code is still repetitive.
After all, why have a Thing class, which contains a name
and a Treasure class which also contains a name? It would
make more sense to regard a Treasure as a type of Thing.
In a complete game, other objects such as Rooms and Weapons
might be yet other types of Thing. It is clearly time
to start working on a proper class hierarchy. That’s
what we shall do next month…
February 2006
|