Professional Documents
Culture Documents
Introduction to Ruby
Ruby is a powerful, flexible programming language. Ruby is:
High-Level, meaning reading and writing Ruby is really easy-it looks a lot like regular English
Interpreted, meaning you don’t need a compiler to write and run Ruby. Interpreters directly
execute the code, compilers do not.
Object-oriented, meaning it allows users to manipulate data structures called objects in order
to build and execute programs. Everything in Ruby is an object.
Easy to use, Ruby was designed by Yukihiro Matsumoto, or “Matz”, in 1995. Matz set out to
design a language that emphasized human needs over those of the computer, which is why
Ruby is so easy to pick up.
Creating variables can be done like in python where you do not need to state the type of variable:
Ruby has the 6 basic arithmetic operators that most languages have:
The print command takes whatever you give it and prints it to the screen. It does not add a line
after it so you can print on the same line. The puts statement does the same as print but adds a
new line after. It is equivalent to System.out.println(); in Java
Because everything in Ruby is an object, everything in Ruby has certain built-in abilities called
methods. For instance strings have built-in methods that can tell you the length of the string,
reverse the string and more.
The interpreter is the program that takes your code you write and runs it. You type code in the
editor, the interpreter reads your code, and its shows you the result of running your code in the
console.
=begin
this is a multi-line comment
do not put anything or spaces after =begin otherwise it breaks comment
=end
Local variables, by convention, should start with a lowercase letter and words should be
separated by underscores, like counter or masterful_method. Ruby won’t stop you from
starting your local variables with other symbols, such as capital letters, $s, or @s, however it is
best you follow convention.
An If statement allows for you to run an expression and execute code depending on whether the
expression evaluates to true or false. Starts with ’if’ and ends with ‘end’.
'elsif’ is equivalent to else if, and you can have any amount of them. ‘else’ and ‘if’ only one.
if user_num < 0
puts "You picked a negative integer!"
elsif user_num > 0
puts "You picked a positive integer!"
else
puts "You picked zero!"
end
Sometimes you want to use control flow to check if something is false, rather than true.
You can reverse your if/else, but you can instead use an unless statement.
unless condition
# do if false
else
# do if true
end
Executes code if the condition is false, if true then the code in the else is executed
The .gsub(pattern, replacement) method, which stands for global substitution, can replace any
substring if it fits a regex pattern. Replaces all instances not just first.
string_to_change.gsub!(pattern, replacement)
A possible pattern could be /s/ which would change all instances where there is the letter ‘s’
string = “shelf”
string.gsub!(/s/, “b”) # string now contains “bhelf”
string = “cat”
string.gsub!(“at”, “ote”) # string now contains “cote”
Loops and Iterators
A while loop checks to see if a certain condition is true, and while it is, the loop keeps running.
while condition
# do while true
end
# example: prints 1 to 10
while counter < 11
puts counter
counter += 1
end
The until loop is a backwards while loop, in that it will continue to loop while false
i = 0
until i == 6
i += 1
end
puts i
Assignment operators:
+= -= *= /=
A for loop is used when you know how many times you will be looping such as in dealing with
arrays.
It is possible to repeat a task using an iterator. An iterator is just a Ruby method that repeatedly
invokes a block of code. The code block is just the bit that contains the instructions to be repeated.
The simplest iterator is the loop method:
In Ruby, curly braces {} are generally interchangeable with the keywords do and end.
Knowing this, we can write a smarter loop than the one above:
break if true
The next keyword can be used to skip over certain steps in the loop. For instance printing out only
odd numbers:
my_array = [1,2,3,4,5]
The .each method can apply an expression to each element of an object, one at a time.
object.each { |item|
# expression
}
# you can also use the do keyword instead:
object.each do |item|
#expression
end
array = [1,2,3,4,5]
array.each do |x| # for each element in the array add 10 to it and print
x += 10
print “#{x}”
end
The .times method is like a super compact for loop: it can be used to run code a specified number
of times. For example printing wow five times:
array = text.split(“,”)
Each element in an array has an index, the first being 0, then 1 and so on. We can access
elements of the array directly through these numbers using brackets like:
string_array = [“hi”,”hello”,”salutations”]
Arrays of Arrays are called multidimensional arrays, for example a two dimensional array:
array_2d = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
Hashes are sort of like python dictionaries, they are a collection of key-value pairs. Syntax:
hash = {
key1 => value1,
key2 => value2,
key3 => value3
}
Values are assigned to keys using =>, you can use any Ruby object for a key or value.
my_hash = {
"name" => "Eric",
"age" => 26,
"hungry?" => true
}
my_hash = Hash.new
Setting a variable equal to Hash.new creates a new, empty hash; same as setting the variable
equal to empty curly braces. It is used over curly braces as it is more concise.
We can add to a hash in two ways: if we created it using literal notation, we can simply add a new
key-value pair directly between the curly braces. If we used Hash.new, we can add to the hash
using bracket notation.
my_hash[key] = value
puts my_hash[key]
When we loop over an array or hash, we say that we iterate over it. We’ll be using the .each
iterator to iterate over arrays and hashes in this section.
Example:
When iterating over hashes, we need two placeholder variables to represent each key/value pair.
Example:
secret_identities = {
"The Batman" => "Bruce Wayne",
"Superman" => "Clark Kent",
"Wonder Woman" => "Diana Prince",
"Freakazoid" => "Dexter Douglas"
}
h=hash.new(default value)
puts h # {} printed
puts h[“any key”] # default value printed
Methods are defined using the keyword def, short for define. They have three parts:
1. The header, which includes the def keyword, the name of the method, and any arguments the
method takes.
2. The body, which is the code block that describes the procedures the method carries out. The
body is indented by convention.
3. The method ends with the end keyword.
Example:
def welcome
puts “Welcome to Ruby!”
end
If a method takes arguments, we say it accepts or expects those arguments. For example:
def square(n)
puts n ** 2
end
The argument is the piece of code you put between the method’s parentheses when you call it,
and the parameter is the name you put between the methods parentheses when you define it.
For example above we gave the method the parameter n and passed it the argument 12.
You can think of parameters as placeholders the method definition gives to arguments since it
doesn’t know what arguments it’s going to get.
Splat arguments are arguments that are preceded by a *, which tells the program that a method
can receive one or more arguments.
def double(n)
return n * 2
end
You can think of blocks as a way of creating methods that don’t have a name, similar to lambdas
in Python. Blocks can be defined with the keywords do and end or curly braces. Example:
1.times do
puts "I'm a code block!"
end
A method can take a block as a parameter, such as what .each method does. Passing a block to a
method is a great way of abstracting certain tasks from the method and defining those tasks when
we call the method.
my_array.sort!
my_array = my_array.sort
We can use an operator called the combined comparison operator to compare two Ruby objects.
The combined comparison operator looks like this: <=>. It returns 0 if the first operand equals the
second, 1 if the first operand is greater than the second, and -1 if the first operand is less than the
second.
puts book_1 <=> book_2 # prints 1, so the first book should go second
A block that is passed into the sort method should return -1 if the first block parameter should
come before the second, 1 if vice versa and 0 if equal.
The sort method assumes by default that you want to sort in ascending order, but it accepts a
block as an additional argument that allows you to specify how two items should be compared.
if 2
if “bacon”
false and nil are not equivalent, false means “not true”, while nil means “nothing at all”.
If you create your hash using the Hash.new syntax, you can specify a default like so:
If you try to access a non-existent key in my_hash, you’ll get “Trady Blix” as a result.
The .object_id method gets the ID of an object-it’s how Ruby knows whether two objects are the
exact same object.
You can think of a Ruby symbol as a sort of name. Symbols aren’t strings.
There’s a key behaviour of symbols that makes them different from strings. While there can be
multiple different strings that all have the same value, there’s only one copy of any particular
symbol at a given time.
Symbols always start with a colon : . They must be valid Ruby variable names, so the first
character after the colon has to be a letter or underscore.
:symbol
Symbols are primarily used as either hash keys or referencing method names. Symbols make
good hash keys for a few reasons:
1. They are immutable, meaning they can’t be changed once created.
2. Only one copy exists at a given time, so they save memory
3. Symbols-as-keys are faster than strings-as-keys because of reasons 1 and 2
:sasquatch.to_s
# ==> "sasquatch"
"sasquatch".to_sym
# ==> :sasquatch
arr = Array.new
arr.push(“www”) # array arr now contains “www”
Besides .to_sym, you can use .intern. This will internalize the string into a symbol and work just
like .to_sym
"hello".intern
# ==> :hello
The hash syntax, with the => symbol, is sometimes nicknamed the hash rocket style. This is
because => looks like a rocket.
Now you can type hashes like in python with the semi-colon at the end of the symbol and it
replacing the rocket. Example:
new_hash = {
one: 1,
two: 2,
three: 3
}
.to_a can convert a range into an array. .zip can combine two equal sized arrays or ranges into a
Hash. Benchmark object can be used to benchmark operations and methods etc.
string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]
string_time = Benchmark.realtime do
1000000.times { string_AZ["r"] }
end
symbol_time = Benchmark.realtime do
1000000.times { symbol_AZ[:r] }
end
This builds two alphabet hashes: one that pairs string letters with their place in the alphabet and
one that uses symbols instead of string letters. We then benchmark the two to compare how fast it
is to look up a key in each array.
We can use .select to filter a hash for values that meet certain criteria.
grades.select will return a hash containing :bob and :chris and their values.
To iterate over just keys or just values we can use .each_key and .each_value.
case language
when "Ruby"
puts "Web apps!"
else # ran if no other matches
puts "I don't know!"
end
CRUD, Create, Read, Update, Delete, are the four basic functions of persistent storage.
The Zen Of Ruby
As a language Ruby prioritizes programmer productivity over program optimization. This mean
that it will not always run a program in the fastest way possible but strives to be a language that
programmers find easy and fun to use.
You don’t need an end when you write your if statement all on one line.
You can do the same with the unless statement
The ternary conditional expression is an more concise version of if/else. It’s called ternary because
it takes three arguments: a Boolean, an expression to evaluate if the Boolean is true, and an
expression to evaluate if false.
case language when "Ruby" then puts "Web apps!" else puts "I don't
know!" end
The conditional assignment operator ||= assigns a value to a variable if it hasn’t already been
assigned.
Ruby’s methods will return the last evaluated expression if there is no return statement.
def add(a,b)
return a + b
end
#and
def add(a,b)
a + b
end
Ruby has short-circuit evaluation, which means that it doesn’t look at both expressions unless it
has to.
It we want to repeat something a specific number of times we can use the .times method. If we
want to repeat an action for every element in a collection we can use .each.
We can use .upto and .downto to print out a specific range of values:
.repsond_to takes a symbol and returns true if an object can receive that method and false
otherwise. For example,
[1, 2, 3].respond_to?(:push)
Would return true as you can call .push on an array object. However
[1, 2, 3].respond_to?(:to_sym)
.next will return the integer immediately following the integer its called on, meaning 4.next will
return 5.
Instead of typing out the .push method name, you can simply use <<, the concatenation operator,
also known as the shovel to add an element to the end of an array:
[1, 2, 3] << 4
# ==> [1, 2, 3, 4]
drink = "espresso"
"I love " + drink
# ==> I love espresso
"I love " << drink
# ==> I love espresso
But if you want to do it for non-string values, you have to use .to_s to make it a string:
age = 26
"I am " + age.to_s + " years old."
# ==> "I am 26 years old."
"I am " << age.to_s << " years old."
# ==> "I am 26 years old."
A better way is to use string interpolation which works with all types:
Refactoring is improving the structure or appearance of our code without changing what it does.
The Refactor Factory
Blocks, Procs, and Lambdas
A Ruby block is just a bit of code that can be executed. Blocks can be combined with methods like
.each and .times to execute an instruction for each element in a collection, like a hash or array.
The collect method takes a block and applies the expression in the block to every element in an
array and returns it.
my_nums = [1, 2, 3]
my_new_nums = my_nums.collect { |num| num ** 2 }
# my_new_nums ==> [1, 4, 9]
A method ending with ! will always change the value of the object, not return a copy.
Methods that accept blocks have a way of transferring control from the calling method to the block
and back again. We can build this into the methods we define by using the yield keyword.
def block_test
puts "We're in the method!"
puts "Yielding to the block..."
yield
puts "We're back in the method!"
end
def yield_name(name)
puts "In the method! Let's yield."
yield("Kim")
puts "In between the yields!"
yield(name)
puts "Block complete! Back in the method."
end
blocks are not objects, and this is one of the very few exceptions to the “everything is an object”
rule in Ruby. Because of this blocks can’t be saved to variables and don’t have all the powers and
abilities of a real object.
A proc is like a “saved” block: you can name a block and turn it into a proc. Procs are great for
keeping your code DRY, which stands for Don’t Repeat Yourself. With a proc you only need to
write your code once and can use it many times.
To define a proc, you call Proc.new and pass in the block you want to save then assign those to a
variable.
We can than pass the proc to a method that would otherwise take a block, and we don’t have to
rewrite the block over and over. We do this with the & character which converts the proc into a
block.
[1, 2, 3].collect!(&cube)
# ==> [1, 8, 27]
[4, 5, 6].map!(&cube)
# ==> [64, 125, 216]
1. Procs are full-fledged objects, so they have all the powers and abilities of objects.
2. Unlike blocks, procs can be called over and over without rewriting them.
By mapping &:to_i over every element of strings, we turned each string into an integer!
Like procs, lambdas are objects. Lambdas are almost identical to procs in exception of syntax and
a few behavioural quirks.
1. A lambda checks the number of arguments passed to it, while a proc does not.
This means that a lambda will throw an error if you pass it the wrong number of arguments,
whereas a proc will ignore unexpected arguments and assign nil to any missing.
2. When a lambda returns, it passes control back to the calling method; when a proc returns, it
does so immediately, without going back to the calling method.
.is_a? checks if a method is of a certain type
Object-Oriented Programming 1
A basic class consists only of the class keyword and the name of the class.
class NewClass
# Class magic here
end
By convention, class names start with a capital letter and use CamelCase instead of underscores
Initialize method is the constructor of the class and is equivalent to main method in java.
class Person
def initialize
#code
end
end
In Ruby, we use @ before a variable to signify that it’s an instance variable. This means that it
belongs to the instance of the class not the class itself.
def initialize(name)
@name = name
end
We can create an instance of a class just by calling .new on the class name, like so:
me = Person.new("Eric")
The scope of a variable is the context in which it’s visible to the program. You have global
variables, available everywhere, local variables, only in certain methods, class variables, belongs
to class, and instance variables, belongs to specific instances of the class.
Global variables start with $, instance variables start with @, class variables start with @@
class MyClass
$my_variable = "Hello!" #global variable
end
puts $my_variable
Global variables can be changed from anywhere in your program and are generally not a very
good idea. It’s much better to create variables with limited scope that can be changed from few
places. Global variables can be used to count how many instances of your class there is for
example.
Inheritance is the process by which one class takes on the attributes and methods of another, and
it’s used to express a is-a relationship. In Ruby inheritance works like this:
The derived class is the new class you’re making and the base class is the class from which the
new class inherits. You read “<” as “inherits from”.
To override a method simply define the method in your class with the exact same name.
The interpreter will use your method over the parent method.
You can directly access the attributes or methods of a superclass with the super keyword.
When you call super from inside a method, that tells Ruby to look in the superclass of the current
class and find a method with the same name as the one from which super is called. If it finds it,
Ruby will use the superclass version instead of the method.
Any given Ruby class can have only one superclass. Some languages allow a class to have more
than one parent, which is a model called multiple inheritance.
If you want to end a Ruby statement without going to a new line, you can just type a semicolon.
Like: class Monkey; end
This is a time saver when you’re writing something very short, like an empty class or a method
definition.
Virtual Computer
time.now returns the current time
A class method belongs to the class itself, and as such it’s prefixed with the class name.
Object-Oriented Programming 2
Ruby allows you to explicitly make some methods public and private. Public methods allow for an
interface with the rest of the program. Private methods cannot be accessed outside of the class.
Methods are public by default in Ruby. However we want to make it clear which methods are
public. We do this by putting public before our method definitions. Like:
class ClassName
# Some class stuff
public
def public_method
# public_method stuff
end
end
Everything after the public keyword to the end keyword will be public unless we say otherwise.
Private methods are private to the object where they are defined. This means that you can only
call these methods from the other code inside the object. The method cannot be called with an
explicit receiver. These are the objects on which methods are called. In object.method, object is
the receiver of method.
In order to access private information we need to create public methods that know how to get it.
This separates the private implementation from the public interface. So you create getter and
setter methods to validate all data that goes in and out.
Ruby needs methods in order to access attributes. We can use attr_reader to access a variable
and attr_writer to change it. If we write:
class Person
attr_reader :name
attr_writer :name
def initialize(name)
@name = name
end
end
def name
@name
end
def name=(value)
@name = value
end
We can now read and write values as we please. We just pass our instance variables as symbols
to attr_reader or attr_writer. name= is just a Ruby convention saying “hey, this method sets a
value!”
You can think of modules as being very much like classes, only modules can’t create instances
and can’t have subclasses. They’re just used to store things. Example of a module Circle:
module Circle
PI = 3.141592653589793
def Circle.area(radius)
PI * radius**2
end
def Circle.circumference(radius)
2 * PI * radius
end
end
You can pull in pre-existing modules, but you can also make your own. You just use the module
keyword, like so:
module ModuleName
# Bits 'n pieces
End
It doesn’t make much sense to include variables in modules, since variables change. Constants
however are supposed to always stay the same, so including helpful constants in modules is a
great idea.
Ruby doesn’t make you keep the same value for a constant once its’s initialized, but it will warn
you if you try to change it. Ruby constants are written in ALL_CAPS and are separated with
underscores if there’s more than one word.
One of the main purposes of modules is to separate methods and constants into named spaces.
This is called namespacing, is it’s how Ruby doesn’t confuse Math::PI with Circle::PI.
The double colon we used is called the scope resolution operator, which tells Ruby where you’re
looking for a specific bit of code. If we say Math::PI, Ruby knows to look inside the Math module to
get that PI, not any other PI.
require ModuleName
Any class that includes a certain module can use those module’s methods!
include ModuleName
A nice effect of this is that you no longer have to prepend your constants and methods with the
module name. Sine everything has been pulled in, we can simply write PI instead of Math::PI
When a module is used to mix additional behaviour and information into a class, it’s called a mixin.
Mixins allow us to customize a class without having to rewrite code.
Whereas include mixes a module’s methods in at the instance level, allowing instances of a
particular class to use the methods, the extend keyword mixes a module’s methods at the class
level. This means that class itself can use the methods as opposed to instances of the class.
module ThePresent
def now
puts "It's #{Time.new.hour > 12 ? Time.new.hour - 12 :
Time.new.hour}:#{Time.new.min} #{Time.new.hour > 12 ? 'PM' : 'AM'}
(GMT)."
end
end
class TheHereAnd
extend ThePresent
end
TheHereAnd.now
Banking on Ruby
class Account
attr_reader :name
attr_reader :balance
private
def pin
@pin = 1234
end
private
def pin_error
return "Access denied: incorrect PIN."
end
public
def display_balance(pin_number)
if pin_number == pin
puts "Balance: $#{@balance}."
else
puts pin_error
end
end
public
def withdraw(pin_number, amount)
if pin_number == pin
@balance -= amount
puts "Withdrew #{amount}. New balance $#{@balance}."
else
puts pin_error
end
end
end
checking_account = Account.new("heloow",150000)