Ruby

Ruby is fun and very cool. The language’s tagline is “A PROGRAMMER'S BEST FRIEND”. Why would you not want to learn it?

Overview

Ruby

Great information here:

Getting Started

Use Ruby in two ways:

Writing simple scripts

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

hello.rb
puts "Hello, world"

To run this script:

$ ruby hello.rb
Hello, world

The method puts (“put string”) automatically puts a newline at the end. Another simple script:

triple.rb
# 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 (this time using print, which does not add the newline):

fib.rb
# 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 =>. 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
>> 6 / 0
ZeroDivisionError (divided by 0)
>> 6.0 / 0
=> Infinity
>> 4 ** 3
=> 64
>> 10 ** 50
=> 100000000000000000000000000000000000000000000000000
>> 10.0 ** 50
=> 1.0e+050
>> 5645674 % 23
=> 2
>> 8.class
=> Integer
>> 7.0.class
=> Float
>> "hello".upcase
=> "HELLO"
>> "hello".capitalize
=> "Hello"
>> _.class
=> String
>> true.to_s
=> "true"

Things you might have noticed:

Learning By Example

Everything is an object

Everything is an object, even nil. Numbers and booleans are symbols are objects. There are no weird “primitive values” like Java and JavaScript has. Even modules and classes are objects too. Every object has an object id:

>> 5.object_id
=> 11
>> 45.3421.object_id
=> 159137057222940850
>> nil.object_id
=> 8
>> 0.object_id
=> 1
>> "dog".object_id
=> 180
>> "dog".object_id
=> 200
>> :dog.object_id
=> 2035868
>> :dog.object_id
=> 2035868
>> Math.object_id
=> 220
>> true.object_id
=> 20
>> [true,75.2,{"x"=>5}].object_id
=> 240
>> [true,75.2,{"x"=>5}].object_id
=> 260
>> [0,1].object_id
=> 280
>> [0,1].object_id
=> 300
>> x = [0,1]
>> x.object_id
=> 320
>> x.object_id
=> 320

Things to notice:

Computation is by sending messages to objects

What does it mean for something to be an object? It means it can receive messages that you send it. When you send an object a message, the object is called the receiver. The message it receives is the name of a method, which is code that the object executes. Then the object responds with the return value of the executed method, which, you guess it, is another object.

>> 5.abs
=> 5
>> 38.succ
=> 39
>> 300.even?
=> true
>> 300.odd?
=> false
>> 573.gcd(99)
=> 3
>> 573.gcd 99
=> 3
>> "ubuntu".gsub("u", "oo")
=> "ooboontoo"
>> "ubuntu".gsub "u",  "oo"
=> "ooboontoo"
>> 295.+(7)
=> 302
>> 798113.digits
=> [3, 1, 1, 8, 9, 7]
>> 798113.send(:digits)
=> [3, 1, 1, 8, 9, 7]
>> 21.send(:*, 5)
=> 105

Things to notice:

Exercise: Explain the different between messages and methods to a friend.
Exercise: Look back at this page and list the methods we have seen so far.
What about puts?

Earlier we ran across methods called puts and print but they didn’t have an explicit receiver. They looked like—gasp—functions or something.

It turns out they really are methods, but hang in there, we’ll see what is going on later.

Every object has a class

Every object has exactly one class. An object is an instance of its class. (For example the object 3 is an instance of the class Integer.) All classes except BasicObject have a superclass. To find out the class, of an object, send it the message class. Note that classes are objects too, and their class is Class.

>> 3.class
=> Integer
>> 243523523523523464635653343453245.class
=> Integer
>> 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
>> Object.class
=> Class
>> (1..10).class
=> Range
>> /\d+/.class
=> Regexp
>> false.class
=> FalseClass
>> true.class
=> TrueClass
>> Math.class
=> Module
>> Module.class
=> Class
>> String.superclass
=> Object
>> Object.superclass
=> BasicObject
>> BasicObject.superclass
=> nil

What are classes for? A class defines methods that its instances can execute. For example, instances of the class Integer can execute abs, odd?, gcd, etc. Since classes are themselves objects, they can execute methods too. The methods that instances can execute are called instance methods and the ones the class itself executes are called class methods.

Learn how to read the Ruby class documentation

Go to the Ruby-Doc.org home page and click on Core API. Scroll down a bit and you will see links to amazing documentation pages for each of the built-in classes.

These docs are absolutely full of examples! Pay particular attention to the distinction between instance methods and class methods.

Variables and constants

Variables start with lowercase; constants with uppercase. Try reassigning a constant. Funny, all you get is a warning. What did you expect?

Exercise: Research why this is so.

Numbers

>> 18 < 8
=> false
>> 9 * 4.0 * Math.sin(Math::PI/2) / 2
=> 18.0
>> Math.acosh(-8.4)
Math::DomainError (Numerical argument is out of domain - "acosh")
>> Math.acosh(9)
=> 2.8872709503576206
>> 79342.to_s(16)
=> "135ee"
>> "135ee".hex
=> 79342

Strings

Strings are sequences of characters together with an encoding.

>> "Это — Руби?".encoding
=> #<Encoding:UTF-8>
>> "Это — Руби?".size
=> 11
>> "Это — Руби?".bytesize
=> 20
>> "Это — Руби?".codepoints.map {|c| c.to_s(16)}.join(' ')
=> "42d 442 43e 20 2014 20 420 443 431 438 3f"
>> "Это — Руби?".bytes.map {|c| c.to_s(16)}.join(' ')
=> "d0 ad d1 82 d0 be 20 e2 80 94 20 d0 a0 d1 83 d0 b1 d0 b8 3f"

Basic Operations

>> "abcdef".length
=> 6
>> "abcdef".reverse
=> "fedcba"
>> "dog".capitalize
=> "Dog"
>> "dog".upcase
=> "DOG"
>> "RatS".downcase
=> "rats"
>> greeting = "Hello there"
>> "rat"[2]
=> "t"
Strings in Ruby are mutable by default!

To get the effect of immutable strings common in most other languages, use symbols. Alternatively, you can invoke s.freeze to make s immutable. Or, you can put the comment # frozen_string_literal: true at the top of a file.
>> greeting[1] = "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

Interpolation

Double-quoted strings support interpolation, single-quoted strings do not.

>> x = 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]]
>> 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 Integer with Array failed)
>> 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

Hashes in Ruby are mutable collections of key-value pairs, with unique keys. [Docs]

>> m = {"a"=>3, "c"=>4, "b"=>6, "y"=>2, "x"=>4}
>> m["y"]
=> 2
>> m.each {|k, v| puts "There are #{v} #{k}'s"}
There are 3 a's
There are 4 c's
There are 6 b's
There are 2 y's
There are 4 x's
=> {"a"=>3, "c"=>4, "b"=>6, "y"=>2, "x"=>4}
>> m.default
=> nil
>> m["f"]
=> nil
>> m["f"]=5
=> 5
>> m["f"]
=> 5
>> m.empty?
=> false
>> m.delete "x"
=> 4
>> m
=> {"a"=>3, "c"=>4, "b"=>6, "y"=>2, "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]

Keys and values can be anything, but symbols are probably the most common kind of key.

>> caps = {:ca => 'sacramento', :hi => 'honolulu', :wa => 'seattle'}
>> caps[:wa] = 'olympia'
>> caps
=> {:ca=>"sacramento", :hi=>"honolulu", :wa=>"olympia"}

If the key is a symbol, then you can write the colon after the symbol name and omit the hashrocket.

>> {a: 90, b: 80, c: 70, d: 60}
=> {:a=>90, :b=>80, :c=>70, :d=>60}

Methods

The most common kind of method is the instance method; it goes inside classes. Let’s add a method to the Integer class:

>> class Integer
>>   def tripled; self * 3; end
>> end
=> :tripled
>> 30.tripled
=> 90

You can write top-level methods in Ruby! These are written outside of a class; they just seem to be out there on their own:

>> def fact(n)
>>   (1..n).inject(1, :*)
>> end
=> :fact
>> fact 12
=> 479001600
>> fact 50
=> 30414093201713378043612608166064768844377641568960512000000000000

How did this work? When you define a method at the “top-level” like this, Ruby actually adds it to the class Object as a private instance method. A private method in Ruby is called without explicitly giving the receiver; this implicit receiver is based on the context. For a Ruby script, or in the irb shell, Ruby gives you an implicit global object of class Object. So what looks like a top-level function is actually a method of this global object.

As in many modern languages, method can have named parameters, rest parameters, defaults on parameters, and splats in arguments.

def positional(x, y, z)      # cannot be called with names
  "#{x} #{y} #{z}"
end

def named(x:, y:, z:)        # must be called with names
  "#{x} #{y} #{z}"
end

def rest(x, *y)              # All args after first collected into array y
  "#{x} #{y}"
end

def defaulted(x, y=100)      # y gets 100 if not passed in
  "#{x} #{y}"
end

def defaulted_and_named(x, y:2)
  "#{x} #{y}"
end

positional(1, 2, 3)          # "1 2 3"
named(z:3, x:1, y:2)         # "1 2 3"
rest(1)                      # "1 []"
rest(1, 2, 3, 4)             # "1 [2, 3, 4]"
positional(1, *[2, 3])       # "1 2 3"
defaulted(1)                 # "1 100"
defaulted(1, 2)              # "1 2"
defaulted_and_named(1)       # "1 2"
defaulted_and_named(1,y:3)   # "1 3"

Blocks

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

blockexamples.rb
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 " -> ")

Two syntaxes for blocks

You can use
  do ... end
or you can use
  { ... }
They are semantically the same.
Calling methods with blocks is one of the most characteristic bits of Ruby syntax.

We’ve seen it before in the method upTo.

Procs

Blocks can’t be assigned to variables, so Ruby has procs which can. There are two kinds of procs: those defined with Proc.new (don't use these), and a better kind of proc, called a lambda. You should always use lambdas, pretty much.

There are two syntaxes for writing lambdas, and they are semantically exactly the same. There are also three syntaxes for calling lambdas, and they are exactly the same. There’s more than one way to do things in this language!

>> addSeven = lambda {|x| x + 7}
>> addSeven = -> (x) {x + 7}
>> addSeven.class
=> Proc
>> addSeven.call(5)
=> 12
>> addSeven[5]
=> 12
>> addSeven.(5)
=> 12
>> def multiplier(n)
>>   -> x {x * n}
>> end
=> :multiplier
>> timesFour = multiplier(4)
>> timesTen = multiplier(10)
>> timesTen.call(8)
=> 80
>> timesTen[8]
=> 80
>> timesTen.call("abc")
=> "abcabcabcabcabcabcabcabcabcabc"
>> def adder(n)
>>   -> x {x + n}
>> end
=> :adder
>> addSix = adder(6)
>> def compose(f, g)
>>   -> x {f.call(g.call(x))}
>> end
=> :compose
>> 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?

It turns out you can pass a block directly to a method without first converting it to a proc. This gives you a non-lambda proc, interestingly enough:

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

Enumerables

The module Enumerable has tons of methods, many of which are iterators. The methods include all?, any?, chunk, collect, collect_concat, count, cycle, detect, drop, drop_while, each_cons, each_entry, each_slice, each_with_index, each_with_object, entries, find, find_all, find_index, first, flat_map, grep, group_by, include?, inject, map, max, max_by, member?, min, min_by, minmax, minmax_by, none?, one?, partition, reduce, reject, reverse_each, select, slice_before, sort, sort_by, take, take_while, to_a, zip

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

>> (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 also 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
=> :each
>> c = C.new
>> 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]]

Fibers

Fibers are Ruby’s mechanism for lighweight cooperative concurrency. You “call” the fiber with resume and it runs until it yields. Then you resume it again and it picks up where it left off.

>> f = Fiber.new do
>>   Fiber.yield 1
>>   Fiber.yield 2
>>   Fiber.yield 3
>> end
>> f.resume
=> 1
>> f.resume
=> 2
>> f.resume
=> 3
>> f.resume
=> nil
>> f.resume
FiberError (dead fiber called)

You can return a fiber from a method call and it will be able to access the local variables and parameters of the surrounding method.

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 to_roman(2983)
$ ruby useroman.rb
MMCMLXXXIII

Designing your own Classes

Instance fields have names beginning with @. The initialize method is called during the call to new on the class.

circle.rb
# 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.rb'
=> true
>> c = Circle.new
>> c.expand! 4
=> #<Circle:0x0000010112f168 @x=0, @y=0, @r=4>
>> c.expand!(2).perimeter.to_s
=> "50.26548245743669"
>> c = Circle.new 1, 1, 10
>> c.expand!(6).move!(2, -10)
=> #<Circle:0x0000010113dab0 @x=3, @y=-9, @r=60>
>> class Circle
>>   def to_s
>>     "<Circle at (#{@x},#{@y}) with radius #{@r}>"
>>   end
>> end
=> :to_s
>> c
=> #<Circle:0x0000010113dab0 @x=3, @y=-9, @r=60>
>> c.to_s
=> "<Circle at (3,-9) with radius 60>"
>> "#{c}"
=> "<Circle at (3,-9) with radius 60>"

Inheritance

animals.rb
# 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
$ ruby animals.rb
CJ says neigh
Bessie says moo
Little Lamb says baaaaa

Classes are open

Define your class bit by bit:

>> class C
>>   def method1
>>     0
>>   end
>> end
=> :method1
>> c = C.new
>> c.method1
=> 0
>> c.method2
NoMethodError (undefined method `method2' for #<C:0x00007f9464143ae8>)
>> 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:Integer)
>> class Integer
>>   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
=> :value=
>> c = C.new
>> c.value = 20
>> c.value
=> 20

...but that’s not the Ruby way. Normally you would use the attr_reader, attr_writer, and attr_accessor methods to automatically generate the variables and the reader and writer methods.

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

Instance Methods and Class Methods

Methods that are called on instances of a class are called instance methods; methods called on the class object are called class objects.

>> class Point
>>   attr_accessor :x, :y
>>   def initialize(x, y); @x = x; @y = y; end
>>   def midpoint(q); Point.new((@x + q.x) / 2, (@y + q.y) / 2); end
>>   def Point.random(xmin, xmax, ymin, ymax)
>>     Point.new(xmin + rand*(xmax - xmin), ymin+rand*(ymax-ymin));
>>   end
>> end
=> :random
>> p = Point.new(18, 77.5)
>> q = Point.random(-2, 2, -10, 10)
>> q.midpoint(p)
=> #<Point:0x00007f9512a763a0 @x=9.001095195360934, @y=34.2459443872966>
>> Point.singleton_methods
=> [:random]
>> Point.instance_methods(false)
=> [:x, :x=, :y, :y=, :midpoint]

Okay so, since q is an instance of class Point, and midpoint is defined within Point, we can call q.midpoint. But with class methods it is not so simple, right? When we call Point.random, it can’t possibly be the case that random was defined within the class of which Point is an instance, right?

Exercise: Why not?

So Ruby is doing something else. But what? Note first of all that in the documentation you will see the term class methods, but to access them you use the method singleton_methods. Singleton what? What is really going on here? Before answering this let’s look at one other interesting thing about methods.

Object-Specific Methods

When we called Point.random above, this worked because random is a method that we attached only to Point and to no other object. In fact, Ruby lets us do this for any object, not just classes. That’s right: You can attach new methods directly to an object. This is cool.

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

Wait, what? So we somehow attached a method to b but not to any other array? Uh... don’t methods belong to classes and not objects? Did we change the class of b? Let’s see:

>> b.class
=> Array

Nope. What’s going on? Answer: Ruby has singleton classes.

Singleton Classes

In addition to each object being an instance of a class, each object has attached to it its very own singleton class: a class object that is owned solely by the object. So while the class String, for example, can have bazillions of instances, an object’s singleton class belongs only to the object and to no other objects. The singleton class is where the “object-specific methods” go.

Let’s work with the singleton class a bit:

>> a = []
>> b = []
>> def b.hello; "Hi there"; end
=> :hello
>> b.hello
=> "Hi there"
>> bs = b.singleton_class
>> b.class
=> Array
>> bs.class
=> Class
>> bs.superclass
=> Array
>> bs.instance_methods(false)
=> [:hello]
>> b.singleton_methods(false)
=> [:hello]

Visibility

Call these methods to set the visibility of methods you are about to define:

Oh yeah, and visibility is dynamic:

>> 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 #<C:0x00007f9512ad4ba8>)
>> class C
>>   public :b
>> end
=> C
>> c.b
=> 1

Modules

A module is an object that contains methods. A class is a module that you can instantiate.

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
=> :bracket
>> class Dog
>>   include M
>>   def bark
>>     "woof"
>>   end
>> end
=> :bark
>> sparky = Dog.new
>> sparky.bark
=> "woof"
>> sparky.bracket
=> "(#<Dog:0x000001011a1f88>)"
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?

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})/
>> 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:

ModuleSelected Methods
Kernelbinding caller global_variables local_variables p
Objectclass instance_of? instance_variable_get instance_variable_set instance_variables is_a? kind_of? method methods object_id private_methods protected_methods public_methods respond_to? singleton_methods
Moduleconstants nesting ancestors class_variables const_defined? const_get const_set constants include? included_modules instance_method instance_methods method_defined? name private_instance_methods protected_instance_methods public_instance_methods.
Classsuperclass
ObjectSpace_id2ref, each_object

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. Some of them are:

Classes Modules
BasicObject
    Object (Kernel)
        Symbol
        NilClass
        TrueClass
        FalseClass
        String (Comparable)
        Numeric (Comparable)
            Integer
            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 cmath complex continuation coverage csv curses date dbm debug delegate digest dl drb e2mmap English erb etc extmk fcntl fiber fiddle fileutils find forwardable gdbm generator getoptlong gserver iconv io/wait ipaddr json logger mathn matrix minitest/benchmark minitest/mock minitest/spec minitest/unit 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 pp prettyprint profile profiler pstore pty racc racc/parser rational rdoc readline resolv resolv-replace rexml rinda ripper rss rubygems scanf sdbm securerandom set shell shellwords singleton socket stringio strscan syck sync syslog tempfile test/unit thread thwait time timeout tk tmpdir tracer tsort un uri weakref webrick Win32API win32ole xmlrpc 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:

testroman.rb
require 'test/unit'
require './roman.rb'

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.001735 seconds.
--------------------------------------------------------------------------------------
2 tests, 24 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications
100% passed
--------------------------------------------------------------------------------------
1152.74 tests/s, 13832.85 assertions/s

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