Ruby language Details

This chapter will concern itself with a broad overview of the Ruby Language.   It is not meant to be a detailed explanation.   If the reader is already familiar with the Ruby,  this chapter may be skipped.

For a more detailed discussion of the language,  get a copy of  Programming Ruby  by  Dave Thomas.   There are also many free sources of information available on the Web.

Literals

The use of literals  gives Ruby much of it's expressive power.   While a single literals are powerful,  it is the ability to assemble complicated literals that sets Ruby apart.   In the coming sections we will discuss a wide variety of literal types.  

The following are some examples of literal expressions:

String Literal:     s = "text string"
Array Literal:      a = [1, "tmp", 30]
Hash Literal:       h = {'cello' => 'string'. 'horn' => 'brass'}
Symbol Literal:     s = :name
Numeric Literal:    n = 3.0
RegExp Literal:     /pattern/

Character strings

We group of sections will discuss character strings and regular expressions.   Literals are often used to initialize strings and create regular expressions.   These features are used extensively within Ruby.

Single quoted character strings

Single quoted strings only allow two substitutions.   Two backslashes,  ' \ \',  are replaced by a single backslash.   A backslash followed by a single quote is interpreted as a literal single quote,  that is,  just as a character.

'string'         # "string"     - Simple text
'\\new doc'      # "\new doc"   - Literal backslash followed by text
'\n'             # "\n"         - literal '\n' not a newline!
'\''             # "'"          - A string consisting of a single quote

One of the features of string literals, some say a quirk,  is that carriage returns do not break a literal string.   That is,  the carriage returned is part of the string.  

'Multi
     Line
String'           # String = "Multi\n   Line\nString"

---Translation Here about Multi-byte Character Strings --------

Double quote character strings

Double quoted strings provide more character processing when building the literal.   First,  it provides for substations for character groups starting with a backslash.   Second,  it allows for expression interpolation within the string body.

The following is a list backslash substitutions supported by double quoted strings:

\t        tab  - (0x09) 
\n        newline  - (0x0a) 
\r        carriage return  - (0x0d) 
\f        form feed  - (0x0c) 
\b        backspace  - (0x08) 
\a        bell  - (0x07) 
\e        escape  - (0x1b) 
\s        whitespace  - (0x20) 
\nnn      character in octal value nnn 
\xnn      character in hexadecimal value nn 
\cx       control x 
\C-x      control x 
\M-x      meta(alt) x  
\M-\C-x   meta(alt) control x
\x        character x itself 

One of the powerful features of double quoted string literals is the ability to embed expression interpolation into the string body.  Within the a string, the sequence #{expression}  is replaced by the value of that expression.

lvar = 2  
@ivar = 3

def md(arg) ... end        # md(1) returns "Hello" 
                           # md(2) returns ""

a = "text #{lvar}"         # "text 2"
b = "text #{@ivar}"        # "text 3"
c = "text #{1 + @ivar}"    # "text 4
d = "#{md(1)}, World!"     # "Hello, World!"
e = "#{md(2)}abcd""        # "abcd"
f = "#{"Nested"} String"   # "Nested String 

One Ruby short-cut is that when the expression is simply a global,  instance,  or class variable,  the braces can be dispensed with.    However, local variables must be  parenthesized or the result will be just the insertion of the literal  '#<local>'.

a = "text #@ivar"          # "text 3"

b = "text #lvar"           # "text #lvar"  -- Local Variable must be
                                              parenthesized (Next Line) 
c = "text #{lvar}"         # "text 2"

Delimited character strings

There are five basic forms of delimited string literals.

1.  %q        Single-quoted string
2.  %Q        Double-quoted string
3.  %         Double-quoted string
4.  %w        Array of Strings (Single-quote processing)
5   %W        Array of Strings (Double-quote processing)

All of these forms consist a ' %'  sign followed by a classification character,  with the exception of item 3.   A bare ' %'  is treated the same as a ' %Q'

The first character following one these forms becomes the delimiter  for the string.   The delimiting character can be any non-alphanumeric  or non-mulitbyte  character.   Except for the four(4) paired bracketing  characters  ' ( [ { <',  the delimiting character is also used to terminate the string literal.   In the case of the characters  ' ( [ { <',  the closing delimiter must be the closing bracketing  character   ' ) ] } >'  that matches the opening delimiter.  

The examples below are for table item 1 shown above.   Items four and five, array of strings processing,  will be discussed separately.

%q(--text--)               # "--text--"
%q#--text--#               # "--text--"

%q2--text--2               # Error:  unknown type of %string
%qz--text--z               # Error:  unknown type of %string

The valid use of  ' # '  may be a surprise.   This is an exception to the rule that  ' # '  character starts a comment.   This is because since it is accepted as a delmiter,  the parser will not look for comments until the matching character is found that terminates the string liberal.

The type  character  ' Q '  and the bare ' %'  sign indicate double quote processing.   These are listed as item 2 and 3 in the table above.

%Q(\ttext)                 # "<tab>text"
%Q#--text--#                # "--text--"

%(text \010here)            # "texthere"

a = "one"
b = "two"
%|#{a + b}three|            # "onetwothere"

The items 4 and 5  from the table above are for processing an array of strings.   These forms are a short cut for generating lists of strings.   For example,  the normal method to create a list of animals is as follows:

arr = ['cat', 'dog', 'whale', 'tiger', 'great\ dane' ]

Notice that this list is being created with single quote strings.   As we have discussed before,  the only processing performed is substitution of backslash quoted backslash,  '\\',  and a backslash quoted single quote,  '  \'  '.  

With item four,  ' %w'  an array of strings is created.  However,  since both  ' %w' and ' %W'  parse a white-space delimited list,  Ruby allows these constructions an additional processing option,  a backslash quoted space,  '  '.    The following example demonstrates this feature.

arr = %w(cat dog whale tiger great\ dane)

printf("%s\n", arr[4])          # "great dane"

One of the benefits is that since delimited input is that you can chose the delimited character.   You must, at times, create string literals that contain many characters that are normally standard delimiters.   These most often include, single and double quotes, and slashes.   By choosing the right delimiter the literal is much easier to create.

list1 = "The quote was \"test the waters\" and \" to be sure\""

list1 = %q(The quote was "test the waters" and " to be sure")

Here Document

The here document  is yet another way to create strings without having to resort to quoting functions.   A here document starts with initial character string,  either   ' << '  or  ' <<-'.   Immediately after the initial starting string,  the following characters until terminated by white-space or a carriage return,  will constitute the terminating character string.   The following example produces strings with the same contents:

str1 = << EOS       
  test line 1
test line 2
  test line 3
EOS
#
printf("%s\n", str1)
#
str2 = "  test line 1
test line 2
  test line 3
"
#
printf("%s", str2)

The result of the program fragment above is as follows:

  test line 1
test line 2
  test line 3

  test line 1
test line 2
  test line 3

Letter

A letter in a string can is equivalent to the integer value stored the that character position,  at least if it not a multi-byte character.   There are several ways to store non-printing characters in a string location.

C1 = "\n"                 # By a standard backslash code
C2 = "\C-j"               # By the equivalent Control Code
#
p "C1 = #{C1.to_s}"
p "C2 = #{C2.to_s}"

The result is:

"C1 = \n"
"C2 = \n"

Regular expression

A Regular Expression  in Ruby is delimited by forward slashes.

/regular expression/

A Regular Expression literals are a object of type Regexp.   They can be created in the following ways,  either with or without an option specification.

1.  /pattern/              
2.  /pattern/i 
3.  %r{pattern}
4.  %r{pattern}i
5.  Regexp.new('pattern'))
6.  Regexp.new('pattern', 'i' )
Ignoring for the moment what patterns consist of,  the processing of such patterns can be modified by appending an option.   Some of these options are:
 i  - Case Insensitive
 m  - Multiline Mode
 x  - Extended Mode

Regexp is an abbreviation of Regular Expression and will be used interchangeably with it.

The thing to remember about Regexp is that it is a language  with exacting rules.   However,  Regexp is a powerful tool for parsing strings in a wide variety of circumstances.   It is a subject that every programmer should be familiar with.

Let us start with a simple Regexp,  matching it with a text string.   If there is only text and no Regular Expression operators,  the Regexp will match any string that contains the text specified in the Regular Expression embedded within it.   In this and following examples the  ' =~'  operator compares a Regexp with a normal string and returns true if the Regexp matches a portion of text within the string.

/abc/  =~ 'zxyabcdef'     # Returns TRUE
/ABC/  =~ 'zxyabcdef'     # Returns FALSE
/ABC/i =~ 'zxyabcdef'     # Returns TRUE

The next examples are using the Regexp operators  ' ^',   ' $'  .   These operators match the beginning of the line  and End of the line  respectfully.   These operators are called Anchors.   They only match text that is immediately adjacent the beginning or end of a line.

/^From:/ =~ "From: jack"    # Returns TRUE
/^ To:/  =~ "To: jill"     # Returns FALSE

/end$/   =~ "to the end"    # Returns TRUE
/end$/   =~ "to an end!"    # Returns False

The language of Regular Expressions are made up characters that represent themselves and characters the are Regular Expression Operaters.   The following are these operators:

These characters are used to form Regular Expressions Operators:

.     --period
|     --Bar
(  )  --Open/Close Parenthesis 
[  ]  --Open/Close Brackets 
{  }  --Open/Close Braces
+     --Plus
\     --Backslash
^     --circumflex 
$     --Currency Sign
*     --Asterisk
?     --Question Mark

Some these characters stand by themselves,  such as  ' .'  which represents any character except end of line.   Note:  there is an exception to even this simple operator if single line mode is on!

Other operators introduce whole classes of additional operators.   Backslash is such an operator that not only quotes operator characters so the represent themselves,  but also represents character classes.

%  Regular expression

The study Regular Expressions is beyond the scope of this document.   Whole books written on the subject.   So far we have shown examples of creating Regexp literals by enclosing them within slashs.   We have also shown how compare Regexp and strings with the  ' =~'  Operator.   The following shows two other methods for creating Regular Expressions.

Regular Expressions also have a delimited input form, ' %r'.   For the same reasons that other delimited forms were convenient,  the ability to create Regular Expressions without having to quote special characters works well for these expressions..

%r{^From:} =~ "From: jack"    # Returns TRUE
%r{^ To:}  =~ "To: jill"      # Returns FALSE
%r{
%r{end$}   =~ "to the end"    # Returns TRUE
%r{end$}   =~ "to an end!"    # Returns False

Regular expression Objects

Up to now we have used Regexp literals for comparisons,  without specifying that we were actually creating objects of type Regexp.   The following forms all create Regexp Objects:

a = Regexp.new('\s+and\s+')      # Regexp that matches an "and" 
                                 # surrounded by white-space
b = /\s+and\s+/                  # -- same --
c = %r{\s+and\s+}                # -- same --

a =~ "jack and jill"             # Returns TRUE
a =~ "sand dunes"                # Returns TRUE

Array's

Arrays are sequential lists  of objects that can be stored,  modified,   and returned by using a wide variety of ruby commands.   Since Ruby employs  "duck"  typing,  arrays may contain a collection of non-heterogeneous items.

Array literals are created by enclosing a comma separated list of items between two brackets.   These items may be literals, variables, constants, symbols, or expressions combining these elements.

[ 1, 2, 3 ]
[ 'This', 'is', 'an', 'array', 'of' and 'string' ]

[ /regexp/, {'hash'=> 3} 4, 'string'?\C-a ]

Lvar = $gvar = @ivar = @@cvar = nil
[ Lvar, $gvar, @ivar and @@cvar ]

[ Object.New (), Object.New (), Object.New () ]

Arrays may also be created with the method new  and loaded in various ways.   One of the common ways is to create an empty array and push  items onto the end of the array.

The following example demonstrates this concept,  and additionally illustrates that each literal has a different  object id.

#!/usr/bin/ruby
#
i = 0
a = Array.new()
while i < 5
  a.push([1,2,3].id)         #The object ID is different for each pass 
  printf("%d)  %d\n",i,a[i]) #Print formatted result
  i = i + 1
end

#Result of program above:

0)  91447254836
1)  91447254776
2)  91447254716
3)  91447254656
4)  91447254596

Arrays of Strings

One that standard uses of array's is to contain a list of words,  in other words a array of strings.   This happens often enough that Ruby provides two delimited constructions to handle assembling such arrays.  

The  ' %w' and ' %W'  forms convert space delimited lists of strings into arrays of strings.   The lower case form uses single quote processing, with the addition of  ' \<spc>> '&  processing.   The upper case form, ' %W',  performs double quote processing on the individual strings!

%w(alpha beta gamma delta)  
   # Returns ['alpha', 'beta', 'gamma', 'delta']

%w<month fire water wooden gold earth day>
   # Returns ['month', 'water', 'gold', 'earth', 'day']

%w{Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec}
   # Returns ['jan','Feb','Mar','Apr', ... ,'Sep','Oct','Nov','Dec']

The  ' %W'  uses double quote processing on the isolated strings.   This means that expressions can be embedded within strings.   The following example shows the difference in the processing by the two forms.

n = 5
%w (list0 list #{n})       # Returns [ 'list0', 'list #{n}' ]
%W (list0 list #{n})       # Returns [ 'list0', 'list5' ]

Hash

A Hash Table  is an indexed collection of data accessible by a key.   Data stored in a Hash Table consists of a pair of values,  a  Key  and a value.   A Hash literal is constructed by enclosing in braces  Key/Value  pairs separated by commas.

{'key' => 'value', 'key2' => 'value2'}
{3 => 0, 'string' => 5, [ 'array' ] => 9}
{Object.New () => 3, Object.New () => 'string'}

A Hash literal can also be created over multiple lines
{0 => 0,
 1 => 3,
 2 => 6}

You will find that Hash Tables are the most commonly used data structures in Ruby.   Chapter 3,  "Name and Name Charts",  extensively documents Hash Table usage and construction.

The Hash Table is the most efficient to implement lookup tables.   As documented in the book,  they are used extensively in the construction of the Ruby Language itself.

New entries can be added to a Hash Table using the  ' [ ]= '  operator,  and retrieved with the  ' [ ] '  operator.   In addition to using Literals to populate a new Hash Table, the new  method can create an empty Hash Table.

h = Hash.new()
h['key1'] = 'value1'
h['key2'] = 'value2'     # h = {'key1' => 'value1', 'key2' => 'value2'}

a = h['key2']            # a = 'value2'

Range

A Range literal is the oddball which did not come from another language.   Ranges are often seen as a way create indexes for arrays.   When a Range is connected with two dots it includes the terminal value,  if three dots the terminal value is not included in the Range.

0..5               # 0 up to 5 (Including 5)
0...5              # 0 up to 5 (NOT including 5)
1+2..9             # 3 up to 9 (Including 9)
'a'..'z'           # 'a' thru 'z' (Including 'z')

Ranges provide three separate features: sequences, conditions, and intervals.

1. Sequences
2. Conditions
3. intervals

Sequences generate a series of objects which can be used feedstock for a variety of uses.   As stated above they can be used a index's.   They can also create an array and populate it.

(1..5).to_a        # a = [1,2,3,4,5]

The following example uses a Range to provide indexes:

#!/usr/bin/ruby
#
a = 10             
(1..5).each { |b|
  puts a + b
}

They can be used to create conditionals.   The following program prints out all input lines between the time it sees a "start"  until it sees a "end"  in the input stream.   Notice that a Regular Expressions are used for testing the line contents.

 
#!/usr/bin/ruby
#
while line = gets
   puts line if line =~ /start/..line =~/end/
end

Finally,  they can be used for providing intervals.

(1..10) === 5      # Returns TRUE
(1..10) === 15     # Returns FALSE

('a'..'j') === 'e' # Returns TRUE
('a'..'j') === 'm' # Returns FALSE

Intervals are often seen is case  statements:

kind = case year
     when 0..9 then "tens"
     when 10..99 then "hundreds"
     when 100..999 then "thousands"
     else               "Out of Range"
end

Symbol

Symbols  are an alternative to constants for creating names with unique values.   Often you wish to create a set of constants,  lets say the main points of a compass:  North,  East,  South,  and West.   The program does not care what the value is,  as long as it is unique.

#!/usr/bin/ruby
#
def walk(dir)
  case dir
    when :North
       puts "North Pasture"
    when :east
       puts "Homestead"
    when :South
       puts "ocean"
    when :West
       puts "city"
    else
       puts "unkown"
  end
end
#
   puts "Show Direction:"
   walk(:North)
   puts "Show Direction"
   walk(:South)
   puts "Done"

Hash keys are often symbols.   This ensures that the keys are unique.   Why?   By definition Symbols always have unique values  and are guaranteed to be the same value through out the program.

h = { :North => "NORTH", :South => "SOUTH" }

An Important aspect of symbols and strings is that they can be converted to each other.   This does not imply that symbols and strings are always replaceable with each other,  but they can substitute for each other in some cases.   For example a catch  block specification may be a string or a symbol:

#!/usr/bin/ruby
#
catch ("done") do                    # done is a string
  for num in (1..6)
    throw "done" if num == 4
    puts num
  end
    puts "DONE"
end
puts "After catch block"

  ------------------------

#!/usr/bin/ruby
#
catch (:done) do                     # done is a symbol
  for num in (1..6)
    throw :done if num == 4
    puts num
  end
    puts "DONE"
end
puts "After catch block"

Another place symbols are used is to specify names for attr_reader and attr_writer.   In the example below we replace long winded structure with the more compact attr_reader version.

class getdata                    # Long winded way
  def name
    @name
  end
  def age
    @age
  end
  def initialize(name, age)
    @name = name
    @age  = age
  end
end

class Getdata                    # With attr_reader form
  attr_reader :name, :age

  def initialize(name, age)
    @name = name
    @age  = age
  end
end

#Both classes are initialized the same way

ga = Getdata.new("john", "55")

#Both load variables of the same names
#Both produce the same output!

puts "Name = #{ga.name} -- Age = #{ga.age}"

Result:  Name = john -- Age = 55

Numerical value

Numerical values come in three basic flavors:  Fixnum,  Bignum,  and Floats.   Both Fixnum and Bignum store fixed point numbers. However, the Fixnum stores numbers as a binary word and that limits it size either 31 or 63 bits, depending on the word size of the processor.   Numbers larger than this size are stored as Bignum, which stores the number as a series of integers.   The only limit is the size of the available memory for Ruby.

Decimal Numbers:

Fixnum: 2
Fixnum: ::
Fixnum: 65536
Fixnum: 4294967296
Bignum: 18446744073709551616  # Exceed size of Fixnum

123_199_001        # Underbars are ignored in decimal numbers

Floats:

float: 12.34       # 12.34
float: -0.1234e2   # -12.34
float: 1234e-2     # 12.34

Method

Methods are the workhorses of Ruby.   If defined at the top level they become functional methods.   That is,  a method not bound to class.   All other methods are defined within the class definition,  and used to implement class functions.

Definition call

A method is created by def  statement.   It consists of the reserved word,  def,  followed by the name of the method.   The name is followed by the method parameters.   These may be enclosed with parenthesis,  but that is optional.

The following demonstrates both kinds of methods in a working program:

#!/usr/bin/ruby
#
def fun1(arg)
  puts "fun1 = #{arg}"
end

class C
  def initialize
    puts "Initialized!"
  end
  def method1(arg)
    puts "method1 = #{arg}"
  end
end
#
fun1(11)           # No class prefix required (Top-Level method)
c = C.new()        # Create instance of Class 'C'
c.method1(99)      # Execute it method!

Results:
fun1 = 11
Initialized!
method1 = 99

Value of method

The value  returned by a method is the last value evaluated by the method.   If a return statement is used,  it's argument is the last value evaluated.   It is stating the obvious,  that all methods return a value.

def rone()
  return 1
  999
end
#
def rboth(arg)
  if arg == 2
    return 2
  else
    999
  end
end
#
puts rone()
puts rboth(2)
puts rboth(1)

Results:
1
2
999

Abbreviation possible argument

Ruby makes it possible to define default arguments for methods.   Without this feature,  the following program would produce errors for dtest  and dtest().

def dtest(arg1=nil)
  if !arg1
    puts "Nil Arg"
  else
    puts "#{arg1}"
  end
end

dtest
dtest()
dtest(23)

Results:

Nil Arg
Nil Arg
23

However,  there is a rule the affects where default values may be used.   Default arguments must always follow arguments without defaults.   In particular,  you can not place defaulted arguments in the middle defaulted arguments.

The following produces a syntax error when run.

def wrong_decl (arg1, arg2 = nil and arg3)
  puts " #{arg2}"
end

Abbreviation of argument parenthesis

While parenthesis are certainly allowed in statements and expressions,  Ruby in many cases does not required the syntax queues that many other languages require.   If parenthesis are not required to parse a statement,  they are optional.

puts 'Hello, World!'      # Instead of:  puts('Hello, World!')
obj = Object.new          # Instead of:  obj = Object.new()  

As of Ruby 1.8.6 some of optional parenthesis were scheduled to be enforced future versions of Ruby.   The following is an example of where this enforcement was rescinded in Ruby 1.9.

#!./ruby
fname = "~/Bin/zznumb"
puts(File.basename(fname))           # zz:4
puts(File.basename fname)            # zz:5
puts File.basename fname

Results 1.8.6:
./zz:4: warning: parenthesize argument(s) for future version
./zz:5: warning: parenthesize argument(s) for future version
zznumb
zznumb
zznumb

Results 1.9.0:
zznumb
zznumb
zznumb

Another example of optional parenthesis is the definition and calling of methods.   Methods may have many parameters or none and parenthesis are still optional in many cases:

def method1 p1, p2, p3
  printf("P1: %s  P2: %s  P3: %s\n", p1,p2,p3)
end
def method2                     # No paramters
  puts "No parameters"
end

method1 "good", "bad", "ugly"
method2

Results:
P1: good  P2: bad  P3: ugly
No parameters

Variable Length Arguments

The may be times when a method be called with a unknown number of arguments.   Ruby supports variable length argument lists.   A variable length argument is made up of two parts.   First, zero to n number of fixed argument specifications,  followed parameter prefixed with a asterisk.

The fixed arguments must be present,  and not defaulted.   Any arguments beyond the fixed items are collected into an array and passed in last argument (i.e. *arg).

def varg(arg1, *rest)
  p "#arg1-#{arg1},  rest=#{rest.inspect}"
end

varg("one")
varg("one", "two")
varg("one", "two", "three)

Results:
"arg1-one,  rest=[]"
"arg1-one,  rest=[\"two\"]"
"arg1-one,  rest=[\"two\", \"three\"]"

Splat! Expanding collection arguments

The previous section created variable length argument lists using the asterisk prefix.   This section shows another use of the asterisk prefix.   The asterisk is often refereed to as splat,  due to the fact the an asterisk looks somewhat like a bug that hit a windshield!

You can convert any collection or enumerable object into its individual items and pass those

def do_job(a, b, c)
  p(a, b, c)
end

list = [ 1, 2, 3 ]
do_job(*list) 

results:
1
2
3

It is also possible to transfer arguments in this manner through more than one level. The following example extends the previous code.

def get_job(*args)
  do_job(*args)
end

get_job(*list)

results:             # do_job generates same result!
1
2
3

Various Method call types

Since almost no exception,  all expressions in Ruby are essentially method calls.   The presence of "syntactic sugar" sometimes masks this truth.   The following are method calls!

1 + 2                  # 1.+(2) 
a == b                 # a.==(b)
-/regexp/              # /regexp/.-
obj.attr = val         # obj.attr=(val)
obj[i]                 # obj.[](i)
obj[k] = v             # obj.[]=(k, v)

It takes some time to become accustomed to the object oriented constructions underlying the Ruby syntax.   For example,  "1 + 2" is actually:  Object '1',  receiving method '+',  with argument '2'.

This is because unlike some other object oriented languages,  even integers  are objects in Ruby.   So to perform calculations it necessary to apply methods to integers to get results.

(Message Passing)

Figure 1: Method Execution Sequence

Symbolic attachment

Standard method names come in three classes.   Names without the postfix symbols,  question marks and explanation marks,  are the primary methods.   The question mark postfix indicates that the method is a conditional method.   The explanation point postfix indicates a possibly dangerous method.   These methods are generally methods that make changes to objects in-place.

In the example below,  the include?  method returns true if the parameter is a sub-string of the receiver.

str = "Test Job xx"

str.include? "job"         # False (Compare is case sensitive)
str.include? "xx"          # True

In the example below,  downcase!  returns the updated string or nil if no changes are made.   Just downcase  always returns the updated string,  even if no changes are made.

str = "This is BIG"
#
s = str.downcase          # s = "this is big"  -- str = "This is BIG"
#
x = str.downcase!         # x = "this is big"  -- str = "this is big"
#
y = str.downcase!         # y = nil            -- str = "this is big"

Binary operators type

Ruby supports,  as most languages,  both binary and unary operators.   The most prevalent are the binary operators.   As the name binary suggest,  these operators take two parameters.  

1 + 2                # 1.+(2)  -- '1' is the receiver, + is the method,
                                  '2' is the methods parameter.

In more complicated expressions either operator precedence or parenthesis determine the order of execution.   The chart below shows the precedence of the variable binary operators:

(PRECEDENCE:  Top(highest) to bottom(lowest))
::
[]
**
-(unary) +(unary) ! ~
*  /  %
+  -
<<  >>
&
|  ^
>  >=  <  <=
<=> == === != =~ !~
&&
||
.. ...
=(+=, -=...)
not
and or

All of the above are just methods except these:

=, .., ..., !, not, &&, and, ||, or, !=, !~

In addition, assignment operators(+= etc.) are not user-definable.

Unary operator type

There are two unary operators,  '+',  and '-'.   As unary suggests these operators only take one parameter.

+2                    # A positive integer value of 2
-1.0                  # A negative floating value of minus 1.0

Attribute Writer Type

Objects often provide methods to access,  set,  and/or modify their internal states.   The internal states exposed in this manner are called Attributes. An Attribute writer takes the following form:

class C
  def arg=(new_value)
    @arg = new_value
  end
end

C.arg = next_value         # Sets '@arg' to 'next_value'

Ruby supplies a short cut for this structure,  the attr_writer  statement.   The following is exactly equivalent to the previous example.

class C
  attr_writer :arg
end

C.arg = next_value         # Sets '@arg' to 'next_value'

Attribute Reader Type

The attr_reader  statement works in a similar manner,  but is setup for read attribute operations.   The underlying form is as follows:

class C
  def arg
    @arg
  end
end

puts "Argument = #{C.arg}"         # Display's value of '@arg'

Using the attr_reader statement is exactly equivalent to the previous example.

class C
  attr_reader :arg
end

puts "Argument = #{C.arg}"         # Display's value of '@arg'

Index type

When left value side of an expression (the lvalue) is an array element reference,  the ' [ ]=' operator in the receiver is called.   It is passed with one or more indexes,  followed by the rvalue of the expression.

obj[1]    = "data one"       # obj.[]=(1, "data one")
obj[1, 3] = "data two"       # obj.[]=(1, 3, "data two")

Super

The key word super  is used to indicate that a method should execute it's parents version of a method before processing the local version.   This allows the inherited method behavior to be executed as part of the current method processing.   This is the heart of a feature,  that allows methods to inherit behavior's so that their functionality can be extended.

In the following example,  a method is defined in class A  that prints a message.   Then class B  is sub-classed from is parent A.   Both have a method named test.   Now,  normally this means that the method test  residing in class B  would be executed, for it would have precedence.

However,  the sub-classed method test  has the statement super  within it.   This means that the inherited method is called with any necessary parameters at that point.   Typically this means that any further processing for the method follows the super invocation.

class A
  def test(arg)
    puts "#{arg} executed in A"
  end
end
class B < A
  def test(arg, next)
    super(arg)
    puts "#{next} called from B"
  end
end

B.new.test("ARGA", "NEXT")

Results:
ARGA executed in A
NEXT Called from B

At this point,  we can use another short-cut provided by Ruby.   If the call to super  is execute with zero parameters  by a sub-classed method,  it will call the method in the super-class with the parameters that the current method received.   It should be noted that this assumes super-class has  a method of the same name.

Notice in the following example,  that zero parameters is not  the same thing as an empty parameter list.   The call super  is different from super().   The latter sends an empty  parameter list!

class A
  def test(*args)
    p args
  end
end

class B < A
  def test(a, b, c)
    super
  end
end

class C < A
  def test(a, b, c)
    super()
  end
end

B.new.test(1,2,3)
[1, 2, 3]
C.new.test(1,2,3)
[]

Access Control

Ruby defines three levels of protection for object, methods, modules and class constants.

  1. Public   -      Accessible to anyone.
  2. Protected -  Can be called only by objects of the defining class and it subclasses.
  3. Private -       Can only be called by the instances of the class in which they are defined.

Access control is set withing the class definition.   The default protection for classes and modules is private.  The default protection for structures is public.   The access control set will remain in effect until is changed or the cless definition is closed.

As a side note,  these three statements are methods  not reserved words!   They can and are most often used without parenthesis,  as in the example below.   When used in this manner they set the default protection  for all subsequently define methods until changed.

Class C
  public
  def a1 ()              # public
  def a2 ()              # public
                        
  private               
  def b1 ()              # private
  def b2 ()              # private
                        
  protected             
  def c1 ()              # protected
  def c2 ()              # protected
end

The alternate form,  names of methods and constants are sent as parameters.   This sets the protection for just the specified methods and constants.

Class C
  public
  def a1 ()              # public
  def a2 ()              # public
  def z1 ()              # public at the time of definition
   ::   ::            
  private(z1)            # private protection until changed

There is one subtle 'feature' related to access control and visibility.   It is possible in a sub-class definition to change the visibly of a method or constant in a super-class of a object.   In the sub-class changes the access control setting for a method in it's parent,  the visibility of the method is changed for sub-class instances.

class Base
   def btest
     puts "Test in Base Called!"
   end
   private :btest
end

class C < Base
  public :btest
end

class D < Base
end

c = C.new()
puts "Class C calling btest"
c.btest()
d = D.new()
puts "Class D calling btest"
d.btest()

Results:
Class C calling btest
Test in Base Called!
Class D calling btest
./zz:24: private method `btest' called for # (NoMethodError)

Module function

Normally the a module method can not be called until the module in mixed-in with a class.

module Math
  def sin(x)
    puts "Math 'sin(#{x}) was called!"
  end
  module_function :sin
end

include Math
sin(2)

Result:
Math 'sin(2) was called!

However, Ruby provides a way to call properly setup module functions without having to include the module.   This an advantage when one one tor two module functions are need.   The reason that the module methods can be called with polluting the name-space with unnecessary module functions and constants.

Ruby accomplishes this with the module_function()  method.   Within the Module the module_function() statement designates those methods that can be called externally without including the module itself.   The module_function() creates module functions for the named methods.   These are copies of the originals and may modified independently.   Note:  that the module itself is the receiver,  not the class calling the module method.

module Math
  def sin(x)
    puts "Math 'sin(#{x}) was called!"
  end
  module_function :sin
end

Math.sin(999)
include Math
sin(001)


# Math 'sin(999) was called!
# Math 'sin(1) was called!

Iterators

An iterators  is construction traverses a collection of objects and invokes a block  for each element in the collection.   We will look at the C/C++,  but keep in mind many other languages contain similar constructs.   These languages typically use for  and while  loops to traverse collections,  primarily arrays.

arr = [ 1, 3, 4 9, 14]              # Given: Used below also

for (i = 0; i < 5; i++) {           # 'for' loop
  -- action ---
}

i = 0
while i < 5 {                       # 'while' loop
  --- action ---
  i += 1;
}

Ruby blocks are groups of code inside a pair of parenthesis,  or a do ... end  construction.   When Ruby encounters a block it does not execute it,  rather it stores the code and the environment  at time it was encountered.   That includes local variables,  current object,  etc.   Also this block can only appear adjacent to the method call and starting on the source line.

arr.each do | item |
   puts "Next Item = #{item}"
end

Notice that an iterator does not need to be told the length of the collection.   Since Ruby has 'duck' typing,  we don't even need to know the type of elements in the collection.   This a much safer way to step through collections.

Comparison with the function of higher order

Some points about blocks need to be covered.   Methods can not transfer to plural blocks.   For example,  you might try to process a two dimensional array with two blocks:

array2D.each do | i |
  --- some action ---
end do | j |
  --- some action ---
end

This sort of processing is possible but must be contained inside one block.

array2D.each do | i, j |
  --- some action ---
end

Another point is the ability of block code to reference local variables outside the block itself.   The following is example of referencing a local variable.

lvar = 'ok'
[ 0,1,2 ].each do |i|
  puts "#{i} -- " + lvar
end

Result:
0 -- ok
1 -- ok
2 -- ok

There is a concern to be covered when modifying local variables outside the block.   If the block modifies the outer local variable,  it's value will reflect the final value set by the block when the block exits!   We will slightly modify the previous code to show this effect.

lvar = "OK"
[ 0,1,2 ].each do |i|
  if i == 2
    lvar = "Error"
  end
  puts "#{i} -- " + lvar
end
#
puts "Exiting value of 'lvar' " + lvar

Results:
0 -- OK
1 -- OK
2 -- Error
Exiting value of 'lvar' Error

Block local variable

If a variable occurs inside a block, but is not present in surrounding scope it will be local to the block and will disappear when the block ends.

[0, 2].each do |element|
  c = element + 1
end
puts C.to_s

Result:
...uninitialized constant C (NameError)

Now let's discuss when variable is both in the block and the surrounding scope.   Two things can happen here.   If the variable is specified in the parameter list,  then the version in the block is local to the block.   The variable in the surrounding scope will not be modified.

If the variable is not  in the parameter list,  the variable in the block is actually the same as the variable in the surrounding scope.   If the block changes the variables value,   it will also change the variable in the surrounding scope.

The block local variable can never been seen outside it's block.   If blocks are nested,   then the surrounding scopes can be seen by the nested block. The following example illustrates this concept.

Lvar = 0
[ 1 ] .Each do
  Var1 = 1
  [ 2 ] .Each do
    Var2 = 2
    [ 3 ] .Each do
      Var3 = 3
      # here lvar and var1, var2, var3 are visible
    End
    # here lvar and var1, var2 are visible
  End
  # here lvar and var1 are visible
End
# here just lvar is visible

% Ruby -e '
[ 0 ] .Each do
  I = 0
End
P i # here it becomes error
'
-e:5: Undefined local variable or method `i'
For #< Object:0x40163a9c> (NameError)

Sentence structure of blocks

The are two ways to create a block.   First, the block can be contained in a do...end  form.   Second it can be contained in matching pair of parenthesis.

arr = [ 0, 1, 2]

arr.each do |i|
  puts i
end

arr.each { |i|
  puts i
}

The only functional difference between these forms the precedence of the operators.   In this case the parenthesis bind tighter  than the do...end  form.   As you can see in the following example,  the precedence of the parenthesis changes the how the statement is interpreted.

M m do...end              # m (m) do...end

M m {....}                # m (m() {....})

A convention has crystallized among Ruby programmers to use parenthesis for single line blocks and do...end  for multi-line blocks.   While this is not in any mandatory,  it is coming close to being an best practice.

Yield

Yield  is the magic code that creates iterators.   How is that?   Within iterators there is logic to step through the collection specified.   Typically a iterator steps through a collection one at a time.   Although this is how iterators like each work,  there are other schemes that could be used.   Even and odd numbers might be a canidate,  or how about a iterator that scanned collections for prime numbers?   These types of iterators can also be created by users using standard iterators and applying the selection criteria at the block itself.

In the following example we create our own iterator.   Notice the yield()  statement.   When a yield executed it branches to the associated block and loads any parameters that are specified.   When the block finishes execution,  it returns to the statement immediately following the yield()  statement.

class Array
  def my_each
    i= 0
    while i < self.length
      yield self [ i ]          # Yield (jump) to block
      i+= 1                     # Return from block
    end
  end
end

[ 0,1,2,3,4 ] .each do |i|
  p i
end

[ 0,1,2,3,4 ] .my_each do |i|
  p i
end

A yield()  call is somewhat similar to a function call in C or C++.   It jumps to a function code location with the specified parameters and when it returns to the next statement after the function call.   Further,  it returns a value.

One last point.   There must be a block  associated with method using an iterator. To see why,  try entering the following code into 'irb'.

% Ruby -e ' [ 0,1,2 ].Each'

-e:1:In `each': No block given (LocalJumpError)
        From -e:1

Proc

As hopefully you remember that blocks  are chucks of code associated with a method.   These blocks execute in the context in which they were defined.   Blocks are not  objects.   They are simply blocks of code.   They can be converted to proc  objects in several ways.

The first way is to add a ampersand prefixed variable after the parameters of the method.   This variable(&block) will contain the proc object created when this function is executed.   Within the method body this proc can be executed by the call  method.

def meth1(p1, p2, &block)
  puts block.call(p1,p2)
end

meth1(1,2) { puts "a block" }
meth1(3,4) { |p1, p2| puts "NEW CALL #{p1} #{p2}" }

Results:
a block
nil
NEW CALL 3 4
nil

The second method is to call proc.new and provide it a block.   Extending the previous code,  we show how to create proc objects with Proc.new().

block1 = Proc.new { |p1, p2| puts "NEXT CALL #{p1} #{p2}"}
block1 .call(5, 7)

Results:
NEXT CALL 5 7

The last method creates the proc object with lambda method.  

block = lambda { |p1, p2| puts "NEXT CALL #{p1} #{p2}"}
block.call(5, 7)

Results:
NEXT CALL 5 7

Proc objects internally respond to the command next.   The next command causes the block to exit.   The following shows examples of all three kinds of Proc object generation.

def meth
    res = yield
  "The block returns! #{res}"
end

puts meth { next 99}

pr = Proc.new { next 98}
puts pr.call

pr = lambda { next 97}
puts pr.call

Results:
The block returns! 99
98
97

Proc continued

def print_block (&block)
    puts  block
end

print_block() { puts "Test !"} 
print_block()

#
nil

Another way to attack the problem is to create a proc object and use that as a substitute for enclosing the code itself with the parenthesis.   In the example bwlow,  both methods produce the exact same results.


xblock = Proc.new {|i| p i}          # Method 1 - Create a proc object
[ 0,1,2 ].each(&xblock)   
     --- OR --- 
[ 0,1,2 ].each {|i| p i}             # Method 2 - Use a Raw Block

Results:
0
1
2

The following code is also equivalent to the two methods above!

def each_item(&block)
  [ 0,1,2 ].each(&block)
end

each_item do |i| 
  p i
end

Results:
0
1
2

Introduction of Ruby Expressions

Since almost all things in Ruby are actual objects,  then the statements created in the language are primary made up of various expressions.   For example,  the control structure if  is an expression and it has some value.

If

The explanation of  if  is probably not necessary,  but a discussion of all the basic statements is required.   When a conditional expression is true  it executes itself.   For programmers from 'C' and other languages it is important that they understand what constitutes true and false in Ruby.

If the value of an expression is nil  or  false   it is false.   All other values are true.   The following example shows the results of some basic conditional values.

z = 0             # Hey 'C' guys: Notice the zero is true NOT FALSE!
o = 1
m = -1
n = nil
f = false
t = true

if z then puts "z is true" end
if o then puts "1 is true" end
if m then puts "m is true" end
if n then ; else puts "n is false" end
if f then ; else puts "f is false" end
if t then puts "t is true" end

Results:
z is true
1 is true
m is true
n is false
f is false
t is true

Some standard conditional statements:

P (if true then 1 else 2 end)                         
P (if false then 1 else 2 end)                        
P (if false then 1 elsif true then 2 else 3 end)      

Results:
1
2
2

Finally,   if no part of the  if  has a matching expression,  then nil  is returned.   The following statements both return nil.

p (if false then 1 end)
p (if true then end)  

Results:
nil
nil 

One unusual aspect of Ruby is the ability to add a conditional on the end of a statement.   If the conditional is false  then the statement will not execute.

 print "debug\n" if $debug     # Prints if variable '$debug' is true.

Unless

The unless  statement is the reverse of the if statement,   While it provides an  else statement,  there is no elsif.   The following is the syntax of the unless statement.

unless expr [then]
  expr...
[else
  expr...]
end

The above syntax is the equivalent to the following if syntex.

if !(cond)
  ...
else
  ...
end

As it was with the if  statement,  an unless  modifier can be added to a statement.   If the unless  condition is true  the statement will not execute.

print "stop\n" unless valid($passwd)

And &&, or ||, not !

The Boolean  'AND'  operator and the Boolean  'OR'  operator come in two flavors.   The alphabetic versions and  and or.  and the &&  and || operators respectfully. The following conditional statements are identical with the exception that the alphabetic versions have a low precedence than the symbol versions.   This can be significant if expressions are not properly parenthesized.

if <cond> and <cond>   
  puts "OK!"                # Print if both <cond> are true 
end

if <cond> &&; <cond> 
  puts "OK!"                # Print if both <cond> are true 
end
end

The following Boolean 'OR' statements are also identical except for precedence.

if <cond> or <cond> 
  puts "OK!"                 # Print if either <cond> is true  
end

if <cond> || <cond> 
  puts "OK!"                 # Print if either <cond> is true  
end

The following Boolean 'NOT' statement also comes in alphabetic and symbol form.

if not false
  puts "OK!"                 # Print if 'not false' is true  
end

if !false
  puts "OK!"                 # Print if !false is true  
end

Ternary operator

The ternary  operator is an old friend from the 'C' and 'C++' languages.   If syntax form is as follows:

<boolean-expression>  ? <expr1>  : <expr2> 

This statement returns <expr1>  if the boolean-expression is true,  <expr2>  if false.

Loops: each, for, loop, while, and until

Ruby provides a number of loop constructs.   The while  and until  should be familiar with most programmers.   These syntax for these is as follows:

While boolean-expression  [ do | : ]
  --- body ---
end

until boolean-expression  [ do | : ]
  --- body ---
end

The while and until expressions can also be used as modifiers.   In both cases if the expression is a begin/end block the will be executed at least one time.   If is not a block,   it will execute zero or more times depending on the value of the Boolean-expression.

expression  while  boolean-expression

expression  until  boolean-expression

Now,  the following loop constructs should be new to 'C'/'C++' programmers.   In particular,  the for  loop is nothing like the 'C' language version.   Let us look at it's syntax.

for name [, name]... in expression  [ do | : ]
  --- body ---
end

A for  is executed like an each loop  (See below).   The difference is that local variables defined within it's loop are available outside to loop.   This is not the case an each loop.

expression.each do | name [, name]... |
  --- body ---
end

Finally, there is the loop  statement that iterates over it's associated block.   It should be noted that is actually an method  defined in the kernel, not a language construct.

loop do
  --- body ---
  --- break ---        # loop does not provide an exit
  --- body ---
end 

The following is an example of a working loop block:

i = 0
loop do
   puts "Line: #{i}"
   i += 1
   break if i > 2
end

Results:
Line: 0
Line: 1
Line: 2

Loops: break, redo, next, and retry

The break, redo, next  and   retry are used to change the flow of while, until,  and iterator  controlled loops.   These changes made to the flow are as follows:

1.  Break -  Terminates the immediately enclosing loop.  Control
             resumes at the statement following the block
2.  redo  -  Repeats the loop, without advancing the iterator,
             or reevaluating the condition expression.
3.  next  -  Skips to end of loop,  starts next iteration.
4.  retry -  Restarts to loop and reevaluates to condition expression.

The following is an example using break  and next.

i = 1
while true
  if i > 10 
    puts "i > 10 = #{i}"
    break                 # Execute break if 'i' greater than 10
  elsif i % 2 == 0
    i *= 2
    puts "i * 2 = #{i}"
    next                  # Execute next following (i * 2)
  end
  i += 1
  puts "i + 1 = #{i}"     # Default execution (i + 1)
end

Results:
i + 1 = 2
i * 2 = 4
i * 2 = 8
i * 2 = 16
i > 10 = 16

Case

The case statement is a powerful language structure for sorting through a large number of conditions affecting a decision or decisions.   It in it's simpler form is like a multi-way if  statement.   The syntax of this first form is:

case 
  when <object> boolean-expression <object>
    --- body ---
  when <object> boolean-expression <object> 
    --- body ---
  else
    --- body ---
  end

An actual case expression looks like the following:

e 
[1, 6, 10, 18, 20 ].each do |numb|
case 
  when numb == 10
     puts "It was 10"
  when numb < 5 
     puts "It was less than 5"
  when numb < 20
     puts "It was less than 20"
  else
     puts "Else Called!"
  end
end

It was less than 5
It was less than 20
It was 10
It was less than 20
Else Called!  

The more common form of the case  statement depends on the case equality operator: '==='.   This form compares the target,  as specified by the case statements parameter, to the various when clause  values.   The following shows the syntax of the 'target' form of case.

case <target>
  when <object>      # <object> === <target>
    --- body ---
  when  <object>     # <object> === <target>
    --- body ---
  else
    --- body ---
  end

The nature of the comparison is dependent on the class involved.   Strings are simple string compression between the when clause  value and the target.   Regular expressions are just pattern matches between the when and target values.   Ranges check for the target being within the range specified by the when clause.

[ "exit", 12, "debug", "quit", "fail", 8, "donut"].each do |command|
case command
  when "debug"
    puts "Debug Found"
  when /^\d*$/
    puts "Digit Commmand Found"
  when "exit", "quit", "fail"
    puts "Time to get out!"
  when (1..10) 
    puts "Number between 1 and 10 Found!"
  else
    puts "Unknown target: #{command}"
  end
end

Results:
Time to get out!
Unknown target: 12
Debug Found
Time to get out!
Time to get out!
Number between 1 and 10 Found!
Unknown target: donut

Exception

An exception is a way for the programmer to handle various errors.   This can be done several ways.   If the error is recoverable,  then the exception can be caught  and handled by the user.   If it is not recoverable,  the use may still need to process the exception to prevent other errors.  Closing files,  deleting partial results, etc.

However,  there are times when the user wishes to generate an exception.   This generally happens when a program must unwind  the stack to get back to a position where the error can be handled.   As often occurs,  the portion of the program generating the error may have no way of knowing how process the error.

The underlying purpose of exceptions is that the exceptions are passed up through the layers of a program.   At each level,  a particular exception can be caught and any other errors may be passed on.   In a well written program,  all recoverable errors should be caught by the entity that knows how to recover from that error.

The following is an example of a simple exception being raised by the user.   In this first case the error is generated but not handled.   In cases where errors are eventually not  handled,  Ruby is the handler of last resort.   Errors of this sort are reported to the user and the program is aborted.

[ 0, 2, 4,7,8,10].each do |cond|
  puts "Cond = #{cond}"
  raise(ArgumentError, "Cond is not even number") if (cond % 2) != 0
end

Results:
Cond = 0
Cond = 2
Cond = 4
Cond = 7
.... Cond is not even number (ArgumentError)

How do we handle this situation?   Ruby provides the rescue clause.   The user surrounds the code that may generate an exception with a begin...end  block.   At the end the block a rescue  clause will catch the specified exceptions.

["puts 'start'", "x","puts 'end'"].each do |string|
  begin
     eval string
     raise ArgumentError, "Error Raised" if string.include?('start')
#
  rescue NameError => boom
    print "String doesn't compile: " + boom
    puts ""
# 
  rescue ArgumentError => bang
    print "Argument Error: " + bang 
    puts ""
  end
end

Results
start
Argument Error: Error Raised
String doesn't compile: (eval): undefined local variable or method `x'
end

The previous results show that the first and third strings evaluate correctly.   However, because we raised a bogus exception for strings containing 'start',  an ArgumentError  exception was raised for the first string!   The second string does not evaluate and raises an NaemError  exception.

Rescue will only process the exceptions specified.   After you process an exception,  you may place a 'bare' raise statement that will process any additional exceptions.

Since Exception objects are represented in a hierarchy,  using a high level exception such as Standard Error  will preempt errors such as ArgumentError.   For example replacing the first rescue group above with the following produces a different result.   As you can see below,  the StandardError  clause processed the ArguementError  exception.

  ::         ::        ::
  rescue StandardError, NameError => boom     # Inserted StandardError
    print "String doesn't compile: " + boom
    puts ""
  ::         ::        ::
end

Results:
start
String doesn't compile: Error Raised
String doesn't compile: (eval): undefined local variable or method `x'
end

If no exception is detected,  then none of the rescue clauses bodies are executed. In some cases the user may wish execute some code if no exception occurred.   The else  clause is used for this purpose:

[1, 2, 3, 4].each do | x |
begin
  print "line: #{x}"
  if x > 4 
     raise ArgumentError, "Argument Error" 
  end

  rescue StandardError => boom
    print "String doesn't compile: " + boom
    puts ""
  else
    print " printed Successfully!\n"
  end
end    

Result:
line: 1 printed Successfully!
line: 2 printed Successfully!
line: 3 printed Successfully!
line: 4 printed Successfully!
% Ruby raise.Rb
Raise.Rb:2:In `raise_exception': Wrong number of argument (ArgumentError)
        From raise.Rb:7

Variable and constant

Ruby's variables and constants are differentiated by their starting characters:

Object Type        First Character
----------------   ----------------
Local Variables    Lower Case or '_'
Method Parameters  Lower Case or '_'
Method Names       Lower Case or '_'

Globals            $			 

Instance Variables @

Class Variables    @@

Class Name         Upper Case

Constants          Upper Case

Ruby has a good number of predefined variables.   They all start with a '$' character.   This also classifies them a global.   These are being used less as experience with Ruby grows. A list of some these globals follows:

$$         The process number of the Ruby running this script.
$?         The status of the last executed child process.
$:         Load path for scripts and binary modules by load or require.
$"         The array contains the module names loaded by require.
$DEBUG     The status of the -d switch.
$FILENAME  Current input file from $<. Same as $<.filename.
$LOAD_PATH The alias to the $:.

There are six pseudo variables:

self
 nil
true
false
__FILE__
__LINE__
self, nil, true, false, __FILE__, and __LINE__
.

Finally, there are a number of Pre-defined global constants.   A sampling of these are:

TRUE
FALSE
NIL
ARGV
RUBY_VERSION

Substitution of Variable Values

The value of a variable can be changed with an assignment statement.   The value assigned can be another value, constant, literal, or just about any expression.

Var = 1
Obj = Object.New
@ivar = 'string'
@@cvar = [ 'array' ]
PI = 3.1415926535
$gvar = {'key' => 'value'}

As for attributes we do not set directly,  but rather set and retrieve these values through a proxies(methods).  

Self substitution

Lets look at a typical 'C/C++' shortcut to perform basic operations on a lvalue:

var += 1                # Equivalent:  var = var + 1
var *= 2                # Equivalent:  var = var * 2

This can be combined with an attribute methods to provide the same functionality:

class C
  def i= (n) 
    @i = n
  end
  def i
    @i
  end
end

obj = C.new
obj.i = 1
obj.i += 2
p obj.i

Results:
3

Defined?

The method defined?  is a powerful tool for insuring that arbitrary expressions can be successfully executed.   If the argument is not going to result in a valid return value,  then this method returns nil.   The result for arguments that are defined is the return of a string describing the argument.

defined? 5                # "expression"
defined? $;               # "global-variable"
defined? Alien            # "constant"
defined? Math::PI         # "constant"
defined? a = 6            # "assignment"
defined? 50.integer?      # "method"

The following is a simple example of defined?  usage.   Since any valid argument will return something other than nil,  logically testing the result will return true when the argument is defined!

var = 1
if defined? var
  puts "defined!"
end

puts defined? var

Results:
defined!
local-variable

Sentence

The sentence  structure of Ruby is far more relaxed than 'C' or 'C++'.   As we see the following sections,  Ruby provides more flexibility in statement structures and with much less in the way of syntax 'hints' to the parser.

Terminal of sentence

   Whereas those languages require  a semicolon at the end of sentences to terminate them,  Ruby does not.   Ruby only requires a semicolon if you are putting more than one statement on a line,  or indicating a break in the normal sentence structure.

puts "Hello, World!" ; puts "Hello, Redundantly!"

Results:
Hello, World!
Hello, Redundantly!

As long statements on separate lines much to syntax of Ruby is optional.   The following is a small sample of the Ruby syntax.

a = 1
b = 3

if a == 1           # [then] is optional here
  c = 4
else
  c = 5
end

# When it is expressed on one line then
# more tokens are needed to parse the statement

if b == 3 then c = 7 else c = 8 end  
puts "c = " + c.to_s

# Here the semicolon stands in for the empty 
# 'then'  structure.
if c != 7 ;  else c = 9 end
puts "c = " + c.to_s

Results:
c = 7
c = 9

Another feature of Ruby is that indentation is not significant.   If line breaks occur within binary operators or between parenthesis they are treated as white space.

# The following is equivalent to: "1 + 3 * method(6, 7 + 8)"

1 +
   3 *
         method(
   6,
      7 + 8 )

If a user wish to insert a line break in a location that would normally be significant it can be quoted  with a backslash.

p 1 +        # Line break not significant 
  2

p 1 \        # Line break is significant and must be 'quoted'
  + 3

Results:
3
4

If/Unless Modifier

The if and unless modifiers are similar to the while and until  modifiers.   They provide conditional execution of an expression.   The expression is executed if the conditional modifier evaluates as true for if, or false for unless.

expression if boolean-expression

expression unless boolean-expression

While/Until Modifier

The while and until modifiers were discussed previously.  

expression  while  boolean-expression

expression  until  boolean-expression

Some examples of these modifiers are shown below:

sleep(1) until ready?

begin
   res = get_response(id)
end while need_continue?(res)

Class definition

The class definition syntax is as follows:

class  [ scope:: ] classname [ < super-expression ]
  --- Body ---
end

The scope operator usage is not often used and will not be discussed here in this abbreviated discussion.

The class definition will create a class that is a subclass of either Object  if the super-expression  is missing,  or as subclass of super-expression  if it is present.

Class B                # Class B is a subclass of Object
  --- Body ---
end

Class C < B            # Class C is a subclass of B
  --- Body ---
end

Method definition

The syntax of method definition is shown below.   Methods may be created with a variety of arguments or none at all.

def  [ ( arg, ... )
  --- Body ---
end

Some example method definitions:

def med1                       # No Args
  a = 1
end

def med2(arg1, arg2)           # 2 Args
  a = arg1 + arg2
end

def med3(arg1, arg2 = 0)       # 2 Args -  2nd Arg default = 0
  a = arg1 + arg2
end

def med4(arg1, *rest)          # 1 Arg and array of variable args
  a = arg1
end

def med5(arg1, arg2, &block)   # 2 Args plus block
  a = arg1 + arg2
end

Singleton method definition

While most methods are defined for a class as a whole,  there are times that a particular object instance requires a unique method or modification of a method.   This is accomplished by defining the method with a object prefix:

def obj.unique_msg[ (arg [,arg] [,arg = default] [,*arg] [, &block] )
  --- body ---
end

The unique method is stored in a singleton object attached to the instance object.   When the object in question calls the method,  it is not found in that instance.   The search up the hierarchy runs into the attached singleton containing the method before reaching the super-class.

In the following example the method cls_msg  is modified for obj2.   Even obj2 calls the same method until the singleton message is defined.

 class C
   def cls_msg
     puts "class message"
   end
end

obj1 = C.new
obj2 = C.new
obj3 = C.new

obj2.cls_msg

def obj2.cls_msg
  puts "Singleton message!"
end

obj1.cls_msg
obj2.cls_msg
obj3.cls_msg

Results:
class message       # Before obj2.cls_msg defined!
class message
Singleton message!  # After obj2.cls_msg defined!
class message 

Singleton class definition

As we discussed in the previous section,  creating a singleton method  also creates an singleton class  or anonymous class.

It should be understood that a singleton class is a virtual class,  it can not be sub-classed or instatiated.   It's only purpose is to provide a place to mount methods and instance variables that are uniquely associated with the specified Class Name.

machine = "Dozer"
class << machine
  def blade_down
    puts "The #{self} moves dirt"
  end
end

machine.blade_down

Results:
The Dozer moves dirt

Multiple substitution

Multiple substitutions, or Parallel assignment, is another feature of Ruby.   Let us look a simple case. The following assignments are all equivalent.

a, b, c = 1, 2, 3          # Assignment 1

a = 1                      # Assignment 2
b = 2
c = 3

a, b c = [1, 2, 3]         # Assignment 3

The sort of assignment can be done with any expression that contains multiple values on the right side to the assignment.

tmp = [ 1, 2, 3 ]
a, b, c = tmp
ret1, ret2 = some_method()   # If returns multiple values

The process of parallel assignment begins when the right side(rvalue) of an assignment is evaluated and contains multiple values.   The rvalue side is evaluated left to right and collected into an array.

At this point the left side(lvalue) is inspected for multiple values.   If it is a single variable,  then that variable is loaded with the rvalue array.   If it contains multiple variables,  they are load left to right with the values from the rvalue array.   The results depend on the number of items in the rvalue's and lvalue's.

a, b, c = [1, 2, 3]
puts "a = #{a} b = #{b} c = #{c}"

a, b, c = [1, 2]
puts "a = #{a} b = #{b} c = #{c}"
unless c 
  puts "nil or false loaded in c"
end

a, b, c = [1, 2, 3, 4]
puts "a = #{a} b = #{b} c = #{c}"

Results:
a = 1 b = 2 c = 3
a = 1 b = 2 c =
nil or false loaded in c
a = 1 b = 2 c = 3

The arrays on both sides to the assignment will be evaluated and 'flatten' to a single dimension before the actual assignment takes place.

a, (b, c, d) = [ 1, [ 2, 3, 4 ] ]
puts "a = #{a} b = #{b} c = #{c} d = #{d}"

a, (b, (c, d)) = [ 1, [ 2, [ 3, 4 ] ] ]
puts "a = #{a} b = #{b} c = #{c} d = #{d}"

(a, b), (c, d) = [ [ 1, 2 ] , [ 3, 4 ] ]
puts "a = #{a} b = #{b} c = #{c} d = #{d}"

Results:
a = 1 b = 2 c = 3 d = 4
a = 1 b = 2 c = 3 d = 4
a = 1 b = 2 c = 3 d = 4

Index and attribute substitution on the left is still possible:

i = 0
arr = [ ]
arr [ i ], arr [ i+1 ], arr [ i+2 ] = 0, 2, 4
p arr

class C
  def attr0= (n)
    @attr0 = n
  end
  def attr1= (n)
    @attr1 = n
  end
  def attr2= (n)
    @attr2 = n
  end
end

obj = C.new

obj.attr0, obj.attr1, obj.attr2 = "a", "b", "c"
p obj.inspect 

results:
[0, 2, 4]
@attr0 = "a", @attr1 = "b", @attr2 = "c"    # Inspect result cleaned up

Finally, like the asterisk prefixed parameters in methods,  the same can be used for an last element of the lvalue.

first, *rest = 0, 1, 2, 3, 4
p first 
p rest 

Results:
0
[1, 2, 3, 4]

Block parameter multiple substitution

Multiple substitution is deeply embedded in the Ruby syntax.   So it should be of no suprise that multiple paramters can be passed by a yield  into a block.   As you will notice yield and blocks follow the same multiple assignment rules as we discussed in the previous section!

def doit
  yield("one", "two", "three")  
  yield("1", "2", "3")
  yield("one", "two")  
  yield("1", "2")
  yield("one", "two", "three", "four")  
  yield("1", "2", "3", "4")
end

doit {|one,two,three| puts "one= #{one} two= #{two} three= #{three}"}

Results:
one= one  two= two  three= three
one= 1  two= 2  three= 3
one= one  two= two  three=          # Three = nil
one= 1  two= 2  three=              # Three = nil
one= one  two= two  three= three    # Forth value thrown away
one= 1  two= 2  three= 3            # Forth value thrown away

Alias

The alias statement creates a new name for methods, operators, globals, and expression backreference(&$, $', $`, $+). A backrefernce holds the various results of an regular expression matchs.

As we will show below,  when a method is aliased it points to a copy of the old method body.   If the method is subsequently changed,  the aliased method still executes to old method body.

def method1
  puts "Method one"
end

alias method2 method1         # method2 now points at body of method1

method1
method2

def method1
   puts "Method Changed!"     # Change Method1
end

method1
method2

Results:
Method one
Method one
Method Changed!               # method1 calls new method body
Method one                    # Aliased method2 calls old method1 body

Undef

The undef  command undefines a method name.   This is similar to the unix unlink command.

Class C
  Undef method_name
end

If the method-name has been aliased,  the method's body is still available through the alias.

def method1
  puts "Method one"
end

alias method2 method1    # Name method2 now points at body of method1

method1
method2

undef method1
begin
  method1
  rescue
  puts "Method1 Gone!"
end
method2              # Method2 still points at a copy of method1's body

Results:
Method one
Method one
Method1 Gone!            # Called by 'method1'
Method one               # Called by 'method2'

If the rescue clause above did not intercept the Name Error,  a message similar to the following would be printed and the program aborted!

... undefined local variable or method `method1' for ... (NameError)

Trifling item

Comment

Any time Ruby sees a pound sign,  ' #',  that is not quoted in the form  ' \#',  Ruby will ignore that character and all following characters till the end of line.    Comments are always ignored by Ruby.

# Comment line 

# comment            -- Multi-line comment
#  :: ::      
# comment

1 + 1     # Comment  -- In-line comment

=begin ... =end document

This form of documentation must be followed by the rdoc  tag to distinguish the block from other forms of documentation,  provided that it is intended as Rdoc Documentation..

=begin rdoc
This type is used for documenting programs with Rdoc.
How to setup for Rdoc is beyond the scope of this document.
=end

However, for use as a method for inserting multi-line comments,  the following form should be used.

=begin
It is the multiline comment
comment lines continue until =end
=end

The original work is Copyright © 2002 - 2004 Minero AOKI.
Translations,  additions,  and graphics by C.E. Thornton
Creative Commons License
This work is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike2.5 License.