Ruby Tutorial with Code Samples

Ruby for the Attention Deficit Disorder Programmer - let's start learning Ruby fast!

"Java is a pair of scissors, Ruby is a chainsaw." -Mitch Fincher

  1. Install Ruby

    You can download Ruby from https://www.ruby-lang.org/en/downloads/

  2. Our first program

    Enter the following into the file, "test.rb".

     
    puts "Howdy!"
    
    At the C: prompt enter,
     
    C:>ruby test.rb
    

    This produces:

     
    Howdy!
    

    OK, daylight's burning, let's move on.

  3. Output in Ruby


    "puts" writes to the screen with a carriage return at the end.
    "print" does the same thing without the carriage return.
    "printf" formats variables like in C and Java.

     
    puts "puts works"
    puts " with line breaks."
    
    print "print works"
    print " with no line breaks."
    
    printf("\n\nprintf formats numbers like %7.2f, and
    strings like %s.",3.14156,"me")
    

    This produces:

    puts works
     with line breaks.
    print works with no line breaks.
    
    printf formats numbers like    3.14, and strings like
    me.
    
  4. Reading from the Console

    Use "gets"

    puts "What is your name?"
    $name = STDIN.gets
    puts "Hi "+$name
    
  5. Functions
    1. Our first Ruby function

      'def' starts the definition of a method, and 'end' ends it - no cute little curly braces.

      def welcome(name)
         puts "howdy #{name}"   # inside double quotes, #{ } will evaluate the variable
      end
      welcome("nana")           # traditional parens
      

      This Produces:

      howdy nana
      
    2. Parentheses are optional
      def welcome(name)
         puts "howdy #{name}"   # inside double quotes, #{ } will evaluate the variable
      end
      welcome "visitor"         #look, ma, no parentheses
      

      This Produces:

      howdy visitor
      

      The same is true of methods without arguments

      "hello".upcase() => "HELLO"
      "hello".upcase => "HELLO"
      
    3. How to return values from a function

      We can use the faithful 'return'

      def multiply(a,b)
        product = a * b
        return product
      end
      puts multiply(2,3)   =>6
      

      Oddly enough you can leave out the "return" statement, and Ruby will helpfully return the last expression:

      def mult(a,b)
        product = a * b
      end
      puts mult(2,3)
      

      or even simpler, leave out "product" and ruby returns the contents of the last expression:

      def mult(a,b)
         a * b
      end
      puts mult(3,3)   =>9
      
    4. Optional argument values

      Ruby lets you assign values to arguments which may, or may not be supplied as shown below:

      def test(a=1,b=2,c=a+b)
        puts "#{a},#{b},#{c}"
      end
      test        =>  1,2,3
      test 5      =>  5,2,7
      test 4, 6   =>  4,6,10
      test 3, 4, 6   =>  3,4,6
      
    5. Extra arguments

      Extra arguments are gathered into the last variable if preceded with a "*". ("each" is an iterator that loops over its members).

      def test(a=1,b=2,*c)
        puts "#{a},#{b}"
        c.each{|x| print " #{x}, "}  # We will learn about "each" very soon.  I promise.
      end
      test 3, 6, 9, 12, 15, 18
      

      This produces:

      3,6
       9,  12,  15,  18,
      
    6. Multiple return values
      def getCostAndMpg
          cost = 30000  # some fancy db calls go here
          mpg = 30
          return cost,mpg
      end
      AltimaCost, AltimaMpg = getCostAndMpg
      puts "AltimaCost = #{AltimaCost}, AltimaMpg = #{AltimaMpg}"
      

      Produces:

      AltimaCost = 30000, AltimaMpg = 30
      
  6. Open Classes

    You can add methods to existing library classes. For example, in C# 2.0, Microsoft added the very helpful string function, IsNullOrEmpty() which replaces the unwieldly construct:
    if(mystring != null && mystring != "")
    In Ruby you don't have to wait for the mavens in Redmond to decide you need a new string function, you can add it yourself.

    class String
        def NullOrEmpty?
        (self == nil || self == "")
        end
    end
    puts "test".NullOrEmpty?
    puts "".NullOrEmpty?
    

    Is this way cool? Yes. Is this very dangerous? Yes. Remember, Ruby is a chainsaw.

  7. Variable naming

    Ok, let's slow down and learn some basics about variable names

    1. Global variables start with '$'
    2. Class variables start with '@@'
    3. Instance variables start with '@'
    4. Local variables, method names, and method parameters start with a lower case letter
    5. Class names, module names and constants start with an uppercase letter
    6. Variables names are composed of letters, numbers and underscores
    7. Method names may end with "?", "!", or "=". Methods ending with a "?" imply a boolean operation (eg, "instance_of?"). Methods ending with "!" imply something dangerous, like strings being modified in place (eg, "upcase!")
  8. Interesting tidbits about Ruby,
    1. '#' is the line comment character, all characters after this are ignored. Confusingly '#' can appear within quotes with a different meaning.
    2. No semi-colons are needed to end lines, but may be used to separate statements on the same line
    3. A backslash (\) at the end of a line is used for continuation
    4. Indenting is not significant, unlike python
    5. Types of variables do not need to be declared
    6. Lines between =begin and =end are ignored
    7. Lines following "__END__" on its own line with no white space, are ignored
    8. A tiny demonstration of these:
      # sample program showing special characters like comments
      # I'm a comment line
      a = 1  #notice no semicolon and no type declaration
      b = 2; c = 3 #notice two statements on one line
      name = "Abraham \
      Lincoln"   # a line continued by trailing \
      puts "#{name}"
      =begin
      I'm ignored.
      So am I.
      =end
      puts "goodbye"
      __END__
      1
      2
      3
      4
      
      Abraham Lincoln
      goodbye
      
  9. Variable Types

    In Ruby, variables don't have a specific type associated with them. All variables are objects, so we only play with pointers to those objects, and those pointers are type agnostic.

  10. bat = "Louisville slugger"
    bat = 1.23
    
  11. Quotes

    Like in Perl, single quotes and double quotes have different meanings.

    Double quotes means "please interpret special characters in this string". Things like backslash n ('\n') are converted to their typical values. The #{name} construct is converted to its value.

    With single quotes, no special characters are interpreted.

    Examples:

    name="Mike"
    puts "hi #{name}" =>hi Mike
    puts "hi\n #{name}" => hi (carriage return)Mike
    puts 'hi\n #{name}' => hi\n #{name}    (no substitutions are made since using single quote)
    

    The braces are optional for global and instance variables

    $myname="Ishmael"
    puts "hi #$myname" =>hi Ishmael
    
  12. Objects

    A great thing about Ruby is that numbers and strings are real objects.

    1.5.floor()   => "1" 
    

    This lets us do some cool things. Instead of

    if( x > 7 && x < 12 ) { ... }
    

    We can write

    if x.between?(7,12) do ...
    
  13. Big Numbers

    Ruby automatically increases the precision of variables

    for i in 1..1000
      puts "2 ** #{i} = #{2**i}"
    end
    

    Produces:

    2 ** 1 = 2
    2 ** 2 = 4
    2 ** 3 = 8
    2 ** 4 = 16
    2 ** 5 = 32
    2 ** 6 = 64
    2 ** 7 = 128
    2 ** 8 = 256
    2 ** 9 = 512
    2 ** 10 = 1024
    ...
    2 ** 1000 = 107150860718626732094842504906000181056140481170553360744375038837035105112493612249319837881569585812759467291755314682518714528569231404
    359845775746985748039345677748242309854210746050623711418779541821530464749835819412673987675591655439460770629145711964776865421676604298316526243868
    37205668069376
    

    Ruby will increase the precision of the number, or decrease it as needed:

    x = 1000000
    puts "#{x}  "+x.class.to_s    => 1000000  Fixnum
    x = x * x
    puts "#{x}  "+x.class.to_s    => 1000000000000  Bignum
    x = x / 1000000
    puts "#{x}  "+x.class.to_s    => 1000000  Fixnum
    
  14. Parallel Assignment

    You can swap the values in variables without the use of a temp variable. Remember your first programming class: Swap the values in "i" and "j"? You had to use a "t" variable to store one of the values first. Not needed in Ruby.

    i = 0
    j = 1
    puts "i = #{i}, j=#{j}"
    i,j = j,i
    puts "i = #{i}, j=#{j}"
    

    Produces:

    i = 0, j=1
    i = 1, j=0
    
  15. Collections
    1. Arrays
      1. An array of known objects can be created by enclosing them in square brackets.
        nums = [1, 2.0, "three"]
        puts nums[2]  => three
        

        Ruby arrays, like all right-thinking collections, are zero based.

      2. You can use negative indexes to start from the end of the array

        nums = [1, 2.0, "three", "four"]
        puts nums[-1]   => four
        

        Using "-1" is so much more concise than "nums[nums.length()-1]".

      3. You can even use the handy "first" and "last" methods.
        [1,2,3].last  => 3
        [1,2,3].first => 1
        
      4. length

        To get the count, or size, of an array, use the "length" method.

        mystuff = ["tivo","nokia", "ipaq"]  # make a string array
        puts mystuff.length  => 3
        
      5. %w shortcut

        Since many arrays are composed of single words and all those commas and quote marks are troublesome, Ruby provides a handy shortcut, %w:

        mystuff = %w{tivo nokia ipaq}  # make a string array
        
      6. inspect

        To look at contents of an object use the "inspect" method. Even more convenient is to use "p" as a shorthand for "puts obj.inspect"

        myarray = [1,2,5,7,11]
        puts myarray
        puts myarray.inspect
        p myarray
        

        Produces:

        1
        2
        5
        7
        11
        [1, 2, 5, 7, 11]
        [1, 2, 5, 7, 11]
        
      7. Arrays can act like queues and sets
        # & is the intersection operator 
        puts [1,2,3] & [3,4,5]  #  prints 3
        
        # + is the addition operator 
        puts [1,2,3]+ [3,4,5]  #  prints 1,2,3,3,4,5
        
        # - removes items from the first array that appear in the second
        puts [1,2,3] - [3,4,5]  #  prints 1,2
        
        # pop returns the last element and removes it from the array
        alpha = ["a","b","c","d","e","f"]
        puts "pop="+alpha.pop   # pop=f
        puts alpha.inspect      # ["a", "b", "c", "d", "e"]
        
        # push appends elements to the end of an array
        alpha = ["a","b","c"]
        alpha.push("x","y","z")
        puts alpha.inspect      # ["a", "b", "c", "x", "y", "z"]
        
        # shift returns the first element and removes it from the array
        alpha = ["a","b","c","d","e","f"]
        puts "shift="+alpha.shift   # shift=a
        puts alpha.inspect      # ["b", "c", "d", "e", "f"]
        
        # unshift appends elements to the beginning of an array
        alpha = ["a","b","c"]
        alpha.unshift("x","y","z")
        puts alpha.inspect      # ["x", "y", "z", "a", "b", "c"]
        
    2. Hashes

      This type of collection is also called a dictionary or an associative array.

      1. Simple hash of cars and their makers
        cars = {
        'altima' => 'nissan',
        'camry' => 'toyota',
        'rx7' => 'mazda'
        }
        puts cars['rx7']   =>   mazda
        
      2. You can create a hash and fill it dynamically
        dict = {}  # create a new dictionary
        dict['H'] = 'Hydrogen' #associate the key 'H' to the value 'Hydrogen'
        dict['He'] = 'Helium'
        dict['Li'] = 'Lithium'
        p dict['H']       # prints "Hydrogen"
        p dict.length     # prints 3
        p dict.values     # prints ["Lithium", "Helium", "Hydrogen"]
        p dict.keys       # prints ["Li", "He", "H"]
        p dict            # prints {"Li"=>"Lithium", "He"=>"Helium", "H"=>"Hydrogen"}
        
      3. Hash[]

        You can also create Hashes with square brackets by prefixing with "Hash":

        toppings = Hash["pancakes","syrup","Pizza","Pepper","Cereal","Sugar"]
        puts toppings.inspect
        

        Produces:

        {"Pizza"=>"Pepper", "Cereal"=>"Sugar", "pancakes"=>"syrup"}
        
      4. each

        The "each" method is a wonderful way to iterate over the keys

        toppings = Hash["pancakes","syrup","Pizza","Pepper","Cereal","Sugar"]
        toppings.each{|key, value| puts "#{key} points to #{value}"}
        

        Produces:

        Pizza points to Pepper
        Cereal points to Sugar
        pancakes points to syrup
        
      5. select

        The "select" method populates a new array with members which meet a criteria

        salaries = Hash["bob",10.9,"larry",7.5,"jimmy",6.0,"jerry",6.5]
        salaries.inspect
        mySalaryArray = salaries.select{|name,salary| salary > 7.0}
        puts mySalaryArray.inspect #prints  [["larry", 7.5], ["bob", 10.9]]
        
    3. Ranges

      Ranges are composed of expr..expr or expr...expr. Two dots includes the last element, three dots excludes it.

      ('a'..'g').each{ |letter| puts letter }
      

      Produces:

      a
      b
      c
      d
      e
      f
      g
      
      (1...3).each{ |num| puts num }
      

      Produces only two numbers since "..." does not include the last element.:

      1
      2
      
  16. Control Statements
    1. if In an "if" statement anything but the two special values, "false" and "nil" are considered true. Even zero is true for all you C/C++ programmers.
      income = 30000.00
      if income < 10000
        rate = 0.02
      elsif income < 30000
        rate = 0.28
      else
        rate = 0.5
      end
      puts rate
      
    2. case

      grade = 10
      school = case grade
      when 0..5
         "elementary"
      when 6..8
         "middle school"
      when 9..12
         "high school"
      else
         "college"
      end
      puts "grade #{grade} is in #{school}"
      
    3. for

      for i in 1..4 
       puts "hi #{i}"
      end
      

      The ranges can of course have variables

      top = 6
      for i in 1..top
       puts "hi #{i}"
      end
      
    4. exit

      lines = IO.readlines("data.txt")
      if lines.length < 100
         exit 2
      end
      puts lines.length
      
    5. loop

      iterates over code until a "break" or eternity ends

      i=0
      loop do
         break if i > 5
         puts i
         i += 1  
      end
      
  17. Statement modifiers

    These are just syntatic sugar.

    1. if

      The "if" clause may be placed at the end of a statement

      balance = -10.0
      puts "Bankrupt" if balance < 0.0
      
    2. unless

      "unless" is placed at the end of a statement

      balance = -10.0
      puts "Bankrupt" unless balance > 0.0
      
    3. while

      "while" may be after its block

      f=2
      puts f=f+2 while f < 10
      =>4
      =>6
      =>8
      =>10
      
  18. Iterators
    1. while
      i = 0
      while i  < 5
        i = i+1
        puts i
      end
      
    2. "times"
      n = 10
      n.times { |i| print i}
      

      Produces:

      0123456789
      
    3. "each"
      animals = %w(lions tigers bears)
      animals.each{|kind| print kind}
      
      lionstigersbears
      
    4. "each" with ranges
      ('m'..'z').each {|ch| print ch}
      
      mnopqrstuvwxyz
      
    5. "upto"
      n=0 ; max=7
      n.upto(max) {|num| print num}
      
      01234567
      
  19. You gotta have class.
    1. Classes

      Class definitions start with "class" and end with "end". Remember that class names start with a capital letter. Notice the syntax is "object.new" for creating an object and that the "initialize" method contains code normally found in the constructor. Here's a small example:

      class Person
        def initialize(fname, lname)
         @fname = fname
         @lname = lname
        end
      end
      person = Person.new("Augustus","Bondi")
      print person
      

      Produces:

      #<Person:0x257c020>
      

      which is true, but not helpful.

    2. The "ToString" method, to_s
      class Person
        def initialize(fname, lname)
         @fname = fname
         @lname = lname
        end
        def to_s
           "Person: #@fname #@lname"
        end
      end
      person = Person.new("Augustus","Bondi")
      print person
      

      Produces:

      Person: Augustus Bondi
      
    3. Subclassing

      In Ruby subclassing is done with the "<" character

      class Employee < Person
        def initialize(fname, lname, position)
          super(fname,lname)
          @position = position
        end
        def to_s
           super + ", #@position"
        end
      end
      employee = Employee.new("Augustus","Bondi","CFO")
      print employee
      

      Produces:

      Person: Augustus Bondi, CFO
      

      if we try to print the first name directly like

      print employee.fname
      
      we get the error message,
      CFOtest.rb:21: undefined method 'fname'
      

      But why is that? We've printed variables a zillion times up til now and it's always worked. What changed? Up until now we've created variables in a program without classes (actually all are variables were members of a default object that were accessable inside itself). Now we are using real classes and that brings up the point of visibility of members outside the class. We now have to specify if a variable is open to the outside, like "public", "private", "protected", "internal" in other languages.

      To grant access to read a variable we declare it after "attr_reader". attribute with the following:

        attr_reader :fname, :lname
      
      then
      print employee.fname  =>   "Augustus"
      

      To allow writing to a variable use "attr_writer",

      class Employee < Person
        def initialize(fname, lname, position)
          super(fname,lname)
          @position = position
        end
        def to_s
           super + ", #@position"
        end
        attr_writer :position
      end
      employee = Employee.new("Augustus","Bondi","CFO")
      puts employee
      puts employee.fname
      employee.position = "CEO"
      puts employee
      
    4. Virtual Attributes
      class Employee < Person
        def initialize(fname, lname, position)
          super(fname,lname)
          @position = position
        end
        def to_s
           super + ", #@position"
        end
        attr_writer :position
        def etype
           if @position == "CEO" || @position == "CFO"
               "executive"
           else
               "staff"
           end
        end
      end
      employee = Employee.new("Augustus","Bondi","CFO")
      employee.position = "CEO"
      puts employee.etype    =>  executive
      employee.position = "Engineer"
      puts employee.etype    =>  staff
      
  20. Regular Expressions

    Strings can be compared to a regular expression with "=~". Regular expressions are surrounded by "//" or "%r{}". Anything but the two special values, "false" and "nil" are considered true.

    Expression Result Description
    /a/ =~ "All Gaul is divided into three parts" 5finds the first "a" at position 5
    %r{a} =~ "All Gaul is divided into three parts" 5same thing with alternate syntax
    /ree/ =~ "All Gaul is divided into three parts" 27finds "ree" at position 27
    /^a/ =~ "All Gaul is divided into three parts" nil"^" implies at the beginning of a line. nil is false.
    /^A/ =~ "All Gaul is divided into three parts" 0case-sensitive, remember that "0" is true
    /s$/ =~ "All Gaul is divided into three parts" 35"$" implies at the end of a line
    /p.r/ =~ "All Gaul is divided into three parts" 31"." matches any character
  21. Blocks

    And now to one of the coolest things about Ruby - blocks. Blocks are nameless chunks of code that may be passed as an argument to a function.

    1. Simple Example
      def whereisit
         yield
         yield
         yield
      end
      whereisit {puts "where is the money?"}
      

      Produces:

      where is the money?
      where is the money?
      where is the money?
      

      In the above example '{puts "where is the money?"}' is called a block. That chunk of code is passed to the method "whereisit" and executed each time the "yield" statement is executed. You can think of the "yield" being replaced by the block of code.

    2. Blocks can take arguments

      Here the method "cubes" takes the max value.

      def cubes(max)
         i=1
         while i < max
            yield i**3
            i += 1
         end
      end
      cubes(8) { |x| print x, ", "}   => 1, 8, 27, 64, 125, 216, 343,
      sum = 0
      cubes(8) { |y| sum += y}
      print "\nsum=",sum              => sum=784
      product = 1
      cubes(8) { |z| product *= z}
      print "\nproduct=",product      => product=128024064000
      

      Think of the "yield i**3" in the function cubes as being replaced with the block, '|x| print x, ", "'. The value following the "yield" is passed as the value "x" to the block.

    3. Multiple arguments may be passed to blocks.

      def employee(empId)
            #next 2 lines simulated from calling a database on the empId
            lastname = "Croton"
            firstname = "Milo" 
            yield lastname,firstname  #multiple arguments sent to block
      end
      employee(4) { |last,first| print "employee ",": ",first, " ",last}
      

      Produces:

      employee : Milo Croton
      
    4. Local variables can be shared with a block

      Even though rate is a local variable, it is used inside the block.

      def tip(mealCost) 
         yield mealCost
      end
      rate = 0.15
      mytip = tip(10.0) { |cost| cost * rate }
      print "tip should be: ",mytip
      

      Produces:

      tip should be: 1.5
      
    5. Blocks are built in to many objects in ruby

      1. each

        iterates through each item of a collection

        [1,2,3,4].each{|x| print x**2," "}
        

        Produces:

        1 4 9 16
        
      2. detect

        returns the first item matching a logical expression

        numbers = [1,3,5,8,10,14]
        firstDoubleDigit = numbers.detect {|x| x > 9}
        print firstDoubleDigit  => 10
        
      3. select

        returns all items matching a logical expression

        numbers = [1,2,3,4,5,6,7];
        evens = numbers.select{|x| x % 2 == 0}
        p evens =>   [2, 4, 6]
        
      4. collect

        returns an array created by doing the operation on each element.

        [1,2,3,4].collect{|x| x**3}   => [1, 8, 27, 64]
        ["the","quick","brown", "lox"].collect{|x| x.upcase} => ["THE", "QUICK", "BROWN", "LOX"]
        
      5. inject

        "inject" is the "fold" or "reducer" function in Ruby. "inject" loops over an enumerable and performs an operation on each object and returns a single value.

        primes = [1,3,5,7,11,13];
        #using "inject" to sum.  We pass in "0" as the initial value
        sum = primes.inject(0){|cummulative,prime| cummulative+prime}
        puts sum    =>40
        #we pass in no initial value, so inject uses the first element
        product = primes.inject{|cummulative,prime| cummulative*prime}
        puts product    =>15015
        #just for fun let's sum all the numbers from 1 to, oh, say a million
        sum = (1..1000000).inject(0){|cummulative,n| cummulative+n}
        puts sum   =>500000500000
        #you can do interesting things like build hashes
        hash = primes.inject({}) { |s,e| s.merge( { e.to_s => e } ) }
        p hash  #   =>  {"11"=>11, "7"=>7, "13"=>13, "1"=>1, "3"=>3, "5"=>5}
        
  22. File I/O
    1. Read an entire file into a string

      file = File.new( "t1.php" )
      mytext = file.read
      
    2. Read an entire file into an array of lines

      lines = IO.readlines("data.txt")
      puts lines[0]  #prints the first line
      
    3. Read a file line by line

      file = File.open("res.txt")
         while line = file.gets
            puts line
         end
      

      Or you can use the IO class

      IO.foreach("data.txt") { |line| puts line }
      
    4. Read a file line by line

      You should ensure the file is closed as well.

      begin
      file = File.open("res.txt")
         while line = file.gets
            puts line
         end
      ensure
         file.close
      end
      
    5. Read only a few bytes at a time

      The following snippet of code reads a file which may have no line breaks and chops it into 80 character lines

      require 'readbytes'
      file = File.new( "C:/installs/myapp_log.xml" )
      while bytes = file.readbytes(80) 
         print bytes+"\r\n"
      end
      file.close
      
    6. Reads a large XML file and inserts line breaks

      Uses TruncatedDataError to grab the last few slacker bytes from the end.

      # reads an xml file without line breaks and puts a line break before each '<'
      require 'readbytes'
      file = File.new( "C:/installs/MyProject_log.xml" )
      begin
         while bytes = file.readbytes(80) 
            print bytes.gsub(/</,"\r\n<")
         end
      rescue TruncatedDataError #the last read had less chars than specified
         #print the rest of the data.  $! is the exception.
         # ".data" has the extra bytes
         print $!.data.gsub(/</,"\r\n<") 
      ensure
         file.close unless file.nil?
      end
      
  23. method_missing - a wonderful idea

    In most languages when a method cannot be found and error is thrown and your program stops. In ruby you can actually catch those errors and perhaps do something intelligent with the situation. A trivial example:

    class MathWiz
      def add(a,b) 
        return a+b
      end
      def method_missing(name, *args)
        puts "I do not know the method #{name}"
      end
    end
    mathwiz = MathWiz.new
    puts mathwiz.add(1,4)
    puts mathwiz.subtract(4,2)
    

    Produces:

    5
    I don't know the method subtract
    nil
    
  24. While the ruby program is loading, you can execute code inside a special block labeled "BEGIN" - pretty nifty. After the interpretor has loaded the code, but before execution, you can execute code in the "END" block.

    puts "main program running"
    END {
    puts "program ending"
    }
    BEGIN {
    puts "I am loading"
    }
    

    Produces:

    I am loading
    main program running
    program ending
    
  25. converting between strings and ints

    Use the to_i and to_s methods

    "3".to_i  #return an integer
    3.to_s    # returns a string
    
  26. Using XML Dom Parser

    REXML goes standard with Ruby 1.8. Sample to print all "div" elements whose "class" attribute is set to "entry".

    require "rexml/document"
    file = File.new( "t0.xml" )
    doc = REXML::Document.new file
    doc.elements.each("//div[@class='entry']") { |element| puts element }
    
  27. Run a few lines directly from the command line with the "-e" option

    c:\home\mfincher>ruby -e 'sleep 2'
    c:\home\mfincher>ruby -e 'puts 3*4'
    12
    c:\home\mfincher>ruby -e 'puts 3*4; puts 4*4'
    12
    16
    
  28. Editing files in place

    Ruby offers a simple way to make a string substitution in many files all at once with a single line of code. The "-p" option loops over the files, the "-i" is the backup extension. With this command we are changing all the documentation from version 1.5 to 1.6, but the original files are renamed to ".bak".

    C:\home\mfincher\ruby>more v2.txt
    Regarding version 1.5 ...
    ....
    version 1.5 is easy to install
    
    C:\home\mfincher\ruby>ruby -pi.bak -e "gsub(/1.5/,'1.6')" v*.txt
    
    C:\home\mfincher\ruby>more v2.txt
    Regarding version 1.6 ...
    ....
    version 1.6 is easy to install
    
    C:\home\mfincher\ruby>more v2.txt.bak
    Regarding version 1.5 ...
    ....
    version 1.5 is easy to install
    
  29. Example of printing duplicate lines in sorted file.

    #prints duplicate lines in sorted files in the file passed in as first arg
    file = File.open(ARGV[0])
    lastLine = ""
    counter = 0
       while line = file.gets
            counter += 1
    	if lastLine == line 
               puts "#{counter-1}: #{line}#{counter}: #{line}\r\n"
            end
    	lastLine = line	
       end
    puts "done. Processed #{counter} lines"
    
  30. Ruby has its own interpreted shell, irb.
    C:\home\mfincher>irb
    irb(main):001:0> puts "Hello World"
    Hello World
    => nil
    irb(main):002:0> a=1
    => 1
    irb(main):003:0> a*2
    => 2
    irb(main):004:0>
    
  31. ruby can take input from stdin

    echo 'puts "hello"' | ruby
    
  32. to pass a string on the url it needs to be "escape"'d first.

    require 'uri'
    ...
    URI.escape("some string...")
    
  33. Example to remove "funny" characters from a filename

    Example of iterating over the filenames in a directory, using regular expression substitution in strings, and renaming files.

    #replaces any "funny" characters in a filename in the current directory with an underscore
    #if the new file name already exists, this skips it.
    Dir.foreach(".") { |f| 
      print "testing \"#{f}\""
      if f =~ /[^\w\-\.]/   #filename contains something other than letters, numbers, _,-, or .
         puts "\r\n   name with funny characters: #{f}"
         newName = f.gsub(/[^\w\.\-]/,"_")   # \w is any word character, letter,num or _
         if File.exist?(newName)
    	puts "   File #{newName} already exists.  Not renaming."
         else
    	puts "   renaming #{f} to #{newName}"
    	File.rename(f,newName)
         end
      else
        puts "   it's ok."
      end
    }
    
    
  34. Looping over list of arguments

    ARGV.each {|f|
    puts f
    counter = 0
    file = File.open(f,"r")
       ftmp = f+".tmp"
       tmp = File.open(ftmp,"w")
       while line = file.gets
       if line =~ /pid="2"/
         counter += 1
         line = line.gsub(/pid="2"/,"pid=\"#{f}:#{counter}\"")
         puts line
       end
       tmp.print line
    end
    file.close
    tmp.close
    puts "renaming #{ftmp} to #{f}"
    File.rename(ftmp,f)
    }
     
    
  35. Miscellanous Commands
    command description example result
    global_variables returns all global variables
    local_variables returns all local variables
    sleep seconds sleeps specified seconds
    rand returns a random number between 0 and 1
    rand(max) returns int between 0 and max
    warnlike print, but writes to STDERR

    Interesting string functions

    command description example result
    center centers string "City".center(20) "________City________"
    ljust left justifies "State".ljust(30) "State_________________________"
    rjust right justifies "State".rjust(30) "_________________________State"
    include? does the string include this substring"this is a test".include?('is') true
    gsub global regex replacesments "this is a test".gsub(/[aeiou]/,'_\1') th_s _s _ t_st
    tr translates "The greatest of these is".tr('aeiou','*') Th* gr**t*st *f th*s* *s
    each splits and iterates "one:two:three".each(':'){|x| puts x}
    one:
    two:
    three
  36. DateTime

    puts DateTime.now  #prints 2006-11-25T14:26:15-0600
    puts Date.today    #prints 2006-11-25
    puts Time.now      #prints Sat Nov 25 14:29:57 Central Standard Time 2006
    
  37. Using 'require'

    require will let your access code from other files. 'require' looks in directories specified in $LOAD_PATH to find the files. The environmental variable RUBYLIB can be used to load paths into $LOAD_PATH.

    C:\home\mfincher\ruby>irb
    irb(main):001:0> p $LOAD_PATH
    ["c:/opt/ruby/lib/ruby/site_ruby/1.8", "c:/opt/ruby/lib/ruby/site_ruby/1.8/i386-msvcrt", "c:/opt/ruby/lib/ruby/site_ruby", "c:/opt/ruby/lib/ruby/1.8",
     "c:/opt/ruby/lib/ruby/1.8/i386-mswin32", "."]
    => nil
    

    You can put a library like 'startClicker.rb' in any of those directories and ruby will find it.

    require 'startClicker'
    
  38. BuiltIn Command Interpreter

    With the "eval" method you can create your own interpreter language and run it during execution.

    irb(main):007:0> a = 6
    => 6
    irb(main):008:0> b = 7
    => 7
    irb(main):009:0> eval "c=a+b"
    => 13
    irb(main):010:0> puts c
    13
    
  39. Introspection with ObjectSpace

    You can find all the objects in your program of a particular type using ObjectSpace.each_object.

    class Person
      def initialize(name,age)
        @name = name
        @age = age
      end
      attr_reader :name
    end
    p = Person.new("Alfred",34)
    p2 = Person.new("Janie",31)
    ObjectSpace.each_object(Person) {|s| 
       puts s.name 
    } 
    

    Produces:

    Janie
    Alfred
    
  40. Testing

    Ruby comes right out of the box with a testing framework. Here's a quick example:

    require 'test/unit'
    
    class TestMe < Test::Unit::TestCase
    
      def test_add
        s = 1 + 1
        assert_equal(2, s)
      end
    
    end
    
  41. Read a URL and print the web page to the screen.

    This will get a particular page and print to the screen:

    require 'open-uri'
    open('http://www.fincher.org/Misc/Pennies'){ |f| print f.read }
    

    This will read a file of urls and print all to the screen:

    #Reads first argument as file containing urls and prints them
    #usage:  ruby wget.rb wget.txt
    require 'open-uri'
    IO.foreach(ARGV[0]) { |line| open(line){ |f| print f.read } }
    
  42. Example of drawing a line on a canvas in Tk

    Tk is a graphical subsystem used in languages like Perl and Tcl.

    #draws a single line on a big canvas
    require 'tk'
    include Math
    
    TkRoot.new do |root|
       title "Solo Line"
       geometry "600x600"
       canvas2 = TkCanvas.new(root) do |canvas|
          width 600
          height 600
          pack('side' => 'top', 'fill'=>'both', 'expand'=>'yes')
          points = []
       end
       TkcLine.new(canvas2, 0,0,100,100)
    end
    Tk.mainloop
    
  43. irb - interactive ruby

    Ruby comes with an REPL (Read Eval Print Loop) utility to let you try ruby interactively. ("inf-ruby.el" provides an internal shell in emacs for irb).

    C:>irb
    irb(main):001:0> puts "hello"
    puts "hello"
    hello
    nil
    irb(main):002:0> Object.methods
    Object.methods
    ["send", "name", "class_eval", "object_id", "new", "singleton_methods", "__send__", "private_method_defined?", "equal?", "taint", "frozen?", "instance_variable_get", "constants", "kind_of?", "to_a", "instance_eval", "require", "ancestors", "const_missing", "type", "instance_methods", "protected_methods", "extend", "protected_method_defined?", "eql?", "public_class_method", "const_get", "instance_variable_set", "hash", "is_a?", "autoload", "to_s", "class_variables", "class", "tainted?", "private_methods", "public_instance_methods", "instance_method", "untaint", "included_modules", "private_class_method", "const_set", "id", "<", "inspect", "<=>", "==", "method_defined?", ">", "===", "clone", "public_methods", "protected_instance_methods", "require_gem", ">=", "respond_to?", "display", "freeze", "<=", "module_eval", "autoload?", "allocate", "__id__", "=~", "methods", "gem", "method", "public_method_defined?", "superclass", "nil?", "dup", "private_instance_methods", "instance_variables", "include?", "const_defined?", "instance_of?"]
    irb(main):003:0> 
    
  44. RubyGems a ruby package installer

    You can download RubyGems from http://rubyforge.org. Unzip the files (eg, C:\opt\ruby) then install by entering:

    C:>cd C:\opt\ruby\rubygems-0.9.0
    C:\opt\ruby\rubygems-0.9.0>ruby setup.rb all
    
  45. Ruby on Rails
    1. How to write a log message

      You can use logger's methods "warn", "info", "error", and "fatal".

          logger.info("request.remote_ip"+request.remote_ip);
      
    2. Field names ending with "_at" are assumed to be datetime fields and are filled in automagically by rails for ActiveRecord objects. The suffix "_on" are assumed to be dates.
    3. Console

      to dubug applications it's convenient to use the console script

      myapp>ruby script/console
      
    4. debug method

      You can use the debug() method inside web pages to dump info about an object.

      <p>Thanks for visiting</p>
      <%= debug(@myobject) %>
      
    5. How to Freeze a version

      Since your hosting company may upgrade the rails version you need to "freeze" the current version. The following copies all the 1.2.6 libraries from the shared directory to your own private one.

      rake rails:freeze:edge TAG=rel_1-2-6
      
    6. Active record notes
      1. Find all records meeting a criteria

        def self.suitable_jokes(sort_key)
        if sort_key == "Newest"
           find(:all,
                :conditions => "suitable  = \"1\"",
                :order => "entry_date DESC"
               )
        elsif sort_key == "Worst"
           find(:all,
                :conditions => "suitable  = \"1\"",
                :order => "entry_date ASC"
               )
        else
           find(:all,
                :conditions => "suitable  = \"1\"",
                :order => "current_rating DESC"
               )
        end
        end
        

        The first argument to find can also be ":first" or ":last".

      2. Find the count of records meeting a criteria
        def self.waiting_jokes()
          count("suitable  = \"0\"")
        end
        def self.total()
          count("suitable  = \"1\"")
        end
        

        Find the total number of items

        count = Joke.count
        

        Find the total number of items meeting a criteria

        count = Joke.count(["suitable = \"1\""])
        
      3. Pagination

        The ":limit" and ":offset" options allow easy pagination.

        To return the fifth page of items use the following:

        find(:all,
                :conditions => "suitable  = \"1\"",
                :order => "current_rating DESC",
        	:limit => 10,
        	:offset => 40
               )
        
      4. Use raw SQL and return two values
        def getAverageRatingAndCount
            record = Rating.find_by_sql(["select count(*) as count,avg(rating) as average from ratings WHERE joke_id = ?",id]);
            return record[0].average.to_f , record[0].count.to_i
        end
        
      5. The "create" method in ActiveRecord will do "new" and "save" operations simultanously.

        mydog = Dog.create(
        :name => "Fido"
        :breed => "Collie"
        )
        
    7. Watir

      Watir is a GUI testing tool written in Ruby. Here is a script to open Google and search for pictures of kittens.

      require "watir"
      ie = Watir::IE.new  #create an object to drive the browser
      ie.goto "http://www.google.com/"
      ie.url == "http://www.google.com/"
      ie.link(:text, "Images").flash #flash the item text "Images"
      ie.link(:text, "Images").click #click on the link to the images search page
      ie.text.include? "The most comprehensive image search on the web" #test to make sure it worked
      searchTerm = "kittens" #set a variable to hold our search term
      ie.text_field(:name, "q").set(searchTerm) # q is the name of the search field
      ie.button(:name, "btnG").click # "btnG" is the name of the google button
      if ie.contains_text(searchTerm)
        puts "Test Passed. Found the test string: #{searchTerm}. Actual Results match Expected Results."
      else
         puts "Test Failed! Could not find: #{searchTerm}"
      end
      
    8. Selecting a JavaScript popup box

      stolen from http://wiki.openqa.org/display/WTR/FAQ

      #Watir script to show clicking a JavaScript popup box
      require "watir"
      require 'watir\contrib\enabled_popup'
      require 'startClicker'
      require 'net/http'
      require 'net/https'
      
      $ie = Watir::IE.new  #create an object to drive the browser
      $ie.goto "http://mydomain.com/ListGroups.aspx"
      if $ie.contains_text("Log In")
        $ie.text_field(:name, "Login1$UserName").set("fincherm")
        $ie.text_field(:name, "Login1$Password").set("mitch")
        $ie.button(:name, "Login1$LoginButton").click 
      end
      $ie.link(:text, "Baseline").click 
      $ie.link(:text, "MoonManC").click 
      def setDdlPriority(priority) 
         ddlPriority = $ie.select_list( :name , /ddlPriority/)
         puts ddlPriority
         ddlPriority.select(priority)
         puts ddlPriority
         $ie.button(:name, "ctl00$btnSave").click_no_wait 
            startClicker( "OK", 4 , "User Input" )
            sleep 1   
      end
      setDdlPriority("2")
      setDdlPriority("9")
      

      startClicker.rb:

      #method startClicker from http://wiki.openqa.org/display/WTR/FAQ
      def startClicker( button , waitTime= 9, user_input=nil )
        # get a handle if one exists
        hwnd = $ie.enabled_popup(waitTime)  
        if (hwnd)  # yes there is a popup
          w = WinClicker.new
          if ( user_input ) 
            w.setTextValueForFileNameField( hwnd, "#{user_input}" )
          end
          # I put this in to see the text being input it is not necessary to work
          sleep 3         
          # "OK" or whatever the name on the button is
          w.clickWindowsButton_hwnd( hwnd, "#{button}" )
          #
          # this is just cleanup
          w=nil    
        end
      end
      
    9. How to use Watir with NUnit

      Here is an example of connecting it to NUnit.

      using System;
      using System.Diagnostics;
      using System.Text.RegularExpressions;
      using NUnit.Framework;
      
      namespace MyProject.Test.Watir
      {
          /// <summary>
          /// from http://www.hanselman.com/blog/IntegratingRubyAndWatirWithNUnit.aspx
          /// with changes by Liz Buenker
          /// </summary>
          public class WatirAssert
          {
              public static void TestPassed(string rubyFileName, string directoryPath)
              {
                  string output = String.Empty;
                  using (Process p = new Process())
                  {
                      p.StartInfo.UseShellExecute = false;
                      p.StartInfo.RedirectStandardOutput = true;
                      p.StartInfo.FileName = "ruby.exe";
                      p.StartInfo.Arguments = rubyFileName + " -b";
                      p.StartInfo.WorkingDirectory = directoryPath;
                      p.Start();
                      output = p.StandardOutput.ReadToEnd();
                      p.WaitForExit();
                  }
                  Console.Write(output);
                  Trace.Write(output);
                  Regex reg = new Regex(@"(?<tests>\d+) tests, (?<assertions>\d+) assertions, (?<failures>\d+) failures, (?<errors>\d+) errors", RegexOptions.Compiled);
                  Match m = reg.Match(output);
                  try
                  {
                      int tests = int.Parse(m.Groups["tests"].Value);
                      int assertions = int.Parse(m.Groups["assertions"].Value);
                      int failures = int.Parse(m.Groups["failures"].Value);
                      int errors = int.Parse(m.Groups["errors"].Value);
                      if (tests > 0 && failures > 0)
                      {
                          Assert.Fail(String.Format("WatirAssert: Failures {0}", failures));
                      }
                      else if (errors > 0)
                      {
                          Assert.Fail(String.Format("WatirAssert: Errors {0}", errors));
                      }
                  }
                  catch (Exception e)
                  {
                      Assert.Fail("WatirAssert EXCEPTION: " + e.ToString());
                  }
              }
          }
      }

      The above code would be used by something like this:

      using System;
      using NUnit.Framework;
      
      namespace MyProject.Test.Watir
      {
          [TestFixture]
          public class WatirTest
          {
              private static readonly string testDir = Utilities.Properties.baseDir + "\\Test\\Watir";
              public WatirTest() { }
              [Test]
              public void Sd01Test()
              {
                  WatirAssert.TestPassed("sd01_test.rb",testDir);
              }
              [Test]
              public void Sd02Test()
              {
                  WatirAssert.TestPassed("sd02_test.rb",testDir);
              }
      
          }
      }
      
  46. Ruby Quotes:


    "Ruby is a language for clever people." -Matz
    "Ruby is the perlification of Lisp." -Matz
    "Type Declarations are the Maginot line of programming." -Mitch Fincher
    "Java is a pair of scissors, Ruby is a chainsaw." -Mitch Fincher, with a pair of scissors you can cut yourself and draw blood but with a chainsaw you can lose a limb in a moment of carelessness."