Download - A quick Ruby Tutorial, Part 2
A quick Ruby Tutorial, Part 2
COMP313
Source: Programming Ruby, The Pragmatic Programmers’ Guide by Dave Thomas, Chad
Fowler, and Andy Hunt
class Song def
duration=(new_duration)
@duration = new_duration
end
end
song = Song.new("Bicylops", "Fleck", 260)
song.duration → 260
song.duration = 257 # set attribute with updated value
song.duration → 257
Writing attributes
class Song
attr_writer :duration
end
song = Song.new("Bicylops", "Fleck", 260)
song.duration = 257
Simpler
class Song def duration_in_minutes @duration/60.0 # force floating point end def duration_in_minutes=(new_duration) @duration = (new_duration*60).to_i end end song = Song.new("Bicylops", "Fleck", 260) song.duration_in_minutes → 4.33333333333333 song.duration_in_minutes = 4.2 song.duration → 252
Virtual attributes
class Song @@plays = 0 def initialize(name, artist, duration) @name = name @artist = artist @duration = duration @plays = 0 end def play @plays += 1 @@plays += 1 "This song: #@plays plays. Total #@@plays plays." end end
Class variables
class Example
def instance_method # instance method
end
def Example.class_method # class method
end
end #uses:Example.class_methodExample.new
Class methods
class MyLogger
private_class_method :new
@@logger = nil
def MyLogger.create
@@logger = new unless @@logger
@@logger
end
end
Singleton class example
• public: default for all methods (except initialize), accessible by everyone
• protected: access within class and all subclasses (!= Java)
• private: only the current object (again != Java)
• checked at runtime only!
Access control
class MyClass def method1 # default is 'public' #... end protected # subsequent methods will be 'protected' def method2 # will be 'protected' #... end private # subsequent methods will be 'private' def method3 # will be 'private' #... end public # subsequent methods will be 'public' def method4 # and this will be 'public' #... end end
How to define access
class MyClass
def method1
end
# ... and so on
public :method1, :method4
protected :method2
private :method3
end
Alternatively
class Accounts def initialize(checking, savings) @checking = checking @savings = savings end private def debit(account, amount) account.balance -= amount end def credit(account, amount) account.balance += amount end public #... def transfer_to_savings(amount) debit(@checking, amount) credit(@savings, amount) end #... end
Account example
class Account
attr_reader :balance # accessor method 'balance'
protected :balance # and make it protected
def greater_balance_than(other)
return @balance > other.balance
end
end
Protected example
person1 = "Tim" person2 = person1 person1[0] = 'J' person1 → "Jim" person2 → "Jim”
person1 = "Tim" person2 = person1.dup person1[0] = "J" person1 → "Jim" person2 → "Tim"
Assignment semantics (like Java)
person1 = "Tim"
person2 = person1
person1.freeze # prevent modifications to the object
person2[0] = "J" produces: prog.rb:4:in `[]=': can't modify frozen string (TypeError)
from prog.rb:4
“freeze” objects
a = [ 1, 3, 5, 7, 9 ] a[-1] → 9 a[-2] → 7 a[-99] → nil a[1, 3] → [3, 5, 7] a[3, 1] → [7] a[-3, 2] → [5, 7] a[1..3] → [3, 5, 7] a[1...3] → [3, 5] a[3..3] → [7] a[-3..-1] → [5, 7, 9]
Array access (again)
class SongList def initialize @songs = Array.new end def append(song) @songs.push(song) self end def delete_first @songs.shift end def delete_last @songs.pop end end
Implement own sample container
class SongList
def [](index)
@songs[index]
end
end
Defining array-like access
require 'test/unit' class TestSongList < Test::Unit::TestCase def test_delete list = SongList.new s1 = Song.new('title1', 'artist1', 1) s2 = Song.new('title2', 'artist2', 2) list.append(s1).append(s2) assert_equal(s1, list[0]) assert_equal(s2, list[1]) assert_nil(list[9]) assert_equal(s2, list.delete_last) assert_equal(s1, list.delete_first) assert_nil(list.delete_last) end end
Unit testing
class SongList
def with_title(title)
for i in [email protected]
return @songs[i] if title == @songs[i].name
end
return nil
end
end
Finding songs by title
class SongList
def with_title(title)
@songs.find {|song| title == song.name }
end
end
Finding songs by title (the Ruby way)
class Array
def find
for i in 0...size
value = self[i]
return value if yield(value)
end
return nil
end
end
[1, 3, 5, 7, 9].find {|v| v*v > 30 } → 7
Hypothetical find in Array
[1,3,5,7].inject(0) {|sum, element| sum+element} → 16
[1,3,5,7].inject(1) {|product, element| product*element} → 105
[1,3,5,7].inject {|sum, element| sum+element} → 16
[1,3,5,7].inject {|product, element| product*element} → 105
inject iterator
• are “internal”, I.e. just another method
• Java et.al.: “external”, I.e. explicit additional object
• pros and cons
Iterators in Ruby
class File
def File.my_open(*args)
result = file = File.new(*args)
# If there's a block, pass in the file and close it on returns
if block_given?
result = yield file
file.close
end
return result
end
end
more on code blocks
def n_times(thing) return lambda {|n| thing * n } end
p1 = n_times(23) p1.call(3) → 69 p1.call(4) → 92
p2 = n_times("Hello ") p2.call(3) → "Hello Hello Hello "
proc and lambda
• ints are either in -2^30 to 2^30-1 (or -2^62 to 2^62-1) or arbitrarily large BigNums
num = 81
6.times do
puts "#{num.class}: #{num}"
num *= num
end
Numbers
3.times { print "X " }
1.upto(5) {|i| print i, " " }
99.downto(95) {|i| print i, " " }
50.step(80, 5) {|i| print i, " " }
Loops using numbers
• 1.to_i or Integer(1)
some_file = File.new(“xyz”,”r”)
some_file.each { |line|
v1, v2 = line.split
print v1.to_i + v2.to_i, " ”
}
Strings to numbers
/jazz/j00132.mp3 | 3:45 | Fats Waller | Ain't Misbehavin'
/jazz/j00319.mp3 | 2:58 | Louis Armstrong | Wonderful World
/bgrass/bg0732.mp3| 4:09 | Strength in Numbers | Texas Red
break lines into fieldsconvert runtime into secondsremove extra spaces in artists’ names
Song database
File.open("songdata") do |song_file|
songs = SongList.new
song_file.each do |line|
file, length, name, title = line.chomp.split(/\s*\|\s*/)
name.squeeze(“ “)
mins, secs = length.scan(/\d+/)
songs.append(Song.new(title, name, mins.to_i*60+secs.to_i))
end
songs
end
Song database: code