A Crash Course in Ruby

From 'Hello World' to Metaprogramming

Nikolas Coukouma

Ingredients

Numeric Literals

String Literals

Longer String Literals

Arrays, Hashes, and Regular Expressions (Oh My!)

Ranges and Singletons

Arithmetic Operators

Addition1 + 12
Subtraction4 - 31
Multiplication7 * 214
Integer Division18 / 44
Real Division18 / 4.04.5
Exponentiation3**481

Bitwise operators

Not~0-1
And3 & 22
Or5 | 37
Xor3 ^ 12
Left Shift3 << 212
(Logical) Right Shift9 >> 22

Logical Operators

Andtrue && falsefalse
And (alternate)true and falsefalse
Ortrue || falsetrue
Or (alternate)true or falsetrue
Not!falsetrue
Not (alternate)not falsetrue
Exclusive Ortrue ^ falsetrue

Comparison Operators

Equals1 == 2false
Not equals1 != 2true
Less than1 < 2true
Less than or equal to2 <= 2true
Greater than1 > 2false
Greater than or equal to2 >= 2true
Cmp, aka "Spaceship"1 <=> 2-1
  • <=> returns a value less than, equal to, or greater than 0 if the same comparison holds for the left side to the right side. It returns nil if the comparison "doesn't make sense" (e.g. 1 < "hi"nil

String Operators

Concatenation"Hello" + " World""Hello World"
Repetition"Echo " * 3"Echo Echo Echo "
Pattern Matching (Success)"Foobar" =~ /ob?a/2
Pattern Matching (Failure)"Foobar" =~ /z+/nil
Formatting (sprintf)"Line %04d" % 217"Line 0217"
Formatting, Multiple Values"Result %03d: %f" % [19, 85.2]"Result 019: 85.200000"

Array Operators

Indexing (Fetch)[1,2,3][0]1
Reverse Indexing[1,2,3][-1]3
Concatenation[1,2] + [3,4][1,2,3,4]
Repetition[1,2,3] * 2[1,2,3,1,2,3]
Append[1,2] << 3[1,2,3]
Set Difference[1,2,3] - [1,3,4][2]
Set Union[1,2] | [2,3][1,2,3]
Set Intersection[1,2] & [2,3][2]
Slicing[1,2,3,4,5][1,3][2,3,4]
... Negative Lengths?[1,2,3][2,-1]nil

Variables (and Constants)

  • Capitalized 'words' are constants
  • this and that could be local variable names
  • $frequency is a global variable
  • self is special; it refers to the current object ('this' in many languages)
  • @mine is an object variable
  • @@mine is a class variable variable. It is shared by all objects of that class (including its subclasses)

Assignment Operators

  • a = b performs assignment
  • All variables in Ruby are references
    a = [1,2,3]
    b = a
    a << 4
    b
    [1,2,3,4]
  • a += b is semantically equivalent to a = a + b
  • Yes, that means addition is done and then assignment; there is no separate += operator
  • No, Ruby does not have ++ or --

If, Elsif, Else, End, Unless

  • The usual
    if score < 50
      "That stinks"
    elsif score < 70
      "It needs more work"
    else
      "It's okie dokie pokie"
    end
  • "Things have gone pear-shaped" if 2 < 1
  • "City of Townsville needs you!" unless safe
  • got_ruby ? "Mmm" : "Eww"

Switching to Case

  • case mystery
    when "bob"
      "Hi bob!"
    when /\w+/
      "Looks like a word ..."
    when Numeric
      "A number of some sort ..."
    else
      "I don't know"
    end
  • No need for breaks
  • Works with any object
  • Uses the liberal "goes with" operator: ===
  • Instead of default, just use else

Going Loopy

  • while presenting
      pants_on = true
      slide_number += 1
    end
  • until comfortable
      salary += 1
    end
  • salary -= 1 while unsatisfactory
  • widgets *= 2 until enough

Getting Methodical

def hello
  "Hello World!"
end
  • The last line in a block is returned as it's value, unless something makes it end earlier
  • return can be used to make a method return early (with a value if you like)
  • hello takes no arguments
  • hello → "Hello World!"
  • No parentheses required! However, they're still welcome
  • hello() → "Hello World!"

Getting Optionally Methodical

def greet(name = "sir or madam")
  "Good day #{name}"
end
  • greet takes one argument, and it's optional
  • greet"Good day sir or madam"
  • greet "Bob""Good day Bob"
  • Method names can end in ? or !
    • Use ? for methods that ask questions, like [1,2].include? 'a'
    • Use ! when there are two versions of a method: one that that modifies the original and one that doesn't. Array#sort returns a new array that's sorted, while Array#sort! sorts an array in-place (and returns that same array)

Getting Variably Methodical

def greet_many(*names)
  "Good day #{names.join(' and ')}"
end
  • greet_many takes no, one, or many arguments
  • greet_many"Good day "
  • greet_many "Linda""Good day Linda"
  • greet_many "Flopsy", "Mopsy""Good day Flopsy and Mopsy"

Getting Nameably Methodical

def make_captcha(name, options = {})
  width = options[:width] || 200
  width = options[:height] || 80
  # more code ...
end
  • make_captcha takes one argument and some (optional) named arguments
  • make_captcha "captcha1"
  • make_captcha "captcha2", :width => 100
  • Named arguments are collected in a hash and stored in the last argument

Noticing Objects

  • There are three things in Ruby: keywords (e.g. if), punctuators (e.g. ','), and objects
  • A previous example used Array#join
  • There's also String#split "one, two, three".split(/,\s*/) → ["one","two","three"]
  • And Numeric#round (5.3).round5
  • Operators are also methods. Trying to apply common semantics doesn't always work
    • "hi"*3"hihihi"
    • 3*"hi" ! TypeError

Blockiness

e = "Something"
["hi",98,:bar].each do |e|
  puts e
end
  • Blocks encapsulate code and can use (and clobber!) the variables from the surrounding code
  • They are often used for iteration
  • retry causes the block to run again with the same arguments
  • next ends this run of the block
  • break ends this run and the method calling the block

Yielding and Calling

def attend_sessions(*sessions)
  i = 0
  sessions.each do |s|
    puts "Attending session #{i}: #{s}"
    yield s
  end
end
  • Blocks are objects, too
  • You can explicitly access this object, like so:
def attend_sessions(*sessions, &block)
    # same code
    block.call s
    # same code
end

Getting Classy

class BarCamp
  DEFAULT_FUN = 10**6
  def initialize
    @fun = DEFAULT_FUN
  end
  def bored_attendees(num)
    @fun = DEFAULT_FUN - 100*num
  end
  def is_boring?
    @fun < 1000
  end
end
event = BarCamp.new
event.bored_attendees 10
event.is_boring?

Subclassing

class Hero
  def save(name)
    "Doing CPR on #{name}"
  end
  def is_awesome?
    true
  end
end
class SuperHero < Hero
  def initialize(*ps)
    @powers = ps
  end
  def save(victim, villain)
    "Rescuing #{victim} from #{villain}"
  end
  def powers
    @powers
  end
end
sally = Hero.new
flash = SuperHero.new "Super speed", "Chemistry"

Exceptional Circumstances

  • Catching Exceptions:
    begin
      1/0
    rescue Exception => e
      puts "Caught Exception!"
      puts e
    finally
      puts "This is always printed"
    end
  • throw obj will throw any object as an exception
  • raise MyExceptionClass, arg, backtrace will create and throw a MyException with arg and print backtrace instead of the actual trace. all arguments are optional
  • raise with no arguments within a rescue block will re-raise the exception that was caught

Modules

  • Provide a namespace for constants and "objectless"-methods
  • Store methods for mixing into classes
  • The mixing of methods provides something like multiple inheritance
  • Can be used for interfaces, but with code!
  • include other modules
  • Usually assigned to constants, like classes

Mixing In Comparisons

class Lame
  include Comparable
  def <=>(o)
    3 <=> o
  end
end
  • Comparable provides all other comparison operators, based on the definition of <=>

Modularizing Your Code

module CrookedVein
  include Comparable
  BUDGET = 500_000_000
  def self.publish
    puts "Here's a book"
  end
  def investigate
    # do stuff
  end
end
  • CrookedVein::BUDGET500000000
  • CrookedVein.publish prints "Here's a book"
  • If a class include CrookedVein, then it's instances will have an investigate method
  • Everything that includes CrookedVein, then it also acquires comparison operators (from Comparable)

Order in the Hierarchy

  • Classes are searched, going up the inheritance chain
  • Modules are searched in the reverse order they were included (so modules included later override those included earlier)
  • If a module includes other modules, they're also searched (in the same order)
  • No need to hunt through code, AClass.ancestors will show you the order
  • 1.class.ancestors[Fixnum, Integer, Precision, Numeric, Comparable, Object, Kernel]

Relationships and Divisions

  • (Almost) everything in Ruby is an Object
  • A module is Object that can hold methods for instances and act as a namespace
  • Classes are modules that can be instantiated and derived from (but not included)
  • Every Object has a class (some_obj.class). Modules are instances of Module and classes are instances of Class
  • You can determine if something is an instance of a class using "".is_a? Objecttrue
  • You don't need instances to tell if two things are related Fixnum < Numerictrue

Is That Code?

class Address
  # getter
  def number
    @number
  end
  # setter
  def number=(n)
    @number = n
  end
  attr_accessor :street, :city, :state
end
  • attr_accessor makes setters and getters for you
  • However, it's not magic; it's a method - a class method
  • This means that the class definition is live code

Metaprogramming

class Barker
  def self.sayer(meth_name)
    define_method meth_name do
      puts "#{meth_name}!!"
    end
  end
  sayer :hey
  sayer :stop!
end
b = Barker.new
b.hey
b.stop

What am I?

  • self has been used a lot to add methods
  • self is an Object but objects can't have methods
  • ... there must be "hidden" Class that the methods are added to
  • It's called a metaclass or singleton
  • self.sayer wasn't a method in the Class itself, but in it's metaclass

Cracking it Wide Open

o = Object.new
def o.works
  "This works"
end
class << o
  MY_CONST = 3
  def also
    "This works too, and gives more access"
  end
  # no access to outside variables :(
end
some_var = 2
(Foo = Module.new).module_eval do
  define_method "Some#{some_var}" do
    "This is Some#{some_var}"
  end
end

More Madness

  • class Foo < some_method() is valid
  • If you have multiple classes or modules that need the same methods, don't duplicate code between them
  • class Bar < Module is valid
  • Metaclasses can include Modules, just like normal classes
  • Class, Modules, and metaclasses can all be modified at runtime
  • There are even callbacks (see method_added and method_missing)