The Ruby Programming Language

A quick overview of the more surprising bits of Ruby. Examples taken from: I also recommend you get a cheat sheet [2].

1 History

Yukihiro Matsumoto

2 Datatypes

#Arrays
a = [1, 2, "hi", :yo] #anything goes
a[0] #  1
a[-1] # :yo, negative counts from end
a.length # 4
a[1..3]  #  [2, "hi", :yo] This is a Range
a[1...3] # [2, "hi"] Three dots exclude end

digits = 0..9
digits.include?(5) # true
digits.min # 0
digits.max # 9
digits.reject {|i| i < 5} # [5,6,7,8,9]
digits.each {|digit| puts digit}  #prints 0 to 9

#Hashes
h={:grade => 88, 'name' => 'Peter', :age => 23}
h['name']  # "Peter"
h['grade'] # nil
h[:grade]  # 88

#Numbers
6.times do
  puts "Hi" #writes it 6 times.
end
1.upto(5) {print "hi"}
99.downto(95) {print "hi"}
50.step(80.5) {print "hi"}

#Regular expressions, like Perl
b = /^\s*[a-z]/ #define a regexp

name = "Fats Waller"
name =~ /a/ # 1, it contains a
name =~ /z/ # 0, but not z
/a/ =~ name # 1
#Sideffect of a match is setting variables:
$& #contains the text of the match



3 Loops

6.times do
  puts "Hi" #writes it 6 times.
end

#prints a b c d each on a new line
['a', 'b', 'c', 'd'].each do |i|
  puts i 
end

#the same
for i in ['a', 'b', 'c', 'd']
    puts i 
end


#This is how we get input from user
while line = gets
 # do stuff
end

file = File.open("filename")
while line = file.gets
 # do stuff
  break #goes to first line after loop
  redo  #repeats loop at the current iteration
  next  #skips to next iteration
  retry #restarts loop from first iteration
end


#Mostly, use the class's iterators: each

4 Classes

class Song

  # The constructor:
  def initialize(name, artist, duration)
    @name = name  #instance variables start with @
    @artist = artist 
    @duration = duration
  end

  #defalt method for turning this instance to a string
  def to_s
    "Song: #@name--#@artist (#@duration)" #perl-like replacement, no return.
  end
end

# Uses 'new'
song = Song.new("Re: Your Brains", "Jonathan Coulton", 272)

# Write to stdout
puts song #Song: Re: Your Brains--Jonathan Coulton (272)

song.name = "new title" # This fails! by default they are private.

puts song.name # This also fails.

# Subclass Song
class KaraokeSong < Song
  
  def initialize(name, artist, duration, lyrics)
    super(name, artist, duration) #call parent constructor
    @lyrics = lyrics
  end

  def to_s
    super + " [#@lyrics]"
  end

end

4.1 Class Variables

class Song
  attr_reader :name # If it starts with : then its a symbol
  attr_writer :name, :artist

  @@plays = 0  # Class variables start with @@

  # The constructor:
  def initialize(name, artist, duration)
    @name = name
    @artist = artist 
    @duration = duration
  end

  def to_s
    "Song: #@name--#@artist (#@duration)" #perl-like replacement, no return.
  end

  def Song.maxLength # Class methods start with the class name
    5000
  end
end

song = Song.new("Re: Your Brains", "Jonathan Coulton", 272)

puts song #Song: Re: Your Brains--Jonathan Coulton (272)

song.artist = "Me" # This fails! by default they are private.

puts song #Song: Re: Your Brains--Me (272)

puts Song.maxLength # 5000

4.2 Access Control

class Accounts
  def initialize(checking, savings)
    @checking = checking
    @savings = savings
  end
  
  private #keyword

  def debit(account, amount)
    account.based -= amount
  end

  def credit(account, amount)
    account.balance += amount
  end

  public #keyword

  def transfer_to_savings(amount)
    debit(@checking, amount)
    credit(@savings, amount)
  end
end

5 Calling Methods

#There is a lot of flexibility!

def my_method(a,b,c)
  puts a + " " + b + " " + c
end

my_method( 'j', 'o', 'e') # j o e

#default args

def get_data(name="peter")
  "#{name}"
end

get_data         #peter
get_data("john") #john

#Variable number of arguments
def varargs(first, *rest)
  "Got #{first} and #{rest.join(', ')}"
end

varargs("one") #Got one and 
varargs("one", "two", "three")#Got one and two, three
varargs "one", "two", "three" #Got one and two, three

5.1 Blocks and Iterators

#EVERY method takes a optional block as its last argument.

#A block is either a function 
['paul', 'john', 'ringo', 'george'].each do 
  puts 'hi'
end

#Or, if it starts with |x| then x iterates:
['paul', 'john', 'ringo', 'george'].each do 
  |x| puts x
end

# the same thing, but with {}
['paul', 'john', 'ringo', 'george'].each { |x| puts x }


#You use these for iterators

class Grades

  # *grades will mash up all arguments into an array.
  def initialize (*grades)
    @grades = grades
  end

  #This method takes no argument BUT
  # like all methods takes an optional block argument
  # at the end (block is ALWAYS the last argument).
  def hiToEach ()
    @grades.length.times do
      yield #calls the block.
    end
  end

  #This time pass the block a value (i). This method is thus an iterator
  def eachGrade()
    @grades.each do |i|
      yield(i)
    end
  end

  #Of course, the iterator can take arguments
  def eachBiggerThan(x)
    if block_given? #use this to check if we are given a block
      @grades.each do |i|
        yield(i) if (i >= x)
      end
    else
      puts "You forgot the block!"
    end
  end

end

g = Grades.new(88,99,80,77,87)

g.hiToEach {puts "hi"} #prints "hi" 5 times

#Use the iterator. Notice that this block starts with |x|
#Prints out: 105.6 118.8 96.0 92.4 104.4
g.eachGrade do |x|
  puts x * 1.2     
end

#Prints out 88 99 87
g.eachBiggerThan(85) do |x|
  puts x  
end

g.eachBiggerThan(85) # You forgot the block!

5.2 Proc

class Student
  def initialize
    @data = {}   #empty hash
    @grades = [] #empty array
  end

  def addThreeGrades(a,b,c)
    @grades.push(a)
    @grades.push(b)
    @grades.push(c)
  end

  def addData(d)
     d.each do |key, value|
       @data[key] = value
     end
  end

  def eachGrade
    @grades.each do |g|
      yield(g)
    end
  end

end

s = Student.new()

g = [70,80,90]

#The * before the g blows up the array into arguments
s.addThreeGrades(*g)

# Adds all these to @data hash table
s.addData :name => "joe", :id => "12345", :year => 3

#Add another one
s.addData :major => "CSCE"

#Create a Proc object. prints x*2
timesTwo = lambda {|x| puts x * 2}

#To pass a Proc as a block it must be preceeded by &
# and be the last argument.
s.eachGrade(&timesTwo) #140 160 180, each on a new line

6 Modules

module MyLibrary
 #In here I can define my classes, etc.
end

#Modules can also be used as mixins. 
#Define a mixin
module Debug
  def who_am_i?
    "#{self.class.name} (\##{self.object_id}): #{self.to_s}"
  end
end

#If Debug was in another file would would have need to
require Debug

#Use the mixin
class Phonograph
  include Debug #adds the mixin. This just adds a dynamic link

  #....
end

ph = Phonograph.new("West End Blues")
ph.who_am_i? # Phonograph (#942232): West End Blues

6.1 More Mixin

class Song
  include Comparable #a built-in mixin, requires me to define <=>
  attr_reader :duration #so I can access other.duration

  def initialize(name, artist, duration)
    @name = name
    @artist = artist
    @duration = duration
  end

  def <=>(other)
    return @duration <=> other.duration
  end

end

s1 = Song.new('a', 'a', 100)
s2 = Song.new('a', 'a', 200)

s1 <=> s2 # -1 
s1 < s2 #true
s1 > s2 #false

URLs

  1. Programming Ruby: The Pragmaic Programmers' Guide, http://jmvidal.cse.sc.edu/lib/thomas05a.html
  2. cheat sheet, http://ruby.cenophobie.com/rubycheat.php

This talk available at http://jmvidal.cse.sc.edu/talks/ruby/
Copyright © 2009 José M. Vidal . All rights reserved.

12 March 2008, 02:00PM