ruby-logo.png

Introduction to Ruby

Ruby is fun and very cool. Why would you not want to learn it?

Overview

Ruby

You can find some good sources 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

Note that Ruby’s puts function (“put string”) automatically puts a newline at the end. Cool, huh? 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:

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
=> 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
=> Integer
>> 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
>> Integer

Good to Know

Learning By Example

Everything is an object

Really everything is an object, even nil. And every object has an object id:

>> 4.object_id
=> 9
>> 45.3421.object_id
=> 159137057222940850
>> nil.object_id
=> 8
>> 0.object_id
=> 1
>> "dog".object_id
=> 70178536838120
>> "dog".object_id
=> 70178515892100
>> :dog.object_id
=> 1160668
>> :dog.object_id
=> 1160668
>> Math.object_id
=> 70178520188380
>> true.object_id
=> 20
>> [true,75.2,{"x"=>5}].object_id
=> 70178533022540
>> [true,75.2,{"x"=>5}].object_id
=> 70178515942600
>> [0,1].object_id
=> 70178515937840
>> [0,1].object_id
=> 70178536856060
>> x = [0,1]
=> [0, 1]
>> x.object_id
=> 70178536851160
>> x.object_id
=> 70178536851160

Every object has a class

Every object has exactly one class. All classes except BasicObject have a superclass.

>> 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
>> 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
=> BasicObject
>> BasicObject.superclass
=> nil

Numbers

>> 18 < 4
=> 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"
        from (irb):110:in `acosh'
        from (irb):110
        from /usr/local/bin/irb:12:in `<main>'
>> 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"
=> "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 a symbol. 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"
=> "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
=> 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 Array with 10 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

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}
=> {"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'}
=> {:ca=>"sacramento", :hi=>"honolulu", :wa=>"seattle"}
>> caps[:wa] = 'olympia'
=> "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.

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

Methods

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 " -> ")

Procs

>> addSeven = lambda {|x| x + 7}
=> #<Proc:0x000001020f72f8@(irb):1 (lambda)>
>> addSeven.class
=> Proc
>> addSeven.call(4)
=> 11
>> addSeven[4]
=> 11
>> def multiplier(n)
>>   Proc.new {|x| x * n}
>> end
=> nil
>> timesFour = multiplier(4)
=> #<Proc:0x00000102934650@(irb):2>
>> timesTen = multiplier(10)
=> #<Proc:0x00000102939880@(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:0x00000102945a68@(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 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
=> nil
>> c = C.new
=> #<C:0x00000100a428f0>
>> 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 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
=> #<Circle:0x0000010112f168 @x=0, @y=0, @r=1>
>> 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
=> #<Circle:0x0000010113dab0 @x=1, @y=1, @r=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
=> nil
>> c
=> "<Circle at (3,-9) with radius 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
=> nil
>> c = C.new
=> #<C:0x000001009a2008>
>> c.method1
=> 0
>> c.method2
NoMethodError: undefined method `method2' for #<C:0x000001009a2008>
        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:Integer
        from (irb):46
>> 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
=> nil
>> c = C.new
=> #<C:0x0000010113a7e8>
>> c.value = 20
=> 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 autmatically generate the variables and the reader and writer methods.

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

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
=> nil
>> p = Point.new(18, 77.5)
=> #<Point:0x0000010091c9f8 @x=18, @y=77.5>
>> q = Point.random(-2, 2, -10, 10)
=> #<Point:0x00000100916b98 @x=-1.912211807236475, @y=-7.417932981097266>
>> q = Point.random(-2, 2, -10, 10)
=> #<Point:0x00000100910e78 @x=-1.1662009962277118, @y=-1.3778010808444954>
>> q = Point.random(-2, 2, -10, 10)
=> #<Point:0x00000100909ee8 @x=0.16073518460527403, @y=6.795005688264965>
>> q.midpoint(p)
=> #<Point:0x000001008f9908 @x=9.080367592302636, @y=42.14750284413248>
>> 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
=> nil
>> b.hello
=> "hi there"
>> a.hello
NoMethodError: undefined method `hello' for []:Array
        from (irb):9

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
=> nil
>> b.hello
=> "Hi there"
>> bs = b.singleton_class
=> #<Class:#<Array:0x00000100998738>>
>> 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:0x00000002c90da8>
>> c.b
NoMethodError: private method `b' called for #<C:0x00000002c90da8>
        from (irb):17
>> 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
=> nil
>> class Dog
>>   include M
>>   def bark
>>     "woof"
>>   end
>> end
=> nil
>> sparky = Dog.new
=> #<Dog:0x000001011a1f88>
>> 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?

Operator Overloading

You can surely think of better examples than this...

>> class Integer
>>   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:

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
Run options:

# Running tests:

..

Finished tests in 0.002850s, 701.7544 tests/s, 8421.0526 assertions/s.

2 tests, 24 assertions, 0 failures, 0 errors, 0 skips

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