object types
variables
first_var = 1
#global
$var
#class
@@var
#instance
@var
#local
var
#block
var
integer
#exp
4 ** 2
x += 2
# return integer 3
10/3
# Fixnum or Bignum
123.class
x = 1234 ** 3
x.class
#method
-200.abs
200.next
floats
12345.67.class
#method
123.34.round
123.34.floor
123.34.ceil
123.34.to_i
strings
"hello"
'world'
"hello" + ' ' + "world"
"hello"*5
"1" * 5
'I\'m escaped.'
"I'm good"
#tab
"\t"
#return
"\n"
# quote variable with double quote
# ruby evaluation
a = "hi"
puts "I want to say #{a}."
puts "1 + 1 = #{1+1}"
#method
"hi".reverse
"hi".capitalize
"hi".downcase
"hi".upcase
"hi".length
"hi".reverse.upcase
arrays
any objects can go in an array.
data_set = []
data_set = ["a",1,"2"]
data_set[0] = "d"
data_set[2] = nil
#append
data_set << "e"
#clear
data_set.clear
data_set = []
arrays methods
array = [1,2,3,4,5]
array2 = [1,"2.0",3.0,[1,2],"dog"]
puts array.inspect
array2.to_s
array.join(", ")
x = "1,2,3,4,5"
y = x.split(",")
y.reverse
array.sort
#unique
array.uniq
array.uniq! #will replace the original
array.delete_at(2) # delete the position
array.delete(4) # delete the value
array.push(4)
array.pop
array.shift
array.unshift(1)
array + array2
hashes
unordered key-value pairs
person = { 'first' => 'Kevin', 'last' => 'Jordan'}
person['first']
person.index('Kevin')
person.keys
person.values
person.length
person.clear
person['gender'] = 'male'
symbols
a label used to identify a piece of data
can be used for keys in hashes
:test
booleans
#not
!
#and
&&
#or
||
true
false
#method
x.nil?
2.between?(1,4)
[].empty?
a.include?(2)
hash.has_key?('a')
hash.has_value?(2)
ranges
#range
# inclusive, include the last
1..10
# exclusive, exclude the last
1...10
x.begin
x.end
x.first
x.last
alpha = 'a'..'m'
[*alpha]
constants
anything beginning with capital will be constant
value is not recommended to be changed
TEST = 100
control structures
if, else, elsif
if boolean
...
elsif boolean
...
else
...
end
# or
puts "sth" if name == "a"
unless, case
unless boolean
...
end
# or
if !boolean
...
end
# case
case
when boolean
...
when boolean
...
else
...
end
# or
case test_value
when value
...
when value
...
else
...
end
# ternary operator
boolean ? code1 : code2
loops
- break
- next: jump to the next loop
- redo: redo this loop
- retry: start the whole loop over
loop do
...
end
while boolean
...
end
until boolean
...
end
puts x += 2 while x < 100
iterators
5.times do
puts 'hello'
end
1.upto(5) {puts "hello"}
(1..5).each {puts "hello"}
1.upto(5) do |i|
puts "hello" + i.to_s
end
for i in something
...
end
array.each {|num| puts num * 2}
- integer/floats: times, upto, downto, step
- range: each, step
- string: each,
each_line
,each_byte
- array: each,
each_index
,each_with_index
- hash: each,
each_key
,each_value
,each_pair
code blocks
find
#return a single object
(1..10).find {|i| i==5 }
(1..10).detect {|i| i==5 }
#find all
(1..10).find_all {|i| i%3 == 0}
(1..10).select {|i| i%3 == 0}
#return a single boolean
(1..10).any? {|i| i%3 == 0}
(1..10).all? {|i| i%3 == 0}
#delete if
[*1..10].delete_if {|i| i%3 == 0}
merge
used for hash only
hash1.merge(hash2)
hash1.merge(hash2) {|key,old,new| new*5}
hash1.merge(hash2) {|key,old,new| old < new? old:new}
collect/map
array.collect{|i| i+1}
sort
#compare with spaceship operator
1 <=> 2
array.sort {|v1,v2| v1 <=> v2}
inject
accumulator: like map, but accumulate each time
(1..10).inject {|memo,n| memo+n if n != 3}
methods
def some_name(args="default")
...
# return the last value or
return 1+1
end
# load methods file
ruby methods.rb
require "methods.rb"
operators are methods
8+2
8.+(2)
class
class SomeName
...
def some_method
...
end
end
# use
animal = Animal.new
animal.method1
attribute
class Animal
def noise=noise
@noise = noise
end
def noise
@noise
end
end
a1 = Animal.new
a1.noise = "Hoo!"
puts a1.noise
attr_* method
attr_reader :name
attr_writer :name
# use both reader and writer
attr_accessor :name, :age
# equal to
def name
@name
end
def name =(value)
@name = value
end
initialize method
def initialize(name, legs =4)
@name = name
@legs = leg
end
# will always be executed
a1 = Animal.new("a")
class methods
can be called without an instance
Animal.new
def self.method_name
end
def self.create_with_attributes(noise,color)
animal = Animal.new(noise)
#or
animal = self.new(noise)
animal.color = color
return animal
end
class attributes
store values that apply to the class generally
Class variable: @@var
Class reader and writer
class Animal
@@species = ['cat','dog']
@@total = 0
@@current = []
def self.current
@@current
end
def self.current= (array=[])
@@current = array
end
def initialize()
...
@@total += 1
@@current << self
end
end
inheritance
only one father
subclass can override the father’s methods
also can override one class’s methods
class Cow < Animal
def new_method
end
end
class Array
def to_s
self.join(',')
end
end
superclass
Add super in subclass’s method to do the original or parent’s version.
Super is just a method call.
class Cow < Animal
def new_method
super
return 'xx'
end
end
modules
namespacing
Namespacing allows for class names that don’t conflict.
module Romantic
class Date
...
end
end
# self defined Date class
dinner = Rommantic::Date.new
# ruby internal Date class
dinner.date = Date.new
mix-ins
put methods in module, then include module in classes
module info
...
end
class Student
include info
end
load, require and include
# load module, use path, load every times
load 'info.rb'
# require module, like load but only load once
require 'info.rb'
enumerable as a mixin
method: sort, detect, select, reject, collect, inject
use: Array, Hashes, Ranges, Strings
class ToDoList
include Enumerable
attr_accessor :items
def initialize
@items = []
end
# told enumerable which variable to use
def each
@ items.each {|item| yield item}
end
end
files
input and output
# chomp remove the escaping ending charactor
input = gets "hello.\r\n".chomp
# or
input = gets.chomp
file system
unix, linux, mac : /
windows: \
Can create path by:
File.join('path1','path2','name')
chmod and chown to change the permission
path
__FILE__
is THIS file.
# absolute path
File.expand_path(__FILE__)
File.dirname(__FILE__)
# back up to parent dir
File.join('current`,`..`,`file`)
# get ex file
"ex\ file"
# get ex\ file
'ex\ file`
accessing files
mode: r, w(erase existing files), a(append), r+(read and write), w+, a+
# create new file object, name, mode
file = File.new('some.txt','w')
file.close
File.open('file1.txt','r') do |file|
# read and will automatically be closed
end
writing to files
file = File.new('file1.txt','w')
# has a line return
file.puts "abc"
# no return
file.print "eff"
file.write "an"
# append
file << "mno"
file.close
reading
file = File.new('file1.txt','r')
file.gets
file.gets.chomp
file.close
File.open('file1.txt','r') do |file|
while line = file.gets
puts "** " + line.chomp.reverse + " **"
end
end
# or each line
File.open('file1.txt','r') do |file|
file.each_line {|line| puts line.upcase}
end
file pointer
file = File.new('file1.txt','r+')
# return the position of pointer
file.pos
file.read(3)
file.pos
# get from the pointer to the return
file.gets
file.pos = 13
# end of the file?
file.eof?
# move the pointer to 0
file.rewind
# no. of gets
file.lineno
while line = file.gets
puts "Line #{file.lineno}: #{line}"
end
renaming and deleting files
File.rename('old.txt','newname.txt')
File.delete('file.txt')
# copy
require 'fileutils'
FileUtils.copy('old.txt','new.txt')
examining file details
File.exist?(file)
File.directory?(file)
File.readable?(file)
File.writable?(file)
File.executable?(file)
# size in bytes
File.size(file)
File.dirname(file)
File.expand_path(file)
File.basename(path)
# extension
File.extname(file)
# modify, access, status change
File.mtime, .atime, .ctime
#instance method
myfile = File.new("file")
myfile.stat
myfile.stat.size
myfile.stat.readable?
working with directories
Dir.pwd
Dir.chdir("..")
Dir.entries('.').each do |entry|
print entry + ": "
if File.file?(entry) && File.readable?(entry)
File.open(entry, 'r') do |file|
puts file.gets
end
else
puts
end
end
Dir.foreach('.') {|entry| puts entry}
Dir.mkdir('temp')
Dir.delete('temp')
# use FileUtils to remove dir with inside contents
project
init
APP_ROOT = File.dirname(__FILE__)
# load files
$:.unshift( File.join(APP_ROOT,'lib') )
require 'guide'
guide = Guide.new('restaurants.txt')
guide.launch!
guide class
require 'Restaurant'
class Guide
class Config
@@actions =['list','find','add','quit']
def self.action; @@actions; end
end
def initialize(path=nil)
Restaurant.filepath = path
if Restaurant.file_usable?
puts "Found restaurant file."
elsif Restaurant.create_file
puts "Create restaurant file."
else
puts "Exiting. \n\n"
exit!
end
end
def launch!
introduction
until result == :quit
action, args = get_action
result = do_action(action, args)
end
conclusion
end
def get_action
action = nil
until Guide::Config.actions.include?(action)
puts "Actions: " + Guide::Config.actions.join(", ") if action
print ">"
user_response = gets.chomp
args = user_response.downcase.strip.split(" ")
action = args.shift
end
return action, args
end
def do_action(action,args=[])
case action
when 'list'
list(args)
when 'find'
keyword = args.shift
find(keyword)
when 'add'
add
when 'quit'
return :quit
else
puts "\nI don't understand that.\n"
end
end
def list(args=[])
sort_order = args.shift || "name"
sort_order = "name" unless ['name','cuisine','price'].include?(sort_order)
puts "\nListing restaurants\n\n".upcase
restaurants = Restaurant.saved_restaurants
restaurants.sort! do |r1,r2|
case sort_order
when 'name'
r1.name.downcase <=> r2.name.downcase
when 'cuisine'
r1.cuisine <=> r2.cuisine.downcase
when 'price'
r1.price.to_i <=> r2.price.to_i
end
end
restaurants.each do |rest|
puts rest.name + " | " + rest.cuisine + " | " + " | " rest.price
end
def add
puts "\nAdd a restaurant\n".upcase
restaurant = Restaurant.build_with_questions
if restaurant.save
puts "\nRestaurant Added\n\n"
else
puts "\nSave Error: not added\n\n"
end
def find(keyword ="")
output_action_header("Find a restaurant")
if keyword
restaurants = Restaurant.saved_restaurants
found = restaurants.select do |rest|
rest.name.downcase.include?(keyword.downcase) ||
rest.cuisine.downcase.include?(keyword.downcase) ||
rest.price.to_i <= keyword.to_i
end
output_restaurant_table(found)
#search
else
puts "Find using a key phrase"
end
end
def introduction
puts "welcome"
end
def conclusion
puts "goodbye"
end
end
restaurant class
class Restaurant
@@filepath = nil
def self.filepath=(path=nil)
@@filepath = File.join(APP_ROOT,path)
end
attr_accessor :name, :cuisine, :price
def self.file_exist?
if @@filepath && File.exists?(@@filepath)
return true
else
return false
end
end
def self.file_usable?
return false unless @@filepath
return false unless File.exists?(@@filepath)
return false unless File.readable?(@@filepath)
return false unless File.writable?(@@filepath)
return true
end
def sef.create_file
File.open(@@filepath, 'w') unless file_exists?
return file_usable?
end
def self.saved_restaurants
restaurants = []
if file_usable?
file = File.new(@@filepath,'r')
file.each_line do |line|
restaurants << Restaurant.new.import_line(line.chomp)
end
file.close
end
return restaurants
end
def self.build_with_questions
args = {}
print "Restaurant name: "
args[:name] = gets.chomp.strip
print "Restaurant cuisine: "
args[:cuisine] = gets.chomp.strip
print "Restaurant price: "
args[:price] = gets.chomp.strip
restaurant = self.new(args)
end
def initialize(args={})
@name = args[:name] || ""
@cuisine = args[:cuisine] || ""
@price = args[:price] || ""
end
def import_line(line)
line_array = line.split("\t")
@name, @cuisine, @price = line_array
return self
end
def save
return false unless Restaurant.file_usable?
File.open(@@filepath, 'a') do |file|
file.puts "#{[@name,@cuisine,@price].join("\t")}\n"
end