A quick Ruby Tutorial, Part 4

32
A quick Ruby Tutorial, Part 4 COMP313 Source: Programming Ruby, The Pragmatic Programmers’ Guide by Dave Thomas, Chad Fowler, and Andy Hunt

description

A quick Ruby Tutorial, Part 4. COMP313 Source: Programming Ruby, The Pragmatic Programmers’ Guide by Dave Thomas, Chad Fowler, and Andy Hunt. 2 forms of assignment. hard coded to variables and constants: instrument = "piano" MIDDLE_A = 440 - PowerPoint PPT Presentation

Transcript of A quick Ruby Tutorial, Part 4

Page 1: A quick Ruby Tutorial, Part 4

A quick Ruby Tutorial, Part 4

COMP313

Source: Programming Ruby, The Pragmatic Programmers’ Guide by Dave Thomas, Chad

Fowler, and Andy Hunt

Page 2: A quick Ruby Tutorial, Part 4

hard coded to variables and constants:

instrument = "piano"

MIDDLE_A = 440

user defined for object attributes and other element references:

song.duration = 234

instrument["ano"] = "ccolo"

2 forms of assignment

Page 3: A quick Ruby Tutorial, Part 4

problems

class BrokenAmplifier attr_accessor :left_channel, :right_channel def volume=(vol) left_channel = self.right_channel = vol end end

ba = BrokenAmplifier.new ba.left_channel = ba.right_channel = 99 ba.volume = 5 ba.left_channel → 99 ba.right_channel → 5

Page 4: A quick Ruby Tutorial, Part 4

Parallel assignment

a = [1, 2, 3, 4]

b, c = a → b == 1, c == 2

b, *c = a → b == 1, c == [2, 3, 4]

b, c = 99, a → b == 99, c == [1, 2, 3, 4]

b, *c = 99, a → b == 99, c == [[1, 2, 3, 4]]

b, c = 99, *a → b == 99, c == 1

b, *c = 99, *a b == 99, c == [1, 2, 3, 4]

Page 5: A quick Ruby Tutorial, Part 4

Nested assignment

b, (c, d), e = 1,2,3,4 → b == 1, c == 2, d == nil, e == 3

b, (c, d), e = [1,2,3,4] → b == 1, c == 2, d == nil, e == 3

b, (c, d), e = 1,[2,3],4 → b == 1, c == 2, d == 3, e == 4

b, (c, d), e = 1,[2,3,4],5 → b == 1, c == 2, d == 3, e == 5

b, (c,*d), e = 1,[2,3,4],5 → b == 1, c == 2, d == [3, 4], e == 5

Page 6: A quick Ruby Tutorial, Part 4

Overwriting operators like + class Bowdlerize def initialize(string) @value = string.gsub(/[aeiou]/, '*') end def +(other) Bowdlerize.new(self.to_s + other.to_s) end def to_s @value end end a = Bowdlerize.new("damn ") → d*mn a += "shame" → d*mn sh*m*

Page 7: A quick Ruby Tutorial, Part 4

defined? operator

defined? 1 → "expression"

defined? dummy → nil

defined? printf → "method"

defined? String → "constant"

defined? $_ → "global-variable"

defined? Math::PI → "constant"

defined? a = 1 → "assignment"

defined? 42.abs → "method”

defined? yield → ”yield” or nil

Page 8: A quick Ruby Tutorial, Part 4

Case expressionsleap = case when year % 400 == 0: true when year % 100 == 0: false else year % 4 == 0 end case input_line when "debug" dump_debug_info dump_symbols when /p\s+(\w+)/ dump_variable($1) when "quit", "exit" exit else print "Illegal command: #{input_line}" end

Page 9: A quick Ruby Tutorial, Part 4

another case examples

kind = case year

when 1850..1889 then "Blues"

when 1890..1909 then "Ragtime"

when 1910..1929 then "New Orleans Jazz"

when 1930..1939 then "Swing"

when 1940..1950 then "Bebop"

else "Jazz"

end

Page 10: A quick Ruby Tutorial, Part 4

case as instanceof test

case shape

when Square, Rectangle

# ...

when Circle

# ...

when Triangle

# ...

else

# ...

end

Page 11: A quick Ruby Tutorial, Part 4

For .. in .. looping

based on each => works for all classes defining eachclass Periods def each yield "Classical" yield "Jazz" yield "Rock" end endperiods = Periods.new for genre in periods print genre, " " end produces: Classical Jazz Rock

Page 12: A quick Ruby Tutorial, Part 4

break, redo, next, retry

while line = gets next if line =~ /^\s*#/ # skip comments break if line =~ /^END/ # stop at end # substitute stuff in backticks and try again redo if line.gsub!(/`(.*?)`/) { eval($1) } # process line ... end

for i in 1..100 print "Now at #{i}. Restart? " retry if gets =~ /^y/end

Page 13: A quick Ruby Tutorial, Part 4

Loops, blocks, and scope of variables

while/until/for do NOT create a new scope (!= java), but explicit code blocks can:

[ 1, 2, 3 ].each { |x| y = x + 1 } [ x, y ] --> error, but

x=nily=nil[ 1, 2, 3 ].each { |x| y = x + 1 } [ x, y ] --> [3,4]

Page 14: A quick Ruby Tutorial, Part 4

Exceptions

op_file = File.open(opfile_name, "w") begin # Exceptions raised by this code will # be caught by the following rescue clause while data = socket.read(512) op_file.write(data) endrescue SystemCallError $stderr.print "IO failed: " + $! op_file.close File.delete(opfile_name) raise end

Page 15: A quick Ruby Tutorial, Part 4

more on Exceptions

begin

eval string

rescue SyntaxError, NameError => boom

print "String doesn't compile: " + boom

rescue StandardError => bang

print "Error running script: " + bang

end

empty rescue catches StandardError instances,

may also use expression returning an Exception class

Page 16: A quick Ruby Tutorial, Part 4

ensure (cf. Java’s finally)

f = File.open("testfile")

begin

# .. process

rescue

# .. handle error

ensure

f.close unless f.nil?

end

Page 17: A quick Ruby Tutorial, Part 4

retry

@esmtp = true begin # First try an extended login. If it fails because the # server doesn't support it, fall back to a normal login if @esmtp then @command.ehlo(helodom) else @command.helo(helodom) end rescue ProtocolError if @esmtp then @esmtp = false retry else raise end end

Page 18: A quick Ruby Tutorial, Part 4

Raising exceptions

raise raise "bad mp3 encoding"

raise InterfaceException, "Keyboard failure", caller

raise "Missing name" if name.nil?

if i >= names.size raise IndexError, "#{i} >= size (#{names.size})" end

raise ArgumentError, "Name too big", caller[1..-1]

Page 19: A quick Ruby Tutorial, Part 4

Retry example

class RetryException < RuntimeError attr :ok_to_retry def initialize(ok_to_retry) @ok_to_retry = ok_to_retry end end

def read_data(socket) data = socket.read(512) raise RetryException.new(true), "transient read error” if data.nil? # .. normal processing end

Page 20: A quick Ruby Tutorial, Part 4

Retry example cont.

begin stuff = read_data(socket)

# .. process stuff

rescue RetryException => detail

retry if detail.ok_to_retry

raise

end

Page 21: A quick Ruby Tutorial, Part 4

Modules

• provide namespace against clashes• implement mixin facility

module Trig PI = 3.141592654 def Trig.sin(x) # .. endend

require 'trig' y = Trig.sin(Trig::PI/4)

Page 22: A quick Ruby Tutorial, Part 4

Module mixin

• Modules are not classes, cannot have instancesmodule Debug def who_am_i? "#{self.class.name} (\##{self.object_id}): #{self.to_s}" end end class Phonograph include Debug # ... end

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

Page 23: A quick Ruby Tutorial, Part 4

Mixin interactionclass Song include Comparable def initialize(duration) @duration = duration end def <=>(other) self.duration <=> other.duration end end song1, song2 = Song.new(225), Song.new(260)song1 <=> song2 → -1 song1 < song2 → true song1 == song1 → true song1 > song2 → false

Page 24: A quick Ruby Tutorial, Part 4

Resolving name clashes

methods: immediate class, thenlocal mixins last one first, thensuperclass, it’s mixins, etc …

variable before method name:a = 1def a 2enda -> 1a() -> 2

Page 25: A quick Ruby Tutorial, Part 4

2 ways of IO

utility methods: gets, open, print, printf, puts, putc, readline, readlines, test

proper classes: IO, File, BasicSocket, …

endl = ”\n"

STDOUT << 99 << " red balloons" << endl

Page 26: A quick Ruby Tutorial, Part 4

File IO examples

File.open("testfile") do |file| file.each_byte {|ch| putc ch; print "." }end

IO.foreach("testfile") {|line| puts line }

# read whole file into one string str = IO.read("testfile") str.length → 66 str[0, 30] → "This is line one\nThis is line " # read all lines into an array arr = IO.readlines("testfile") arr.length → 4 arr[0] → "This is line one\n"

Page 27: A quick Ruby Tutorial, Part 4

StringIO

require 'stringio'

ip = StringIO.new("now is\nthe time\nto learn\nRuby!")

op = StringIO.new("", "w")

ip.each_line do |line|

op.puts line.reverse

end

op.string →”\nsi won\n\nemit eht\n\nnrael ot\n!ybuR\n"

Page 28: A quick Ruby Tutorial, Part 4

Profiler

require 'profile' count = 0 words = File.open("/usr/share/dict/words")

while word = words.gets word = word.chomp! if word.length == 12 count += 1 end end

puts "#{count} twelve-character words”

Page 29: A quick Ruby Tutorial, Part 4

Profiler output

% cumulative self self total time seconds seconds calls ms/call ms/call name 7.70 8.60 8.60 234938 0.04 0.04 IO#gets 7.65 17.14 8.54 234937 0.04 0.04 Fixnum#== 7.43 25.43 8.29 234937 0.04 0.04 String#length 7.18 33.45 8.02 234937 0.03 0.03 String#chomp! 0.70 34.23 0.78 20460 0.04 0.04 Fixnum#+ 0.00 34.23 0.00 1 0.00 0.00 Fixnum#to_s 0.00 34.23 0.00 1 0.00 0.00 Kernel.puts 0.00 34.23 0.00 1 0.00 0.00 File#initialize 0.00 34.23 0.00 2 0.00 0.00 Kernel.respond_to? 0.00 34.23 0.00 1 0.00 0.00 File#open 0.00 34.23 0.00 2 0.00 0.00 IO#write 0.00 34.23 0.00 1 0.00 0.00 Profiler__.start_profile 0.00 34.23 0.00 1 0.00 111640.00 #toplevel

Page 30: A quick Ruby Tutorial, Part 4

Faster solution

require 'profile' words = File.read("/usr/share/dict/words") count = words.scan(/^.........…\n/).size puts "#{count} twelve-character words"

20460 twelve-character words % cumulative self self total time seconds seconds calls ms/call ms/call name 95.24 0.20 0.20 1 200.00 200.00 String#scan 4.76 0.21 0.01 1 10.00 10.00 File#read 0.00 0.21 0.00 2 0.00 0.00 IO#write

Page 31: A quick Ruby Tutorial, Part 4

Duck typing

If it walks and talks like a duck, treat it like one (laissez-faire)

def append_song(result, song) result << song.title << " (" << song.artist << ")" end###### when checking, check for capability instead of class:def append_song(result, song) unless result.respond_to?(:<<) fail TypeError.new("'result' needs `<<' capability") end unless song.respond_to?(:artist) && song.respond_to?(:title) fail TypeError.new("'song' needs 'artist' and 'title'") end result << song.title << " (" << song.artist << ")”end

Page 32: A quick Ruby Tutorial, Part 4

other ruby stuff

• threads and processes• GUI: Tk library• JavaDoc like Rdoc• Package management: RubyGems• Web stuff (including Ruby on Rails)• JRuby• Extending using host language (C/Java)