a quick ruby tutorial, part 4
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 PresentationTRANSCRIPT
![Page 1: A quick Ruby Tutorial, Part 4](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/1.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/2.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/3.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/4.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/5.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/6.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/7.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/8.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/9.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/10.jpg)
case as instanceof test
case shape
when Square, Rectangle
# ...
when Circle
# ...
when Triangle
# ...
else
# ...
end
![Page 11: A quick Ruby Tutorial, Part 4](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/11.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/12.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/13.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/14.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/15.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/16.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/17.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/18.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/19.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/20.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/21.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/22.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/23.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/24.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/25.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/26.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/27.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/28.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/29.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/30.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/31.jpg)
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](https://reader035.vdocuments.site/reader035/viewer/2022062217/56813fe9550346895daadd28/html5/thumbnails/32.jpg)
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)