Ruby and Ruby on Rails Demonstration (Part I)

This page provides a brief demonstration of the Ruby programming language presented at the St. John's Linux Users' Group on February 12, 2008. The demo used Ruby v1.8.6. It is not intended to be a comprehensive overview of all features of the language. This presentation was eventually broken into two parts. This initial presentation dealt primarily with the Ruby programming language. Part II discussed the Ruby on Rails web application framework.

(A previous presentation on Ruby was prepared a few years ago, but is a bit dated now.)

Installation

Ruby and Ruby on Rails can be installed on most Linux systems using conventional package management software (rpm, apt-get, synaptic, etc.). Unfortunately, some of these packages (especially the packages associated with Rails) may be out dated.

This script will download and install ruby version 1.8.6 and rails version 2.0.2 in the "local/" subdirectory of the directory in which is it run. Rails is installed by the RubyGems package manager. (Note: The ftp.ruby-lang.org server is a bit slow. You can download ruby-1.8.6-p111.tar.gz from here instead.) The installation script assumes that wget and various database and development tools and libraries are present on your system (e.g., gcc, sqlite3, zlib1g-dev, libssl-dev, libsqlite3-dev, etc.) are already installed on your system.

To subsequently use Ruby and Ruby on Rails, you must update/set your PATH and RUBYLIB environment variables by running the short script generated by the above installation script. On some gentoo systems, it may also be necessary to unset the RUBYOPT variable.

The Ruby Programming Language (examples)

Most of these examples can be performed in the interactive ruby environment. Type irb at the command prompt to enter this environment. Many of these examples were explained during the presentation.

Hello world
$ irb
irb(main):001:0> puts "Hello world!"
Hello world
=> nil

The nil is the return result of puts.

Data types (numbers, strings) and Dynamic Typing
irb(main):002:0> a = 1
=> 1

irb(main):003:0> b = 2
=> 2

irb(main):004:0> a + b
=> 3

irb(main):005:0> a = "Hello"
=> Hello
Method invocation
irb(main):006:0> a.size
=> 5

irb(main):007:0> b.class
=> Fixnum

irb(main):008:0> a.class
=> String

irb(main):009:0> -2.abs
=> 2

irb(main):010:0> "Hello".size
=> 5

Note that methods can be invoked on variables and literals/constants.

Type Errors
irb(main):011:0> a + " World!"
=> Hello World!

irb(main):012:0> a + b
TypeError: can't convert Fixnum into String
        from (irb):10:in `+'
        from (irb):10
        from :0

irb(main):013:0> a.to_i + b
=> 2

irb(main):014:0> a + b.to_s
=> "Hello2"


irb(main):015:0> b.to_s + " World!"
=> "2 World!"

irb(main):016:0> b + " World!".to_i
=> 2 

Type conversions are not automatic, generally.

Interpolation

Ruby expressions enclosed by #{...} inside double quotes are evaluated and converted to strings.

irb(main):017:0> "#{b} World!"
=> "2 World!"

irb(main):018:0> "Super Mario #{b ** 6}"
=> "Super Mario 64"
Arrays
irb(main):019:0> a = ["Hello", 2, "world", [1, "abc"]]
=> ["Hello", 2, "world", [1, "abc"]]

irb(main):020:0> a[2]
=> "world"

irb(main):021:0> a[0]
=> "Hello"

irb(main):022:0> a[-1]
=> [1, "abc"]

irb(main):023:0> a[-1][-1]
=> "abc"

irb(main):024:0> a.first == a[0] && a.last == a[-1]
=> true

irb(main):025:0> a[10]
=> nil

irb(main):026:0> a[10] = "final"
=> "final"

irb(main):027:0> a
=> ["Hello", 2, "world", [1, "abc"], nil, nil, nil, nil, nil, nil, "final"]

irb(main):028:0> a << "new-final"
=> ["Hello", 2, "world", [1, "abc"], nil, nil, nil, nil, nil, nil, "final", "new-final"]

irb(main):029:0> a.pop
=> "new-final"

irb(main):030:0> a.shift
=> "Hello"

irb(main):031:0> a
=> [2, "world", [1, "abc"], nil, nil, nil, nil, nil, nil, "final"]

irb(main):032:0> a.join("-")
=> "2-world-1-abc-------final"

irb(main):033:0> words = "hello world this is a string".split
=> ["hello", "world", "this", "is", "a", "string"]

irb(main):034:0> words.include?("this")
=> true
Hashes
irb(main):035:0> h = { "NL" => "St. John's", "NS" => "Halifax" }
=> {"NS"=>"Halifax", "NL"=>"St. John's"}

irb(main):036:0> h.has_key?("NL")
=> true

irb(main):037:0> h["NL"]
=> "St. John's"

irb(main):038:0> h["NB"] = "Fredericton"
=> "Fredericton"

irb(main):039:0> h
=> {"NS"=>"Halifax", "NL"=>"St. John's", "NB"=>"Fredericton"}

irb(main):040:0> h.keys
=>  ["NS", "NL", "NB"]

irb(main):041:0> h.value
=> ["Halifax", "St. John's", "Fredericton"]
Symbols
irb(main):042:0> :hello
=> :hello

irb(main):043:0> :hello.to_s
=> "hello"

irb(main):044:0> "sym".intern
=> :sym

irb(main):045:0> font = {:size => "10pt", :weight => "bold"}
=> {:weight=>"bold", :size=>"10pt"}

irb(main):046:0> font[:weight]
=> "bold"

Using symbols as keys in a hash is very common in Ruby (especially in Rails).

Symbols can also be used as crude enumerated types (e.g., COLORS = [:red, :green, :blue]) or "function pointers":

irb(main):047:0> fp = :puts
=> :puts

irb(main):048:0> send(fp, "Hi There!")
Hi There!
=> nil
Ranges
irb(main):049:0> valid_input = 100..200
=> 100..200

irb(main):050:0> valid_input === 150
=> true

irb(main):051:0> valid_input === 50
=> false

irb(main):052:0> alphabet = ('A'..'Z').to_a
["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
 "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
irb(main):053:0> alphabet[10..-10]
=> ["K", "L", "M", "N", "O", "P", "Q"]
Iteration/Blocks
irb(main):054:0> 5.times { puts "Hello world" }
Hello world
Hello world
Hello world
Hello world
Hello world
=> 5

irb(main):055:0> 100.times { |x| puts "%2d" % x }
 0
 1
 2
...
98
99
=> 100

irb(main):056:0> a = ["hello", 2, "world"]
=> ["hello", 2, "world"]

irb(main):057:0> a.each {|element| puts element }
hello
2
world
=> ["hello", 2, "world"]

irb(main):058:0> a.each_with_index {|e, idx| puts "(%d) %s" % [idx+1, e] }
(1) hello
(2) 2
(3) world
=> ["hello", 2, "world"]

irb(main):059:0> for i in a
irb(main):059:1>   puts i
irb(main):059:2> end
hello
2
world
=> ["hello", 2, "world"]

irb(main):060:0> font = {:size => "10pt", :weight => "bold"}
=> {:weight=>"bold", :size=>"10pt"}

irb(main):061:0> font.each {|k, v| puts "#{k} is #{v}" }
weight is bold
size is 10pt
=> {:weight=>"bold", :size=>"10pt"}

irb(main):062:0> (5..10).each { |x| puts "=" * x }
=====
======
=======
========
=========
==========
=> 5..10

The gets method is used to perform line input. The p method is similar to puts, except that it provides a 'lower-level' or verbose (but still human readable) output. By default, chomp will remove the terminating carriage return/newline characters from a string (which is usually present on strings obtained via gets). splitting a string using an empty string delimiter will break the string into a list of all of its characters.

irb(main):063:0> while line = gets
irb(main):063:1>   p line.chomp.split("")
irb(main):063:2> end
Hello world!
["H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!"]
a test string
["a", " ", "t", "e", "s", "t", " ", "s", "t", "r", "i", "n", "g"]
Ctrl-D
=> nil
Enumerable mixin methods (included by Arrays and Ranges)
irb(main):064:0> (1..1000).find_all { |x| x % 7 == 0 && x % 13 == 0 }
=> [91, 182, 273, 364, 455, 546, 637, 728, 819, 910]

irb(main):065:0> (1..10).map { |x| x * x }
=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

irb(main):066:0> animals = ["elk", "bear", "aardvark", "dog", "cat"]
=> ["elk", "bear", "aardvark", "dog", "cat"]

irb(main):067:0> animals.sort
=> ["aardvark", "bear", "cat", "dog", "elk"]

irb(main):068:0> animals.sort_by { |a| a.length }
=> ["elk", "dog", "cat", "bear", "aardvark"]

irb(main):069:0> animals.sort_by { |a| [a.length, a] }
=> ["cat", "dog", "elk", "bear", "aardvark"]

irb(main):070:0> (1..20).sort_by { rand }
=> [19, 9, 18, 2, 7, 11, 16, 5, 10, 12, 3, 8, 15, 14, 17, 6, 1, 13, 20, 4]

irb(main):071:0> random_numbers = (1..10).map {rand(100)}
=> [80, 7, 77, 4, 46, 98, 85, 37, 90, 79]

irb(main):072:0> random_numbers.all? { |x| x < 95 }
=> false

irb(main):073:0> random_numbers.any? { |x| x < 10 }
=> true

irb(main):074:0> sum = 0; random_numbers.inject(0) {|sum, n| sum + n}
=> 603

irb(main):075:0> while line = gets
irb(main):075:1>    puts line.chomp.split("").sort_by{rand}.join("")
irb(main):075:2> end
Hello world!
llor!o Hlewd
What the....
.a.. e.hhtWt
ooooooops!
oooo!spooo
Ctrl-D
=> nil
Conditionals

These scripts are a little longer. They can be stored in a file and executed using: ruby filename.rb

if 1 > 2
  puts "Ruby is lame!"
else
  puts "Okay!"
end
words = %w(this is a string) # same as: words = ["this", "is", "a", "string"]
if words.include?("this")
  puts "\"this\" is in the list"
fi

Unlike other languages, zero is true — the only false values are false and nil (also note that then is required only when the if statement is all on one line.):

if 0 then puts "zero is true!" else puts "zero is false" end  # outputs "zero is true!"

The ternary operator ? : is also supported:

puts 0 ? "zero is true!" : "zero is false" 

The traditional case statement is also supported to express more conditional.

print "Birth year> "
status = case gets.to_i
           when 1950..1978 then "old."
           when 1979..1990 then "not old."
           when 1991..2008 then "n00b."
	   else "not dead yet?!"
         end
puts "You are #{status}"
'Here' documents/regular expressions
$ cat re.rb
html =<<END
<html>
  <head>
  <title>
    Here is the Title
  </title>
  <body>
  <p>
    Paragraph One
  </p>
  <p>
    Paragraph Two
  </p>
  </body>
</html>
END

md = /<title>(.*?)<\/title>/m.match(html)
puts "Title: #{md[1]}"

puts "Tags:"
html.scan(/<.*?>/) { |tag|
  puts "\t#{tag}"
}

puts "Detagged:"
puts html.gsub(/<.*?>\s*/, "")

Output:

$ ruby re.rb
Title: 
    Here is the Title
  
Tags:
	<html>
	<head>
	<title>
	</title>
	<body>
	<p>
	</p>
	<p>
	</p>
	</body>
	</html>
Detagged:
Here is the Title
  Paragraph One
  Paragraph Two
Libraries, parsing RSS feeds
$ cat wx.rb
require 'rss/2.0'
require 'open-uri'

WX_RSS = 'http://www.weatheroffice.gc.ca/rss/city/nf-24_e.xml'

content = ""
open(WX_RSS) { |s| content = s.read }

rss = RSS::Parser.parse(content, false)

# The current conditions item may not be at items[1]
# if there is more than one weather warning.  Use 'find'
# to identify where in the items array the "Current
# Conditions" item is located.
current = rss.items.find { |i|
  /^Current Conditions:/.match(i.title)
}
location = current.description.split("\n").first
location_detagged = location.gsub(/\s*<.*?>/, "")

puts location_detagged
puts "~" * location_detagged.size
rss.items.each { |item|
  puts "- #{item.title}"
}

Output:

$ ruby wx.rb
Observed at: St. John's Int'l Airport 7:30 PM NST Monday 11 February 2008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- No watches or warnings in effect, St. John's
- Current Conditions: Light Snow, -3.3°C
- Monday night: Flurries. Low minus 4.
- Tuesday: Flurries. High minus 2.
- Tuesday night: Cloudy periods. Low minus 8.
- Wednesday: A mix of sun and cloud. High minus 4.
- Thursday: Snow or rain. Low minus 6. High plus 3.
- Friday: Cloudy. Low minus 5. High plus 2.

Another output sample with weather warnings:

$ ruby wx.rb
Observed at: St. John's Int'l Airport 11:30 AM NST Thursday 14 February 2008
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- RAINFALL WARNING CONTINUED, St. John's
- WIND WARNING, St. John's
- Current Conditions: Light Rain, 1.3°C
- Thursday: Rain at times heavy. High plus 4.
- Thursday night: Rain or drizzle. Low plus 1.
- Friday: Chance of drizzle. Temperature falling to minus 1 by noon. POP 40%
- Saturday: Chance of rain showers or flurries. Low minus 3. High plus 2. POP 60%
- Sunday: A mix of sun and cloud. Low minus 10. High minus 6.
- Monday: A mix of sun and cloud. Low minus 14. High minus 7.
Method definition, ARGV, exceptions, :symbols as hash keys, hashes as parameters, statement modifiers
$ cat wb.rb
require 'open-uri'

def get_page(url, opts = {})
  html = ""
  open(url) { |s| html = s.read }
  html = html.gsub(/&rsquo;/, "'").gsub(/&copy;/, "(c)")
  html.gsub!(/<.*?>/m, "")         if opts[:detag]
  html.gsub!(/(^[ \t]*\n)+/, "\n") if opts[:compress_newlines]
  html
end

raise "Usage: #{$0} <URL>" unless ARGV.size == 1

puts get_page(ARGV[0], :detag => true, :compress_newlines => true)

gsub! modifies the given string, destroying its old value (hence the exclamation mark). The return value of the method is the value of the last statement (the return is implicit, but can be used if desired).

Output:

$ ruby wb.rb http://www.slug.nf.net

		SLUG - St. John's Linux Users' Group

				St.
				John's Linux Users' Group

What is SLUG?

	SLUG is a
	Linux users group based in
	St. John's,
	Newfoundland,
	Canada.  We began
	in 1994 and are constantly looking to expand our membership
	base.

	Each term we hold regular public informational seminars on
	Linux in the Engineering building
	at the Memorial University of
	Newfoundland campus, with the support of the
	Department of Computer Science.

Announcements

Ruby On Rails
Date: February 12th, 2008
Time: 7:30pm
Location: Memorial University, S.J. Carew Building, Room EN-2022
Presenter: Donald Craig (donald at cs dot mun dot ca)
Ruby on Rails is an multi-platform, open source web application framework
based upon the Model Controller View (MVC) architecture.  Its adherence to
principles such as Don't Repeat Yourself (DRY), convention over configuration,
testing, and, more recently, database migrations and RESTful interfaces has
made the framework increasingly popular over the past few years.  This talk
will present an brief introduction to Ruby and the Ruby on Rails framework.
Some of the topics to be discussed include: ">

- Installing Ruby on Rails on Linux

- Overview of Ruby.

- Creating a Rails application.

- Scaffolding/Migrations

- ActiveRecord (Models)

  - Object Relational Mapping.

  - Associations/Relationships

- ActionPack (Controller/Views)

		Home

			Mailing List

				Sign Up
				Archives

			Events / Seminars

				Upcoming
				Past
				Robocode
				Keysigning

			Resources

				Linux Documentation Project
				nf.comp.linux
				Canadian LUGs

		(c) 2005 St. John's Linux Users' Group.

	Site design by Geoff Holden.

Classes, instantiation, member variables, inheritance, modules, class methods
$ cat cl.rb
module MyModule
  class Aclass
    attr_reader :aval
  
    def initialize(value)
      @aval = value
    end
  
    def to_s
      "[A:#{@aval}]"
    end

    self.cm
      "This is a class method"
    end
  end
end
  
class Bclass < MyModule::Aclass
  attr_accessor :bval

  def initialize(avalue, bvalue)
    super(avalue)
    @bval = bvalue
  end

  def to_s
    super + "[B:#{@bval}]"
  end
end

a = MyModule::Aclass.new(10)
puts a
p a

# Class methods are invoked without a corresponding object.
puts MyModule::Aclass.cm

puts a.aval
#a.aval = 20   #  Error

b = Bclass.new(20, "hello")
puts b
p b
puts b.bval
b.bval = 30  # OK!
puts b.bval
Output:
$ ruby cl.rb
[A:10]
#<MyModule::Aclass:0xb7e6ed3c @aval=10>
This is a class method
10
[A:20][B:hello]
#<Bclass:0xb7e6e2d8 @aval=20, @bval="hello">
hello
30

More information on the core and standard APIs is available at ruby-doc.org

Part II of this presentation was presented on March 25, 2008.


Donald Craig (donald@mun.ca)
Last modified: April 4, 2008 15:23:05 NDT
Valid XHTML 1.0 Strict Valid CSS!