Home
Archives
About us...
Advertising
Contacts
Site Map
 

ruby in steel

 

 AN INTRODUCTION TO RUBY
Ruby is a modern language which harks back to the founding principles of Object Orientation. This month Huw Collingbourne begins a series which will guide you, step-by-step, into the fascinating world of Ruby programming
Requirements:
Ruby

 

Download The Source Code:
ruby1src.zip

 

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

 


Home | Archives | Contacts

Copyright © 2006 Dark Neon Ltd. :: not to be reproduced without permission