Introduction to Ruby

Overview

Ruby

Links

Getting Started

Like several other languages including ML and Python, you use Ruby in two ways:

Writing simple scripts

Make a file called hello.rb that looks like this:

# The classic hello world script.

puts "Hello, world"

To run this script

$ ruby hello.rb Hello, world

Note that Ruby's puts function ("put string") automatically puts a newline at the end. Cool, huh? Another simple script:

# A script to print some Pythagorean triples.

for c in 1..100
  for b in 1..c
    for a in 1..b
      if a * a + b * b == c * c
        printf("(%d,%d,%d)\n", a, b, c)
      end
    end
  end
end

Wait, uh, that's not very Ruby-like. Do it this way instead:

# A script to print some Pythagorean triples.

1.upto(100) do |c|
  1.upto(c) do |b|
    1.upto(b) do |a|
      puts "(#{a},#{b},#{c})" if a * a + b * b == c * c
    end
  end
end

And another

# A script to write the Fibonacci numbers up to and including the
# first commandline argument.

n = ARGV[0].to_i;

a, b = 0, 1
while b <= n
  print "#{b} "
  a, b = b, a+b
end
$ ruby fib.rb 300 1 1 2 3 5 8 13 21 34 55 89 144 233 $ ruby fib.rb blah blah

(Ruby treats the string "blah" as the value 0; Python would have given a type error).

Using interactive mode

The prompt is ">>". Results are indicated with "=>". You can use "_" to refer to the result of the last expression.

$ irb --simple-prompt
>> 5 / 2
=> 2
>> 5.0 / 2
=> 2.5
>> 5 + 6 * 4 / 3
=> 13
>> 5 + 6 * (4 / 3)
=> 11
>> 'dog'
=> "dog"
>> x = 4
=> 4
>> 6 / 0
ZeroDivisionError: divided by 0
        from (irb):1:in `/'
        from (irb):1
>> 6.0 / 0
=> Infinity
>> 4 ** 3
=> 64
>> 10 ** 50
=> 100000000000000000000000000000000000000000000000000
>> 10.0 ** 50
=> 1.0e+050
>> 5645674 % 23
=> 2
>> 8.class
=> Fixnum
>> 7.0.class
=> Float
>> "hello".upcase
=> "HELLO"
>> "hello".capitalize
=> "Hello"
>> def fact(n)
>>   (1..n).inject(1) {|x, y| x * y}
>> end
=> nil
>> fact 12
=> 479001600
> fact 50
=> 30414093201713378043612608166064768844377641568960512000000000000
>> _.class
>> Bignum

Good to Know

Learning By Examples

Commonly used classes

Here are some simple expressions:

>> 3.class
=> Fixnum
>> 243523523523523464635653343453245.class
=> Bignum
>> 2.0.class
=> Float
>> ["dog", 4, 4.2, 3, 2, "rat"].class
=> Array
>> :abc.class
=> Symbol
>> {:CA => "Sacramento", :HI => "Honolulu"}.class
=> Hash
>> nil.class
=> NilClass
>> "hello there".class
=> String
>> String.class
=> Class
>> Class.class
=> Class
>> self.class
=> Object
>> Object.class
=> Class
>> (1..10).class
=> Range
>> /\d+/.class
=> Regexp
>> 3 > 10
=> false
>> false.class
=> FalseClass
>> true.class
=> TrueClass
>> Math.class
=> Module
>> Module.class
=> Class
>> String.superclass
=> Object
>> Object.superclass
=> nil

Everything is an object

...and every object has an id:

>> 4.object_id
=> 9
>> 45.3421.object_id
=> 23412012
>> nil.object_id
=> 4
>> 0.object_id
=> 1
>> "dog".object_id
=> 23395620
>> "dog".object_id
=> 23393040
>> :dog.object_id
=> 6119694
>> :dog.object_id
=> 6119694
>> Math.object_id
=> 20824752
>> true.object_id
=> 2
>> [true,75.2,{"x"=>5}].object_id
=> 23374584
>> [true,75.2,{"x"=>5}].object_id
=> 23368800
>> [0,1].object_id
=> 23365356
>> [0,1].object_id
=> 23361936
>> x = [0,1]
=> [0, 1]
>> x.object_id
=> 23358204
>> x.object_id
=> 23358204

Numbers and Strings

Watch out!!

>> 18 < 4
=> false
>> 9 * 4.0 * Math.sin(Math::PI/2) / 2
=> 18.0
>> Math.acosh(-8.4)
Errno::EDOM: Domain error - acosh
        from (irb):40:in `acosh'
        from (irb):40
>> Math.acosh(9)
=> 2.88727095035762
>> "abcdef".length
=> 6
>> "abcdef".reverse
=> "fedcba"
>> "dog".capitalize
=> "Dog"
>> "dog".upcase
=> "DOG"
>> "RatS".downcase
=> "rats"
>> greeting = "Hello there"
=> "Hello there"
>> "rat"[2]
=> 116
>> greeting[1] = "u"
=> "u"
>> greeting
=> "Hullo there"
>> greeting.split
=> ["Hullo", "there"]
>> greeting.chop
=> "Hullo ther"
>> greeting
=> "Hullo there"
>> greeting.chop!
=> "Hullo ther"
>> greeting
=> "Hullo ther"
>> " Lots   of   space   ".squeeze
=> " Lots of space "
>> " Lots   of   space   ".strip
=> "Lots   of   space"
>> "%4d + %4d = %f\n" % [6, 3, 9]
=> "   6 +    3 = 9.000000\n"
>> "abc" << "def" << "g"
=> "abcdefg"
>> "a000".hex
=> 40960
>> "small" <=> "Big"
=> 1
>> "dog" == "dog"
=> true
>> "HO" * 3
=> "HOHOHO"
>> 355.to_s
=> "355"
>> "345847583".to_i
=> 345847583
>> x = 10
=> 10
>> 'There are #{x} things here'
=> "There are \#{x} things here"
>> "There are #{x} things here"
=> "There are 10 things here"
>> %q{There are #{x} things here}
=> "There are \#{x} things here"
>> %Q{There are #{x} things here}
=> "There are 10 things here"
>> %q(Strings
can go on
multiple
     lines)
=> "Strings\ncan go on\nmultiple\n     lines"

Arrays

>> a = [8, 4, 3, 10, 6, "dog", :rat, [3.1]]
=> [8, 4, 3, 10, 6, "dog", :rat, [3.1]]
>> a.length
=> 8
>> a[3..5]
=> [10, 6, "dog"]
>> a[3...5]
=> [10, 6]
>> a.reverse
=> [[3.1], :rat, "dog", 6, 10, 3, 4, 8]
>> a.sort
ArgumentError: comparison of Fixnum with Array failed
        from (irb):6:in `sort'
        from (irb):6
>> a.reverse!
=> [[3.1], :rat, "dog", 6, 10, 3, 4, 8]
>> a
=> [[3.1], :rat, "dog", 6, 10, 3, 4, 8]
>> a.each {|x| print x, "-"}; puts
3.1-rat-dog-6-10-3-4-8-
=> nil
>> a.join "/"
=> "3.1/rat/dog/6/10/3/4/8"
>> a.reject! {|x| !(Integer === x)}
=> [6, 10, 3, 4, 8]
>> a.sort.reverse
=> [10, 8, 6, 4, 3]

Hashes

>> m = {"a"=>3, "c"=>4, "b"=>6, "y"=>2, "x"=>4}
=> {"a"=>3, "x"=>4, "b"=>6, "y"=>2, "c"=>4}
>> m["y"]
=> 2
>> m.each {|k, v| puts "There are #{v} #{k}'s"}
There are 3 a's
There are 4 x's
There are 6 b's
There are 2 y's
There are 4 c's
=> {"a"=>3, "x"=>4, "b"=>6, "y"=>2, "c"=>4}
>> m.default
=> nil
>> m["f"]
=> nil
>> m["f"]=5
=> 5
>> m["f"]
=> 5
>> m.empty?
=> false
>> m.delete "x"
=> 4
>> m
=> {"a"=>3, "b"=>6, "y"=>2, "c"=>4, "f"=>5}
>> m.delete_if {|k, v| v % 2 == 0}
=> {"a"=>3, "f"=>5}
>> m.has_key? "c"
=> false
>> m.length
=> 2
>> m.values
=> [3, 5]

Blocks

Every method call can have parameters then a block. The block is executed when the method body calls yield. A method that calls yield is called an iterator.

def hello
  yield "hello"
  yield "hi"
  yield "hola"
end

hello {|s| puts s}

def collatz(n)
  while true
    yield n
    return if n == 1
    n = n % 2 == 0 ? n / 2 : 3 * n + 1
  end
end

result = []
collatz(577) {|n| result << n}
puts(result.join " -> ")

Procs

>> addSeven = lambda {|x| x + 7}
=> #<Proc:0x02c834d0@(irb):22>
>> addSeven.class
=> Proc
>> addSeven.call(4)
=> 11
>> addSeven[4]
=> 11
>> def multiplier(n)
>>   Proc.new {|x| x * n}
>> end
=> nil
>> timesFour = multiplier(4)
=> #<Proc:0x02ca4f98@(irb):2>
>> timesTen = multiplier(10)
=> #<Proc:0x02ca4f98@(irb):2>
>> timesTen.call(8)
=> 80
>> timesTen[8]
=> 80
>> timesTen.call("abc")
=> "abcabcabcabcabcabcabcabcabcabc"
>> def adder(n)
>>   Proc.new {|x| x + n}
>> end
=> nil
>> addSix = adder(6)
=> #<Proc:0x02c97438@(irb):10>
>> def compose(f, g)
>>   Proc.new {|x| f.call(g.call(x))}
>> end
=> nil
>> compose(addSix, timesFour)[3]
=> 18
>> addSix.arity
=> 1
Exercise: There is a subtle difference between procs created with Kernel.lambda and those created with Proc.new. What is this difference?

You can pass a block directly to a method without first converting it to a proc with Proc.new:

>> def f(x, y, &p)
>>   p.call(x, y+1)
>> end
=> nil
>> f(2, 4) {|x, y| y * x}
=> 10

Enumerables

The module Enumerable has tons of iterators: all?, any?, collect, detect, each_cons, each_slice, each_with_index, entries, enum_cons, enum_slice, enum_with_index, find, find_all, grep, include?, inject, inject, map, max, member?, min, partition, reject, select, sort, sort_by, to_a, to_set, zip.

Many of the built-in classes mixin Enumerable: String, Range, Array, Hash, IO, Dir, and Struct.

>> "dog\nbird\nrat\nrabbit".map {|s| s.length}
=> [4, 5, 4, 6]
>> (1..10).inject {|x,y| x + y}
=> 55
>> (1..10).inject(4) {|x,y| x + y}
=> 59
>> (1..4).entries
=> [1, 2, 3, 4]
>> [10, 20, 30, 40, 50, 60].find {|x| x % 15 == 0}
=> 30
>> [10, 20, 30, 40, 50, 60].find_all {|x| x % 15 == 0}
=> [30, 60]
>> {:a=>5, :b=>7, :c=>20}.entries
=> [[:c, 20], [:b, 7], [:a, 5]]
>> [6, 4, 3, 9, -2, 11].max
=> 11
>> {:a=>5, :b=>7, :c=>20}.any? {|k,v| v > 5}
=> true
>> {:a=>5, :b=>7, :c=>20}.any? {|k,v| v > 500}
=> false
>> Dir.new(".").entries[0..3]
=> [".", "..", "animals.rb", "circle.rb"]

If you mixin Enumerable into your own class, you must implement each yourself, and if you want to call max, min, or sort, then you have to write <=>, Then all the other methods will work automatically.

>> class C
>>   include Enumerable
>>   def each
>>     yield 1
>>     yield 2
>>     yield 3
>>   end
>> end
=> nil
>> c = C.new
=> #<C:0x2c699e8>
>> c.find_all {|x| true}
=> [1, 2, 3]
>> c.include? 2
=> true
>> c.include? 20
=> false
>> c.zip ["a", "b", "c"]
=> [[1, "a"], [2, "b"], [3, "c"]]
>> c.partition {|x| (x&1).zero?}
=> [[2], [1, 3]]

Code in Multiple Files

You could put some code reusable code in one file (roman.rb)

# This file contains a function that returns a roman numeral for
# a given integer.  You can also interpret this file directly,
# in which case you get a simple commandline program which prompts
# for an integer input then repsonds with the number that the program
# thinks was entered as a roman numeral.

# Returns a string representing the roman numeral for the positive integer
# passed in.  Raises an exception if n isn't a positive integer in the
# range 1..4999.

def to_roman(n)
  raise ArgumentError, "Not in 1..4999" unless (1..4999) === n

  roman_map = [
    [1000, 'M'], [900, 'CM'], [500, 'D'],  [400, 'CD'],
    [100, 'C'], [90, 'XC'], [50, 'L'], [40, 'XL'],
    [10, 'X'], [9, 'IX'], [5, 'V'], [4, 'IV'],
    [1, 'I']
  ]

  result = "";
  for value, letters in roman_map
    while value <= n
      result << letters
      n -= value
    end
  end
  result;
end

if __FILE__ == $0
  puts "Enter an integer:"
  begin
    n = gets.chomp.to_i
    puts "#{n} is #{to_roman(n)}"
  rescue ArgumentError, NoMethodError => e
    puts e.message
  end
end

and use it in another (useroman.rb)

# An example script that shows how to use code from another fle.

require 'roman.rb'

puts toRoman(2983)

Designing your own Classes

# A trivial circle class.

class Circle
  def initialize(x = 0, y = 0, r = 1)
    @x = x
    @y = y
    @r = r
  end
  def area
    Math::PI * @r * @r
  end
  def perimeter
    Math::PI * 2.0 * @r
  end
  def expand!(factor)
    @r *= factor
    self
  end
  def move!(dx, dy)
    @x += dx
    @y += dy
    self
  end
end
>> require 'circle'
=> true
>> c = Circle.new
=> #<Circle:0x2ca5fa0 @x=0, @r=1, @y=0>
>> c.expand! 4
=> #<Circle:0x2ca5fa0 @x=0, @r=4, @y=0>
>> c.expand!(2).perimeter.to_s
=> "50.2654824574367"
>> c = Circle.new 1, 1, 10
=> #<Circle:0x2c9f2b0 @x=1, @r=10, @y=1>
>> c.expand!(6).move!(2, -10)
=> #<Circle:0x2c9f2b0 @x=3, @r=60, @y=-9>
>> class Circle
>>   def to_s
>>     "[Circle at (#{@x},#{@y}) with radius #{@r}]"
>>   end
>> end
=> nil
>> c
=> #<Circle:0x2ca3318 @x=3, @r=60, @y=-9>
>> c.to_s
=> "[Circle at (3,-9) with radius 60]"
>> "#{c}"
=> "[Circle at (3,-9) with radius 60]"

Inheritance

# A typical example of inheritance and poymorphism

class Animal
  def initialize(name)
    @name = name
  end
  def speak()
    "#{@name} says #{sound()}"
  end
end

class Cow < Animal
  def sound()
    "moo"
  end
end

class Horse < Animal
  def sound()
    "neigh"
  end
end

class Sheep < Animal
  def sound()
    "baaaaa"
  end
end

if __FILE__ == $0
  s = Horse.new "CJ"
  puts(s.speak())
  c = Cow.new("Bessie")
  puts(c.speak())
  puts(Sheep.new("Little Lamb").speak())
end

Classes are open

Define your class bit by bit...

>> class C
>>   def method1
>>     0
>>   end
>> end
=> nil
>> c = C.new
=> #<C:0x2c899c8>
>> c.method1
=> 0
>> c.method2
NoMethodError: undefined method `method2' for #<C:0x2c899c8>
        from (irb):141
        from ?:0
>> class C
>>   def method2
>>     1
>>   end
>> end
=> nil
>> c.method2
=> 1

You can even add fields and methods to built-in classes

>> 2.squared
NoMethodError: undefined method `squared' for 2:Fixnum
        from (irb):46
>> class Fixnum
>>   def squared
>>     self * self
>>   end
>> end
=> nil
>> 2.squared
=> 4

Attributes

Fields aren't directly accessible. You can write getters and setters if you want to ...

>> class C
>>   def value
>>     @value
>>   end
>>   def value=(v)
>>     @value = v
>>   end
>> end
=> nil
>> c = C.new
=> #<exitC:0x2c9e968>
>> c.value = 20
=> 20
>> c.value
=> 20

Or you can use the attr_reader, attr_writer, and attr_accessor methods to autmatically generate the variables and the reader and writer methods.

>> class Point
>>   attr_accessor :x, :y
>> end
=> nil
>> p = Point.new
=> #<Point:0x2ca4890>
>> p.x = 5
=> 5
>> p.x
=> 5
>> p
=> #<Point:0x2ca4890 @x=5>
>> class Q
>>   attr_writer :x
>> end
=> nil
>> q = Q.new
=> #<Q:0x2c9c8c8>
>> q.x = 4
=> 4
>> q.x
NoMethodError: undefined method `x' for #
        from (irb):13

Object-Specific Classes

You can attach new methods to an object. This is cool.

>> a = Array.new
=> []
>> b = Array.new
=> []
>> class <<b
>>   def hello
>>     "hi there"
>>   end
>> end
=> nil
>> b.hello
=> "hi there"
>> a.hello
NoMethodError: undefined method `hello' for []:Array
        from (irb):9

If you simply HAVE to think in terms of classes: This gives the object a new virtual class (whose superclass is the object's previous class).

Since classes are objects, how about adding methods to a class object? Yes, these are what Java programmers call static methods. Sort of. There' more to it — see writeups at mobcode and by why.

Visibility

The class Module has methods that set the visibility of methods:

Oh yeah, and visibility is dynamic (surprise, surprise)

>> class C
>>   def a; 0; end
>>   private
>>   def b; 1; end
>>   protected
>>   def c; 2; end
>>   def d; 2; end
>>   public
>>   def e; 2; end
>>   private :d
>> end
=> C
>> C.private_instance_methods(false)
=> ["b", "d"]
>> C.protected_instance_methods(false)
=> ["c"]
>> C.public_instance_methods(false)
=> ["e", "a"]
>> c = C.new
=> #
>> c.b
NoMethodError: private method `b' called for #
        from (irb):17
>> class C
>>   public :b
>> end
=> C
>> c.b
=> 1

Modules

Use modules for namespacing (e.g. Math) or mixins (e.g. Enumerable and Comparable). Modules may be included into other modules and classes.

>> module M
>>   def bracket
>>     "(" + self.to_s + ")"
>>   end
>> end
=> nil
>> class Dog
>>   include M
>>   def bark
>>     "woof"
>>   end
>> end
=> nil
>> sparky = Dog.new
=> #<Dog:0x2c9cb50>
>> sparky.bark
=> "woof"
>> sparky.bracket
=> "(#<Dog:0x2c9cb50>)"
Exercise: Define two modules, each defining a method called "bark" with zero arguments. Make one return "woof" and one return "arf". Create a class that includes both modules. Call bark on an instance of this class. What happens and why?

Operator Overloading

You can think of better examples than this...

>> class Fixnum
>>   def +(n)
>>     self - n
>>   end
>> end
=> nil
>> 100 + 5
=> 95

Regexes

Three ways to write a regex:

/\by(?:es)?|no?/i
%r{\by(?:es)?|no?}i
Regexp.new('\by(?:es)?|no?', Regexp::IGNORECASE)

Regexp methods: ===, =~. String methods: =~, [], []=, gsub, gsub!,match, scan, slice, slice!, split, sub, sub!

>> phone = /((\d{3})(?:\.|-))?(\d{3})(?:\.|-)(\d{4})/
=> /((\d{3})(?:\.|-))?(\d{3})(?:\.|-)(\d{4})/
>> phone =~ 'Call 555-1212 for info'
=> 5
>> [$`, $&, $', $1, $2, $3, $4, $5]
=> ["Call ", "555-1212", " for info", nil, nil, "555", "1212", nil]
>> phone =~ '800.221.9989'
=> 0
>> [$`, $&, $', $1, $2, $3, $4, $5]
=> ["", "800.221.9989", "", "800.", "800", "221", "9989", nil]
>> phone =~ '1800.221.9989'
=> 1
>> [$`, $&, $', $1, $2, $3, $4, $5]
=> ["1", "800.221.9989", "", "800.", "800", "221", "9989", nil]
>> message = "Your phone number is 800.555.1212."
=> "Your phone number is 800.555.1212."
>> message.sub(phone, "---")
=> "Your phone number is ---."
>> message[/o./] = "X"
=> "X"
>> message
=> "YXr phone number is 800.555.1212."

Exceptions

To raise exceptions, use Kernel.raise:

Exceptions are caught in a rescue clause within a block. You can also use ensure and else clauses.

More Introspection

Some wild stuff can be found in

Reference Material

Keywords

    alias   and     BEGIN   begin   break   case    class   def     defined
    do      else    elsif   END     end     ensure  false   for     if
    in      module  next    nil     not     or      redo    rescue  retry
    return  self    super   then    true    undef   unless  until   when
    while   yield

Operators

From lowest to highest precedence

OperatorDescription
[] []= Element get, element set
** Exponentiation
! ~ + - Not, complement, unary plus, unary minus
* / % Multiplication, division, modulo
+ - Binary plus, binary minus
<< >> Left shift, right shift
& And (bitwise for integers)
| ^ Inclusive or, exclusive or (bitwise for integers)
< <= >= > Less, Less or equal, Greater or equal, greater
<=> == != === =~ !~ Comparison, equal, not equal, case-equal, match, no match
&& Short circuit logical and
|| Short circuit logical or
.. ... Inclusive range, exclusive range
?: Ternary conditional
= **= *= /= %= += -=
<<= >>= &= |= ^= &&= |=
Assignment
defined? Is the operand defined?
not Logical not
and or Short circuit logical and, short circuit logical or
if unless while until If, unless, while, until
begin/end block

The following are just methods that you can override

    []  []=  **  !   ~  @+  @-   *    /   %   +  -  << >>
    &    |   ^   <=  <  >=  >   <=>  ==  ===  =~

(Note @+ and @- are method names for the unary plus and minus)

Exercise: Explain, for each operator, why the designers of Ruby chose to make it or not make it a method

Expressions

Ruby has the following kinds of expressions (and probably more):

Things that aren't really expressions

While a Ruby script really is a sequence of expressions, some forms are pretty complex: they're not exactly primitive values, method calls, or operators. They're not really statments either. Anyway here are a few of these:

Built-Ins and Standard Libraries

The built-in classes and modules are available to every Ruby program without the need to say require.

Classes Modules
Object (Kernel)
    Symbol
    NilClass
    TrueClass
    FalseClass
    String (Enumerable, Comparable)
    Numeric (Comparable)
        Integer
            Fixnum
            Bignum
        Float
    Regexp
    Range (Enumerable)
    Array (Enumerable)
    Hash (Enumerable)
    Exception
    Time (Comparable)
    Proc
    Thread
    ThreadGroup
    Continutation
    Binding
    IO (Enumerable)
        File
    Dir (Enumerable)
    File::Stat (Comparable)
    Process::Status
    MatchData
    Module
        Class
    Method
    UnboundMethod
    Struct (Enumerable)
        Struct::Tms
Kernel
Comparable
Enumerable
Math
ObjectSpace
Marshal
GC
FileTest
Process
Process::Sys
Process::GID
Process::UID
Signal
Errno

The so-called standard library consists of libraries that need to be required in your program, but are "standard" in the sense they're part of pretty much every Ruby installation.

abbrev, base64, benchmark, bigdecimal, cgi, complex, csv, curses, date, dbm, delegate, digest, dl, drb, English, enumerator, erb, etc, fcntl, fileutils, finalize, find, forwardable, ftools, gdbm, generator, getoptlong, gserver, iconv, importenv, io/wait, ipaddr, jcode, logger, mailread, mathn, matrix, mkmf, monitor, mutex_m, net/ftp, net/http, net/imap, net/pop, net/smtp, net/telnet, nkf, observer, open-uri, open3, openssl, optparse, ostruct, parsedate, pathname, ping, pp, prettyprint, profile, profiler, pstore, pty, racc, racc/parser, rational, rdoc, readbytes, readline, resolv, resolv-replace, rexml, rinda, rss, runit, scanf, sdbm, set, shell, singleton, soap, socket, stringio, strscan, syck, sync, syslog, tcltklib, tempfile, test/unit, thread, thwait, time, timeout, tk, tmpdir, tracer, tsort, un, uri, weakref, webrick, Win32API, win32ole, wsdl, xmlrpc, xsd, yaml, zlib

Predefined Variables

$! $@ $& $` $' $+ $1 $2 $3 $4 $5 $6 $7 $8 $9 $~ $= $/ $\ $, $; $. $< $> $_ $0 $* $$ $? $: $" $DEBUG $FILENAME $LOAD_PATH $stderr $stdin $stdout $VERBOSE $-0 $-a $-d $-F $-i $-I $-l $-p $-v $-w

Working with Ruby

Tools

The command line apps you will use most are

If you prefer to get your documentation on the web, you can look at Ruby-Doc.org.

Documentation is often bundled inside of IDEs, such as fxri, FreeRIDE, Arachno, RDT, etc.

Unit Testing

The library Test::Unit does some magic, so just make a test case class and run it:

require 'test/unit'
require 'roman'

class TestRoman < Test::Unit::TestCase

  def test_ok
    expectations = [
      [1, 'I'], [2, 'II'], [3, 'III'], [4, 'IV'], [5, 'V'],
      [8, 'VIII'], [9, 'IX'], [49, 'XLIX'], [50, 'L'], [70, 'LXX'],
      [499, 'CDXCIX'], [900, 'CM'], [4876, 'MMMMDCCCLXXVI']
    ];
    expectations.each do |arabic, roman|
      assert_equal(to_roman(arabic), roman);
    end
  end

  def test_argument_validation
    [1, 2, 35, 4998, 4999].each do |n|
      assert_nothing_raised() {to_roman n}
    end
    [-234, -1, 0, 5000, 5001, 235234235235235].each do |n|
      assert_raise(ArgumentError) {to_roman n}
    end
  end
end
$ ruby testroman.rb
Loaded suite testroman
Started
..
Finished in 0.0 seconds.

2 tests, 24 assertions, 0 failures, 0 errors

There are many assert methods: assert assert_block assert_equal assert_in_delta assert_instance_of assert_kind_of assert_match assert_nil assert_no_match assert_not_equal assert_not_nil assert_not_same assert_nothing_raised assert_nothing_thrown assert_operator assert_raise assert_raises assert_respond_to assert_same assert_send assert_throws build_message flunk use_pp=

More

Many things are not covered here, including