My personally compiled cheatsheet for the Ruby programming language.
##
# Interactive Ruby (irb)
##
# From the console/cli, enter:
irb
##
# Special values
##
nil = no value
# Check for nil with nil? method, such as: if name.nil?
# Comments Begin with #
##
# Constants Start with a capital letter, like: Constant
##
##
# Symbols
##
# Lightweight objects, beginning with a colon. Best used for comparisons and internal logic.
# Use in place of strings for performance reasons for anything that's not
# displayed to the user.
##
# Arrays and hashes
##
a = [1, 2, 3]
empty = []
empty = Array.new
empty << "new item"
food = {
'fruit' => 'orange',
'vegetable' => 'broccoli'
}
histogram = Hash.new(0)
##
# Method declaration
##
def hi
puts "Hello world"
end
# Call with hi() or just hi. Parentheses are not necessary if method has no arguments.
# Method name may end with ? ! or =
# = indicates a setter.
# ? returns a boolean that answers the question.
# ! indicates that you should be cautious calling
# that version of the method because it may be
# destructive (such as sort! which is in-place,
# rather than regular sort, which copies)
##
# Method with argument
##
def hi(name)
puts "Hello #{name}"
end
hi("Steve")
##
# Class declaration
##
class Greeter
def initialize(name = "World")
@name = name
end
def say_hi
puts "Hi #{@name}"
end
def say_bye
puts "Bye #{@name}"
end
def return_zero
return 0
end
end
##
# In the above:
##
@name is an instance variable
@@foo is a class variable
# Instantiation and use
g = Greeter.new("Steve")
g.say_hi
g.say_bye
##
# Class and object introspection
##
Greeter.instance_methods # returns an array of every method offered by the class, including inherited methods.
Greeter.instance_methods(false) # returns just the methods defined in the subclass.
g.respond_to?("say_hi") # determines if the object responds to the named method.
myObject.is_a?(Integer) # to determine class of object
##
# Instance variable accessors
##
class Greeter
attr_accessor :name
end
# Allows you to get the value of name with the name method and set it
# with the name= method.
# attr_reader for read-only, attr_writer for write-only
##
# Subclassing
##
class Student < Human
# stuff here...
end
# Conditional structure
if expression
stuff
elsif
other stuff
else
other stuff
end
##
# While loop
##
while weight < 100 and numPallets <= 30
weight += 1
end
##
# List iteration
# each is a method that accepts a code block
# (the bit between do and end)
##
names.each do |name|
puts "Hello #{name}"
end
##
# Code blocks
##
{ puts "Hello" }
do
puts "Hello"
end
# yield statement executes a code block:
def callBlock
yield
yield
end
callBlock { puts "Hello" }
# Results in:
# Hello
# Hello
##
# Joining string list elements
##
names.join(", ")
##
# Regular expression matching
##
if line =~ /Perl|Python/
puts "Scripting language mentioned: #{line}"
end
##
# Regular expression replacing
##
line.sub(/Perl/, 'Ruby') # replace first
line.gsub(/Python/, 'Ruby') # replace every
##
# Ranges
##
(1..3) is the numbers 1 through 3
(0...5) is the numbers 0 through 4
##
# Heredocs
##
puts <<EOF
Blah
Blah
Blah
EOF
##
# String interpolation or "embedding"
# the result of some code inside a string.
##
name = "Andrew"
greeting = "Hello, my name is #{name}"
addition = "25 x 8 = #{25 * 8}"
##
# There are two ways to create an empty array:
##
my_arr = Array.new
my_other_arr = []
# init an array with values
my_third_array = ["one", "two", "three"]
##
# Hashes
##
my_hash = Hash.new
my_other_hash = {}
my_hash["name"] = "Andrew"
my_hash[:age] = 20
# a hash with objects
person = {
:name => "Joe",
:age => 35,
:job => "plumber"
}
# Sort a Ruby Hash by number value
metrics = { "sitea.com" => 745, "siteb.com" => 9, "sitec.com" => 10 }
metrics.sort_by { |key, value| value }
# ==> [["siteb.com", 9], ["sitec.com", 10], ["sitea.com", 745]]
##
# Methods on Objects
##
# a ruby method
def sayMoo
puts 'mooooooo...'
end
# sayMoo
# sayMoo
# puts 'coin-coin'
# sayMoo
# sayMoo
# Method Parameters
def sayMoo numberOfMoos
puts 'mooooooo...'*numberOfMoos
end
# sayMoo 3
# puts 'oink-oink'
# sayMoo # This should give an error because the parameter is missing.
# mooooooo...mooooooo...mooooooo...
# oink-oink
# <ArgumentError: wrong number of arguments (0 for 1)>
# Local Variables
def doubleThis num
numTimes2 = num*2
puts num.to_s+' doubled is '+numTimes2.to_s
end
# doubleThis 44
# 44 doubled is 88
def doubleThis num
numTimes2 = num*2
puts num.to_s+' doubled is '+numTimes2.to_s
end
# doubleThis 44
# puts numTimes2.to_s
# 44 doubled is 88
# <NameError: undefined local variable or method `numTimes2' for #<StringIO:0x82ba21c>>
##
# Return Values
##
def englishNumber number
if number < 0 # No negative numbers.
return 'Please enter a number that isn't negative.'
end
if number == 0
return 'zero'
end
# No more special cases! No more returns!
numString = '' # This is the string we will return.
onesPlace = ['one', 'two', 'three', 'four', 'five',
'six', 'seven', 'eight', 'nine']
tensPlace = ['ten', 'twenty', 'thirty', 'forty', 'fifty',
'sixty', 'seventy', 'eighty', 'ninety']
teenagers = ['eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen',
'sixteen', 'seventeen', 'eighteen', 'nineteen']
# "left" is how much of the number we still have left to write out.
# "write" is the part we are writing out right now.
# write and left... get it? :)
left = number
write = left/100 # How many hundreds left to write out?
left = left - write*100 # Subtract off those hundreds.
if write > 0
# Now here's a really sly trick:
hundreds = englishNumber write
numString = numString + hundreds + ' hundred'
# That's called "recursion". So what did I just do?
# I told this method to call itself, but with "write" instead of
# "number". Remember that "write" is (at the moment) the number of
# hundreds we have to write out. After we add "hundreds" to "numString",
# we add the string ' hundred' after it. So, for example, if
# we originally called englishNumber with 1999 (so "number" = 1999),
# then at this point "write" would be 19, and "left" would be 99.
# The laziest thing to do at this point is to have englishNumber
# write out the 'nineteen' for us, then we write out ' hundred',
# and then the rest of englishNumber writes out 'ninety-nine'.
if left > 0
# So we don't write 'two hundredfifty-one'...
numString = numString + ' '
end
end
write = left/10 # How many tens left to write out?
left = left - write*10 # Subtract off those tens.
if write > 0
if ((write == 1) and (left > 0))
# Since we can't write "tenty-two" instead of "twelve",
# we have to make a special exception for these.
numString = numString + teenagers[left-1]
# The "-1" is because teenagers[3] is 'fourteen', not 'thirteen'.
# Since we took care of the digit in the ones place already,
# we have nothing left to write.
left = 0
else
numString = numString + tensPlace[write-1]
# The "-1" is because tensPlace[3] is 'forty', not 'thirty'.
end
if left > 0
# So we don't write 'sixtyfour'...
numString = numString + '-'
end
end
write = left # How many ones left to write out?
left = 0 # Subtract off those ones.
if write > 0
numString = numString + onesPlace[write-1]
# The "-1" is because onesPlace[3] is 'four', not 'three'.
end
# Now we just return "numString"...
numString
end
puts englishNumber( 0)
puts englishNumber( 9)
puts englishNumber( 10)
puts englishNumber( 11)
puts englishNumber( 17)
puts englishNumber( 32)
puts englishNumber( 88)
puts englishNumber( 99)
puts englishNumber(100)
puts englishNumber(101)
puts englishNumber(234)
puts englishNumber(3211)
puts englishNumber(999999)
puts englishNumber(1000000000000)
# zero
# nine
# ten
# eleven
# seventeen
# thirty-two
# eighty-eight
# ninety-nine
# one hundred
# one hundred one
# two hundred thirty-four
# thirty-two hundred eleven
# ninety-nine hundred ninety-nine hundred ninety-nine
# one hundred hundred hundred hundred hundred hundred
##
# Arrays and Iterators
##
names = ['Ada', 'Belle', 'Chris']
puts names
puts names[0]
puts names[1]
puts names[2]
puts names[3] # This is out of range.
# Ada
# Belle
# Chris
# Ada
# Belle
# Chris
# nil
languages = ['English', 'German', 'Ruby']
languages.each do |lang|
puts 'I love ' + lang + '!'
puts 'Don't you?'
end
puts 'And let's hear it for C++!'
puts '...'
# I love English!
# Don't you?
# I love German!
# Don't you?
# I love Ruby!
# Don't you?
# And let's hear it for C++!
# ...
3.times do
puts 'Hip-Hip-Hooray!'
end
# Hip-Hip-Hooray!
# Hip-Hip-Hooray!
# Hip-Hip-Hooray!
foods = ['artichoke', 'brioche', 'caramel']
puts foods
puts
puts foods.to_s
puts
puts foods.join(', ')
puts
puts foods.join(' :) ') + ' 8)'
200.times do
puts []
end
# artichoke
# brioche
# caramel
#
# artichokebriochecaramel
#
# artichoke, brioche, caramel
#
# artichoke :) brioche :) caramel 8)
favorites = []
favorites.push 'raindrops on roses'
favorites.push 'whiskey on kittens'
puts favorites[0]
puts favorites.last
puts favorites.length
puts favorites.pop
puts favorites
puts favorites.length
# raindrops on roses
# whiskey on kittens
# 2
# whiskey on kittens
# raindrops on roses
# 1
##
# Array Iterator example
##
sites = ["vpn", "ftp", "m"]
sites = sites.map do |s|
"#{s}.jonlabelle.com"
end
# => ["net.jonlabelle.com", "psd.jonlabelle.com", "mobile.jonlabelle.com"]
# The map method collects whatever values are returned from each iteration of
# the block. Then, an array of those values is returned from the method. In this
# case, we're reassigning the sites variable to the new array.
# There's a better way to do this, though. Several Ruby methods have
# duplicates with the exclamation mark (or bang); this means they are
# destructive: they replace the value they are working on. So the above
# could be done this way:
sites.map! { |site_prefix| "#{site_prefix}.jonlabelle.com" }
##
# The method "<<" is commonly used with arrays. It appends a value to its receiver.
##
ages = []
for person in @people
ages << person.age
end
##
# Ruby has a shortcut for creating an array of words:
##
a = [ 'ant', 'bee', 'cat', 'dog', 'elk' ]
# this is the same:
a = %w{ ant bee cat dog elk }
toast = Proc.new do
puts 'Cheers!'
end
toast.call
toast.call
toast.call
# Cheers!
# Cheers!
# Cheers!
doYouLike = Proc.new do |aGoodThing|
puts 'I *really* like '+aGoodThing+'!'
end
doYouLike.call 'chocolate'
doYouLike.call 'ruby'
##
# Methods Which Take Procs
##
def doSelfImportantly someProc
puts 'Everybody just HOLD ON! I have something to do...'
someProc.call
puts 'Ok everyone, I'm done. Go on with what you were doing.'
end
sayHello = Proc.new do
puts 'hello'
end
sayGoodbye = Proc.new do
puts 'goodbye'
end
doSelfImportantly sayHello
doSelfImportantly sayGoodbye
# Everybody just HOLD ON! I have something to do...
# hello
# Ok everyone, I'm done. Go on with what you were doing.
# Everybody just HOLD ON! I have something to do...
# goodbye
# Ok everyone, I'm done. Go on with what you were doing.
def doUntilFalse firstInput, someProc
input = firstInput
output = firstInput
while output
input = output
output = someProc.call input
end
input
end
buildArrayOfSquares = Proc.new do |array|
lastNumber = array.last
if lastNumber <= 0
false
else
array.pop # Take off the last number...
array.push lastNumber*lastNumber # ...and replace it with its square...
array.push lastNumber-1 # ...followed by the next smaller number.
end
end
alwaysFalse = Proc.new do |justIgnoreMe|
false
end
puts doUntilFalse([5], buildArrayOfSquares).inspect
puts doUntilFalse('I'm writing this at 3:00 am; someone knock me out!', alwaysFalse)
# [25, 16, 9, 4, 1, 0]
# I'm writing this at 3:00 am; someone knock me out!
##
# Methods Which Return Procs
##
def compose proc1, proc2
Proc.new do |x|
proc2.call(proc1.call(x))
end
end
squareIt = Proc.new do |x|
x * x
end
doubleIt = Proc.new do |x|
x + x
end
doubleThenSquare = compose doubleIt, squareIt
squareThenDouble = compose squareIt, doubleIt
puts doubleThenSquare.call(5)
puts squareThenDouble.call(5)
# 100
# 50
##
# Passing Blocks (Not Procs) into Methods
##
class Array
def eachEven(&wasABlock_nowAProc)
isEven = true # We start with "true" because arrays start with 0, which is even.
self.each do |object|
if isEven
wasABlock_nowAProc.call object
end
isEven = (not isEven) # Toggle from even to odd, or odd to even.
end
end
end
['apple', 'bad apple', 'cherry', 'durian'].eachEven do |fruit|
puts 'Yum! I just love '+fruit+' pies, don't you?'
end
# Remember, we are getting the even-numbered elements
# of the array, all of which happen to be odd numbers,
# just because I like to cause problems like that.
[1, 2, 3, 4, 5].eachEven do |oddBall|
puts oddBall.to_s+' is NOT an even number!'
end
# Yum! I just love apple pies, don't you?
# Yum! I just love cherry pies, don't you?
# 1 is NOT an even number!
# 3 is NOT an even number!
# 5 is NOT an even number!
# So to pass in a block to eachEven, all we had to do was stick the block
# after the method. You can pass a block into any method this way, though
# many methods will just ignore the block. In order to make your method not
# ignore the block, but grab it and turn it into a proc, put the name of the
# proc at the end of your method's parameter list, preceded by
# an ampersand (&). So that part is a little tricky, but not too bad, and
# you only have to do that once (when you define the method). Then you
# can use the method over and over again, just like the built-in methods
# which take blocks, like each and times. (Remember 5.times do...?)
def profile descriptionOfBlock, &block
startTime = Time.now
block.call
duration = Time.now - startTime
puts descriptionOfBlock+': '+duration.to_s+' seconds'
end
profile '25000 doublings' do
number = 1
25000.times do
number = number + number
end
puts number.to_s.length.to_s+' digits' # That is, the number of digits in this HUGE number.
end
profile 'count to a million' do
number = 0
1000000.times do
number = number + 1
end
end
# 7526 digits
# 25000 doublings: 0.246768 seconds
# count to a million: 0.90245 seconds
# Article: Ruby for Newbies: Working with Classes
# http://net.tutsplus.com/tutorials/ruby/ruby-for-newbies-working-with-classes/
##
# Modifying a Blank Object
##
o = Object.new
def o.my_method
1 + 1
end
print o.my_method
# Properties
def o.set_name ( name )
@name = name
end
def o.get_name
@name
end
o.set_name "Andrew"
print o.get_name # => Andrew
##
# Creating a Class
##
class Person
# the GET method for name
def name
@name
end
# the SET method anem
def name= name
@name = name
end
end
p = Person.new
p.name = "jon"
print p.name
class Person2
# Constructor
def initialize (name, age, job = 'unemployed')
@name = name
@age = age
@job = job
@@count += 1
end
# Pass attr_accessor the names of your properties as symbols. This creates
# the var and var= methods for you. Then, you'll be able to use the
# @ versions wherever you need to in your code.
attr_accessor :name, :age, :job
# If you want read-only or write-only properties, you can use attr_reader
# or attr_writer, respectively. Of course, if you use, say, attr_writer,
# you can then create a custom reader method if needed.
# Creating Class Methods and Variables
@@count = 0
# EXPOSE THE COUNT CLASS VARIABLE
# Note – Ruby doesn't really have class methods (or static methods, as some
# languages call them). There's actually a pretty cool bit of "magic" going
# on under the surface that makes these look like class methods. We'll get
# into that—usually called metaprogramming—in a future chapter.
def self.count
@@count
end
protected
def m2 # this method is protected
puts 'Hello'
end
# Private methods are functions that can only be called by other functions
# within the class; they aren't exposed to the outside world. The usual way
# to create private methods is this: under all your public code (the
# instance and class methods), add the keyword private. Any functions that
# follow this keyword are private.
private
def get_real_weight
@weight
end
end
# p2 = Person2.new("jon labelle", 33, "programmer")
# print p2.job
# print p2.get_real_weight
joe = Person2.new("Joe", 35, "plumber")
jill = Person2.new("Jill", 13)
bob = Person2.new "Bob", 70
print Person2.count # => 3
##
# Working with Dates
##
require 'date'
aNewDate=Date::new(2004,8,12) # year, month, day
aNewDate::to_s # gives u the saved date as a string
aNewDate=aNewDate::+1 # increases by 1 day
aNewDate=aNewDate::-1 # decreases by 1 day
day=aNewDate::wday() # gives the day of the week as a number, sun=0, mon=1....
week=aNewDate::cweek() # gives the week of year as a number
toDaysDate=Date::today() # creates a Date object with todays date
##
# Ruby Ternary Operator
##
x = 10
puts x == 10 ? "x is ten" : "x is not ten"
# Or.. an assignment based on the results of a ternary operation:
LOG.sev_threshold = ENVIRONMENT == :development ? Logger::DEBUG : Logger::INFO
##
# Regular Expressions
##
def replace_underscores_with_space(val)
return val.gsub(/_{1,2}/, ' ')
end
def remove_first_three_int_and_space(val)
return val.gsub(/A[0-9]{1,3}s/i, '')
end
def remove_extra_spaces(val)
return val.gsub(/s{2,}/i, ' ')
end
def search_and_replace(searchSubject, searchFor, replaceWith)
return searchSubject.sub(searchFor, replaceWith)
end
def fix_dashes(val)
return val.gsub(/-/i, ' - ')
end
##
# Use ranges instead of complex comparisons for numbers in Ruby
##
# No more if x > 1000 && x < 2000 nonsense. Instead:
year = 1972
puts case year
when 1970..1979: "Seventies"
when 1980..1989: "Eighties"
when 1990..1999: "Nineties"
end
##
# File system
##
# Open a file and read its contents
file='GettysburgAddress.txt'
f = File.open(file, "r")
f.each_line { |line|
puts line
}
f.close
# or
file='GettysburgAddress.txt'
File.readlines(file).each do |line|
puts line
end
# append text to a file.
open('myfile.out', 'a') { |f|
f.puts "Hello, world."
}
# Read, write file sample
#
# The following code is a very simple example of how to use Marshal. At the
# beginning of the program, if a file called counter exists in the current
# directory, it will open it and load an object from it. This object will be a
# number, which will be incremented and then stored back in the file. This is a
# clone of a common "Hello World" PHP script,
# to implement a counter for a web page.
counter = if File.exists?('counter')
File.open('counter') do|file|
Marshal.load(file)
end
else
0
end
puts "Counter is currently at #{counter}"
puts "incrementing to #{counter + 1}"
counter += 1
File.open('counter','w') do|file|
Marshal.dump(counter, file)
end
# Remove repeated lines in a file
def squeeze
ARGV.each{ |f|
fl = File.new(f).readlines
fl.uniq!
esc = File.open("#{f}","w")
fl.each{ |l| esc.print l}
esc.close
}
end
# `Require` every file from directory
Dir['lib/*.rb'].each{ |f|
require f
}
# Bulk rename files
basepath = "/mp3/Billboard Hot 100 (08.10.2011)"
Dir.chdir(basepath)
Dir.glob("*.mp3") { |filename|
file = File.new(filename)
# remove the first 3 numbers and dash from start of filename
new_filename = filename.gsub(/Ad{1,3}/, '')
puts "Renaming #{filename} to #{new_filename}..."
File.rename(filename, new_filename)
}
# Recursive delete
def recursive_delete(dir)
files = []
Dir.foreach(dir) do |fname|
next if fname == '.' || fname == '..'
path = dir + '/' + fname
if File.directory?(path)
puts "dir #{path}"
recursive_delete(path)
else
puts "file #{path}"
files << path
end
end
files.each do |path|
puts "delete file #{path}"
#File.delete(path)
end
puts "delete dir #{dir}"
Dir.rmdir(dir)
end
# Parse an XML element
def parseSingleElement(filename, elementName)
(REXML::Document.new(File.new(filename))).elements[elementName].text
end
# `which` command Ruby equivalent
def which(name)
ENV['PATH'].split(':').map { |p| File.join p, name }.find { |p| File.file? p and File.executable? p }
end
# Simple encryption class
#
# This class allows you to encrypt strings using Rinjdael encryption routine
# provided in the ruby crypt library (http://crypt.rubyforge.org/installation.html).
# The encrypted string can also be passed in the url.
require 'crypt/Rijndael'
class Crypto
def initialize
key="12345678"
@@r = Crypt::Rijndael.new(key)
end
def encrypt(str)
str=str.to_s
if str.length > 16
raise "string length greater that 16 bytes is not supported"
end
(16-str.length).times {str += '~'}
a = @@r.encrypt_block(str)
[a].pack('m').tr('+','@')
end
def decrypt(str)
str=str.tr('@','+')
str = str.unpack('m')
a = @@r.decrypt_block(str[0])
a = a.tr('~','')
end
end
##
# Ruby file and system utils
##
def sh command
system command
unless $?.success?
log "failed: #{command}"
exit 1
end
end
def write_host_entry ip, host
add_line_to_file "/etc/hosts", "#{ip} #{host}"
end
def add_line_to_file path, line
original_lines = File.read(path).each_line.map{|l| l.chomp}
if original_lines.include?(line)
log "already added line to '#{path}': #{line}"
else
log "adding line to '#{path}': #{line}"
sh "echo '#{line}' >> #{path}"
end
end
def add_line_to_bash_profile line
add_line_to_file("/etc/profile", line)
system "source /etc/profile"
end
def package_installed? package_name
installed = `dpkg-query -W --showformat='${Status}n' #{package_name}` =~ /install ok installed/
log "#{package_name} already installed..." if installed
installed
end
def add_package_source source
add_line_to_file "/etc/apt/sources.list", "deb http://debian.datastax.com/community stable main"
@updated_apt_cache = false
end
def ensure_package package_name
unless package_installed? package_name
unless @updated_apt_cache
sh "apt-get update -y --fix-missing"
@updated_apt_cache = true
end
sh "apt-get install -y #{package_name}"
end
end
def log line
puts "[vosbox] #{line}"
end
def ensure_file path, content
path = File.expand_path(path)
if File.exist?(path) && File.read(path) == content
log "exists: #{path}"
else
log "create: #{path}"
File.open(path, "w"){|f| f.write(content)}
sh "chown vagrant:vagrant #{path}"
end
end
def ensure_symlink from, to
if File.symlink?(to)
log "symlink already exists: #{from} => #{to}"
else
log "creating symlink: #{from} => #{to}"
sh "ln -s #{from} #{to}"
end
end
def ensure_directory directory
if Dir.exist? directory
log "directory exists: #{directory}"
else
log "creating directory: #{directory}"
sh "mkdir -p #{directory}"
sh "chown -R vagrant:vagrant #{directory}"
end
end
def ensure_file_copy from, to
sh "cp #{from} #{to}"
end
def ensure_gem gem
if Gem.available? gem
log "gem '#{gem}' already installed..."
else
sh "gem install #{gem} --no-rdoc --no-ri"
end
end
# Resources
# - http://pleac.sourceforge.net/pleac_ruby/