This is the start of The Odin Project’s instruction of Ruby as a language.
Basic Data Types
Introduction
Ruby is a STRONG OOP language, where everything is indeed an object, even basic data types even the primitive types(integers, floats, strings, symbols, and Booleans).
Numbers
Ruby’s numerical/math operators are the common + - * /
. But to find the exponent of a number,
aka the square, use **
and the modulo is the regular %
.
The two main types of numbers in Ruby are Integers and Floats.
Like Python, Ruby will return an integer when working with integers and a float when a float is brought into the mix.
For example:
19 / 4 # => 4 Since this is integer division it returns an int
19 / 4.0 # => 4.75
Converting number types
It’s very easy to convert number types. Ruby uses these underscore methods that are easy to recall.
- To convert an integer to a float use the method
to_f
- To convert a float to an integer…
to_i
167.8.to_i # => 167
167.to_f # => 167.0
Notice how Ruby DOESN’T round up numbers, the decimals are simply cut off.
Very useful number methods
- Use
.even?
to determine a number is even. - Use
.odd?
to determine a number is…odd. - To get the floor of a float
.floor
- To get the ceiling of a float
.ceil
Strings
I always have a hard time explaining strings but there’s nothing to it. Strings are just sequences of characters…I.e. text. We write strings in single or double quotation marks, these are known as StringLiterals. The difference between double and single quotations is small, a notable difference is String Interpolation, more on that later, and escape characters ONLY work in doubles.
String Concatenation
Like Python, get used to Python comparisons as they’re both OOP and share some philosophies, there’s a few ways to do this.
There’s the old timey overloaded +
operator. But there’s also a method, .concat
and some cool arrow wizardry.
"His" << "Name Is" << "BOB"
"He's".concat(" very awesome with ").concat("the Ladies")
Substrings
Since strings are sequences and they’re also objects like everything else…you can access substrings from a string. By indexing into them like an array. If you know C, then you’d know this is because every string is actually just an array of characters.
"Hello"[0] #=> "H" this is by index 0
"Hello"[0..1] #=> "He" this is range by indices, 0 to 1
"Hello"[0..3] #=> "Hell"
"Hello"[0...3] #=> "el"
"Hello World"[0, 4] # => "Hell" this syntax starts from index 0 with a range of 4
This is done by slicing.
String#[]
(also aliased asString#slice
) returns a slice copied fromself
.String#[]=
returns a copy ofself
with a slice replaced.String#slice!
returnsself
with a slice removed. Each of the above methods takes arguments that determine the slice to be copied or replaced. The arguments have several forms. For stringstring
, the forms are:string[index]
.string[start, length]
.string[range]
.string[regexp, capture = 0]
.String#[]
(also aliased asString#slice
) returns a slice copied fromself
.String#[]=
returns a copy ofself
with a slice replaced.String#slice!
returnsself
with a slice removed. Each of the above methods takes arguments that determine the slice to be copied or replaced. The arguments have several forms. For stringstring
, the forms and there syntax are:string[index]
.string[start, length]
.string[range]
.string[regexp, capture = 0]
.string[substring]
.
Quickly creating an array of strings
We can use this syntax to quickly create an array of strings array_name = %W(elements)
The elements MUST be written without any quotation marks and separated with whitespace.
strings = %W(a b BOB JIM Prime) #=> ["a", "b", "BOB", "JIM", "Prime"]
Escape Characters
These allow you to write representations of whitespace chars and include quotes inside a string without messing with it. Remember they only work in double quotation marks. Here’s a bunch of them
\\ for backslash
\b for backspace
/r for carriage return
\n for newline char
\s for space
\t for tab
\" for double quote "
\' for single quote '
String Interpolation
String Interpolation allows us to manipulate strings that have placeholder variables in them. Similar to JS ${}
notation or Python f'hello, {name}'
.
Here’s an example:\
name = "Bob"
puts("He is the best! And his name is #{name}")
# => "He is the best! And his name is Bob"
String methods
- To capitalize a string
.capatalize
- To query a string for a substring use
.include?(substring)
- To convert to uppercase use
.upcase()
- To convert to lowercase use
.downcase()
- To determine if a string is empty,
.empty?
- Use
.legnth()
to find the length of a string - If you want to reverse a string, it’s easy thanks to
.reverse()
- Split a string can be using
.split(split_char)
this returns an array of the split strings
"Hello BOB!".split() #=> ["Hello", "BOB"] default split char is white space \s
"Hello BOB!".split("") #=> ["H", "e", "l..."B","O","B] since the split char is an empty string it splits every char in the string
- To strip a string of whitespace, use
.strip()
by default it strips trailing white space but can pass args to change that. We’ll have a look later.
For now, here’s a few other methods we’ll explore further later on.
"he77o".sub("7", "l") #=> "hel7o"
"he77o".gsub("7", "l") #=> "hello"
"hello".insert(-1, " dude") #=> "hello dude"
"hello world".delete("l") #=> "heo word"
"!".prepend("hello, ", "world") #=> "hello, world!"
Like most languages, we can convert other types to strings when needed by using the .to_s
method. For example,
nil.to_s #=>""`
48.1.to_s #=> "48.1"
Symbols
This is something new to me, it sounds familiar but not sure…
The gist of it, strings are mutable, and therefore every time one is used or changed in Ruby, it needs to be stored in memory as a separate value, even if an identical string is already in memory. However, Symbols are only stored in memory once. This makes them faster in some instances than conventional strings.
A common convention is to use Symbols as keys in hashes. We’ll focus on these more later on.
To create a symbol, simply declare it like any other variable BUT with a colon at the beginning, this signals to Ruby that this is a symbol. :my_name = "Bob"
.
"string" == "string" #=> true
"string".object_id == "string".object_id #=> false
:symbol.object_id == :symbol.object_id #=> true
By using the object ID, .object_id()
method, similar to Python’s .id()
method, it returns a numeric value that’s used to identify the object in memory.
DO NOT make the noobish mistake of thinking the == checks that two objects are identical, it merely checks they’re equal in value!
To create an array of symbols you can do
symbols = %i(a b c) # => [:a,:b:,c]
Booleans
Like most languages, Ruby has boolean representation. Using true
and false
.
To represent nothing at all, we use nil
similar to None/none in many other languages.
Variables
Variables, often referred to as ‘vars’, allow us to assign a ‘label’ of sort to some data. we’ve seen this before, if not in code then in math.
Declaring variables
Declaring a variable in Ruby is pretty much the same as in Python. Simply type out the variables name and assign it a value.
For example: my_age = 23
Variable can also be reassigned values of operation, my_age = 23 + 1
We can use the basic arithmetic operators to update the value of our variable. The noobs would do my_age = my_age + 1
but instead try the following:
my_age = 23
my_age += 1 #=> 24
my_age -= 1 #=> 22
my_age *= 2 #=> 44
my_age /= 4 #=> 11
Naming variables (naming conventions)
Variable names should easily describe what the variable represents. Avoid Hungarian naming convention! Here’s a list of guidelines, but treat them as rules:
- No single char names
- No abbreviated names
- No data types
- use snake_case for multi_word_names
Variables are references
Treat variables as pointers to some bit of data in memory. This could cause some quirky side effects at times.
my_age = 23
his_age = my_age
his_age += 3
puts(his_age) # 26
puts(my_age) # 23
Some methods have unwanted side effects.
One way to modify a string is to use the upcase!
method, instead of the safe upcase
method. If the string is modified using johns_location.upcase!
then desired_location
will also reflect that change:
johns_location.upcase! #=> "BARCELONA"
desired_location #=> "BARCELONA"
johns_location #=> "BARCELONA"
This example may be hard to completely understand at this point in the lesson. The important concept is that assigning variables to other variables can have unintended side effects. Just because you can do it, doesn’t mean you should. You will have the opportunity to revisit this example in one of the following assignments.
Getting variable values from the user
One way to get a value from user input, via stdin, is the gets
method, which is short for ‘get string’. However since entering a value in the console will also append a ‘\n’ to the value we can daisy-chain the .chomp method to truncate that. `your_name = gets.chomp
Variable scope
Scope is an important thing in programming, it refers to the scope in which variable are defined. Three common scopes are:
- Block scope: in which a variable is only defined and accessible within that block, outside of that block it doesn’t exist.
- Method-definition scope: refers to variables defined within a method, they’re accessible from anywhere within that method’s definition and runtime.
- Class scope: Are class-level variables accessible from within all scopes in a class, these are often knowns as class fields or instance variables. More on these later on when we discuss OOP.
Remember, inner scopes have access to outer scope variables but not the other way around. So a global variable is accessible from block scopes. Please DO NOT use global variables, it leads to ugly side effects and reeks of unprofessionalism.
\
Constants
constants are immutable variables, their values can’t be changed post declaration, and are declared using ALL_CAPS Funny enough Ruby will only give you a warning if you try to change the value of a constant instead of outright stopping you, but you really shouldn’t! Constants are available throughout the app’s scope.
Global Variables
These are declared like normal variables but with a $
prefix, like $admin = me
. They’re also available throughout the entire app and override any scope restrictions. Most Ruby devs avoid global vars.
Class Variables
Are variables declared within a class using the prefix @@
like @@car_wheel_number = 4
. These variables are accessible to all instances of the class and the class itself. They’re used when we need a variable that’s related to the class but not every instance of the class needs its own value of it. Think of them as common variables that all the instances share…
NOTE: Class variables are rarely needed and often misused.
Instance variables
These are variables declared using a single @
prefix, like @car_width = 189.45cm
. These are accessible throughout the current instance of the parent class, i.e. every instance of this class has its own value for this variable. This is a common OOP principle.
This is what’s known as a InstanceVariable and must be initialized at the class level outside any method definitions. These are mutable.
Useful Variable methods:
- Replacing variable values in memory:
.replace
changes the value in memory that a variable points to. This is different from simply reassigning a new value to variable, as this simply points it to another block in memory entirely. use the.object_id
method to test this out.
Input & Output
Output commands
There’s a few ways to output values to the user, the two most common are the print
and puts
commands.
The two are very similar but puts
appends a newline char to the end of the argument passed to it, while print
keeps it all in one line.
Both commands return nil
.
To illustrate this try:
print "My name is Bob."; print "I am learning Ruby!"
# "My name is Bob.I am learning Ruby!"=>nil
puts "My name is Bob."; puts "I am learning Ruby!"
# "My name is Bob."
# "I am learning Ruby!"
# => nil
Notice how we chained commands using the semicolon? This is good for experimenting and for short examples but is not recommended in other cases.
When an array is passed to puts
, every element in the array is outputted individually on a new line. Whereas print will output the array in its entirety on a single line.
puts [1, 2]
1
2
print [1,2]
[1,2]
puts [1, nil, nil,2]
1
2
Another thing to note is that puts
converts everything, when it can, to a string so nil values become empty lines
The puts
command can be shortened to p
for some sweet syntax-sugar.
But p
is also good for showing ‘raw’ versions of the object passed to it as it returns its argument rather than nil
and displays invisible chars and other details, which makes it good for debugging.
The putc
command will output a single character to the console, so you need to specify otherwise it outputs the first char of a string.
Input commands
To accept input from a user via the console we can use a command we mentioned earlier, the gets
command.
your_age = gets.to_i # this will convert the input from a string to an integer
your_age # <value> as int
Unlike print
and puts
, the gets
actually returns a value, not a surprise…
Another quick reminder to use the .chomp
method for string values to truncate the ‘\n’.
Conditional logic
In a nutshell? The famous IF statement!
if good_programmer ==true
hire_person()
end
Every conditional statement needs a predicate/expression that evaluates to true or false, and from there the control-flow logic determines what happens next.
Truthy & Falsy
This concept refers to what Ruby determines is True or False. Unlike other languages, such as Python, Ruby ONLY considers false
and nil
to be falsy values, NOTHING ELSE, not even 0 nor empty strings "".
This makes things tricky and risky, we have to be very careful when evaluating conditions.
Basic If statement syntax
Not much to it.
if condition
do_something
end
In the case of there being only one line inside the if block, we can write this statement in the following way
do_something if condition
Adding else and elsif
When dealing with a single condition, we can opt to have a route for the control-flow to go should the condition fail by adding else
.
But sometimes we want to check the first condition, if it fails, check the second using elsif
, and if that fails go down the else
route.
if condition_one
do_first
elsif condition_two
do_second
else
do_last
end
Note that we can have as many elsif
statements we want but I don’t recommend more than two or three, as that’s bad practice and is where the switch comes in.
Boolean logic
To determine true from false, we need a comparison operator. Luckily there are a few in Ruby.
Operators:
The common and simple == that’s used in many languages, and checks if two VALUES are the same.
It’s negated sister operator, the != to check they’re not the same value.
The mathematical >, >=, <, <=
, you don’t need me to explain these…
Methods:
- The
.eql?
method checks if two objects are of the same value AND type.5.0.eql?(5) # false
- The .equal? method checks if two objects are the EXACT same object in memory.
a = 5 b = 5 a.equal?(b) # => true c = "Bob" d = "Bob" c.equal?(d) # => false, due to how computers store strings in memory...
The Ruby special, the SpaceshipOperator
- Returns -1 if the value on the left is less than the one on the right
- Returns 0 if the value on the left is equal to the one on the right
- Returns 1 if the value on the left is greater than the one on the right
1 <=> 4 # -1
4 <=> 4 # 0
69 <=> 0 # 1
This operator is commonly used in sorting operations.
Logical Operators
You’ll need these when you want to write an expression that has more than one condition. The two main ones are AND and OR, these are represented in Ruby as &&
and ||
respectively.
if not_tired && weather_good # both expressions must be true
puts "Let's go for a walk"
elsif have_chocolate || have_pistachios # at least one needs to be true
puts "Stay home and bake cookies!"
else
puts "Maybe just read a book..."
ShortCircuitEvaluation Logical operators are always evaluated from left to right, so when using && if the first condition is false then there’s no point evaluating the second; but with | | if the first is true, then no point checking the second…
Using the !
negates any condition.
Case statements
Are cool for when you need to write several conditions that would otherwise be a very ugly and messy if statement. These are called switches in other languages. Can be used to assign a value to a variable for later use!
Case-statements process each case one by one, in turn, and when a case is false, it checks the next one, but when it hits a case that is true, it processes what’s in that case. An else
can be added as an option if no case is matched.
lunch = "liver sandwiches"
excited_for_lunch = case lunch
when "liver sandwiches" then true
when "pizza" then true
when "ramen" then false
when "kale salad"
puts "I can't have just a salad for lunch!"
false
else
"Guess I'll have a snack later..."
Notice how we can ditch the then
keyword when we have more than a one liner.
Unless
Like the if statement, but negated. The unless
will only process its block when the condition evaluates to false.
I.e. will execute included code block Unless condition is true…
unless excited_for_lunch
puts "Hungry and mood is down"
else
"Enjoy lunch!"
Ternary operator
This is a one-line if-else statement that’s meant to make code look more concise but personally find that’s not always the case…
The syntax is conditional_statement ? <execute if true> : <execute if false>
if lunch == "couscous"? puts "Lunch was so delicious!" : puts "Lunch was okay, I guess"
Loops in Ruby
Like other languages Ruby has loops. Here’s a bunch of them.
Types of Loop
- The
loop
which keeps going on infinitely until a break condition is reached
i = 0
loop do
puts "Bob says #{i}'"
i += 1
break if i == 69
end
- The
while
loop needs no introduction
i = 0
while i <= 69 do
puts "Bob says #{i}'"
i += 1
end
- The
until
loop which is the negated version of thewhile
, it’ll keep running until the condition is true.
i = 0
until i >=69 do
puts "Bob says #{i}'"
i += 1
end
Ranges
Sometimes we know exactly how many times we want something to run, so we can brute this by making a counter of sorts or use a range.
(1..5) # inclusive range: 1, 2, 3, 4, 5
(1...5) # exclusive range: 1, 2, 3, 4
# We can make ranges of letters, too!
('a'..'d') # a, b, c, d
- The
for
loop allows you to do something while iterating through a collection of info such as an array or range.
#silly example of iterating over a range in a for loop
for i in (1..10) do
puts "Ma name BOB"
end
- The
times
loop allows running a loop a specific number of times… And also allows keeping track of the current ‘index’ in the iteration
10.times do
puts "Hello"
end
2.times do |number| # keeps track of time or index it's iterating through
puts "Number #{number}, please come forward to the kiosk"
end
- The
upto
anddownto
loops that work exactly as they sound like they do
#from 10 upto 30
10.upto(30) do |number|
puts "#{number / 10}"
end
#from 20 down to 2
20.downto(2) {|num| puts "#{number * 2}"}
Basic Data structures
Arrays in Ruby
- In Ruby arrays are ordered collections of data.
- Arrays can contain elements of different types, such as number, strings, booleans, Ruby objects, and even other data structures. But some advise to keep a single data type per array.
To create an array simply declare an array literal, it’s variable name, and place elements withing brackets like so my_numbers = [1,2,3,2,4]
.
Notice how we can have repetition? Arrays don’t demand unique elements within.
Another way to create one is Array.new()
Array.new #=> []
Array.new(3) #=> [nil, nil, nil]
Array.new(3, 7) #=> [7, 7, 7]
Array.new(3, true) #=> [true, true, true]
Accessing Array elements
This is done by indexing into the desired element, remember indices start at zero not one!
my_numbers[2]
will yield the third element in the array, whereas my_numbers[-2]
will return the second to last element!
Ruby also provides two nifty array methods to access elements, and these are Array.first(n)
and Array.last(n)
and they return a new array containing the first or last n elements.
str_array = ["This", "is", "a", "small", "array"]
str_array.first #=> "This"
str_array.first(2) #=> ["This", "is"]
str_array.last(2) #=> ["small", "array"]
Adding and removing elements
We can add to an array using a number of ways.
- The
push
method, used to add an element to the end of an array, and returns the new array - The shovel operator
<<
, does the same. - The
unshift
method adds elements to the start of an array and returns the new one. To remove elements we can use: - The
pop
method to remove the last element from an array and returns that element. - The
shift
method does the same but to the element at the start of an array. NOTE: Bothpop
&shift
take integer arguments to specify the number of elements they target. For example:
num_array = [1, 2, 3, 4, 5, 6]
num_array.pop(3) #=> [4, 5, 6]
num_array.shift(2) #=> [1, 2]
num_array #=> [3]
Adding and subtracting Arrays
We can add arrays, where the end result simply combines both arrays. This can be done using the + or the concat
method.
first_array = [1,2,2]
second_array = [3,5,8]
third_array = first_array + second_array # => [1,2,2,3,5,8]
fourth_array = first_array.concat(second_array) # => [1,2,2,3,5,8]
Subtracting arrays, yields the difference between them.
first_array = [1,8,2,"Hi"]
second_array = [3,5,8,"Hi","bye"]
third_array = first_array - second_array # => [1,2]
fourth_array = second_array - first_array # => [3,5,"bye"]
Basic Array methods
To find the list of methods available, try my_array.methods
[].empty? #=> true
[[]].empty? #=> false
[[]][0].empty? #=> true
[1, 2].empty? #=> false
[1, 2, 3].length #=> 3
[1, 2, 3].reverse #=> [3, 2, 1]
[1, 2, 3].include?(3) #=> true
[1, 2, 3].include?("3") #=> false
[1, 2, 3].join #=> "123"
[1, 2, 3].join("-") #=> "1-2-3"
Hashes
These are data structures that employ a key-value approach, where every value has an associated key. Keys are always unique but values can repeat. Similar to JS objects or Python dictionaries. This makes finding values in the hash easier and faster than going through an array to find a value. The order of the key-value pairs bares no consequence.
Creating a hash in Ruby
To create a hash simply declare a hash literal like so:
my_hash = {
"a random word" => "beach",
"Dorothy's math test score" => 94,
"an array" => [1, 2, 3],
"a hash within a hash" => {"key" => 69}
}
Notice how the hash rocket ⇒ is used to indicate the value of every key, unlike the : commonly used in JS and Python.
You can also use the Hash.new
method but this will initialize an empty hash to be populated.
Accessing values in a hash
To access values simply use brackets and pass the Key of the value as an argument
my_hash["an array"] #=> [1,2,3]
my_hash["a hash within a hash"]["key"] # => 69
my_hash["nothing"] # => nil
Accessing a key that doesn’t exist will return nil
, which can sometimes cause problems to arise.
So we use the fetch
method, which will throw a 'KeyError: Key not found: <key>'
Adding and changing values
To add a new value or change one, call the key and assign a value using =; if the key already exists its value gets updated instead of creating a new key with that value.
my_hash["name"] = "Bob" #=> This creates a new key with the value "Bob"
my_hash["name"] = "Sara" #=> This updates the value of the "name" key to "Sara"
Removing data from a hash
To delete a key value pair, use the delete method and pass it the key who’s value you intend to delete.
However, note that the key-value pair will be deleted not just the value.
my_hash.delete("name")
Methods
- The
keys
method returns an array of the keys in a hash - The
values
method returns an array of the values in a hash. - The
merge
method, merges two hashes together. In the case of identical keys being present in both hashes, the method overwrites the value of that key with the value in the hash that’s passed as an argument (the second hash). For example:
hash1 = { "a" => 100, "b" => 200 }
hash2 = { "b" => 254, "c" => 300 }
hash1.merge(hash2) #=> { "a" => 100, "b" => 254, "c" => 300 }
hash2.merge(hash1) # => { "a" => 100, "b" => 200, "c" => 300 }
Symbols as hash keys
It is common to see symbols used as hash keys in real-world Ruby code, because symbols are more performant than strings in Ruby and therefore make fetching values faster BUT mostly it’s to allow for much cleaner syntax when defining hashes.
menu = {
:appetizer => "Shrimp in garlic butter",
:appetizer2 => "Beef tartar",
:main1 => "Hyderabad Biryani",
:main2 => "Bazin",
:dessert => "Kenefe Nabilsiya"
}
# Another Symbol syntax for hashes
menu2 = {
appetizer: "Spring Rolls",
appetizer2: "Cajun popcorn chicken",
main1: "Lamb roast",
main2: "Pad Thai",
dessert: "Chocolate Souffle"
}
When using symbols as keys in Ruby hashes, we can ditch the hash rocket for a colon which is nicer and more common among OOP languages BUT this only works for symbols as keys!
Methods
In many a language, we can often assign a ‘name’ to blocks of our code, allowing us to reuse them by invoking that name. This is what some languages call a function or procedure. In an OOP language, when a function is tied to an object it’s referred to as a method. However, Ruby is different. Because everything in Ruby is an object, every ‘function’ is actually a method. Here’s an explanation from The Ruby Programming Language book:
“Many languages distinguish between functions, which have no associated object, and methods, which are invoked on a receiver object. Because Ruby is a purely object-oriented language, all methods are true methods and are associated with at least one object.”
So when speaking of Ruby methods, I mean functions…
The notation for methods in documentation is the pound signs (#
), they’re just the convention for writing out Ruby instance methods. Used to write out the full name of an instance method, e.g., Integer#upto
, or just the method name, e.g., #upto
, depending on the context.
Methods are called by invoking them on an instance of the object containing them using like so object_instance.method_name
. This isn’t new you’ve seen this before.
There are also build in methods such as the puts
and print
methods that are simply called without dot notation.
Creating a method
To create one follow this basic structure
def method_name
"Does some stuff" # funny that this 'example' logic is also the return value of the method
end
puts method_name # => "Do some stuff"
Breakdown:
def
is a built-in keyword that tells Ruby that this is the start of a method definition.method_name
is the name of your new method. You can name your methods almost anything you want, but there are some constraints and conventions, which are described in the next section."Does some stuff"
is the code inside the method body. All of the logic for your method is contained in the indented lines of the method body. This particular method returns a string when the method is called.end
is a built-in keyword that tells Ruby that this is the end of the method definition.- To call the method, you need to use its name, as shown in the last line of the example.
Method names
Don’t pick bad names, just like with variables, your method names should be concise and descriptive. Focus on making them easy to understand and follow proper naming conventions.
Method names can use numbers, capital letters, lowercase letters, and the special characters _
, ?
, !
, and =. Just like with variable names in Ruby, the convention for a method name with multiple words is to use snake_case, separating words with underscores.
It’s good practice to start the method name with a lower-case character, because names that start with capital letters are constants in Ruby.
Here are some things you are not allowed to do with your method names:
- You cannot name your method one of Ruby’s approximately 40 reserved words, such as
end
,while
, orfor
. - You cannot use any symbols other than
_
,?
,!
, and =. - You cannot use
?
,!
, or = anywhere other than at the end of the name. - You cannot begin a method name with a number.
method_name # valid
_name_of_method # valid
1_method_name # invalid
method_27 # valid
method?_name # invalid
method_name! # valid
begin # invalid (Ruby reserved word)
begin_count # valid
Parameters and arguments
Some methods need input data to do their task, we call these parameters. Parameters are variables that your method will receive when it is called. You can have more meaningful and useful interactions with your methods by using parameters to make them more versatile.
def method_name(param_one)
"Does some stuff with #{param_one}" # funny that this 'example' logic is also the return value of the method
end
puts method_name("Nissan GTR")
The difference between a parameter and an argument is that parameters are placeholders in the methods ‘blueprint’, whereas Arguments are the actual variables (values) passed to the method when it’s called.
In the previous example, param_one
is the parameter and “Nissan GTR” is the argument.
Default parameters
If your method doesn’t ALWAYS need arguments passed to it, you can create default params.
def method_name(param_one = 'default value')
"Does some stuff with #{param_one}" # funny that this 'example' logic is also the return value of the method
end
method_name("Nissan GTR") # => "Does some stuff with Nissan GTR"
method_name() # => "Does some stuff with default value"
See how the method subs the default param’s value when no arguments are passed to it? This will prove very useful later on.
Method Return values
The previous method examples are simple with only one line of code within, but when a method’s block grows and becomes complex it may be difficult to understand what it returns.
In most languages, if an explicit return value
isn’t included in the method then it simply returns nothing.
Ruby is different, it allows for explicit and implicit returns. The previous example method, method_name
, implicitly returns the 'Does some stuff with default value'
.
Which means that a Ruby method will return the last expression that was evaluated even without the return
keyword. The last expression that was evaluated may or may not be the last line in the code, as you can see in the following example:
def even_odd(number)
if number % 2 == 0
"That is an even number."
else
"That is an odd number."
end
end
puts even_odd(16) #=> That is an even number.
puts even_odd(17) #=> That is an odd number.
The return value is dependent on the result of the if condition.
Even though Ruby offers the ease of using implicit returns, explicit returns still have a place in Ruby code. An explicit return (using the keyword return
) essentially tells Ruby, “This is the last expression you should evaluate.” This example shows how using return
STOPS the method from continuing:
def my_name
return "Joe Smith"
"Jane Doe"
end
puts my_name #=> "Joe Smith"
This is very useful for when you want a method to check for input errors before continuing, like so:
def even_odd(number)
unless number.is_a? Numeric
return "A number was not entered."
end
if number % 2 == 0
"That is an even number."
else
"That is an odd number."
end
end
puts even_odd(20) #=> That is an even number.
puts even_odd("Ruby") #=> A number was not entered.
Difference between puts and return
A common source of confusion for new programmers is the difference between puts
and return
.
puts
is a method that prints whatever argument you pass it to the console.return
is the final output of a method that you can use in other places throughout your code.
This example only prints the value but returns nil
def puts_squared(number)
puts number * number
end
Whereas this one returns it as it’s the last evaluated value
def return_squared(number)
number * number
end
Method chaining (Daisy chaining)
This is one of the coolest thing in high-level languages and especially OOP ones. Where we call one method after another in the same chain using the handy dot notation.
bobs_bits = ["He", " is", "the", "Best", "he's", "BOB"]
puts bobs_bits.join(" ").upcase() # => HE IS THE BEST HE'S BOB
Each ‘link’ in the chain builds on the value returned by the previous method, the strings are joined with spaces as the joining factor and then everything is cast to Upper case.
Predicate methods
In many a Ruby method, some you’ve seen, you’ll notice a ?
at the end of their names. This indicates that the return value of this method is a Boolean.
Examples include, .even?
, .odd?
, .is_a? Numeric
…etc.
You can apply this convention to your own methods that return Booleans.
Bang Methods
When a method is called on an object it doesn’t overwrite the original value. This is common in programming as you we don’t want to irreversibly overwrite our data by accident…
You can overwrite values by reassigning them.
Ruby offers a convention that forces overwriting the value of an object once a method is called upon it using the !
at the end of the method name.
whisper = "I KNOW YOUUU..."
puts whisper.downcase! #=> "i know youuu..."
puts whisper #=> "i know youuu..."
Exception Handling
Similar to how other languages have try-except or try-catch blocks to handle exceptions, RubyExceptions can be handled with begin-rescue
.
begin
num = 69/0
value = my_array["BOB"]
rescue ZeroDivisionError #the error that gets thrown by the Ruby interpreter
puts "Division by Zero error!!!"
rescue TypeError
puts "Wrong type used!"
rescue => e
puts "Caught error: #{e}"
end
- Can specifiy the exception/error to catch by writing its name after
rescue
. If not specified then will catch all errors… - Can store the error in a variable to log using ⇒
Raising exceptions
We can raise exceptions when we need to (provided we handle them somewhere) using the raise
keyword.
Example:
# Ruby program to illustrate
# use of raise statement
begin
puts 'This is Before Exception Arise!'
# using raise to create an exception
raise 'Exception Created!'
puts 'After Exception'
end
Code block credit GeeksForGeeks