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 as String#slice) returns a slice copied from self.
  • String#[]= returns a copy of self with a slice replaced.
  • String#slice! returns self 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 string string, the forms are:
  • string[index].
  • string[start, length].
  • string[range].
  • string[regexp, capture = 0].
  • String#[] (also aliased as String#slice) returns a slice copied from self.
  • String#[]= returns a copy of self with a slice replaced.
  • String#slice! returns self 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 string string, 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:

  1. No single char names
  2. No abbreviated names
  3. No data types
  4. 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

  1. 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
  1. The while loop needs no introduction
i = 0
while i <= 69 do
	puts "Bob says #{i}'"
	i  += 1
end   
  1. The until loop which is the negated version of the while, 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
  1. 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
  1. 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
  1. The upto and downto 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: Both pop & 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, or for.
  • 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