Ruby
Ruby
By Satish Talim
Free Study Notes on Ruby
You may freely copy and distribute this eBook as long as you do not
modify the text or remove this copyright notice. You must not make
any charge for this eBook.
Notice: These notes are to be used for reference purposes only. The
author will not accept any liability for any damage caused by the use
of this material.
Table of Contents
Learn Ruby with me.......................................................................................................5
Preamble ....................................................................................................................5
Assumptions...............................................................................................................5
Using this eBook........................................................................................................5
What is Ruby?............................................................................................................7
How Ruby can help you, in more detail ....................................................................7
Downloading Ruby and an Editor..............................................................................7
Ruby Programming Environment ..............................................................................8
Our First Ruby program.................................................................................................9
Some Features of Ruby................................................................................................11
Numbers in Ruby .........................................................................................................12
Operators and Precedence....................................................................................13
Fun with Strings...........................................................................................................15
Variables and Assignment ...........................................................................................16
Scope............................................................................................................................18
Global scope and global variables .......................................................................18
Built-in global variables.......................................................................................18
Local scope ..........................................................................................................18
Getting input ................................................................................................................19
Names in Ruby.............................................................................................................20
More on methods .........................................................................................................22
Writing Own Methods in Ruby....................................................................................23
Intercepting unrecognized messages with method_missing ................................25
More on the String class ..............................................................................................27
Listing all methods of a class or object................................................................28
Comparing two strings for equality .....................................................................28
Simple Constructs in Ruby ..........................................................................................30
Case Expressions .................................................................................................31
Arrays in Ruby.............................................................................................................32
Ranges..........................................................................................................................35
Blocks and Procs..........................................................................................................36
Random Numbers ........................................................................................................39
Reading from / Writing to text files.............................................................................40
Traversing Directory Trees ..................................................................................40
Random Access....................................................................................................40
Writing our own Class .................................................................................................42
Literal Constructors .............................................................................................44
Garbage Collection ..............................................................................................44
Class Methods......................................................................................................45
Self - The current/default object ..................................................................................46
Including Other Files ...................................................................................................49
Open classes.................................................................................................................51
Inheritance....................................................................................................................52
Duck Typing ................................................................................................................55
Overloading Methods...................................................................................................57
Overriding Methods .....................................................................................................58
Symbols........................................................................................................................59
Hashes ..........................................................................................................................61
Appendix B ................................................................................................................111
Solutions to Assignments.......................................................................................111
Appendix C ................................................................................................................115
Preamble
My interest in Ruby was aroused after I read an article Ruby the Rival
in November 2005. I decided to learn Ruby myself and started making
my study notes. What's presented here is a result of that.
I'd like to thank everyone on the ruby-talk mailing list for their
thoughts and encouragement; all of my wonderful PuneRuby RUG
members for their help in making these study notes far better than I
could have done alone. Since these are my study notes, I have made
extensive references to the blogs and articles of various Ruby Gurus
and to the Ruby books from Manning, O'Reilly and Pragmatic
Programmers; my thanks to all of them.
Assumptions
def hello
puts ‘hello’
end
This is an explanatory note. You can skip it if you like – but if you do
so, you may miss something of interest…!
hello.rb
You can download the source code of all the programs shown in the
eBook from here.
What is Ruby?
The year 2004 saw a massive surge of interest in Ruby, with the
introduction of the Ruby on Rails Web application framework by David
Heinemeier Hansson.
Do note that these instructions assume that you are going to use a
Windows platform. For installation on other platforms, you can refer
here, here and here.
Let’s assume that you have installed Ruby in the folder c:/ruby, then
the installation creates a number of sub-folders. The folders:
c:/ruby/bin is where the Ruby executables (including ruby and irb)
have been installed.
c:/ruby/lib/ruby/1.8 you’ll find program files written in Ruby. These
files provide standard library facilities, which you can require from
your own programs if you need the functionality they provide.
c:/ruby/lib/ruby/1.8/i386-mswin32 contains architecture-specific
extensions and libraries. The files in this directory generally have
names ending in .so or .dll (depending on your platform). These files
are C-language extensions to Ruby; or, more precisely, they are the
binary, runtime-loadable files generated from Ruby’s C-language
extension code, compiled into binary form as part of the Ruby
installation process.
c:/ruby/lib/ruby/site_ruby folder is where you and/or your system
administrator store third-party extensions and libraries. Some of these
may be code you yourself write; others are tools you download from
other people’s sites and archives of Ruby libraries.
c:/ruby/lib/ruby/gems is the Ruby-Gems packaging system. Inside the
gems directory are one or more subdirectories; and if you explore
these, you’ll find (possibly among other things) the source code for the
Rails framework.
c:/ruby/src is where you will find the Ruby source code.
c:/ruby/samples/RubySrc-1.8.4/sample is where you will find some
sample Ruby programs.
Let's start our Ruby editor SciTE. To do so, on your windows desktop
click on start/Programs/Ruby-185-21/SciTE. The editor window
opens. Press the F8 key to open an output window. Now, click on
Options/Open Global Options File and search for 'tabsize'. Edit and
make tabsize=2 and indent.size=2. I like my SciTE window to be
maximised at start and for that set the position.width=-1 and
position.height=-1 Press Ctrl+S and the Ctrl+W. Next, press
Ctrl+Shift+I - this opens the Indentation Settings window. Here,
ensure that the Tab Size and Indent Size is set to 2 and that the Use
tabs box is not checked. Click OK. We are now ready to write our first
Ruby program.
All Ruby source files have the .rb file extension. The Ruby coding
convention states that file/directory name is lower case of
class/module name with .rb extension. For example, Foo class has
name foo.rb
In the left window of SciTE type: puts 'Hello' and then click File/Save
As... Give the name hello.rb and store it in your rubyprograms folder.
Press F5 to run your program. You should see Hello in the output
window on the right.
Some methods such as puts and gets are available everywhere and
don't need to be associated with a specific object. Technically
speaking, these methods are provided by Ruby's Kernel module
(more on this later) and they are included in all Ruby objects (the
Kernel module is included by class (more on this later) Object, so its
methods are available in every Ruby object). When you run a Ruby
application, an object called main of class Object is automatically
created. This object provides access to the Kernel methods.
The above can be verified by the following program (do not worry if you
do not understand the program, right now).
Observe
1. Free format
2. Case sensitive
3. Comments - Anything following an unquoted #, to the end of the
line on which it appears, is ignored by the interpreter. Also, to
facilitate large comment blocks, the ruby interpreter also ignores
anything between a line starting with =begin and another line
starting with =end
4. Statement delimiters - Multiple statements on one line must be
separated by semicolons, but they are not required at the end of a
line; a linefeed is treated like a semicolon. If a line ends with a
backslash (\), the linefeed following it is ignored; this allows you to
have a single logical line that spans several lines
Numbers in Ruby
=begin
Ruby Numbers
Usual operators:
+ addition
- subtraction
* multiplication
/ division
=end
puts 1 + 2
puts 2 * 3
# Integer division
# When you do arithmetic with integers, you'll get integer answers
puts 3 / 2
puts 10 - 11
puts 1.5 / 2.6
:: Scope
[] Indexing
** Exponentiation
+-!~ Unary pos/neg, not,...
Multiplication,
*/%
Division...
+- Addition, subtraction...
<< >> Logical shifts,...
& Bitwise and
|^ Bitwise or, xor
> >= < <= Comparison
== === <=> != =~ !~ Equality, inequality...
&& Boolean and
|| Boolean or
.. ... Range operators
= (also +=, -=,...) Assignment
?: Ternary decision
not Boolean negation
Assignment: Write a Ruby program that tells you how many minutes
are there in a year (do not bother right now about leap years etc.).
Suggestions:
1. Read online Chris Pine’s Learn to Program book
2. Read Matz’s The Philosophy of Ruby
3. Hear Geoff Grosenbach’s Ruby Basics
'' (ie. two single quotes) does not have anything in it at all; we call that
an empty string.
=begin
Ruby Strings
=end
puts "Hello World"
# Can use " or ' for Strings, but ' is more efficient
puts 'Hello World'
# String concatenation with +
puts 'I like' + ' Ruby'
# Escape sequence with \
puts 'It\'s my Ruby'
# displays the string three times
puts 'Hello' * 3
Local variables have the quality of barewords; they must start with
either a lowercase letter or the underscore character (_), and they
must consist entirely of letters, numbers, and underscores. When
Ruby sees a plain word sitting there, it interprets it as one of three
things: a local variable, a method call, or a keyword.
# stringusage.rb
# Defining a constant
PI = 3.1416
puts PI
# Defining a local variable
my_string = 'I love my city, Pune'
puts my_string
=begin
The Ruby standard is to use two spaces for indentation
Conversions
.to_i, .to_f, .to_s
=end
var1 = 5;
var2 = '2'
In the example:
x = "100".to_i
the dot means that the message “to_i” is being sent to the string
“100”, or that the method to_i is being called on the string “100”.
The string “100” is called the receiver of the message. Thus, when
you see a dot in what would otherwise be an inexplicable position,
you should interpret it as a message (on the right) being sent to an
object (on the left).
Scope
Scope refers to the reach or visibility of variables. Different types of
variables have different scoping rules. We’ll be talking chiefly about
two types: global and local variables.
Local scope
You can tell by looking at a Ruby program where the local scopes
begin and end, based on a few rules:
• The top level (outside of all definition blocks) has its own local
scope.
• Every class or module definition block (class, module) has its
own local scope, even nested class/module definition blocks.
• Every method definition (def) has its own local scope.
Getting input
So far we had seen a method like puts that writes to the screen. How
does one accept user input? For this gets (get a string) and chomp are
useful. The example methods.rb below illustrates the same.
In the above example when you run it in SciTE, click on the output
window and then type the name of your city.
chomp is a string method and gets retrieves only strings from your
keyboard. You must have realised that gets returns a string and a '\n'
character, while chomp removes this '\n'.
Assignment:
Write a Ruby program that asks for a numeric value of the
temperature in degrees Fahrenheit. Finally, the program displays the
equivalent value in degrees Centigrade. To format the output to say 2
decimal places, we can use the Kernel's format method. For example, if
x = 45.5678 then format("%.2f", x) will return a string 45.57. Another
way is to use the round function as follows puts (x*100).round/100.0
Names in Ruby
3. Method names should begin with a lowercase letter. ''?'' and ''!'' are
the only weird characters allowed as method name suffixes (! or
bang labels a method as dangerous—specifically, as the dangerous
equivalent of a method with the same name but without the bang).
It's to be noted that any given variable can at different times hold
references to objects of many different types. A Ruby constant is
also a reference to an object. Constants are created when they are
first assigned to (normally in a class or module definition; they
should not be defined in a method - more of this later). Ruby lets
you alter the value of a constant, although this will generate a
warning message. Also, variables in Ruby act as "references" to
objects, which undergo automatic garbage collection.
The basic types in Ruby are Numeric (subtypes include Fixnum, Integer,
and Float), String, Array, Hash, Object, Symbol, Range, and RegEx. Ruby
doesn't require you to use primitives (data types) when manipulating
data of these types—if it looks like an integer, it's probably an integer;
if it looks like a string, it is probably a string. class returns the class of
an object, for example:
s = ‘hello’
s.class # String
More on methods
If objects (such as strings, integers and floats) are the nouns in Ruby
language, then methods are the verbs. Every method needs an object.
It's usually easy to tell which object is performing the method: it's
what comes right before the dot. Sometimes, though, it's not quite as
obvious. When we are using puts, gets - where are their objects? In
Ruby, the implicit object is whatever object you happen to be in. But
we don't even know how to be in an object yet; we've always been
inside a special object (main) Ruby has created for us that represents
the whole program. You can always see what object you are in (current
object) by using the special variable self.
puts self
Let's look at writing one's own methods in Ruby with the help of a
simple program mymethods.rb. Observe that we use def and end to
declare a method. Parameters are simply a list of local variable names
in parentheses.
# A simple method
def hello
puts 'Hello'
end
#use the method
hello
# Method with an argument - 1
def hello1(name)
puts 'Hello ' + name
return 'success'
end
puts(hello1('satish'))
# Method with an argument - 2
def hello2 name2
puts 'Hello ' + name2
return 'success'
end
puts(hello2 'talim') # A method returns the value of the last line
>ruby mymethods.rb
Hello
Hello satish
success
Hello talim
success
mymethods.rb:20: warning: parenthesize argument(s) for future version
>Exit code: 0
>ruby mymethods1.rb
Dibya, Shashank, Shashank.
ruby, Shashank, Shashank.
>Exit code: 0
def oldmtd
"old method"
end
alias newmtd oldmtd
def oldmtd
"old improved method"
end
puts oldmtd
puts newmtd
>ruby aliasmtd.rb
>Exit code: 0
>ruby aliasmtd.rb
old improved method
old method
>Exit code: 0
def foo(*my_string)
my_string.each do |words|
puts words
end
end
foo('hello','world')
foo()
The asterisk is actually taking all arguments you send to the method
and assigning them to an array named my_string. As you can see, by
making use of the asterisk, we’re even able to pass in zero arguments.
The code above will result in the words hello and world written on
successive lines in the first method call and nothing being written on
the second call, as you can see in the following output:
>ruby vararg.rb
hello
world
>Exit code: 0
If you want to include optional arguments (*x), they have to come after
any non-optional arguments:
def opt_args(a,b,*x) # right
def opt_args(a,*x,b) # wrong
def downer(string)
string.downcase!
end
a = "HELLO" # -> "HELLO"
downer(a) # -> "hello"
a
class Dummy
def method_missing(m)
puts "There's no method called #{m} here -- please try again."
end
end
Dummy.new.anything
>ruby tmp.rb
There's no method called anything here -- please try again.
>Exit code: 0
There are many methods in the String class (you don't have to
memorize them all; you can look up the documentation) like the
reverse that gives a backwards version of a string (reverse does not
change the original string). length that tells us the number of
characters (including spaces) in the string. upcase changes every
lowercase letter to uppercase, and downcase changes every uppercase
letter to lowercase. swapcase switches the case of every letter in the
string, and finally, capitalize is just like downcase, except that it
switches the first character to uppercase (if it is a letter), slice gives
you a substring of a larger string.
String.instance_methods.sort
This method tells you all the instance methods that instances of
String are endowed with.
String.instance_methods(false).sort
With this method, you can view a class’s instance methods without
those of the class’s ancestors.
s1 = 'Jonathan'
s2 = 'Jonathan'
s3 = s1
if s1 == s2
puts 'Both Strings have identical content'
else
puts 'Both Strings do not have identical content'
end
if s1.eql?(s2)
puts 'Both Strings have identical content'
else
puts 'Both Strings do not have identical content'
end
if s1.equal?(s2)
puts 'Two Strings are identical objects'
else
puts 'Two Strings are not identical objects'
end
if s1.equal?(s3)
puts 'Two Strings are identical objects'
else
puts 'Two Strings are not identical objects'
end
Exercise
Given a string, let us say that we want to reverse the word order
(rather than character order). You can use String.split, which gives
you an array of words. The Array class has a reverse method; so you
can reverse the array and join to make a new string:
# if else end
var = 5
if var > 4
puts "Variable is greater than 4"
puts "I can have multiple statements here"
if var == 5
puts "Nested if else possible"
else
puts "Too cool"
end
else
puts "Variable is not greater than 5"
puts "I can have multiple statements here"
end
end
Some common conditional operators are: ==, != >=, <=, >, <
Loops like the while loop are available. Again, the example below
illustrates the same.
# Loops
var = 0
while var < 10
puts var.to_s
var += 1
end
Assignment:
1. Write a Ruby program that asks for a year and then displays to
the user whether the year entered by him/her is a leap year or
not.
2. Write a method leap_year. Accept a year value from the user,
check whether it’s a leap year and then display the number of
minutes in that year.
Case Expressions
This form is fairly close to a series of if statements: it lets you list a
series of conditions and execute a statement corresponding to the first
one that’s true. For example, leap years must be divisible by 400, or
divisible by 4 and not by 100. Also, remember that case returns the
value of the last expression executed.
year = 2000
leap = case
when year % 400 == 0: true
when year % 100 == 0: false
else year % 4 == 0
end
puts leap
# output is: true
Arrays in Ruby
# Arrays
# Empty array
var1 = []
# Array index starts from 0
puts var1[0]
# an array holding a single number
var2 = [5]
puts var2[0]
# an array holding two strings
var3 = ['Hello', 'Goodbye']
puts var3[0]
puts var3[1]
flavour = 'mango'
# an array whose elements are pointing
# to three objects - a float, a string and an array
var4 = [80.5, flavour, [true, false]]
puts var4[2]
languages.delete('Mumbai')
languages.each do |lang|
puts 'I love ' + lang + '!'
puts 'Don\'t you?'
end
def mtdarry
10.times do |num|
square = num * num
return num, square if num > 5
end
end
num, square = mtdarry
puts num
puts square
>ruby mtdarry.rb
6
36
>Exit code: 0
The times method of the Integer class iterates block num times,
passing in values from zero to num-1. As we can see, if you give return
multiple parameters, the method returns them in an array. You can
use parallel assignment to collect this return value.
Assignment:
Ranges
The first and perhaps most natural use of ranges is to express a
sequence. Sequences have a start point, an end point, and a way to
produce successive values in the sequence. In Ruby, these sequences
are created using the “. .” and “. . .” range operators. The two dot
form creates an inclusive range, and the three-dot form creates a
range that excludes the specified high value. In Ruby ranges are not
represented internally as lists: the sequence 1..100000 is held as a
Range object containing references to two Fixnum objects. If you need to,
you can convert a range to a list using the to_a method.
Ranges implement methods that let you iterate over them and test
their contents in a variety of ways.
digits = 0..9
digits.include?(5) -> true
digits.min -> 0
digits.max -> 9
digits.reject {|i| i < 5 } -> [5, 6, 7, 8, 9]
The Ruby standard is to use braces for single-line blocks and do… end
for multi-line blocks. Keep in mind that the braces syntax has a
higher precedence than the do..end syntax.
Matz says that any method can be called with a block as an implicit
argument. Inside the method, you can call the block using the yield
keyword with a value.
Once you have created a block, you can associate it with a call to a
method. Usually the code blocks passed into methods are anonymous
objects, created on the spot. For example, in the following code, the
block containing puts “Hello” is associated with the call to a method
greet.
greet {puts ‘Hello’}
A method can then invoke an associated block one or more time using
the Ruby yield statement.
def call_block
puts 'Start of method'
yield
yield
puts 'End of method'
end
call_block {puts 'In the block'}
>ruby codeblock.rb
Start of method
In the block
In the block
End of method
>Exit code: 0
If you provide a code block when you call a method, then inside the
method, you can yield control to that code block—suspend execution
of the method; execute the code in the block; and return control to the
method body, right after the call to yield.
You can provide parameters to the call to yield: these will be passed to
the block. Within the block, you list the names of the arguments to
receive the parameters between vertical bars (|).
def call_block
yield('hello', '99')
end
call_block {|str, num| puts str + ' ' + num}
>ruby codeblock2.rb
hello 99
>Exit code: 0
Note that the code in the block is not executed at the time it is
encountered by the Ruby interpreter. Instead, Ruby remembers the
context in which the block appears and then enters the method.
A code block’s return value (like that of a method) is the value of the
last expression evaluated in the code block. This return value is made
available inside the method; it comes through as the return value of
yield.
Blocks are not objects, but they can be converted into objects of class
Proc. This can be done by calling the lambda method of the module
Kernel. A block created with lambda acts like a Ruby method. If you
don’t specify the right number of arguments, you can’t call the block.
Proc objects are blocks of code that have been bound to a set of local
variables. The class Proc has a method call that invokes the block. The
program proccall.rb illustrates this.
toast.call
>ruby proccall.rb
Hello
Cheers
>Exit code: 0
Remember you cannot pass methods into other methods (but you can
pass procs into methods), and methods cannot return other methods
(but they can return procs).
The next example shows how methods can take procs. Example
mthproc.rb
say = lambda do
puts 'Hello'
end
some_mtd say
>ruby mtdproc.rb
Start of mtd
Hello
End of mtd
>Exit code: 0
Random Numbers
rand1 = rand(one_len)
rand2 = rand(two_len)
rand3 = rand(three_len)
phrase = word_list_one[rand1] + " " + word_list_two[rand2] + " " +
word_list_three[rand3]
puts phrase
The above program makes three lists of words, and then randomly
picks one word from each of the three lists and prints out the result.
Assignment:
This assignment is from Chris Pine’s Book.
a. Write a Deaf Grandma program. Whatever you say to grandma
(whatever you type in), she should respond with HUH?! SPEAK
UP, SONNY!, unless you shout it (type in all capitals). If you
shout, she can hear you (or at least she thinks so) and yells
back, NO, NOT SINCE 1938! To make your program really
believable, have grandma shout a different year each time;
maybe any year at random between 1930 and 1950. You can't
stop talking to grandma until you shout BYE.
b. b. Extend your Deaf Grandma program: What if grandma
doesn't want you to leave? When you shout BYE, she could
pretend not to hear you. Change your previous program so that
you have to shout BYE three times in a row. Make sure to test
your program: if you shout BYE three times, but not in a row,
you should still be talking to grandma.
Let's look at how we can read / write to a text file with the help of a
simple program readwrite.rb.
The File.open method can open the file in different modes like 'r' Read-
only, starts at beginning of file (default); 'r+' Read/Write, starts at
beginning of file; 'w' Write-only, truncates existing file to zero length or
creates a new file for writing. Please check the online documentation
for a full list of modes available. File.open opens a new File if there is
no associated block. If the optional block is given, it will be passed file
as an argument, and the file will automatically be closed when the
block terminates.
require 'find'
Find.find('./') do |f|
type = case
when File.file?(f): "F"
when File.directory?(f): "D"
else "?"
end
puts "#{type}: #{f}"
end
It’s quite easy to access a file randomly. Let’s say we have a text file
(named hellousa.rb) , the contents of which is shown below:
puts ‘Hello USA’
We now need to display the contents of the file from the word USA.
Here’s how:
f = File.new("hellousa.rb")
f.seek(12, IO::SEEK_SET)
print f.readline
The seek method of class IO, seeks to a given offset anInteger (first
parameterof method) in the stream according to the value of second
parameter in the method.
When you design a class, think about the objects that will be created
from that class type. Think about the things the object knows and the
things the object does.
end
def bark
puts 'Ruff! Ruff!'
end
def display
puts "I am of #{@breed} breed and my name is #{@name}"
end
end
# make an object
# method new used to create object
d = Dog.new('Labrador', 'Benzy')
puts d.methods.sort
puts "The id of obj is #{d.object_id}."
if d.respond_to?("talk")
d.talk
else
puts "Sorry, the object doesn't understand the 'talk' message."
end
d.bark
d.display
d1 = d
d1.display
d = nil
d.display
>ruby dog.rb
Ruff! Ruff!
I am of Labrador breed and my name is Benzy
>Exit code: 0
The method new is used to create an object of class Dog. Objects are
created on the heap. The variable d is known as a reference variable.
It does not hold the object itself, but it holds something like a pointer
or an address of the object. You use the dot operator (.) on a reference
variable to say, “use the thing before the dot to get me the thing after
the dot.” For example:
d.bark
innate methods, you can call the methods method (and throw in a sort
operation, to make it easier to browse visually):
puts d.methods.sort
The result is a list of all the messages (methods) this newly minted
object comes bundled with. Amongst these many methods, the
methods object_id and respond_to? are important.
Every object in Ruby has a unique id number associated with it. You
can see an object’s id by asking the object to show you its object_id:
puts "The id of obj is #{d.object_id}."
Literal Constructors
That means you can use special notation, instead of a call to new, to
create a new object of that class. The classes with literal constructors
are shown in the table below. When you use one of these literal
constructors, you bring a new object into existence.
Garbage Collection
The statement:
d =nil
If you stuff something in an array and you happen to keep that array
around, it’s all marked. If you stuff something in a constant or global
variable, it’s forever marked.
Class Methods
The idea of a class method is that you send a message to the object
that is the class rather than to one of the class’s instances. Class
methods serve a purpose. Some operations pertaining to a class can’t
be performed by individual instances of that class. new is an excellent
example. We call Dog.new because, until we’ve created an individual
dog instance, we can’t send it any messages! Besides, the job of
spawning a new object logically belongs to the class. It doesn’t make
sense for instances of Dog to spawn each other. It does make sense,
however, for the instance-creation process to be centralized as an
activity of the class Dog. It’s vital to understand that by Dog.new, we
have a method that we can access through the class object Dog but not
through its instances. Individual dog objects (instances of the class Dog)
do not have this method. A class object (like Dog) has its own methods,
its own state, its own identity. It doesn’t share these things with
instances of itself.
The table below shows an example (borrowed from the book Ruby For
Rails) on the notation used:
class S
puts 'Just started class S'
puts self
module M
puts 'Nested module S::M'
puts self
end
puts 'Back in the outer level of S'
puts self
end
>ruby tmp.rb
Just started class S
S
Nested module S::M
S::M
At the time the method definition is executed, the most you can say is
that self inside this method will be some future object that has access
to this method.
class S
def m
puts 'Class S method m:'
puts self
end
end
s = S.new
s.m
>ruby tmp.rb
Class S method m:
#<S:0x2835908>
>Exit code: 0
obj = Object.new
def obj.show
print 'I am an object; '
puts "here's self inside a singleton method of mine:"
puts self
end
obj.show
print 'And inspecting obj from outside, '
>ruby tmp.rb
I am in object: here's self inside a singleton method of mine:
#<Object:0x2835688>
And inspecting obj from outside, to be sure it's the same object:
#<Object:0x2835688>
>Exit code: 0
class S
def S.x
puts "Class method of class S"
puts self
end
end
S.x
>ruby tmp.rb
Class method of class S
S
>Exit code: 0
When writing your first Ruby programs, you tend to place all of your
code in a single file. But as you grow as a Ruby programmer, your
Ruby programs will also grow, and at some point you will realize that
having a single file contain all of your code just won't do. It is easier to
break your code up into logical groupings and place each group in a
separate file or files. When you begin using multiple files, you have a
need for the Ruby require and load methods that help you include
other files in your program.
The load method includes the named Ruby source file every time the
method is executed:
load ‘filename.rb’
The more commonly used require method loads any given file only
once:
require ‘filename’
Note that you say require 'filename', not require 'filename.rb'. Aside
from looking nicer, this bareword way of referring to the extension is
necessary because not all extensions use files ending in .rb.
Specifically, extensions written in C are stored in files ending with .so
or .dll. To keep the process transparent—that is, to save you the
trouble of knowing whether the extension you want uses a .rb file or
not—Ruby accepts a bareword and then does some automatic file-
searching and trying out of possible filenames until it finds the file
corresponding to the extension you have requested.
class MotorCycle
def initialize(make, color)
# Instance variables
@make = make
@color = color
end
def startEngine
if (@engineState)
puts 'Engine Running'
else
@engineState = true
puts 'Engine Idle'
end
end
end
Open classes
In Ruby, classes are never closed: you can always add methods to
an existing class. This applies to the classes you write as well as the
standard, built-in classes. All you have to do is open up a class
definition for an existing class, and the new contents you specify
will be added to whatever's there.
class MotorCycle
def dispAttr
puts 'Color of MotorCycle is ' + @color
puts 'Make of MotorCycle is ' + @make
end
end
m.dispAttr
m.startEngine
puts self.class
puts self
class String
def writesize
puts self.size
end
end
size_writer = "Tell me my size!"
size_writer.writesize
Inheritance
Inheritance is a relation between two classes. We know that all cats
are mammals, and all mammals are animals. Smaller classes inherit
characteristics from the larger classes to which they belong. If all
mammals breathe, then all cats breathe.
class Mammal
def breathe
puts "inhale and exhale"
end
end
class Cat<Mammal
def speak
puts "Meow"
end
end
rani = Cat.new
rani.breathe
rani.speak
Though we didn't specify how a Cat should breathe, every cat will
inherit that behaviour from the Mammal class since Cat was defined
as a subclass of Mammal. (In OO terminology, the smaller class is a
subclass and the larger class is a super-class.) Hence from a
programmer's standpoint, cats get the ability to breathe for free; after
we add a speak method, our cats can both breathe and speak.
class Bird
def preen
puts "I am cleaning my feathers."
end
def fly
puts "I am flying."
end
end
class Penguin<Bird
def fly
puts "Sorry. I'd rather swim."
end
end
p = Penguin.new
p.preen
p.fly
The above two programs are taken from the online Ruby User’s Guide.
Here’s another example, inherit.rb. Also note that when you invoke
super with no arguments, (though not used here) Ruby sends a
message to the parent of the current object, asking it to invoke a
method of the same name as the method invoking super. It passes this
method the parameters that were passed to the originally invoked
method.
class GF
def initialize
puts 'In GF class'
end
def gfmethod
puts 'GF method call'
end
end
# class F sub-class of GF
class F < GF
def initialize
puts 'In F class'
end
end
# class S sub-class of F
class S < F
def initialize
puts 'In S class'
end
end
son = S.new
son.gfmethod
A class can only inherit from one class at a time (i.e. a class can
inherit from a class that inherits from another class which inherits
from another class, but a single class can not inherit from many
classes at once).
There are many classes and modules (more on this later) built into
the standard Ruby language. They are available to every Ruby
program automatically; no require is required. Some built-in classes
are Array, Bignum, Class, Dir, Exception, File, Fixnum, Float, Integer, IO,
Module, Numeric, Object, Range, String, Thread, Time. Some built-in
modules are Comparable, Enumerable, GC, Kernel, Math.
The Object class is the parent class of all classes in Ruby. Its
methods are therefore available to all objects unless explicitly
overridden.
Duck Typing
You’ll have noticed that in Ruby we don’t declare the types of variables
or methods - everything is just some kind of object. Ruby objects
(unlike objects in some other object-oriented languages) can be
individually modified. You can always add methods on a per object
basis. In Ruby, the behavior or capabilities of an object can deviate
from those supplied by its class.
Hence in Ruby, we rely less on the type (or class) of an object and
more on its capabilities. This is called duck typing. If an object walks
like a duck and talks like a duck, then the Ruby interpreter is happy
to treat it as if it were a duck.
class Duck
def quack
'Quack!'
end
def swim
'Paddle paddle paddle...'
end
end
class Goose
def homk
'Hock!'
end
def swim
'Splash splash splash...'
end
end
class DuckRecording
def quack
play
end
def play
'Quack!'
end
end
def make_it_quack(duck)
duck.quack
end
puts make_it_quack(Duck.new)
puts make_it_quack(DuckRecording.new)
def make_it_swim(duck)
duck.swim
end
puts make_it_swim(Duck.new)
puts make_it_swim(Goose.new)
def make_it_quack(duck)
duck.quack
end
puts make_it_quack(Duck.new)
puts make_it_quack(DuckRecording.new)
def make_it_swim(duck)
duck.swim
end
puts make_it_swim(Duck.new)
puts make_it_swim(Goose.new)
Overloading Methods
You want to create two different versions of a method with the same
name: two methods that differ in the arguments they take. However, a
Ruby class can have only one method with a given name. Within that
single method, though, you can put logic that branches depending on
how many and what kinds of objects were passed in as arguments.
Overriding Methods
class OR
def mtd
puts "First definition of method mtd"
end
def mtd
puts "Second definition of method mtd"
end
end
OR.new.mtd
What happens when we call mtd on an instance of OR? Let’s find out:
OR.new.mtd
The printed result is Second definition of method mtd. The second definition
has prevailed: We see the output from that definition, not from the first.
When you override a method, the new version takes precedence.
Symbols
A symbol looks like a variable name but it’s prefixed with a colon.
Examples - :action, :line_items. You can think of symbols as string
literals that are magically turned into constants. Alternatively, you
can consider the colon to mean “thing named” so :id is “the thing
named id.” You can also think of :id as meaning the name of the
variable id, and plain id as meaning the value of the variable.
A Symbol is the most basic Ruby object you can create. It’s just a
name and an internal ID. Symbols are useful because a given
symbol name refers to the same object throughout a Ruby program.
Symbols are more efficient than strings. Two strings with the same
contents are two different objects, but for any given name there is
only one Symbol object. This can save both time and memory.
# symbol.rb
# use the object_id method of class Object
# it returns an integer identifier for an object
puts "string".object_id
puts "string".object_id
puts :symbol.object_id
puts :symbol.object_id
>ruby symbol.rb
21066960
21066930
132178
132178
>Exit code: 0
object will be unique for each different name but does not refer to a
particular instance of the name, for the duration of a program’s
execution. Thus, if Fred is a constant in one context, a method in
another, and a class in a third, the Symbol :Fred will be the same
object in all three contexts.
class Test
puts :Test.object_id.to_s
def test
puts :test.object_id.to_s
@test = 10
puts :test.object_id.to_s
end
end
t = Test.new
t.test
>ruby tmp.rb
116458
79218
79218
>Exit code: 0
Hashes
The example myhash.rb that follows uses hash literals: a list of key =>
value pairs between braces.
>ruby myhash.rb
4
canine
catfeline12dodecinedonkeyasininedogcanine
dodecine
>Exit code: 0
The Hash class has many methods and you can refer them here.
people = Hash.new
people[:nickname] = 'IndianGuru'
people[:language] = 'Marathi'
people[:lastname] = 'Talim'
puts people[:lastname] # Talim
The Time class in Ruby has a powerful formatting function which can
help you represent the time in a variety of ways. The Time class
contains Ruby’s interface to the set of time libraries written in C. Time
zero for Ruby is the first second GMT of January 1, 1970. Ruby’s
DateTime class is superior to Time for astronomical and historical
applications, but you can use Time for most everyday programs.
The strftime function is modelled after C’s printf. The time.rb program
shows some of these functions.
# You can use the upper case A and B to get the full
# name of the weekday and month, respectively
puts t.strftime("%A")
puts t.strftime("%B")
# You can use the lower case a and b to get the abbreviated
# name of the weekday and month, respectively
puts t.strftime("%a")
puts t.strftime("%b")
# 24 hour clock and Time zone name
puts t.strftime("at %H:%M %Z")
>ruby time.rb
10/09/2006 10:06:31
Sunday
September
Sun
Sep
at 10:06 India Standard Time
>Exit code: 0
Exceptions
An exception is a special kind of object, an instance of the class
Exception or a descendant of that class. Raising an exception means
stopping normal execution of the program and either dealing with the
problem that’s been encountered or exiting the program completely.
Which of these happens - dealing with it or aborting the program –
depends on whether you have provided a rescue clause. If you haven’t
provided such a clause, the program terminates; if you have, control
flows to the rescue clause.
Raising an Exception
Ruby has some predefined classes – Exception and its children – that
help you to handle errors that can occur in your program. The
following figure shows the Ruby exception hierarchy.
def raise_exception
>ruby raise.rb
I am before the raise.
raise.rb:3:in `raise_exception': An error has occured (RuntimeError)
from raise.rb:6
>Exit code: 1
The raise method is from the Kernel module. By default, raise creates
an exception of the RuntimeError class. To raise an exception of a
specific class, you can pass in the class name as an argument to
raise. Refer program inverse.rb
def inverse(x)
raise ArgumentError, 'Argument is not numeric' unless x.is_a?
Numeric
1.0 / x
end
puts inverse(2)
puts inverse('not a number')
>ruby inverse.rb
0.5
inverse.rb:2:in `inverse': Argument is not numeric (ArgumentError)
from inverse.rb:6
>Exit code: 1
To be even more specific about an error, you can define your own
Exception subclass:
Handling an Exception
To do exception handling, we enclose the code that could raise an
exception in a begin/end block and use one or more rescue clauses to
tell Ruby the types of exceptions we want to handle.
def raise_and_rescue
begin
puts 'I am before the raise.'
raise 'An error has occured.'
puts 'Iam after the raise.'
rescue
puts 'I am rescued.'
end
puts 'I am after the begin block.'
end
raise_and_rescue
>ruby handexcp.rb
I am before the raise.
I am rescued.
I am after the begin block.
>Exit code: 0
Observe that the code interrupted by the exception never gets run.
Once the exception is handled, execution continues immediately after
the begin block that spawned it.
begin
# …
rescue OneTypeOfException
# …
rescue AnotherTypeOfException
# …
end
For each rescue clause in the begin block, Ruby compares the raised
Exception against each of the parameters in turn. The match will
succeed if the exception named in the rescue clause is the same as the
type of the currently thrown exception, or is a superclass of that
exception.
begin
raise 'A test exception.'
rescue Exception => e
puts e.message
puts e.backtrace.inspect
end
>ruby excpvar.rb
A test exception.
["excpvar.rb:2"]
>Exit code: 0
If you need the guarantee that some processing is done at the end of a
block of code, regardless of whether an exception was raised then the
ensure clause can be used. ensure goes after the last rescue clause and
contains a chunk of code that will always be executed as the block
terminates. The ensure block will always run.
Access Control
The only easy way to change an object’s state in Ruby is by calling one
of its methods. Control access to the methods, and you have
controlled access to the object. A good rule of the thumb is never to
expose methods that could leave an object in an invalid state.
Top-level methods
When you write code at the top level, Ruby provides you automatically
with a default self. This object is a direct instance of Object. When
you ask it to describe itself
puts self
it says:
main
The object main is the current object as soon as your program starts
up.
def talk
puts "Hello"
end
Who, or what, does the method belong to? It’s not inside a class or
module definition block, so it doesn’t appear to be an instance method
of a class or module. It’s not attached to any particular object (as in
def obj.talk). What is it? Top-level methods are private instance
methods of the Kernel module.
Because top-level methods are private, you can’t call them with an
explicit receiver; you can only call them by using the implied receiver,
self. That means self must be an object on whose method search
path the given top-level method lies. But every object’s search path
includes the Kernel module, because the class Object mixes in Kernel,
and every object’s class has Object as an ancestor. That means you
can always call any top-level method, wherever you are in your
program. It also means you can never use an explicit receiver on a
top-level method.
puts and print are built-in private instance methods of Kernel. That’s
why you can - indeed, must - call them without a receiver.
Accessor methods
Encapsulation is achieved when the instance variables are private to
an object and you have public getters and setters (in Ruby, we call
them attribute readers and attribute writers). To make instance
variables available, Ruby provides accessor methods that return their
values. The program accessor.rb illustrates the same.
# accessor.rb
# First without accessor methods
class Song
def initialize(name, artist)
@name = name
@artist = artist
end
def name
@name
end
def artist
@artist
end
end
song = Song.new("Brazil", "Ricky Martin")
puts song.name
puts song.artist
David Black the author of Ruby for Rails has this to say: Instance
variables are per-object, not per-class, and they're not inherited. But if
a method uses one, and that method is available to subclasses, then it
will still use the variable -- but "the variable" in the sense of one per
object. See the following program:
class C
def initialize
@n = 100
end
def increase_n
@n *= 20
end
end
class D < C
def show_n
puts "n is #{@n}"
end
end
d = D.new
d.increase_n
d.show_n
>ruby instvarinherit.rb
n is 2000
>Exit code: 0
The @n in D's methods is the same (for each instance) as the one in C.
Syntactic sugar
Programmers use the term syntactic sugar to refer to special rules that
let you write your code in a way that doesn’t correspond to the normal
rules but that is easier to remember how to do and looks better.
Let’s say we want to set the name of a dog. As a starting point, name
can be set along with everything else at object creation time, as in
example – newdog.rb
class NewDog
def initialize(breed, name)
@breed = breed
@name = name
end
attr_reader :breed, :name # create reader only
end
nd = NewDog.new('Doberman', 'Benzy')
puts nd.name
Let’s write a set_name method that allows us to set, or reset, the name
of an existing dog. We’ll also rewrite the initialize method so that it
doesn’t expect a name:
class NewDog
def initialize(breed)
@breed = breed
end
attr_reader :breed, :name # create reader only
# setter method
def set_name(nm)
@name = nm
end
end
nd = NewDog.new('Doberman')
nd.set_name('Benzy')
puts nd.name
Ruby allows you to define methods that end with an equal sign (=).
Let’s replace set_name with a method called name=
def name=(nm)
@name = nm
end
name= does exactly what set_name did, and in spite of the slightly odd
method name, you can call it just like any other method:
nd.name=(‘Benzy’)
class NewDog
def initialize(breed)
@breed = breed
end
attr_reader :breed, :name # create reader only
# setter method
def name=(nm)
@name = nm
end
end
nd = NewDog.new('Doberman')
nd.name=('Benzy')
puts nd.name
The equal sign gives you that familiar “assigning a value to something”
feeling, so you know you’re dealing with a setter method. It still looks
odd, but Ruby takes care of that, too.
Ruby gives you some syntactic sugar for calling setter methods.
Instead of this
nd.name=('Benzy')
Freezing Objects
The freeze method in class Object prevents you from changing an
object, effectively turning an object into a constant. After we freeze an
object, an attempt to modiy it results in TypeError. The following
program illustrates this:
Object Serialization
Java features the ability to serialize objects, letting you store them
somewhere and reconstitute them when needed. Ruby calls this kind
of serialization marshaling.
class GameCharacter
def initialize(power, type, weapons)
@power = power
@type = type
@weapons = weapons
end
attr_reader :power, :type, :weapons
end
The program dumpgc.rb creates an object of the above class and then
uses Marshal.dump to save a serialized version of it to the disk.
require 'gamecharacters.rb'
gc = GameCharacter.new(120, 'Magician', ['spells', 'invisibility'])
puts gc.power.to_s + ' ' + gc.type + ' '
gc.weapons.each do |w|
puts w + ' '
end
File.open('game', 'w+') do |f|
Marshal.dump(gc, f)
end
Constants
A Ruby constant is a reference to an object. Constants are created
when they are first assigned to (as per the rules below). In the current
implementation of Ruby, reassignment of a constant generates a
warning but not an error as shown in this trivial example –
constwarn.rb
# constwarn.rb
A_CONST = 10
A_CONST = 20
Produces a warning:
#constalter.rb
A_CONST = "Doshi"
B_CONST = A_CONST
A_CONST[0] = "J" # alter string referenced by constant
puts A_CONST # displays Joshi
puts B_CONST # also displays Joshi
You can find examples of this kind of operation (modify) in the Rails
source code, where constants figure prominently and the objects they
represent undergo fairly frequent changes.
# const.rb
OUTER_CONST = 99
class Const
def get_const
CONST
end
CONST = OUTER_CONST + 1
end
puts Const.new.get_const
puts Const::CONST
puts ::OUTER_CONST
puts Const::NEW_CONST = 123
Modules/Mixins
# mytrig.rb
module Trig
PI = 3.1416
# class methods
def Trig.sin(x)
# ...
end
def Trig.cos(x)
# ...
end
end
# mymoral.rb
module Moral
VERY_BAD = 0
BAD = 1
def Moral.sin(badness)
# ...
end
end
# usemodule.rb
require 'mytrig'
require 'mymoral'
y = Trig.sin(Trig::PI/4)
wrongdoing = Moral.sin(Moral::VERY_BAD)
module Debug
include D
# Methods that act as queries are often
# named with a trailing ?
def who_am_i?
"#{self.class.name} (\##{self.object_id}): #{self.to_s}"
end
end
class Phonograph
# the include statement simply makes a reference
# to a named module
# If that module is in a separate file, use require
# to drag the file in
# before using include
include Debug
# ...
end
class EightTrack
include Debug
# ...
end
ph = Phonograph.new("West End Blues")
et = EightTrack.new("Real Pillow")
puts ph.who_am_i?
puts et.who_am_i?
stuff.rb
# A module may contain constants, methods and classes.
# No instances
module Stuff
C = 10
def Stuff.m(x) # prefix with the module name for a class method
C*x
end
def p(x) # an instance method, mixin for other classes
C + x
end
class T
@t = 2
end
end
puts Stuff::C # Stuff namespace
puts Stuff.m(3) # like a class method
x = Stuff::T.new
# uninitialized constant error, if you try the following
# puts C
stuffusage.rb
require ‘stuff' # loads Stuff module from Stuff.rb
# $: is a system variable -- contains the path
# for loads
class D
The Rails source code makes heavy use of modules, in particular the
technique of reopening the definition bodies of both classes and
modules.
Regular Expressions
You could write a pattern that matches a string containing the text
Pune or the text Ruby using the following regular expression:
/Pune|Ruby/
The forward slashes delimit the pattern, which consists of the two
things we are matching, separated by a pipe character (|). The pipe
character means “either the thing on the right or the thing on the left,”
in this case Pune or Ruby.
Literal characters
Character classes
A character class is an explicit list of characters, placed inside the
regular expression in square brackets:
/[dr]ejected/
This means “match either d or r, followed by ejected. This new pattern
matches either “dejected” or “rejected” but not “&ejected”. A character
class is a kind of quasi-wildcard: It allows for multiple possible
characters, but only a limited number of them.
Every match operation either succeeds or fails. Let’s start with the
simpler case: failure. When you try to match a string to a pattern, and
the string doesn’t match, the result is always nil:
/a/.match("b")
# nil
This nil stands in for the false or no answer when you treat the match
as a true/false test.
To use the MatchData object, you must first save it. Consider an
example where we want to pluck a phone number from a string and
save the various parts of it (area code, exchange, number) in
groupings. Example regexp.rb
>ruby regexp.rb
The whole string we started with: My phone number is (123) 555-1234.
The entire part of the string that matched: (123) 555-1234
The three captures:
Capture #1: 123
Capture #2: 555
Capture #3: 1234
Here's another way to get at the first capture:
Capture #1: 123
>Exit code: 0
The above topic has been adapted from the Ruby for Rails book.
Unit Testing
Basically, unit testing helps you write code more quickly and with
more confidence. You write smaller units of code at a time, reducing
the number of bugs you introduce. You see the bugs immediately as a
failing test and know exactly where to look for them in the code.
It might seem backwards, but coding test first really does mean that
you write your tests before you start writing code. Doing so gives you
some boundaries for your coding: You know what to write and when
to stop writing code. Ruby comes with one preinstalled, Nathaniel
Talbott’s Test::Unit framework that actually does most of the heavy
lifting to make this process work. It creates the test runner and
collects the tests into cases and suites for you.
A rule of thumb: Test anything that's likely to fail. You want to write
tests to ensure that your method does what it's supposed to do with
normal input. You should check that invalid input will be handled
correctly. Finally, test your boundary conditions -- the extreme edges
of your expected input.
As you can see above, your testing class (which is called a test suite)
inherits from Test::Unit::TestCase and each of your tests is a method
called test_that_thing_I_wanted_to_test. Within those tests, you use
assertions to test whether conditions are correct in each situation.
Let's take a look at what kinds of assertions exist for you to test
with. Nearly every assertion takes an optional message string,
which is used to provide more information in the case of test failure.
• assert_not_nil(object, [message])
• assert_nothing_thrown([message], &proc)
Keeping in mind that coding test first really does mean that you write
your tests before you start writing code, we now write a test suite
# testradius.rb
require 'test/unit'
require 'radius'
class TestRadius < Test::Unit::TestCase
def test_key
robj = Radius.new('78')
assert_equal('78', robj.key)
end
end
Let's step through the example and see what needs to be done. Lines 1
and 2 pull in the libraries you need: Test::Unit (line 1) and radius (line
2). Lines 3-8 define the first TestCase, a class for testing Radius
objects. In line 3, you're making the new class a subclass of
Test::Unit::TestCase. Lines 4-7 define your first test, the test_key
method of the TestRadius class.
Because you're writing your application test first, you've written these
tests before you've written any code. The test suite actually forms a
Ruby program on its own. When you try to run it for the first time, it
will fail:
>ruby testradius.rb
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in
`gem_original_require': no such file to load -- radius (LoadError)
from
c:/ruby/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in
`require'
from testradius.rb:2
>Exit code: 1
>ruby testradius.rb
Loaded suite testradius
Started
E
Finished in 0.0 seconds.
1) Error:
test_key(TestRadius):
NameError: uninitialized constant TestRadius::Radius
testradius.rb:5:in `test_key'
1 tests, 0 assertions, 0 failures, 1 errors
>Exit code: 1
Better, but still not quite what you want. This error message means
that your test has no Radius class to use. So, define the Radius class
as follows:
class Radius
end
>ruby testradius.rb
Loaded suite testradius
Started
E
Finished in 0.0 seconds.
1) Error:
test_key(TestRadius):
ArgumentError: wrong number of arguments (1 for 0)
testradius.rb:5:in `initialize'
testradius.rb:5:in `test_key'
1 tests, 0 assertions, 0 failures, 1 errors
>Exit code: 1
To fix these errors, you're going to have to write some actual code.
When you stepped away from the test suite, it still generated errors
because there wasn't a real initializer. You can fix that by writing an
initializer for the object.
class Radius
def initialize(key)
@key = key
end
end
>ruby testradius.rb
Loaded suite testradius
Started
E
Finished in 0.0 seconds.
1) Error:
test_key(TestRadius):
NoMethodError: undefined method `key' for #<Radius:0x2cc6224
@key="78">
testradius.rb:6:in `test_key'
You're almost there. The next step is to create the getter method key.
Ruby makes this process easy. Your whole getter is shown on line 2:
class Radius
attr_reader :key
def initialize(key)
@key = key
end
end
You've written just enough code to pass your first test. The Radius
class can be initialized (with a key) and has a getter method that
returns the key. Now when you run your test suite, you see:
>ruby testradius.rb
Loaded suite testradius
Started
.
Finished in 0.0 seconds.
Consider writing some additional tests to make sure your code doesn't
fail. This method is simple, but what happens if you pass in a key
that's not a String object? If it's a Fixnum object, you can just convert it
to a String object. If it's anything else, your initialize should return
nil. Here's what your new tests should look like:
require 'test/unit'
require 'radius'
class TestRadius < Test::Unit::TestCase
def test_key
robj = Radius.new('78')
assert_equal('78', robj.key)
robj = Radius.new(78)
assert_equal('78', robj.key)
robj = Radius.new([78])
assert_nil(robj.key)
end
end
You know that this code will fail, but run it anyway. You should see
the following output:
>ruby testradius.rb
Loaded suite testradius
Started
F
Finished in 0.016 seconds.
1) Failure:
test_key(TestRadius) [testradius.rb:8]:
<"78"> expected but was
<78>.
1 tests, 2 assertions, 1 failures, 0 errors
>Exit code: 1
The report from your last test run points out some new information.
First of all, you get to see what a failure looks like, instead of an error.
This failure is significant because it means that your test and code
work, but not correctly. You've got a bug. Second, you see that the
report shows only two assertions, even though you wrote three. The
report shows only two assertions because Test::Unit won't continue
past the first failure. (That way, you can focus on getting your code
right one test at a time.)
To fix the first failure, ensure that Fixnum objects are converted to
String objects by changing your code as follows:
class Radius
attr_reader :key
def initialize(key)
@key = key
if @key.class == Fixnum then
@key = @key.to_s
end
end
end
>ruby testradius.rb
Loaded suite testradius
Started
F
Finished in 0.015 seconds.
1) Failure:
test_key(TestRadius) [testradius.rb:10]:
<nil> expected but was
<[78]>.
1 tests, 3 assertions, 1 failures, 0 errors
>Exit code: 1
Now you can fix the next failure with the code by converting arrays to
strings with the to_s method:
class Radius
attr_reader :key
def initialize(key)
@key = key
if @key.class == Fixnum then
@key = @key.to_s
end
if @key.class != String then
@key = nil
end
end
end
>ruby testradius.rb
Loaded suite testradius
Started
.
Finished in 0.0 seconds.
Nearly every test you write for Radius uses the robj object. Instead of
creating the object each time, you can use a setup method to do it for
you. Test::Unit has two special methods: setup and teardown. If a test
class has a setup method, it will be called before any of the assertion
methods. Conversely, any clean-up code that is required after each
test method runs can be placed in a method named teardown.
def setup
robj = Radius.new('78')
end
def test_key
assert_equal('78', robj.key)
# additional functionality removed for brevity
end
The entire topic has been adapted from the articles on this subject by
Pat Eyler, Kevin Clark and the Ruby Cookbook.
Assignment:
Write a test suite for a simple class called Student. This class stores a
first name, a last name, and an age: a person’s full name is available
as a computed value.
Basic Networking
Let us talk a little bit about basic networking.
Internet Addresses
These are the numerical host addresses that consist of four bytes
such as 132.163.4.102. The IP address 127.0.0.1 (localhost) is a special
address, called the local loopback address, which denotes the local machine.
Sockets
Socket classes
Ruby has a rich and diverse set of sockets classes. These classes
range from the standard Socket class (which mimics the BSD Sockets
API) to more refined classes that focus on a particular protocol or
application type.
Thread.start(dts.accept) do |s|
print(s, " is accepted\n")
s.write(Time.now)
print(s, " is gone\n")
s.close
end
end
Since we are testing these programs on our local machine, open a new
command window, go to the folder containing your dtserver.rb
program and type ruby dtserver.rb. The program runs and waits for a
client to connect at port 20000.
Summary
This topic has explored the classes that make networking application
development possible in Ruby. It discussed the basic classes for
sockets programming (such as the Socket class) and also the classes
that help to simplify sockets programming in Ruby, such as TCPSocket
and TCPServer.
I’ll build the SMTP client and the explanation follows. Let’s call it
rubysmtp.rb
# rubysmtp.rb
require 'net/smtp'
user_from = "[email protected]"
user_to = "[email protected]"
the_email = "From: [email protected]\nSubject: Hello\n\nEmail by
Ruby.\n\n"
# handling exceptions
begin
Net::SMTP.start('auth.smtp.1and1.co.uk', 25, '1and1.co.uk',
'[email protected]', 'password', :login) do |smtpclient|
smtpclient.send_message(the_email, user_from, user_to)
end
rescue Exception => e
print "Exception occured: " + e
end
the string, are converted to the CRLF pair. You cannot send a
binary message with this method. the_email should include both
the message headers and body. user_from is a String representing
the source mail address. user_to is a String or Array of Strings,
representing the destination mail address or addresses
• The way we have used Net::SMTP.start it finishes the SMTP
session and closes TCP connection
• We have also used begin and rescue for Exception handling
part of the Ruby standard library and implements SOAP version 1.1.
We use the SOAP RPC Driver in the soap4r library. The simple
program prints a quote of the day. It uses the SOAP RPC Driver to
connect to the SOAP web service at codingtheweb.com.
We can use the following site to decipher a WSDL file. Enter the
location of the web services' WSDL file and it will report back the
various methods/options available.
http://www.strikeiron.com/analyzer/onlineanalyzer.aspx
In our case, it tells us that the method to be used is getQuote and the
service is qotd.
require 'soap/rpc/driver'
driver = SOAP::RPC::Driver.new(
'http://webservices.codingtheweb.com/bin/qotd',
'urn:xmethods-qotd')
driver.add_method('getQuote')
puts driver.getQuote
Once the driver is set up, we define the web service method we want
to call (getQuote). We can then call it like a normal Ruby method and
display the result. Behind the scenes, the call to add_method actually
defines a new method on the SOAP::RPC::Driver object. The SOAP
library uses metaprogramming to create custom Ruby methods that
act like SOAP methods.
Again thanks to Kevin Marshall for this too. Let’s say that PuneRuby
wants to host a SOAP-based web service (we are going to build a trivial
one just to show how it is done) using a standalone server (that is, not
as part of a Rails application).
3. Finally, set up and start your server. Our example runs on port
12321 on www.puneruby.com It’s name is ‘PuneRubyServer’
and its namespace is ‘urn:mySoapServer’:
server = MyServer.new('PuneRubyServer','urn:mySoapServer',
'www.puneruby.com',12321)
trap('INT') {server.shutdown}
server.start
To test your service, start your server in one Ruby session and then
use the simple script (prserver.rb) below in another Ruby session to
call the method it exposes:
require 'soap/rpc/driver'
driver =
SOAP::RPC::Driver.new('http://217.160.200.122:12321/',
‘urn:mySoapServer')
driver.add_method('sayhelloto', 'username')
puts driver.sayhelloto('PuneRuby')
Ruby/Tk
Simple Tk applications
Let’s understand the following program hellotk.rb
require 'tk'
hello = TkRoot.new {title "Hello World"}
Tk.mainloop
program, we use the title and minsize instance methods (of module
Tk::Wm) in the code block to TkRoot.new. We are now ready with our GUI
and we invoke Tk.mainloop
Let us now add some widgets to the above program, namely a TkLabel.
The modified program is hellotk1.rb
require 'tk'
hello = TkRoot.new do
title "Hello World"
# the min size of window
minsize(400,400)
end
TkLabel.new(hello) do
text 'Hello World'
foreground 'red'
pack { padx 15; pady 15; side 'left'}
end
Tk.mainloop
We also need to be able to get information back from our widgets while
our program is running by setting up callbacks (routines invoked
when certain events happen) and sharing data. The next example,
hellotk2.rb does that.
require 'tk'
TkButton.new do
text "EXIT"
command { exit }
pack('side'=>'left', 'padx'=>10, 'pady'=>10)
end
Tk.mainloop
Callbacks are very easy to setup. The command option takes a Proc
object, which will be called when the callback fires. This means that
the code block passed into the command method is run when the user
clicks the button, allowing you to programmatically execute the same
functionality that would be invoked on an actual button press. Note
that the Kernel#exit method terminates your program here.
require 'soap/rpc/driver'
require 'tk'
class SOAPGuiClient
def connect
@buttonconnect.configure('text' => 'Reset')
@buttonconnect.command { reset }
begin
driver = SOAP::RPC::Driver.new('http://217.160.200.122:12321/',
'urn:mySoapServer')
driver.add_method('sayhelloto', 'username')
s = driver.sayhelloto('Satish Talim')
rescue Exception => e
s = "Exception occured: " + e
ensure
@label.configure('text' => s)
end
end #connect
def reset
@label.configure('text' => "")
@buttonconnect.configure('text' => 'Connect')
@buttonconnect.command { connect }
end # reset
#---
def initialize
root = TkRoot.new do
title 'SOAP Client'
# the min size of window
minsize(535, 100)
end
#---
@label = TkLabel.new(root) do
pack
end
#---
@buttonconnect = TkButton.new(root)
@buttonconnect.configure('text' => 'Connect')
@buttonconnect.command { connect }
@buttonconnect.pack('side'=>'bottom')
#---
Tk.mainloop
end #initialize
end # class
SOAPGuiClient.new
#---
require 'logger'
require 'soap/rpc/standaloneServer'
class MyServer < SOAP::RPC::StandaloneServer
def initialize(* args)
super
add_method(self, 'sayhelloto', 'username')
@log = Logger.new("soapserver.log", 5, 10*1024)
end
def sayhelloto(username)
t = Time.now
@log.info("#{username} logged on #{t}")
"Hello, #{username} on #{t}."
end
end
server =
MyServer.new('PuneRubyServer','urn:mySoapServer','www.puneruby.com',1
2321)
trap('INT') {server.shutdown}
server.start
Using Ruby/MySQL
Now, run the mysql client program from the command line, as:
C:\>mysql
You should get the mysql prompt. Next, create a database ruby as:
As a first exercise we try to connect to the MySQL server and print all
the names in the table student. Program rubymysql.rb
require 'mysql'
#my = Mysql.new(hostname, username, password, databasename)
con = Mysql.new('localhost', '', '', 'ruby')
rs = con.query('select * from student')
rs.each_hash {|h| puts h['name']}
con.close
Ruby Tools
I have not gone into the details of some of the important tools that
Ruby provides for programmers. Here’s a brief idea:
irb
irb is an interactive interpreter – which means that instead of
processing a file, it processes what you type during a session. irb is a
great tool for testing Ruby code and for learning Ruby. Since I am
using SciTE, I have not gone into the details of irb. You can refer to
this and this resource for details of irb.
The debugger
Debugging – fixing errors – is part of programming. The Ruby
debugging facility (found in the library file debug.rb) helps you debug
a program by letting you run the program one instruction at a time,
with pauses in between. During the pauses, you are presented with a
prompt; at this prompt, you can examine the values of variables, see
where you are in a nested series of commands, and resume execution.
You can also set breakpoints – places in your program where the
debugger stops execution and presents you with a prompt. You can
refer to this and this resource for details of the debugger.
Profiling
In programming terms, profiling means measuring how much use is
made of system resources - time, principally - by different parts of
your program. This starts to matter with longer programs, particularly
programs that involve looping through instructions many times (for
example, a program that reads in a long file and examines or modifies
the contents of each line as it’s read in). None of the examples on this
site require profiling, because they’re short and simple.
ri and RDoc
ri (Ruby Index) and RDoc (Ruby Documentation) are a closely related
pair of tools for providing documentation about Ruby programs. ri is a
command-line tool; the RDoc system includes the command-line tool
rdoc. ri and rdoc are standalone programs; you run them from the
command line.
ri dovetails with RDoc: It gives you a way to view the information that
RDoc has extracted and organized. Specifically (although not
exclusively, if you customize it), ri is configured to display the RDoc
information from the Ruby source files. Thus on any system that has
Ruby fully installed, you can get detailed information about Ruby with
a simple command-line invocation of ri. Some more information is
available here.
ERb
Ruby provides you with a program called ERb (Embedded Ruby),
written by Seki Masatoshi. ERb allows you to put Ruby code inside an
HTML file.
All the details on this page has been adapted from David Black’s very
informative book – Ruby for Rails.
JRuby
Similarities
Differences
• you don’t need to compile your code. You just run it directly.
• there’s different GUI toolkits. Ruby users can try WxRuby, FXRuby,
or the bundled-in Ruby Tk for example.
• you use the end keyword after defining things like classes, instead
of having to put braces around blocks of code.
• you have require instead of import.
• all member variables are private. From the outside, you access
everything via methods.
• parentheses in method calls are usually optional and often omitted.
• everything is an object, including numbers like 2 and 3.14159.
Classes are objects! For example, Array is a constant name that is
bound to the Array class object. To create a new object, we call new
on the class object as in a = Array.new
• there are no primitives or data types
• variable names are just labels (not objects). They don’t have a type
associated with them.
• there’s no type declarations. You just assign to new variable names
as-needed and they just “spring up” (i.e. a = [1,2,3] rather than int[]
a = {1,2,3};).
• it’s foo = Foo.new("hi") instead of foo = new Foo( "hi" ).
• the constructing and initializing phases of an object are separate
and both can be over-ridden. The initialization is done via the
initialize instance method while the construction is done via the
new class method. initialize is not a constructor!
• you have “mixin’s” instead of interfaces. mixins are examples of
implementation inheritance, they are not equivalent to Java
interfaces
• YAML tends to be favoured over XML.
• it’s nil instead of null. Also, nil is a normal object; you can never
get a null pointer error!
• there is no method overloading.
• it's much more common to put many classes in the same file.
• strings are mutable, unless you freeze them
Appendix A
Ruby Resources
Books/eBooks/Magazines
Useful Links
About.com on Ruby
Ruby Manual
Ruby Documentation
The Ruby Language FAQ
Ruby Coding Convention
Ruby Quick Reference
Ruby Cookbook
More Cookbook
Ruby Weekly News
The Ruby FAQ
Ruby Language FAQ
Don Craig's Presentation
Ruby News
Ruby Notes
Ruby Jobs
Ruby Quiz
RubyIdioms
Ruby NewComers
Misc. Ruby Programs
SciTE Text Editor
NotePad2 for Ruby
FreeRIDE Ruby IDE
Ruby jEdit
RubyGarden
Official Ruby Homepage
PuneRuby Homepage
Ruby Window's Installer
Rubyholic
Code Snippets
Java to Ruby
TryRuby
The Unofficial Ruby Usage Guide
Ruby/Tk FAQ
Ruby/Tk Tutorial
Ruby/Tk Documentation
Freelance Ruby Projects
Ruby MySQL Module
Ruby Reports
Ruby Internals
Programming Language Comparison
Ruby Cheat Sheet
Ruby mySQL module
Test Driven Development
TIOBE Ruby Index
Ruby API Search
Blogs
PuneRuby blog
ChadFowler.com
Loud Thinking
Bruce Tate's Blog
PragDave
O'Reilly Ruby
Andy Hunt
Mindstorm
Open Source Initiative
Forums/User Groups
Ruby-Talk Forum
SitePoint's Ruby Forum
Ruby IndiaGroup
TRUG
MangoSpring, Pune
Persistent Systems, Pune
Thoughtworks, Pune
Allerin, Mumbai
Pinstorm, Mumbai
SurgeWorks, Mumbai
Urban Eye, Mumbai
V2Solutions, Mumbai
360 Degree Interactive, Chennai
Viamentis Technologies Pvt. Ltd., Chennai
BroadSpire, Chennai
Accenture Tech Labs, Bangalore
Aditi Technologies, Bangalore
Apptility, Bangalore
CircleSource, Bangalore
Itellix, Bangalore
Subex Azure, Bangalore
Satyam, Hyderabad
WarmlyYours, Hyderabad
Uzanto, New Delhi
Vinayak Solutions Pvt. Ltd., New Delhi
Induslogic, Noida
Appendix B
Solutions to Assignments
1. Write a Ruby program that tells you how many minutes are there
in a year (do not bother right now about leap years etc.).
puts 365*24*60
3. Write a Ruby program that asks for a year and then displays to the
user whether the year entered by him/her is a leap year or not.
Program leapyear.rb
=begin
Program to determine if a year is a leap year.
To determine if a year is a leap year, follow these steps:
1. If the year is evenly divisible by 4, go to step 2.
Otherwise, go to step 5.
2. If the year is evenly divisible by 100, go to step 3.
Otherwise, go to step 4.
3. If the year is evenly divisible by 400, go to step 4.
Otherwise, go to step 5.
4. The year is a leap year (it has 366 days).
5. The year is not a leap year (it has 365 days).
4. Write a method leap_year. Accept a year value from the user, check
whether it’s a leap year and then display the number of minutes in
that year. Program leapyearmtd.rb
def leap_year(input_year)
((input_year % 4 == 0) && (input_year % 100 > 0)) ||
(input_year % 400 == 0)
end
# Get the input and determine if it is a leap year
puts "Enter the year"
STDOUT.flush
input_year = gets.chomp.to_i
if leap_year(input_year)
puts "Year #{input_year} is a leap year and has #{366*60*24}
minutes in the year"
else
puts "Year #{input_year} is not a leap year and has #{365*60*24}
minutes in the year"
end
a = ( 1930...1951).to_a
puts 'Enter your response: '
STDOUT.flush
until (response = gets.chomp).eql?('BYE')
if (response.eql?(response.upcase ))
puts 'NO, NOT SINCE ' + a[rand(a.size)].to_s + ' !'
else
puts 'HUH?! SPEAK UP, SONNY!'
end
puts 'Enter your response: '
STDOUT.flush
end
a = ( 1930...1951).to_a
puts 'Enter your response: '
STDOUT.flush
until (response = gets.chomp).eql?('BYE BYE BYE')
if response.eql?('BYE')
# do nothing
elsif response.eql?(response.upcase)
puts 'NO, NOT SINCE ' + a[rand(a.size)].to_s + ' !'
else
puts 'HUH?! SPEAK UP, SONNY!'
end
puts 'Enter your response: '
STDOUT.flush
end
collection = [1, 2, 3, 4, 5]
sum = 0
collection.each {|i| sum += i}
puts sum
11. Write a test suite for a simple class called Student. This class
stores a first name, a last name, and an age: a person’s full name
is available as a computed value.
Appendix C
Over a period of time, I spoke to various Ruby / Rails Gurus. Here are
the links.
David Hansson
Bruce Tate
Elliot Temple
Peat Bakke
Phil Tomson
Topher Cyll
James Gray II
Chris Anderson
Peter Cooper
Pat Eyler
Jens-Christian Fischer
Shashank Date
Chang Sau Sheong
Jeremy Voorhis
Charles Nutter
Rida Al Barazi
Dalibor Sramek
Duncan Beevers
Premshree S. Pillai
Yogi Kulkarni
Olle Jonsson
Roberto Nogueira
Robert Evans
Manik Juneja
Kevin Marshall
Obie Fernandez
Karmen Blake
Francisco Alves Cabrita