- Introduction
- Introducing Ruby
- Having Some Fun with Ruby
- Where Next?
Introducing Ruby
Ruby is a pure object-oriented scripting language that can be run interactively. As such, it gives the immediacy of old-style interpreted BASIC, while at the same time offering the full power of the modern object-oriented paradigm. Ruby is copyrighted free software by Yukihiro Matsumoto, and can be freely downloaded.
The really nice thing about interactive Ruby is that beginners don't have to struggle with compilers and separate commands to run their code. All they have to deal with is the command line:
D:\Ruby\bin>ruby irb irb(main):001:0> print 2+3 5 nil irb(main):002:0>
Beginners also don't have to worry about declaring the types of variables, which drastically reduces the potential for making mistakes. The following example creates an array of four numbers and then invokes a few methods to check that it is indeed an array that has been created. It then prints the first element of the array. The nil output from irb is the value returned from the print methodirb always prints the value of the last expression it evaluated, as you can see by just typing 3 + 4 at the irb prompt. Comments start with # and run to the end of the line.
irb(main):002:0> a = [1, 2, 3, 4] [1, 2, 3, 4] irb(main):003:0> a.length 4 irb(main):004:0> a.class Array irb(main):005:0> p a[0] # print element then skip to next line 1 nil irb(main):006:0>
Another really nice thing about using Ruby in an interactive way is that as you type each line, it checks the syntax and reports any errors, as shown in this example of attempting to use an iterator to print each element of the array:
irb(main):006:0> a.each { |i| print_line i } NameError: undefined method ´print_line' for #<Object:0xa04a818> (irb):6:in ´irb_binding' (irb):6:in ´each' (irb):6:in ´irb_binding' irb(main):007:0>
The iterators in Ruby are really powerful because they remove the potential for a whole set of the typical "off by one" errors that are easy to make when explicitly looping over a collection. The block of code is executed once for each element of the array, with elem being set to the value of the element being processed:
irb(main):007:0> sum = 0 0 irb(main):008:0> a.each { |elem| sum = sum + elem } [1, 2, 3, 4] irb(main):009:0> sum 10 irb(main):010:0>
So Where Are the Objects?
The simple answer is, everywhere. Even simple numbers are objects, and it's easy to ask anything what kind of object it is, what it is derived from, and the messages it can respond to. (The list of methods has been truncated here for brevity.)
irb(main):010:0> a[1].class Fixnum irb(main):011:0> a[1].class.ancestors [Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel] irb(main):012:0> a[1].methods ["<=", "zero?", "times", "step", ......] irb(main):013:0>
And just to prove that the numbers really are objects, here's a variation on the familiar "Hello World!" example of using the times method, which executes the passed block of code the appropriate number of times. (Ruby indexes arrays from 0, so inside a times loop Ruby starts counting from 0.)
irb(main):013:0> a[1].times { |n| p "Hello World! " + n.to_s } "Hello World! 0" "Hello World! 1" 2 irb(main):014:0>
Ruby and Objects
All of the above is nice as an introduction to writing software, but the real benefit of using interactive Ruby is that beginners can easily define their own classes. A simple example would be a Book class. To make it slightly interesting, let's give it a single attribute called title, which has to be prefixed with @ to show that it's an instance variable:
irb(main):014:0> class Book irb(main):015:1> def initialize(title) irb(main):016:2> @title = title irb(main):017:2> end irb(main):018:1> end nil irb(main):019:0>
With just this minimal definition, Book objects can now be created. The parameters that are passed into the new method are passed on to the initialize method:
irb(main):019:0> b = Book.new("Software Craftsmanship") #<Book:0xa02f7b8 @title="Software Craftsmanship"> irb(main):020:0>
As it exists, however, this new Book object doesn't have any interesting behavior. We can't even access the title attribute from outside the object:
irb(main):020:0> p b.title NameError: undefined method ´title' for #<Book:0xa0ac6e8> (irb):20:in ´irb_binding' irb(main):021:0>
To fix this oversight, we have to go back and add the accessor methods to the Book class. Defining the title and title= methods can be done by hand if special logic is needed or more normally by using the attr_accessor method. This example uses the "setter" method to change the title of the book and then the "getter" to print out the new title. Finally, the existence of the two new methods is confirmed by asking the Book object for the names of its public methods.
irb(main):021:0> class Book irb(main):022:1> attr_accessor :title irb(main):023:1> end nil irb(main):024:0> b.title = "Software Craftsmanship The New Imperative" "Software Craftsmanship The New Imperative" irb(main):025:0> p b.title "Software Craftsmanship The New Imperative" nil irb(main):026:0> b.public_methods ["title", "title=", ..... ] irb(main):027:0>
Although this example is incomplete in terms of interesting behaviors, it gives enough of a flavor to enable more complicated examples to be constructed. For example, it should be relatively easy for even a beginner to understand the following CircularCounter class and then extend it to have a decrement method:
class CircularCounter def initialize (limit, initialValue) @limit, @value = limit, initialValue end def value @value #could use 'return @value' to be more explicit end def reset @value = 0 end def increment # returns true if overflow occurred @value = @value + 1 if (value >= @limit) reset true else false end end end