Ruby Masterclass
     Kerry Buckley
     16 June 2011
History

• Created in 1993
• First public release Christmas 1995
• ‘Pickaxe’ book first published in 2001
• Ruby on Rails released in 2005
Philosophy

“I wanted a scripting language that was
more powerful than Perl, and more object-
oriented than Python. That’s why I decided
to design my own language”
               — Yukihiro ‘Matz’ Matsumoto
Philosophy
“I hope to see Ruby help every
programmer in the world to be productive,
and to enjoy programming, and to be
happy. That is the primary purpose of
Ruby language.”
              — Yukihiro ‘Matz’ Matsumoto
Characteristics
• Strongly but dynamically typed
• Interpreted
• Object-oriented
• …but can be procedural
• …or even (mostly) functional
• Concise but readable
Applications
• Web apps
• Shell scripting
• Build automation
• Deployment automation
• ‘Glue’ code
Culture
• Permissive open source (MIT)
• Favour simplicity
• Agile (especially TDD)
• Mac/Linux
• Fast-moving
• Fun!
Basic syntax
puts "hello world"
Variables

$global_variable = 123

@@class_variable = "hello"

@instance_variable = [1, 2, 3]

local_variable = true
Built-in types
42
1.5
"string"
:symbol
[1, 2, "foo"]
{1 => "one", 2 => "two"}
(1..10)
/^foo.*$/
true
false
nil
Classes & methods
class MyClass
 def initialize number
   @number = number
 end

 def double
  @number * 2
 end

 def hello(name = "world")
  puts "Hello #{name.capitalize}"
 end
end

my_object = MyClass.new(2)
puts my_object.double #=> prints "4"
my_object.hello       #=> prints "hello World"
my_object.hello "kerry" #=> prints "hello Kerry"
Accessors
Accessors
class Rectangle
 attr_accessor :x
 attr_reader :y

 def initialize x, y
  @x, @y = x, y
 end
end
Accessors
class Rectangle
 attr_accessor :x
 attr_reader :y

 def initialize x, y
  @x, @y = x, y
 end
end

rect = Rectangle.new 2, 4
rect.x #=> 2
rect.y #=> 4
Accessors
class Rectangle
 attr_accessor :x
 attr_reader :y

 def initialize x, y
  @x, @y = x, y
 end
end

rect = Rectangle.new 2, 4
rect.x #=> 2
rect.y #=> 4

rect.x = 3
rect.x #=> 3
Accessors
class Rectangle
 attr_accessor :x
 attr_reader :y

 def initialize x, y
  @x, @y = x, y
 end
end

rect = Rectangle.new 2, 4
rect.x #=> 2
rect.y #=> 4

rect.x = 3
rect.x #=> 3

rect.y = 3 #=> NoMethodError
if, else and unless
if, else and unless
if a == b
  puts "The same"
elsif a < b
  puts "A is lower"
else
  puts "A is higher"
end
if, else and unless
if a == b
  puts "The same"
elsif a < b
  puts "A is lower"
else
  puts "A is higher"
end

unless finished?
 do_stuff
end
if, else and unless
if a == b
  puts "The same"
elsif a < b
  puts "A is lower"
else
  puts "A is higher"
end

unless finished?
 do_stuff
end

raise "Oops" if result.nil?
case
case foo
when nil
 puts "foo is nil"
when /^a/i
 puts "foo begins with an 'A'"
when String
 puts "foo is a string"
when (1..10)
 puts "foo is in the range 1-10"
else
 puts "foo is something else"
end
Loops

for a in (1..10) do
 puts a
end

b=0
while b <= 10
 puts b
 b += 1
end
Including other code

• load
• require
• Rubygems
Exercise 1
 Playing with irb
Everything is an object

42.class       #=> Fixnum

Fixnum.class      #=> Class

nil.class      #=> NilClass

true.class      #=> TrueClass

method(:puts).class #=> Method
Duck typing
class Duck
  def quack
    "Quack"
  end
end

class Dog
  def quack
    "Woof"
  end
end

for animal in [Duck.new, Dog.new] do
  puts animal.quack
end

"a string".quack #=> NoMethodError
Dynamic typing

foo = 123

foo.class #=> Fixnum

foo = "foo"

foo.class #=> String
Open classes

puts 123 #=> "123"

class Fixnum
 def to_s
   "Not telling you!"
 end
end

puts 123 #=> "Not telling you!"
Open classes

puts 123 #=> "123"

class Fixnum
 def to_s
   "Not telling you!"
 end
end

puts 123 #=> "Not telling you!"



         You probably don’t want to do this.
Aliasing methods
require "fileutils"

class << FileUtils
 def rm_with_prompt *files
   print "Are you sure you want to delete #{files}? "
   response = gets.chomp.downcase
   return unless %w{yes y}.include?(response)
   rm_without_prompt *files
 end

 alias_method :rm_without_prompt, :rm
 alias_method :rm, :rm_with_prompt
end

FileUtils.rm "a_file"
Blocks
Blocks
begin
 puts "This is a block, but it's not much use"
end
Blocks
begin
 puts "This is a block, but it's not much use"
end

block = Proc.new do
 puts "Now I'm assigned to a variable"
end

# Nothing printed yet
Blocks
begin
 puts "This is a block, but it's not much use"
end

block = Proc.new do
 puts "Now I'm assigned to a variable"
end

# Nothing printed yet

block.call #=> executes the block
Blocks
begin
 puts "This is a block, but it's not much use"
end

block = Proc.new do
 puts "Now I'm assigned to a variable"
end

# Nothing printed yet

block.call #=> executes the block

Proc.new { puts "curly brackets work too" }
Blocks with arguments

block = Proc.new do |arg|
 puts "You called me with #{arg.inspect}"
end

block.call("foo") #=> You called me with "foo"
Explicit block args
def n_times(n, &block)
 puts "starting..."
 n.times { block.call }
 puts "done."
end

n_times(3) do
 puts "hello"
end

#=>   starting...
#=>   hello
#=>   hello
#=>   hello
#=>   done.
Implicit block args
def n_times(n)
 puts "starting..."
 n.times { yield }
 puts "done."
end

n_times(3) do
 puts "hello"
end

#=>   starting...
#=>   hello
#=>   hello
#=>   hello
#=>   done.
File IO with blocks
File IO with blocks

file = File.open "logfile", "a"
file.puts "log message"
file.close
File IO with blocks

file = File.open "logfile", "a"
file.puts "log message"
file.close

File.open "logfile", "a" do |file|
 file.puts "log message"
end
Modules
Modules
module InfoDisplay
 def display_info
  puts "Class: #{self.class}, ID: #{object_id}"
 end
end

class SomeClass
 include InfoDisplay
end

SomeClass.new.display_info
 #=> "Class: SomeClass, ID: 2159853660"
Modules
module InfoDisplay
 def display_info
  puts "Class: #{self.class}, ID: #{object_id}"
 end
end

class SomeClass
 include InfoDisplay
end

SomeClass.new.display_info
 #=> "Class: SomeClass, ID: 2159853660"

String.send :include, InfoDisplay

"foo".display_info #=> "Class: String, ID: 2159853520"
Modules: Enumerable
class Words
 include Enumerable

 def initialize string
  @words = string.split
 end

 def each
  @words.each {|word| yield word }
 end
end

words = Words.new("The quick brown fox")
puts words.first             #=> "The"
puts words.count             #=> 4
p words.select{|w| w.length == 3 } # => ["The", "fox"]
Exercise 2
Working with Enumerable
Metaprogramming

• Programs writing programs
• Modify behaviour at runtime
• Generate code
• Respond dynamically to messages
Evaluating code
Evaluating code
code = 'puts "hello"'

eval code #=> "hello"
Evaluating code
code = 'puts "hello"'

eval code #=> "hello"

foo = "Ruby"

foo.instance_eval do
 def hello
  puts "Hello, I'm #{self}"
 end
end

foo.hello #=> "Hello, I'm Ruby"
Dynamic calling
Dynamic calling

foo = "Hello"
foo.upcase    #=> "HELLO"
foo.send :upcase #=> "HELLO"
Dynamic calling

foo = "Hello"
foo.upcase    #=> "HELLO"
foo.send :upcase #=> "HELLO"

method = [:upcase, :downcase][rand(2)]
foo.send method #=> Randomly "HELLO" or "hello"
Dynamic calling

foo = "Hello"
foo.upcase    #=> "HELLO"
foo.send :upcase #=> "HELLO"

method = [:upcase, :downcase][rand(2)]
foo.send method #=> Randomly "HELLO" or "hello"

bar = 2
bar.send :+, 2 #=> 4
Dynamic response

class Foo
 def method_missing name, *args
   puts "You called #{name} with #{args.inspect}"
 end
end

Foo.new.bar(1, "hello")
 #=> You called bar with [1, "hello"]
Exercise 3
 Metaprogramming
Rake
require "erb"
require "rake/clean"
CLOBBER.include("output/*")

OUTPUT_FILE = "output/README.txt"
TEMPLATE_FILE = "templates/README.txt.erb"

desc "Build the README file from the template"
file OUTPUT_FILE => TEMPLATE_FILE do
 template = File.read(TEMPLATE_FILE)
 File.open(OUTPUT_FILE, "w") do |f|
   output = ERB.new(template).result(binding)
   f.write output
 end
end

task :default => [OUTPUT_FILE]
Exercise 4
   Rake
Web frameworks




  and many more…
Web servers
Mongrel

                        CGI




              Webrick
Before Rack
Before Rack
Before Rack
Before Rack
Before Rack
With Rack


Rack-compliant interface
A rack app

require "rack"

class HelloWorld
 def call env
   [200, {"Content-Type" => "text/plain"},
    StringIO.new("Hello world")]
 end
end

run HelloWorld.new
Exercise 5
   Rack
Shell scripting
#!/usr/bin/env ruby

require "fileutils"
include FileUtils

ARGV.each do |file|
 cp file, "#{file}.bak"
end

system "service httpd restart"
 #=> returns true or false

users = `users`.split
Testing

• Test/Unit & SimpleTest
• RSpec
• Cucumber
• Capybara
JRuby
• Another implementation of Ruby
• Written in Java
• Comparable or better performance
• Use Java libraries from Ruby code
• Deploy Rails apps as WAR files
Calling Java from Ruby

require 'java'

frame = javax.swing.JFrame.new "Window"
label = javax.swing.JLabel.new "Hello"

frame.getContentPane.add label
frame.setDefaultCloseOperation(
   javax.swing.JFrame::EXIT_ON_CLOSE)
frame.pack
frame.setVisible true
Calling Ruby from Java
import org.jruby.embed.ScriptingContainer;

public class HelloWorld {

    private HelloWorld() {
      ScriptingContainer c = new ScriptingContainer();
      c.runScriptlet("puts "Hello World!"");
    }

    public static void main(String[] args) {
      new HelloWorld();
    }
}
while Time.now.hour < 17
 audience_suggestions.pop.discuss
end

go :home

Ruby

  • 1.
    Ruby Masterclass Kerry Buckley 16 June 2011
  • 2.
    History • Created in1993 • First public release Christmas 1995 • ‘Pickaxe’ book first published in 2001 • Ruby on Rails released in 2005
  • 3.
    Philosophy “I wanted ascripting language that was more powerful than Perl, and more object- oriented than Python. That’s why I decided to design my own language” — Yukihiro ‘Matz’ Matsumoto
  • 4.
    Philosophy “I hope tosee Ruby help every programmer in the world to be productive, and to enjoy programming, and to be happy. That is the primary purpose of Ruby language.” — Yukihiro ‘Matz’ Matsumoto
  • 5.
    Characteristics • Strongly butdynamically typed • Interpreted • Object-oriented • …but can be procedural • …or even (mostly) functional • Concise but readable
  • 6.
    Applications • Web apps •Shell scripting • Build automation • Deployment automation • ‘Glue’ code
  • 7.
    Culture • Permissive opensource (MIT) • Favour simplicity • Agile (especially TDD) • Mac/Linux • Fast-moving • Fun!
  • 8.
  • 9.
  • 10.
    Variables $global_variable = 123 @@class_variable= "hello" @instance_variable = [1, 2, 3] local_variable = true
  • 11.
    Built-in types 42 1.5 "string" :symbol [1, 2,"foo"] {1 => "one", 2 => "two"} (1..10) /^foo.*$/ true false nil
  • 12.
    Classes & methods classMyClass def initialize number @number = number end def double @number * 2 end def hello(name = "world") puts "Hello #{name.capitalize}" end end my_object = MyClass.new(2) puts my_object.double #=> prints "4" my_object.hello #=> prints "hello World" my_object.hello "kerry" #=> prints "hello Kerry"
  • 13.
  • 14.
    Accessors class Rectangle attr_accessor:x attr_reader :y def initialize x, y @x, @y = x, y end end
  • 15.
    Accessors class Rectangle attr_accessor:x attr_reader :y def initialize x, y @x, @y = x, y end end rect = Rectangle.new 2, 4 rect.x #=> 2 rect.y #=> 4
  • 16.
    Accessors class Rectangle attr_accessor:x attr_reader :y def initialize x, y @x, @y = x, y end end rect = Rectangle.new 2, 4 rect.x #=> 2 rect.y #=> 4 rect.x = 3 rect.x #=> 3
  • 17.
    Accessors class Rectangle attr_accessor:x attr_reader :y def initialize x, y @x, @y = x, y end end rect = Rectangle.new 2, 4 rect.x #=> 2 rect.y #=> 4 rect.x = 3 rect.x #=> 3 rect.y = 3 #=> NoMethodError
  • 18.
  • 19.
    if, else andunless if a == b puts "The same" elsif a < b puts "A is lower" else puts "A is higher" end
  • 20.
    if, else andunless if a == b puts "The same" elsif a < b puts "A is lower" else puts "A is higher" end unless finished? do_stuff end
  • 21.
    if, else andunless if a == b puts "The same" elsif a < b puts "A is lower" else puts "A is higher" end unless finished? do_stuff end raise "Oops" if result.nil?
  • 22.
    case case foo when nil puts "foo is nil" when /^a/i puts "foo begins with an 'A'" when String puts "foo is a string" when (1..10) puts "foo is in the range 1-10" else puts "foo is something else" end
  • 23.
    Loops for a in(1..10) do puts a end b=0 while b <= 10 puts b b += 1 end
  • 24.
    Including other code •load • require • Rubygems
  • 25.
  • 26.
    Everything is anobject 42.class #=> Fixnum Fixnum.class #=> Class nil.class #=> NilClass true.class #=> TrueClass method(:puts).class #=> Method
  • 27.
    Duck typing class Duck def quack "Quack" end end class Dog def quack "Woof" end end for animal in [Duck.new, Dog.new] do puts animal.quack end "a string".quack #=> NoMethodError
  • 28.
    Dynamic typing foo =123 foo.class #=> Fixnum foo = "foo" foo.class #=> String
  • 29.
    Open classes puts 123#=> "123" class Fixnum def to_s "Not telling you!" end end puts 123 #=> "Not telling you!"
  • 30.
    Open classes puts 123#=> "123" class Fixnum def to_s "Not telling you!" end end puts 123 #=> "Not telling you!" You probably don’t want to do this.
  • 31.
    Aliasing methods require "fileutils" class<< FileUtils def rm_with_prompt *files print "Are you sure you want to delete #{files}? " response = gets.chomp.downcase return unless %w{yes y}.include?(response) rm_without_prompt *files end alias_method :rm_without_prompt, :rm alias_method :rm, :rm_with_prompt end FileUtils.rm "a_file"
  • 32.
  • 33.
    Blocks begin puts "Thisis a block, but it's not much use" end
  • 34.
    Blocks begin puts "Thisis a block, but it's not much use" end block = Proc.new do puts "Now I'm assigned to a variable" end # Nothing printed yet
  • 35.
    Blocks begin puts "Thisis a block, but it's not much use" end block = Proc.new do puts "Now I'm assigned to a variable" end # Nothing printed yet block.call #=> executes the block
  • 36.
    Blocks begin puts "Thisis a block, but it's not much use" end block = Proc.new do puts "Now I'm assigned to a variable" end # Nothing printed yet block.call #=> executes the block Proc.new { puts "curly brackets work too" }
  • 37.
    Blocks with arguments block= Proc.new do |arg| puts "You called me with #{arg.inspect}" end block.call("foo") #=> You called me with "foo"
  • 38.
    Explicit block args defn_times(n, &block) puts "starting..." n.times { block.call } puts "done." end n_times(3) do puts "hello" end #=> starting... #=> hello #=> hello #=> hello #=> done.
  • 39.
    Implicit block args defn_times(n) puts "starting..." n.times { yield } puts "done." end n_times(3) do puts "hello" end #=> starting... #=> hello #=> hello #=> hello #=> done.
  • 40.
  • 41.
    File IO withblocks file = File.open "logfile", "a" file.puts "log message" file.close
  • 42.
    File IO withblocks file = File.open "logfile", "a" file.puts "log message" file.close File.open "logfile", "a" do |file| file.puts "log message" end
  • 43.
  • 44.
    Modules module InfoDisplay defdisplay_info puts "Class: #{self.class}, ID: #{object_id}" end end class SomeClass include InfoDisplay end SomeClass.new.display_info #=> "Class: SomeClass, ID: 2159853660"
  • 45.
    Modules module InfoDisplay defdisplay_info puts "Class: #{self.class}, ID: #{object_id}" end end class SomeClass include InfoDisplay end SomeClass.new.display_info #=> "Class: SomeClass, ID: 2159853660" String.send :include, InfoDisplay "foo".display_info #=> "Class: String, ID: 2159853520"
  • 46.
    Modules: Enumerable class Words include Enumerable def initialize string @words = string.split end def each @words.each {|word| yield word } end end words = Words.new("The quick brown fox") puts words.first #=> "The" puts words.count #=> 4 p words.select{|w| w.length == 3 } # => ["The", "fox"]
  • 47.
  • 48.
    Metaprogramming • Programs writingprograms • Modify behaviour at runtime • Generate code • Respond dynamically to messages
  • 49.
  • 50.
    Evaluating code code ='puts "hello"' eval code #=> "hello"
  • 51.
    Evaluating code code ='puts "hello"' eval code #=> "hello" foo = "Ruby" foo.instance_eval do def hello puts "Hello, I'm #{self}" end end foo.hello #=> "Hello, I'm Ruby"
  • 52.
  • 53.
    Dynamic calling foo ="Hello" foo.upcase #=> "HELLO" foo.send :upcase #=> "HELLO"
  • 54.
    Dynamic calling foo ="Hello" foo.upcase #=> "HELLO" foo.send :upcase #=> "HELLO" method = [:upcase, :downcase][rand(2)] foo.send method #=> Randomly "HELLO" or "hello"
  • 55.
    Dynamic calling foo ="Hello" foo.upcase #=> "HELLO" foo.send :upcase #=> "HELLO" method = [:upcase, :downcase][rand(2)] foo.send method #=> Randomly "HELLO" or "hello" bar = 2 bar.send :+, 2 #=> 4
  • 56.
    Dynamic response class Foo def method_missing name, *args puts "You called #{name} with #{args.inspect}" end end Foo.new.bar(1, "hello") #=> You called bar with [1, "hello"]
  • 57.
  • 58.
    Rake require "erb" require "rake/clean" CLOBBER.include("output/*") OUTPUT_FILE= "output/README.txt" TEMPLATE_FILE = "templates/README.txt.erb" desc "Build the README file from the template" file OUTPUT_FILE => TEMPLATE_FILE do template = File.read(TEMPLATE_FILE) File.open(OUTPUT_FILE, "w") do |f| output = ERB.new(template).result(binding) f.write output end end task :default => [OUTPUT_FILE]
  • 59.
  • 60.
    Web frameworks and many more…
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
    A rack app require"rack" class HelloWorld def call env [200, {"Content-Type" => "text/plain"}, StringIO.new("Hello world")] end end run HelloWorld.new
  • 69.
  • 70.
    Shell scripting #!/usr/bin/env ruby require"fileutils" include FileUtils ARGV.each do |file| cp file, "#{file}.bak" end system "service httpd restart" #=> returns true or false users = `users`.split
  • 71.
    Testing • Test/Unit &SimpleTest • RSpec • Cucumber • Capybara
  • 72.
    JRuby • Another implementationof Ruby • Written in Java • Comparable or better performance • Use Java libraries from Ruby code • Deploy Rails apps as WAR files
  • 73.
    Calling Java fromRuby require 'java' frame = javax.swing.JFrame.new "Window" label = javax.swing.JLabel.new "Hello" frame.getContentPane.add label frame.setDefaultCloseOperation( javax.swing.JFrame::EXIT_ON_CLOSE) frame.pack frame.setVisible true
  • 74.
    Calling Ruby fromJava import org.jruby.embed.ScriptingContainer; public class HelloWorld { private HelloWorld() { ScriptingContainer c = new ScriptingContainer(); c.runScriptlet("puts "Hello World!""); } public static void main(String[] args) { new HelloWorld(); } }
  • 75.
    while Time.now.hour <17 audience_suggestions.pop.discuss end go :home