RUBY

@ALL NOTE OR PROGRAM ARE COLLECTED FROM SOLOLEARN

Welcome to Ruby!



Ruby is a dynamic, object-oriented, general-purpose programming language.
It is ranked among the top 10 programming languages worldwide. Much of its growth is attributed to the popularity of software written in Ruby, particularly the Ruby on Rails web framework.

A quote from its creator, Yukihiro "Matz" Matsumoto: "Ruby is simple in appearance, but is very complex inside, just like our human body."

Matsumoto has said that Ruby is designed for programmer productivity and fun, following the principles of good user interface design.

In Ruby, everything (even a simple number) is an object. We will learn more about objects in the coming lessons.
Ruby is also completely free. Not only free of charge, but also free to use, copy, modify, and distribute.

Hello, Ruby!



Let's create our very first Ruby program, the classic "Hello World" program.
For this, we use the built-in puts method.
puts "Hello World"Try It Yourself

This code will output the text "Hello World" to the screen.
All text values (strings) must be enclosed in single or double quotes.


Hello, Ruby!



Another method that can be used to display output to the screen is print.
For example:
print "Hello World"Try It Yourself

This code displays the same output as before, except that the puts automatically adds a new line after the output, while print does not.
For example:
puts "Hi"
print "there"
print "Ruby"
Try It Yourself

Result:Hi
thereRuby

Tap Try It Yourself to play around with the code!

Comments



Comments are lines of annotation within Ruby code that are ignored at program runtime.
In Ruby, the hashtag symbol is used to create a single-line comment.
For example:
# printing some text
puts "Hi there"
Try It Yourself


Comments



You can also create multi-line comments.
Everything between the =begin and =end reserved words are considered a comment:
=begin
This comment
spans multiple
lines
=end
puts "Hello"
Try It Yourself

Adding comments to your code is a good practice. It facilitates a clear understanding of the code for you and for others who read it.


Variables



variable is a named storage location for a value. It is called variable because the information stored in that location can be changed when the program is running.
To assign a variable a value, use the equal sign.
For example:x = 8
This assignment statement declares a variable named x and gives it the value 8. The equal sign is called the assignment operator.
We can later use the variable name to access its value. For example, to output the value stored in the variable, we can use puts or print and refer to the variable name:
x = 8
puts x

# outputs 8
Try It Yourself


Constants



Variables beginning with a capital letter are called constants.
The value of a constant variable cannot be changed once it has been assigned.
For example:
MyNum = 42

# Trying to change the value produces a warning

MyNum = 8
# warning: already initialized constant MyNum

Data Types



All variables in Ruby can be of all data types.
Ruby automatically determines data type by the value assigned to the variable.
For example:x = 42 # integer
y = 1.58 # floating point value
z = "Hello" # string

You can reassign a different value to a variable at any time.
To insert the value of a variable into a double quote string (a string is a sequence of characters, such as "Hello"), use the # symbol and curly brackets with the variable name.
For example:
age = 42
puts "He is #{age} years old"

# outputs "He is 42 years old"
Try It Yourself

Doing Math



Math is an important part of programming. Ruby supports the following arithmetic operators:
x = 5
y = 2

# Addition
puts x+y
# outputs 7

# Subtraction
puts x-y
# outputs 3

# Multiplication
puts x*y
# outputs 10

# Division
puts x/y
# outputs 2
Try It Yourself

When you divide two integer values, the result will be an integer, as shown in the above example. If you want to have a floating point result, one operand must be a floating point value:
x = 5.0
y = 2
puts x/y # outputs 2.5



Modulus Operator



The modulus operator, represented by the percentage symbol (%), represents the remainder of a division operation.
For example:
x = 9
y = 5
puts x%y
# outputs 4
Try It Yourself

9 divided by 5 is 1 with a remainder of 4.

Exponent Operator



The ** represents the exponent operator for raising a number to a power to perform exponentiation.
For example:
a = 2
b = 5
puts a**b
# this raises 2 to the power of 5 and outputs 32
Try It Yourself

The result is 32, as 2*2*2*2*2 = 32.
All operators can also be used with floating point values.

Shorthand Assignment Operators



All of the arithmetic operators have corresponding shorthand forms for assignment.
For example, a = a + 8 can be written as a += 8.
The same applies to the other operators:x += y # x=x+y
x -= y # x=x-y
x *= y # x=x*y
x /= y # x=x/y
x %= y # x=x%y
x **= y # x=x**y

These are called self-assignment operators, as they perform an assignment and an arithmetic operation at the same time.

Parallel Assignment



Ruby also supports parallel assignment of variables. This enables multiple variables to be initialized with a single line of code.
For example:x = 10
y = 20
z = 30
may be more quickly initialized using parallel assignment:
x, y, z = 10, 20, 30Try It Yourself

Parallel assignment is also useful for swapping the values held in two variables:
a, b = b, a

Operator Precedence



Ruby evaluates a mathematical expression using an order of operations that is based on operator precedence. Exponentiation has the highest precedence followed by multiplication, division, and modulus from left to right, and then addition and subtraction from left to right.
You can change the order of operations by using parentheses.
For example:
x = (3+2) * 4
puts x
# outputs 20
Try It Yourself

Tap Try It Yourself to play around with the code!

Strings



As mentioned in the previous lessons, a string is text between single or double quotation marks.
However, some characters can't be directly included in a string. For instance, single quotes can't be directly included in a single quote string, because this would designate the end of the string. Characters like these can be included in a string by using an escape sequence, which is indicated by a backslash (\):
text = 'Ruby\'s syntax is fun'
puts text
# outputs Ruby's syntax is fun
Try It Yourself

string formed with double quotation marks can also include the \n escape sequence, which represents a new line.
text = "Hello \n World"
puts text

# Outputs:
# Hello
# World
Try It Yourself

Only the \' and \\ escape sequences can be used with single quote strings.

String Interpolation



You can embed any Ruby expression inside a double quote string using #{ }, just as you did with variable names. Ruby evaluates the placeholders and replaces them with values:
a = 5
b = 2
puts "The sum is #{a+b}"
# outputs "The sum is 7"
Try It Yourself

Note that there is no space between the hash mark (#) and the opening curly brace ({). In case of a space, it will be interpreted as literal text.

Concatenation



Strings can be joined using the + in a process called concatenation.
When concatenating strings, it doesn't matter whether they've been created with single or double quotes.
a = "Hi "
b = 'there'
puts a+b
# outputs "Hi there"
Try It Yourself

Even if your strings contain numbers, they are still added as strings rather than integers. Adding a string to a number produces an error, because even though they might look similar, they are two different entities: "1" is a string, whereas 1 is an integer.

Repeating a String



Strings can be repeated using the * and an integer value.
The order of the string and the integer does matter: the string has to come first.
For example:
a = "abc"
puts a*3
# outputs "abcabcabc"

puts '5'*4
# outputs 5555
Try It Yourself

Strings can't be multiplied by other strings.

Input



To get input from the user in Ruby, you use the gets method, which returns what the user types as a string. To store the input for later use, you can assign the return value to a variable.
For example:
x = gets
puts x
Try It Yourself

gets gets a line of text, including the new line at the end. If you do not want to include the new line, use the gets.chomp method:
puts "Enter your name"
name = gets.chomp
puts "Welcome, #{name}"
Try It Yourself

The value of the input is a string. To convert it to an integer, you can use the gets.to_i method.


Booleans



In Ruby, there are two Boolean values: true and false.isOnline = true

userIsAdmin = false

Another value that you will often encounter is nil. It shows the absence of value.

Booleans



If you try to evaluate a value other than true or false as a Boolean, Ruby will automatically treat it as a Boolean.
When this is done, a non-Boolean value that evaluates to true is called "truthy" and a non-Boolean value that evaluates to false is called "falsey".

In Ruby only false and nil are falsey. Everything else is truthy (even 0 is truthy).
Only true and false are Booleans. nil is not a Boolean. 0 is not a Boolean. The string "Hello" is not a Boolean. However, in a context where a Boolean is expected, Ruby evaluates them as Boolean (truthy and falsey).

Comparison



Boolean comparison using the == operator returns true when two operands are equal, and false when they are not:

a = 5
b = 8

puts a == b # false

puts a == 5 # true
Try It Yourself

Be careful not to confuse assignment (one equals sign) with comparison (two equals signs).

Comparison



Another comparison operator, the not equal operator (!=), evaluates to true if the items being compared aren't equal, and false if they are.
For example:

a = 8
b = 7

puts a != b # true
Try It Yourself

Tap Try It Yourself to play around with the code!

Comparison



Ruby also has operators that determine whether one value is greater than or less than another. These operators are > and < respectively. Similarly, the greater than or equal to, and less than or equal to operators are >= and <=.
For example:

puts 12 > 8 # true

puts 5 < 2 # false

puts 5 >= 5.0 # true

puts 3 <= 6 # true
Try It Yourself

There is also the .eql? method, which results in true only if both arguments have the same type and equal values.
For example:

puts 3 == 3.0 # true
# but
puts 3.eql?(3.0) # false
Try It Yourself

3.eql?(3.0) is false because 3 is an integer and 3.0 is a float.
Greater than and less than operators can also be used to compare strings lexicographically (the alphabetical order of words is based on the alphabetical order of their component letters).

if Statements



You can use an if expression to execute code when a certain condition holds.
If a conditional expression evaluates to true, the if code is carried out. Otherwise, the code is ignored.
For example:

a = 42

if
a > 7
puts "Yes"
end

# outputs "Yes"
Try It Yourself

The condition a > 7 is evaluated. When it is true, the statements inside the if are executed and the program outputs Yes.
You can have multiple statements inside a single if expression.
The end keyword is required to indicate the end of the if.

if Statements



if expressions can be nested, one inside the other.
This means that the inner if is the code part of the outer one. This is one way to see whether multiple conditions are satisfied. Keep in mind that once an if condition fails, the entire block is exited.

num = 16
if num > 7
puts "Greater than 7"
if num < 42
puts "Between 7 and 42"
end
end
# outputs:
# Greater than 7
# Between 7 and 42
Try It Yourself

Each if has to have a corresponding end.

else Statements



An else block in an if expression contains code that is called when the if conditional evaluates to false.
For example:

age = 15
if age > 18
puts "Welcome"
else
puts "Too young"
end

# outputs "Too young"
Try It Yourself

The end keyword is only needed for the if statement, as the else block is part of the if expression.


elsif Statements



The elsif (short for else if) block is useful when you want to test multiple conditions.
A series of if elsif expressions can have a final else block, which is called if none of the if or elsif expressions are true.
For example:
num = 8
if num == 3
puts "Number is 3"
elsif num == 10
puts "Number is 10"
elsif num == 7
puts "Number is 7"
else
puts "Not found"
end
Try It Yourself

When an elsif block executes the entire if expression is exited.


unless



The unless expression is the opposite of an if expression. It executes code when a conditional is false.
For example:
a = 42
unless a < 10
puts "Yes"
else
puts "No"
end

# outputs "Yes"
Try It Yourself

You can use an else block with the unless, just like you did with the if expression. The end keyword is also required to close the block.

The if and unless modifiers can also be used to execute code.
a = 42
puts "Yes" if a > 10

puts "Yes" unless a < 10
Try It Yourself

This code before the if executes only if the condition evaluates to true.
The code before the unless executes only if the condition is false.
As you can see, Ruby code is comparably short and easy to read, making it a very intuitive programming language.

Logical Operators



Logical operators are used to form more complex criteria that test more than one condition for an if expression.
Ruby has three logical operators: and (&&)or (||), and not (!).
A conditional using and evaluates as true if, and only if, both of its operands are true. Otherwise, the entire conditional evaluates to false.
For example:

a = 42
b = 8
if a > 7 && b < 11
puts "Yes"
end

# outputs "Yes"
Try It Yourself

Ruby can use words instead of the logical operator symbols (andornot), but those have lower precedence and are generally avoided.

OR



The or (||) operator evaluates to true if either (or both) of its operands are true, and false if both operands are false.
For example:

a = 3
b = 5
if a ==3 || b > 10
puts "Welcome"
end

# outputs "Welcome"
Try It Yourself

Note that (||) and (or) are not the same and have different precedence in operations.

NOT



The not (!) operator reverses the state of a single operand.
The result of not true is false, and not false is true.

a = 7
puts !(a>5)

# outputs false
Try It Yourself

In this code, a>5 evaluates to true and then the not operator reverses it to false.
You can chain together multiple conditions with the logical operators to check for multiple conditions. Parentheses can be used to group together separate conditions for clarity and to control the order of operations. For example:
(a>b && b < 100) || (a<b && b > 100)

case Statements



As we have seen in the previous lessons, we can check for multiple conditions using the if/elsif/else expression.
A more simplified and flexible option is the case expression, which tests a value in when statements.
For example:

a = 2

case a
when 1
puts "One"
when 2
puts "Two"
when 3
puts "Three"
end

# outputs "Two"
Try It Yourself

You can have as many when statements as you need for a single case.
Note that the case expression must be closed with the end keyword.

case Statements



Multiple values can be tested within a single when by separating the values with commas.
For example:

age = 5

case age
when 1, 2, 3
puts "Little baby"
when 4, 5
puts "Child"
end

# outputs "Child"
Try It Yourself

If you forget to put a comma between two values, Ruby will return an error.

case Statements



An else statement can be provided to execute code if no when condition matches:

age = 18

case age
when 1, 2, 3
puts "Little baby"
when 4, 5
puts "Child"
else
puts "Not a baby"
end

# outputs "Not a baby"
Try It Yourself

Case statements allow us to more easily control program flow. If statements should always be used to determine if a conditional is true, and case statements are for when you need to make different decisions based on a value.

Loops



Loops are used to execute the same block of code a specified number of times.
The while loop executes a block of code while its condition is true.
For example:

x = 0
while x < 10
puts x
x += 1
end
Try It Yourself

This will output the numbers 0 to 9. After the loop control variable becomes 10, the condition x < 10 evaluates to false and the loop ends its execution.
If we leave out the x+=1 statement, the loop will run forever, because the condition will remain true. This situation is called an infinite loop.

until Loops



The until loop is the opposite of a while loop: it will run while its condition is false.
For example:

a = 0
until a > 10
puts "a = #{a}"
a +=2
end
Try It Yourself

This will print all even numbers from 0 to 10.

Ranges



range represents a sequence. 0 to 10, 56 to 82, and a to z are all examples of ranges.
Ruby has special operators to create ranges with ease.
These are the ''..'' and ''...'' range operators. The two-dot form creates an inclusive range, while the three-dot form creates a range that excludes the specified high value.
For example:

a = (1..7).to_a
puts a # [1, 2, 3, 4, 5, 6, 7]

b = (79...82).to_a
puts b # [79, 80, 81]

c = ("a".."d").to_a
puts c # [a, b, c, d]
Try It Yourself

The to_a method is used to convert a range to an array, so we can output it.
You will learn more about arrays in the coming lessons.

Ranges



Ranges can be used in case statements for when values.
For example:

age = 42

case age
when 0..14
puts "Child"
when 15..24
puts "Youth"
when 25..64
puts "Adult"
else
puts "Senior"
end
Try It Yourself

Ranges are also useful in for loops. You will learn about them in the coming lesson.

for Loop



The for loop is a useful statement when you need to loop over a specific set of values, for example, a range.
The for loop consists of an empty variable and a range. At each iteration of the loop, the empty variable will be assigned the corresponding element of the range.
For example:

for i in (1..10)
puts i
end
Try It Yourself

This will output the numbers 1 to 10.
During the first loop the variable i will be assigned the first value of the range, 1.
On the second loop, it will be assigned to the value 2, and so on, until the end of the range.
The for loop executes a block of code once for each element in the range.

break



The break statement can be used to stop a loop.
For example:

for i in 1..5
break if i > 3
puts i
end

# outputs:
# 1
# 2
# 3
Try It Yourself

The loop stops executing when the condition i > 3 is met.

next



Similarly, the next statement can be used to skip one iteration of the loop and continue with the next one.
For example:

for i in 0..10
next if i %2 == 0
puts i
end
Try It Yourself

This will output only the odd numbers from 0 to 10 because the even numbers will skip the loop iteration.
Ruby also has the redo statement, which causes the current loop iteration to repeat.
The retry statement causes the whole loop to start again from the beginning.

loop do



Another looping statement in Ruby is the loop do statement.
It allows code to execute until a break condition is achieved.
For example:

x = 0
loop do
puts x
x += 1
break if x > 10
end
Try It Yourself

This will print the numbers 0 to 10. When x > 10 evaluates to true, the loop will stop.
If we do not include a break condition, the loop will run forever.


Arrays



An Array is essentially a list of numbered items.
The items are declared in a comma-separated list and enclosed in square brackets.
For example:items = ["Apple", "Orange", "Banana"]
The code above declares an array named items containing three strings.

Each item has its own unique index with the first item at index zero.
To refer to an item, we need to refer to its index:
puts items[0]
# outputs "Apple"
Try It Yourself

You can assign any array element a new value by using its index:
items[1] = "Kiwi"Try It Yourself

This will assign the element with index 1 the value "Kiwi".
A negative index is assumed relative to the end of the array. For example, an index of -1 indicates the last element of the array, -2 is the next to last element in the array, and so on.

Adding Elements



An array can contain different types of elements:

arr = [5, "Dave", 15.88, false]

puts arr[0] # 5
puts arr[1] # "Dave"
puts arr[-1] # false
Try It Yourself

To add new elements to the array, you can use the << operator, which is typed as two less than signs:

arr << 8

puts arr
Try It Yourself

This will add an element with the value 8 to the end of the array.
Alternatively, you can use the push and insert methods (we will learn more about methods in the coming module. For now, just remember that a method is code that performs an action).

arr.push(8)Try It Yourself

This will add 8 to the end of the array.

The insert method allows you to insert the element at the desired position:

arr.insert(2, 8)Try It Yourself

The code above will insert an element with value 8 at the position with index 2.

Removing Elements



Similarly, there are pop and delete_at methods available to remove elements from the array.
The pop method removes the last element of the array.
For example:

arr = [1, 2, 3]
arr.pop
print arr
# [1, 2]
Try It Yourself

When used to output arrays, the puts method outputs one element per line, while print actually outputs brackets and a list of items.
You can use the delete_at method to specify the index of the element to be removed:

arr = [2, 4, 6, 8]
arr.delete_at(2)print arr
# [2, 4, 8]
Try It Yourself

Remember, the index starts counting at 0. So the third element in the array has the index 2.

Array Ranges



You can even access a range within the array using the range operators:

nums = [6, 3, 8, 7, 9]
print nums[1..3]
# [3, 8, 7]
Try It Yourself

Don’t forget that there are two types of range operators: (..) and (…). Use the correct one based on your case.

Combining Arrays



You can add two arrays together:

a = [1, 2, 3]
b = [4, 5]

res = a + b
print res # [1, 2, 3, 4, 5]
Try It Yourself

You can also subtract arrays, which will result in the first array removing any elements that also appear in second array.

a = [1, 2, 3, 4, 5]
b = [2, 4, 5, 6]

res = a - b
print res #[1, 3]
Try It Yourself

The elements of an array can be duplicated using the * operator and an integer value. For example: [1, 2, 3] * 3 results in [1, 2, 3, 1, 2, 3, 1, 2, 3]

Boolean Operations



The & operator returns a new array containing the elements common to the two arrays, with no duplicates.

a = [2, 3, 7, 8]
b = [2, 7, 9]

print a & b # [2, 7]
Try It Yourself

The | operator returns a new array by joining the arrays and removing duplicates.

a = [2, 3, 7, 8]
b = [2, 7, 9]

print a | b # [2, 3, 7, 8, 9]
Try It Yourself

Operators mentioned above are not the same as (&&) and (||) operators.

Moving Elements



The reverse method returns a new array containing the original array elements in reverse order.

arr = [5, 3, 8]
res = arr.reverse
print res # [8, 3, 5]
Try It Yourself

You can also reverse the array in place using the reverse! method:

arr = [1, 2, 3]
print arr.reverse!
Try It Yourself

If you use the reverse method without (!) and don’t save it to a new variable, it will just reverse it once and keep the original value.

Array Methods



There are a number of other useful methods available for manipulating arrays.
Here are some of the most used ones:
array.length or array.size returns the number of elements in array.
array.sort returns a new array with the elements sorted
array.uniq returns a new array with duplicate values removed from array.
array.uniq! removes duplicates in place.
array.freeze safeguards the array, preventing it from being modified.
array.include?(obj) returns true if obj is present in array, false otherwise.
array.min returns the element with the minimum value.
array.max returns the element with the maximum value.
Most of the methods also work for strings, which can be thought of as arrays of characters.
for loop is one way to iterate over an array of elements:

arr = ["a", "b", "c"]
for x in arr
puts "Value: #{x}"
end
Try It Yourself

This will loop through all the elements of the array and output them one per line.
Ruby has a more elegant way of iterating over array elements. You will learn about iterators in the coming lessons.

Hashes



Hashes (sometimes known as associative arrays, maps, or dictionaries) are similar to arrays in that they are an indexed collection of elements.
However, while you index arrays with integers, you can index a hash with anything.
That is very useful when you want to map values, for example: "name" => "David". Here, "name" is the index (also called the key) which is mapped to the value "David". So, hashes represent key=>value pairs.
A hash is created with comma separated keys and values inside curly brackets:

ages = { "David" => 28, "Amy"=> 19, "Rob" => 42 }

puts ages["Amy"]
# outputs 19
Try It Yourself

As you can see, the values are accessed using the same index syntax as with an array.
Compared with arrays, hashes have one significant advantage: they can use any object as an index, even an array. For example: { [1,"jan"] => "January" }

Symbols



In the previous example we used strings as keys for the hash, but Ruby has a more elegant and faster way for creating and accessing hash indexes than using strings.
Symbols are similar to strings, but they are immutable, meaning that they cannot be changed.
symbol is created using a colon and a name, for example:a = :id
In the code above :id is a symbol. You can also think of :id as meaning the name of the variable id, and plain id as meaning the value of the variable.
So why use symbols instead of strings? Using symbols not only saves time when doing comparisons, but also saves memory, because they are only stored once.

Hashes & Symbols



Uses of symbols includes using them as hash keys.
For example:

h = {:name=>"Dave", :age=>28, :gender=>"male"}

puts h[:age]
Try It Yourself

In the code above, symbols are used as keys for our hash.
A shorter way of writing the same code as above is the following:

h = {name:"Dave", age:28, gender:"male"}

puts h[:age]
Try It Yourself

In other programming languages hashes are called associative arrays or dictionaries.

Hashes



There are useful methods available for manipulating hashes:
hash.delete(key) removes the key-value pair from hash by key.
hash.key(value) returns the key for the given value in hash, nil if no matching value is found.
hash.invert creates a new hash, reversing keys and values from hash; that is, in the new hash, the keys from hash become values and values become keys.
hash.keys returns a new array with keys from hash.
hash.values returns a new array containing all the values of hash.
hash.length returns the length of hash as an integer.

For example:

car = {brand:"BMW", year:2016, color:"red", length:205}
car.delete(:length)
puts car.values

# outputs:
# BMW
# 2016
# red
Try It Yourself

Tap Try It Yourself to play around with the code!

Iterators



As we have seen in the previous lessons, we can loop over arrays and hashes using for loops.
Ruby provides more elegant looping methods called iterators. Iterators are used to create loops.
The each iterator is one of the most used ones:

arr = [2, 4, 6]

arr.each do |x|
puts x
end
Try It Yourself

The syntax might seem confusing at first, but you just need to remember the pipe symbols around the variable. The each iterator loops through all elements of the array and assigns the corresponding element to the variable inside the pipes with each iteration. This variable is called the block parameter.
We can, for example, calculate the sum of all elements:

arr = [2, 4, 6]
sum = 0
arr.each do |x|
sum += x
end

puts sum # 12
Try It Yourself

Tap Try It Yourself to play around with the code!

Iterators



The each iterator can also be used with hashes:

sizes = {svga:800, hd:1366, uhd:3840}

sizes.each do |key, value|
puts "#{key}=>#{value}"
end
Try It Yourself

In the example above, key and value are variables that get assigned to the corresponding values of the hash elements at each iteration. You can use any name for your variables.
The do and end keywords specify a block of code in Ruby.
After the opening of the block, we have the block parameters within pipes ( | | ).
Ruby provides a shorthand way of writing blocks: you can use curly braces to start and end code blocks.
So the above example can be written shorter as:

sizes = {svga:800, hd:1366, uhd:3840}

sizes.each { |key, value| puts "#{key}=>#{value}" }
Try It Yourself

The each iterator can also be used on ranges.
For strings, you can use the each_char iterator to iterate over the characters.

Iterators



There are also iterators available for numbers.
The times iterator executes a loop the specified number of times:

10.times do
puts "Hi"
end
Try It Yourself

The code above will print "Hi" 10 times.

Letter Frequency



Let's create a program that will count the frequency of letters (number of occurrences) in a given string.
First we need a string:# a sample text
text = "I am learning Ruby and it is fun!"

text.downcase!

The downcase! method is used to convert all letters in the string to lowercase.

Next, we will need a hash to hold the letters as keys and the frequency of the letters as their corresponding values.
For that we need to create an empty hash and set the default value for all values to 0.freqs = {}
freqs.default = 0

The default method is used to set the default value for the hash, meaning that any key that does not have a value assigned will be set to that value.

Letter Frequency



Next, we will need to iterate over each character in the string and calculate the number of occurrences in the hash. We can do that using the each_char iterator:text.each_char { |char| freqs[char] += 1}
During each iteration, the char variable is assigned the corresponding character in our text string and then the value of that character's frequency is incremented in the freqs hash.
So, for example, if the letter "c" appears twice in the text, freqs["c"] will be equal to 2 after the iterator executes.
So, freqs will hold all the characters of the string with their corresponding occurrence number.

To show a nice result output in an alphabetical order, we can create a range of all letters and print their corresponding frequencies:("a".."z").each {|x| puts "#{x} : #{freqs[x]}" }
We do this because not all letters of the alphabet are contained in our text.

The final code:

text = "I am learning Ruby and it is fun!"
text.downcase!
freqs = {}
freqs.default = 0

text.each_char { |char| freqs[char] += 1}

("a".."z").each {|x| puts "#{x} : #{freqs[x]}" }
Try It Yourself

With just 6 lines of code we created a fully working letter frequency counter!

Methods



In the previous lessons we have seen some examples of methods, such as the reverse method for arrays.
method is a set of statements that perform a specific task.
You can define your own methods to perform your desired tasks.
The def keyword is used to define a method.def say
puts "Hi"
end

The above code defines a method called "say" that performs a simple output operation, printing "Hi".
The method name should start with a lowercase letter, so it will not be confused with constants.

Calling Methods



Now that we have defined our method, we can call it by including its name in a line of code:

def say
puts "Hi"
end

say
# outputs "Hi"
Try It Yourself

You can call methods as many times as you need:

say
say
say
Try It Yourself

Methods should be defined before calling them, otherwise Ruby will raise an error.
You can also call methods inside other methods.

Parameters



method declaration can include parameters, which you define inside parentheses after the method name.
For example:

def sqr(x)
puts x*x
end

sqr(8)
# outputs 64
Try It Yourself

The defined sqr method has one parameter (called x) and outputs its square. You can name your parameters anything you like.
When calling the method, we "pass" the actual parameter value to the method using parentheses.
The actual parameter value is called an argument.
So, in the above example, x is the parameter of the method, while 8 is the argument.

Multiple Parameters



method can include multiple parameters, separated by commas.
For example:

def sum(a, b)
puts a+b
end

sum(7, 4)
# outputs 11

sum(88, 164)
# outputs 252
Try It Yourself

You can also pass variable arguments:

def sum(a, b, c)
puts a+b+c
end

x = 2
y = 42
z = 6
sum(x, y, z)
# outputs 50
Try It Yourself

Tap Try It Yourself to play around with the code!

Default Parameters



You can also set default values for the parameters, so that the method will still work even if you do not provide all the arguments.
For example:def sum(a, b=8)
puts a+b
end

Now our parameter b has a default value of 8.
If we pass just one argument, the method will use the value 8 as the second argument:

x = 5
sum(x)
# outputs 13
Try It Yourself

We can also pass two arguments as before, and the method will work as expected. The default value only comes into play when no value for that argument is provided.

Parameters



You can also leave off the parentheses when using methods.
For example, sum(x, y) can be written as sum x, y.

def sum x, y
puts x+y
end

sum 6, 9
Try It Yourself

This leads to more fluid reading of code, but sometimes it can be confusing.
Let's create a program that prompts the user to enter a name and then outputs a greeting based on the input.

def greet(name="")
if name==""
puts "Greetings!"
else
puts "Welcome, #{name}"
end
end

greet(gets.chomp)
Try It Yourself

We defined a method called greet that takes one parameter and outputs a message based on that parameter.
Then we called the greet method passing user input as the argument.
Reminder: gets.chomp is used to take user input and strip the newline at the end of the input.

Optional Parameters



You can also define methods that take any number of arguments using the following syntax:

def someMethod(*p) puts p
end

someMethod(25, "hello", true)
Try It Yourself

Now you can pass any number of arguments to the method.
In the code above p is an array. When you pass arguments to the method, they become elements of p.
If you call the method without any arguments, the array p will be empty.
This technique allows optional parameters for a method, for example:def myMethod(a, b, *c)
#some code
end

myMethod has two mandatory parameters and an optional one. Therefore, you can pass two or more arguments to the method.

Returning Values



Until now all the methods we have defined output values. Sometimes you need a method to perform an action and return the result so that the returned value can be used further in the program (for example, by assigning the return value to a variable).
The return keyword is used to return a value from a method.
For example:

def sum(a, b)
res = a+b
return res
end

x = sum(5, 23)
puts x
Try It Yourself

The sum method returns the sum of its parameters, which is then assigned to variable x.

Returning Values



You can return multiple values from a method by separating them with commas in the return statement.
For example:

def squares(a, b, c)
return a*a, b*b, c*c
end

arr = squares(2, 3, 6)
puts arr
Try It Yourself

The result is an array containing the returned values.

Even when you do not explicitly include a return statement in your method, Ruby always returns the evaluated result of the last line of the method that is executed.
For example:

def demo(a, b)
a = b-2
b = a-3
end

puts demo(5, 6)
# outputs 1
Try It Yourself

The above method will return the result of its last expression, which is b = a-3.
Any code in the method after a return statement will not execute, because a method stops executing once the return is executed.
For example:
def demo(a)
a = 5
return a
a = 9
end

The method above will return the value 5. The statement a=9 is ignored.

Chaining Methods



Because all methods return a value, we can chain multiple methods together, as well as chain methods with iterators.
For example:

def square(x)
x*x
end

square(4).times {puts "Hi"}
Try It Yourself

In the code above we chained our defined square method with the times iterator. As square(4) will return 16, the loop will run and output "Hi" 16 times.
If anywhere along the chain there is a nil or an error, the entire chained call will break down.

Methods as Arguments



We can also pass methods as arguments to other methods. The returned values of those methods will be used as the actual values for the arguments, for example:

def add(a, b)
a+b
end

def mult(a, b)
a*b
end

x = mult(add(2, 3), add(4, 7))
puts x
# outputs 55
Try It Yourself

We defined two methods, add and mult, and passed two calls to the add method as arguments to mult.
Lets break down the code:
add(2, 3) returns 5
add(4, 7) returns 11
so mult(add(2, 3), add(4, 7)) is the same as mult(5, 11) which returns 55.

Variable Scope



Scope defines where in a program a variable is accessible.
Ruby has four types of variable scope: localglobalinstance and class.

Local variables are local to the code construct in which they are declared. For example, a local variable declared in a method or within a loop cannot be accessed outside of that loop or method. Local variable names must begin with either an underscore or a lowercase letter.
For example:def calc(x)
y = 2
puts x*y
end

In the above code x and y are both local variables. They are accessible only inside the calc method.
If we try to access them outside of the method we will get an error:

def calc(x)
y = 2
puts x*y
end

puts y
# output: "undefined local variable or method 'y' "
Try It Yourself

The same applies to loops and iterators:arr = [1, 2, 3]
arr.each {|x| puts x}

x is a local variable available only in the iterator block.
You can have different variables with the same name in different scopes.

Global Scope



Global variables in Ruby are accessible from anywhere in the Ruby program, regardless of where they are declared. Global variable names are prefixed with a dollar sign ($).
For example:

$x = 42

def change
$x = 8
end

change
puts $x
# outputs 8
Try It Yourself

As you can see the $x global variable is accessible in the whole program.
Use of global variables is strongly discouraged. The problem with global variables is that, not only are they visible anywhere in the code, but they can also be changed from anywhere in the application. This can result in hard to find bugs.

Variable Scope



So why does scope exist? Why couldn't all variables be accessible everywhere so we wouldn't have to care about their scope?
First of all, there are naming issues: if you have a big program, you'd have to give all of your variables unique names to avoid conflicts. Imagine keeping track of thousands of variable names.
Then, there are access issues: it's very hard to track who changes what when everyone has the ability to do so.
Scope makes the program more predictable and secure.
The other two types of scope in Ruby are instance and class. We will learn about them in the next module.

Recursion



Another way of creating loops is recursion. It involves the concept of methods calling themselves. It is used to solve problems that can be broken up into easier sub-problems of the same type.

A classic example of a method that is implemented recursively is the factorial method, which finds the product of all positive integers below a specified number.
For example, 5! (5 factorial) is 5 * 4 * 3 * 2 * 1 (120). To implement this recursively, notice that 5! = 5 * 4!, 4! = 4 * 3!, 3! = 3 * 2!, and so on. Generally, n! = n * (n-1)!.
Furthermore, 1! = 1. This is known as the base case, as it can be calculated without performing any more factorials.
Below is a recursive implementation of the factorial method.

def fact(n)
if n<= 1
1
else
n * fact( n - 1 )
end
end

puts fact(5)
# outputs 120
Try It Yourself

The if n<=1 block acts as the base case. The base case is the exit condition of the recursion.
Ruby is fast! Try to run the above code for a larger number and see the output.

Recursion



Recursive methods can become infinite, just like infinite loops. These often occur when you forget to implement the base case.
Below is an incorrect version of the factorial method. It has no base case:

def fact(n)
n * fact( n - 1 )
end

puts fact(5)
# outputs "stack level too deep (SystemStackError)"
Try It Yourself

Remember, an important key concept with recursion is to define and include the base case that makes the recursion stop.

Object Oriented Programming



Ruby is a pure object-oriented language, which means everything in Ruby is an object. Even simple numbers, strings and Booleans are objects.

In programming, objects are independent units, and each has its own identity, just as objects in the real world do. An apple is an object; so is a mug. Each has its own unique identity. It's possible to have two mugs that look alike, but they are still separate, unique objects.

To create objects we use classes.
class defines the data and actions associated with an object, but is separate from the object itself. In other words, a class can be thought of as an object's blueprint, description, or definition. For example, there can be many cat objects of the single class Cat.
Just like in the real world, where you can use a blueprint to construct multiple buildings, in programming you can use the same class as a blueprint for creating multiple objects.

Classes



You can think of classes as basic outlines of what an object should be made of and what it should be able to do. (For example, an object of class Car should have a color, make and model, and be able to move).
A class in Ruby always starts with the keyword class followed by the name of the class. The name should always be in initial capitals. You terminate the class definition with the keyword end.
For example:class Person
end

The above code creates an empty class Person.
Now we need to define the variables and methods (also called data members) of the class. These data members describe the attributes of the objects (for example, a person can be 42 years old and male - these attributes are data members of the class Person).

Classes



There is a special initialize method available for classes which gets called when an object is created.
It is defined inside a class just like any other class method:class Person
def initialize
puts "Hi there"
end
end

The purpose of the initialize method is to initialize the class variables for a new object. (For example, when a Car object is created, the initialize method could set the number of tires to the value 4)
The initialize method is known as the constructor in other object-oriented programming languages.

Objects



After the class and the initialize methods are defined, we can create objects of that class by using the new method. The method new is predefined in the Ruby library and does not need to be added to your class definition.
Below is an example that creates two objects p1 and p2 of the class Person:

class Person
def initialize
puts "Hi there"
end
end

p1 = Person.new
p2 = Person.new

# Output:
# "Hi there"
# "Hi there"
Try It Yourself

Note the syntax for calling new: the class name followed by a dot and followed by the method name.
The code above outputs "Hi there" twice because we created two objects of the class, which each call the initialize method. Now, p1 and p2 are separate objects of the class Person.

Objects are also called instances of a class. The process of creating objects of a class is called instantiation.
Note that in computing objects aren't always representative of physical items. For example, a programming object can represent a date, a time, and a bank account.

Instance Variables



An instance variable is one type of variable defined in a class.
Each object of a class has a separate copy of the instance variables.
Instance variables are preceded by the at sign (@) followed by the variable name (for example: @name)
We can, for example, pass a parameter to the initialize method and assign it to an instance variable for a new object:class Person
def initialize(name)
@name = name
end
end

In the code above, @name is an instance variable for the class Person.
Now, we can create objects of that class and pass an argument to the new method:p1 = Person.new("David")
p2 = Person.new("Amy")

The object p1 now has an instance variable @name with the value "David" which relates only to the object p1.
Similarly, @name for the object p2 is equal to "Amy".
Each instance (object) of a class has its own unique instance variables that store values associated with that instance.
You might wonder why we don't use local variables instead of instance variables. We need instance variables because their scope is the entire object, meaning that they are accessible inside all the methods for the object, opposed to local variables, which are accessible only within the scope they are declared, such as a single method.

Instance Variables



A class can have multiple instance variables.
For example:class Animal
@age = 0
def initialize(name, age)
@name = name
@age = age
end
end

ob = Animal.new("Jacky", 3)

A class can have multiple instance variables.

Instance Methods



In the real world, objects behave in their own way. A car moves, a phone rings, and so on.
The same applies to programming objects. Behavior is specific to the object's type and is defined by methods in the class.
You can declare instance methods that are available to an object of the class.
For example:class Dog
def bark
puts "Woof!"
end
end

We defined a method called bark that outputs text.
Now we can instantiate an object and call the method using the dot syntax:

d = Dog.new
d.bark

# outputs "Woof"
Try It Yourself

As with any method, instance methods can include multiple parameters and return values.

Accessors



An instance method can also be created to access the instance variables from outside of the object.
For example, if we want to access the @name instance variable for a Person object, we need a method that returns the value of that variable:

class Person
def initialize(name)
@name = name
end
def get_name
@name
end
end

p = Person.new("David")
puts p.get_name

# outputs "David"
Try It Yourself

We created an instance method get_name that returns the value of the @name instance variable and then called it for our object p.
Getter and setter methods are called accessors.
The method that is used to retrieve the value of the variable is called a getter method (get_name in our example).
The method that is used to modify the value of the variable is called a setter method.


Setter Methods



Getter methods are used to access the instance variable. If we want to change the value of the instance variables, we need setter methods.
Ruby provides a special syntax for defining setter methods: the method name is followed by an equal sign (=).
For example:
class Person
def initialize(name)
@name = name
end
def get_name
@name
end
def set_name=(name)
@name = name
end
end

p = Person.new("David")
p.set_name = "Bob"
puts p.get_name

# outputs "Bob"
Try It Yourself

In the code above, set_name is a setter method that sets the value of the @name instance variable to the value of its parameter name.
Notice the special syntax used when calling the methodp.set_name = "Bob".
Normally to call a method, you would use p.set_name=("Bob"), where the entire set_name= is the method name, and the string "Bob" is the argument being passed into the method.
However, for setter methods, Ruby allows us to use a more natural assignment syntax: p.set_name = "Bob".
When you see this code, just realize there's a method called set_name= working behind the scenes.

Accessors



In Ruby it is a common practice to name the getter and setter methods using the same name as the instance variable they are accessing.
The previous example can be rewritten as:

class Person
def initialize(name)
@name = name
end
def name
@name
end
def name=(name)
@name = name
end
end

p = Person.new("David")
p.name = "Bob"
puts p.name

# outputs "Bob"
Try It Yourself

Tap Try It Yourself to play around with the code!

Accessors



Imagine having a lot of instance variables and their setter and getter methods. The code would be really long.
Ruby has a built-in way to automatically create these getter and setter methods using the attr_accessor method.
The attr_accessor method takes a symbol of the instance variable name as an argument, which it uses to create getter and setter methods.
We can do the following:

class Person

attr_accessor :name

def initialize(name)
@name = name
end
end

p = Person.new("David")
p.name = "Bob"
puts p.name
Try It Yourself

That one line replaced two accessor method definitions.
Ruby also provides the attr_reader and attr_writer methods in case only a getter or setter method is needed for the instance variable.
We can pass multiple symbols to the attr_accessor, attr_reader and attr_writer methods. For example: attr_accessor :name, :height, :weight

Accessors



The accessor methods can also be called inside the class by using the self keyword.
For example:

class Person
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
def change(n, a)
self.name = n
self.age = a
end
def show_info
puts "#{self.name} is #{self.age}"
end
end

p = Person.new("David", 28)
p.change("Bob", 42)
p.show_info

# outputs "Bob is 42"
Try It Yourself

In the code above, we define a method called change that changes the instance variables via their accessor methods. The show_info method outputs the values of the instance variables.
self represents the current object and is used to call the instance methods and accessors of the object.
One of the benefits of using self is for disambiguation. For example, if you have a variable and a method both called nameself.name would make it clear that you are referring to the method.

Class Methods



Class methods are methods we can call directly on the class itself, without having to instantiate any objects.
This can be useful when there is no logical need to create an object of the class, such as when a class is used to group similar methods and functionality (like mathematical operations).
One example is a Math class that includes a square method for returning the square of its parameter. There is no logical need to create an object of the Math class just to call the method. This is where class methods come into play.
Class methods are defined using the self keyword:class Person
def self.info
puts "A Person"
end
end

Now, the method is a class method and can be called directly from the class, without the need of an object:

Person.info
# outputs "A Person"
Try It Yourself

Remember, when used inside of instance methods, self is representing the current instance (object) of that class.
When defining class methods, self is referring to the class itself, and not to an instance of the class.


Class Variables



Class variables are accessible to every object of a class. A class variable belongs to the class, not the objects.
You declare a class variable using two @ signs, for example @@name.
We can, for example, keep count of all Person objects created using a class variable:
class Person
@@count = 0
def initialize
@@count += 1
end
def self.get_count
@@count
end
end

p1 = Person.new
p2 = Person.new

puts Person.get_count
# outputs 2
Try It Yourself

In the code above, @@count is a class variable. Since the initialize method is called for every object that is created, incrementing the @@count variable will keep track of the number of objects created.
We also defined a class method called get_count to return the value of the class variable.
In the code above, we have created two objects of the Person class so the value of the @@count variable is 2.
Class variables are usually used when you need information about the class, not the individual objects.

Class Constants



A class can also contain constants. Remember, constant variables do not change their value and start with a capital letter. It is common to have uppercase names for constants, as in:class Calc
PI = 3.14
end

You can access constants using the class name, followed by two colon symbols (::) and the constant name, for example:

puts Calc::PI
# outputs 3.14
Try It Yourself

Tap Try It Yourself to play around with the code!

The to_s Method



The to_s method comes built-in with all classes. It gets called when you output the object.
For example:

class Person
#some code
end
p = Person.new
puts p
# outputs "#<Person:0x0000000272e140>"
Try It Yourself

When we call puts p, Ruby automatically calls the to_s method for the object p, so puts p is the same as puts p.to_s.
By default, the to_s method prints the object's class and an encoding of the object id.

The to_s Method



We can define our own to_s method for a class and add custom implementation to it.
For example, we can generate an informative, formatted output for our Person class:

class Person
def initialize(n, a)
@name = n
@age = a
end
def to_s
"#{@name} is #{@age} years old."
end
end
p = Person.new("David", 28)
puts p
# outputs: "David is 28 years old."
Try It Yourself

The to_s method also gets called when the object is used as a value in a string, like "#{p}"
Defining the to_s method makes it easier and shorter to output the information of an object in the format needed, as opposed to defining a custom method and calling it from an object.
When you define the to_s method you call puts on your object (puts obj), where with a custom method you have to explicitly call it from the object (puts obj.info).


Inheritance



Inheritance is when a class receives, or inherits, attributes and behavior from another class. The class that is inheriting behavior is called the subclass (or derived class) and the class it inherits from is called the superclass (or base class).
Imagine several classes, Cat, Dog, Rabbit and so on. Although they may differ in some ways (only Dog might have the method bark), they are likely to be similar in others (all having color and name).
This similarity can be expressed by making them all inherit from a superclass Animal, which contains the shared functionality.
The < symbol is used to inherit a class from another class.
For example:class Dog < Animal
#some code
end

In the code above, Dog is the subclass and Animal is the superclass.

Inheritance



Now, let’s define the Animal and Dog classes:class Animal
def initialize(name, color)
@name = name
@color = color
end
def speak
puts "Hi"
end
end

class Dog < Animal
end

Dog is a subclass of Animal so it inherits Animal's methods and attributes, making code like this possible:

d = Dog.new("Bob", "brown")
d.speak

# outputs "Hi"
Try It Yourself

Now Dog has all the methods and attributes of the Animal class, which is why we can instantiate the object and call the speak method.

Inheritance



The subclass can also have its own methods and attributes. Let's define a Cat class and inherit it from the same Animal class:

class Animal
def initialize(name, color)
@name = name
@color = color
end
def speak
puts "Hi"
end
end

class Dog < Animal
end

class Cat < Animal
attr_accessor :age
def speak
puts "Meow"
end
end

c = Cat.new("Lucy", "white")
c.age = 2
c.speak
# outputs "Meow"
Try It Yourself

In the code above, Cat inherits from Animal. It has an instance variable age and also defines its own speak method. This is called method overriding, because the speak method in Cat overrides, or replaces, the one in the Animal class.
When we called the speak method for our Dog object, its superclass method was called because Dog did not override it.
The Cat object called its own speak method, because it defined its own implementation.

Inheritance



Inheritance is a great way to remove duplication in your code by writing the shared and common functionality in the superclass and then adding individual functionality in the subclasses.
You can have multiple levels of inheritance, for example:class Animal
end
class Mammal < Animal
end
class Dog < Mammal
end

Here, Dog inherits from Mammal, which inherits from Animal.
This can be described as an "is a" relationship because a Dog is a Mammal, which is an Animal. This is an example of single inheritance with multiple levels of hierarchy.
However, Ruby does not support multiple inheritance, meaning you cannot inherit a class simultaneously from multiple classes. (A class cannot have multiple superclasses)
To achieve that behavior, Ruby supports mixins. We will learn about them in the next module.

super



Ruby has a built-in method called super, which is used to call methods from the superclass.
When you call super in a method of the subclass, the method of the same name gets called from the superclass.
For example:class Animal
def speak
puts "Hi"
end
end

class Cat < Animal
def speak
super
puts "Meow"
end
end

super calls the speak method of the Animal class.
Now, if we create an object of class Cat and call its speak method, we will get the following:

c = Cat.new
c.speak
# Outputs
# Hi
# Meow
Try It Yourself

The use of super allows us to remove duplicate code by using and extending the behavior of the superclass in our subclasses.

super



super is more commonly used in the initialize method.
For example, our superclass has a initialize method that takes one argument and initializes an instance variable:class Animal
def initialize(name)
@name = name
end
end

Now, we need a subclass Cat that also has an @age instance variable, and we need to define its own initialize method. Instead of repeating ourselves, and setting the name instance variable in the Cat class, we can use its superclass with the super method as follows:class Cat < Animal
def initialize(name, age)
super(name)
@age = age
end
def to_s
"#{@name} is #{@age} years old."
end
end

We passed one of the arguments to the super method, which calls the initialize method of the Animal class and sets the @name instance variable.
Now we can instantiate an object and output its info:

c = Cat.new("Bob", 3)
puts c

# outputs "Bob is 3 years old."
Try It Yourself

In the example we used super for a simple assignment. Imagine having a complex program with complex calculations and operations being carried out. That's where the real benefits of "not repeating yourself" come in, and calling the super where applicable is one way of achieving it.

Operator Overloading



Ruby allows us to overload operators so that we can perform operations such as adding two objects together.
Let's say we have a class Shape, which has width and height properties. We want to be able to add together two Shape objects and, as a result, get a new object that has its width and height equal to the sum of the corresponding properties of the objects.
All we need to do is define the corresponding operator as a method:

class Shape
attr_accessor :h, :w
def initialize(h, w)
self.h = h
self.w = w
end

def +(other)
Shape.new(self.h+other.h, self.w+other.w)
end


end

a = Shape.new(7, 4)
b = Shape.new(9, 18)
c = a+b
puts c.h # outputs 16
puts c.w # outputs 22
Try It Yourself

As you can see, the + method takes one argument, which is another Shape object, and returns a new Shape object with the corresponding values.
You can override almost all operators in Ruby and have any custom logic defined in the corresponding method.

Access Modifiers



Until now, all the methods that we defined in our classes were publicly available, meaning that you could call those methods from outside of the class.
There are certain situations when methods should only be visible to the class. For example, imagine a Banking class with methods to calculate values for internal transactions and operations. If these methods were available outside the class, the reliability of the data could be at risk.
To control visibility, Ruby provides the publicprivate, and protected access modifiers.
By default, all class methods (except initialize) are public, meaning that they are accessible from both inside and outside of the class.
To make a method accessible only from inside the class, we can use the private access modifier:

class Person
def initialize(age)
@age = age
end
def show
puts "#{@age} years = #{days_lived} days"
end

private
def days_lived
@age * 365
end
end

p = Person.new(42)
p.show
Try It Yourself

In the code above, the method days_lived is private and is only accessible inside the class. We called it from the show method, which is public.
If we try to call the days_lived method from an object (puts p.days_lived), we will get an error.
When the reserved word private is used in a program, anything below it in the class is private (unless public or protected is placed after it to negate it).
Access modifiers can be applied only to methods. Instance variable are always private.

Protected



An interesting thing to note about private Ruby methods is that they cannot be called with an explicit receiver, even if that receiver is itself. When we say "receiver", we mean the object that the method is being called from. Even if we try to call the private method with self we will get an error.
This can be needed when, for example, overloading an operator to compare two objects using a private method.
To demonstrate that, let's define a class Product with a private method id. If the ids of two products are equal, then they are considered equal:

class Product
attr_accessor :name, :num
def initialize(name, num)
@name = name
@num = num
end
def ==(other)
self.id == other.id
end
private
def id
name.length*num
end
end

p1 = Product.new("PC", 5)
p2 = Product.new("Laptop", 3)
puts (p1 == p2)
# outputs "...Error: private method 'id' called..."
Try It Yourself

This code generates an error, because we tried to call the private method id on self and the other object.
To be able to do that without making the method public, Ruby has the protected access control.
If we change the method from private to protected, the code will work:

class Product
attr_accessor :name, :num
def initialize(name, num)
@name = name
@num = num
end
def ==(other)
self.id == other.id
end
protected
def id
name.length*num
end
end

p1 = Product.new("PC", 5)
p2 = Product.new("Laptop", 3)
puts (p1 == p2)
# outputs false
Try It Yourself

So, protected methods are not accessible from outside code, just like private methods, but can be called for an object of the same class or subclasses.

A Simple Game



Object Oriented Programming is very useful when it comes to creating complex applications, such as games.
Let's create a simple fighting game, where two opponents will fight until one of them loses.
We start by creating our Player class:class Player
attr_accessor :name, :health, :power
def initialize(n, h, pow)
@name = n
@health = h
@power = pow
end
def isAlive
@health > 0
end
def hit(opponent)
opponent.health -= self.power
end
def to_s
"#{name}: Health: #{health}, Power: #{power}"
end
end

The Player class has 3 instance variables, namehealth and power, and 3 instance methods:
isAlive shows whether the player is still alive.
hit hits the opponent (decreases the opponent's health by the amount of player's power)
to_s outputs the player information.
We have also defined getter and setter accessors for the instance variables using the attr_accessor method.

A Simple Game



With the Player class defined, we can now define a method to make two opponents fight:def fight(p1, p2)
while p1.isAlive && p2.isAlive
p1.hit(p2)
p2.hit(p1)
show_info(p1, p2)
end

if p1.isAlive
puts "#{p1.name} WON!"
elsif p2.isAlive
puts "#{p2.name} WON!"
else
puts "TIE!"
end
end

def show_info(*p)
p.each { |x| puts x}
end

The fight method uses a loop to make the players hit each other until the isAlive method returns false for one of them. After each iteration, the information of both players is output to the screen using the show_info method we defined.
Once the loop is over (meaning one of the opponents has lost), we check and output the corresponding result.

A Simple Game



All that is left is to create two Player objects and call the fight method. To make the game interesting, we can use random values for health and power of our players using the rand method, which returns a random value in the range of 0 to its argument.p1 = Player.new("Player 1", 1+rand(100), 1+rand(20))
p2 = Player.new("Player 2", 1+rand(100), 1+rand(20))

#show Player info
show_info(p1, p2)

puts "LETS FIGHT!"
fight(p1, p2)

We used 100 as the maximum value for health, and 20 as a maximum value for power. We add 1 to the rand method to avoid the value 0.
Now, each time you run the program, two Players with random health and power will be created and will fight!
The final code:

class Player
attr_accessor :name, :health, :power
def initialize(n, h, pow)
@name = n
@health = h
@power = pow
end
def isAlive
@health > 0
end
def hit(opponent)
opponent.health -= self.power
end
def to_s
"#{name}: Health: #{health}, Power: #{power}"
end
end

def fight(p1, p2)
while p1.isAlive && p2.isAlive
p1.hit(p2)
p2.hit(p1)
show_info(p1, p2)
end

if p1.isAlive
puts "#{p1.name} WON!"
elsif p2.isAlive
puts "#{p2.name} WON!"
else
puts "TIE!"
end
end

def show_info(*p)
p.each { |x| puts x}
end

#initialize Players
puts "PLAYERS INFO"
p1 = Player.new("Player 1", 1+rand(100), 1+rand(20))
p2 = Player.new("Player 2", 1+rand(100), 1+rand(20))

#show Player info
show_info(p1, p2)

puts "LETS FIGHT!"
fight(p1, p2)
Try It Yourself

This was just a simplified version.
You can easily create different subclasses of players, add more properties, define weapons, get user input to make different decisions, and so on.

Modules



Extracting common methods to a superclass, like we did in the previous section, is a great way to model concepts that are naturally hierarchical (a Cat is an Animal which is a Mammal).
Another way of grouping methods together are modules.
module is a collection of methods that can be used in other classes (think about them as libraries providing common functionality).
Modules are defined using the module keyword followed by the module name, which should start with a capital letter.
For example:module Flyable
def fly
puts "I'm flying!"
end
end

The code above declares a module called Flyable, which includes one method called "fly".
A module can include as many methods as you want.
As you can see the syntax is very similar to defining classes.
But why use modules instead of classes? Tap Continue to discover!

Modules



Now, imagine having a class hierarchy, with a superclass Vehicle and subclasses: Car, Jet, Plane.
All have some shared functionality, which they inherit from the Vehicle class, but only Jet and Plane can fly.
Instead of defining separate fly methods for both classes, we can define a module and include it (commonly referred to as "mix" it) in the classes.module Flyable
def fly
puts "I'm flying!"
end
end

class Vehicle
end

class Car < Vehicle
end

class Jet < Vehicle
include Flyable
end

class Plane < Vehicle
include Flyable
end

Now Jet and Plane objects can fly, but objects of other classes won't be able to:

ob = Jet.new
ob.fly
Try It Yourself

A common naming convention for Ruby is to use the "able" suffix on whatever verb describes the behavior that the module is modeling, like Walkable, Swimmable, Runnable, etc.
Not all modules are named in this manner, however, it is quite common.

Modules



As you may recall, Ruby does not allow a class to inherit from multiple classes.
However, a class can mix in multiple modules. Modules used this way are known as "mixins".
So, basically, a class can have multiple mixins:class Human
include Walkable
include Speakable
include Runnable
end

It is important to remember the following:
1. You can only inherit from one class. But you can mix in as many modules as you'd like.

2. If it's an "is-a" relationship, choose class inheritance. If it's a "has-a" relationship, choose modules. Example: a plane "is a" vehicle; a plane "has an" ability to fly.

3. You cannot instantiate modules (i.e., an object cannot be created from a module).
Modules are used only for grouping common methods together.
Classes are about objects; modules are about methods.
Mixins give you a great, controlled way of adding functionality to classes.

Mixins



The true power of mixins comes out when the code in the mixin starts to interact with code in the class that uses it. Ruby has a number of predefined mixins ready for you to use.
Let's take the standard Ruby mixin Comparable as an example. The Comparable mixin can be used to add the comparison operators (<, <=, ==, >=, and >) to a class.
For this to work, Comparable assumes that any class that uses it defines the operator <=>.
So, as a class writer, you define the one method, <=>, include Comparable, and get six comparison functions as a result!
Let's try this with our Cat class, by making the cats comparable based on their age.
All we have to do is include the Comparable module and implement the comparison operator <=>.

class Cat
attr_accessor :name, :age
include Comparable
def initialize(n, a)
self.name = n
self.age = a
end
def <=>(other)
self.age <=> other.age
end
end

c1 = Cat.new("Bob", 3)
c2 = Cat.new("Lucy", 7)

puts c1 < c2 # true
Try It Yourself

The Comparable module allows you to easily add comparison operators based on any custom logic to your classes.

Namespacing



We've already seen how modules can be used to mix-in common behavior into classes. Now we'll see two more uses for modules.
The first case we'll discuss is using modules for namespacing.
In this context, namespacing means organizing similar classes in a module. In other words, we'll use modules to group related classes.
For example:module Mammal
class Dog
def speak
puts "Woof!"
end
end

class Cat
def speak
puts "Meow"
end
end
end

We defined a module Mammal which groups together two classes, Dog and Cat.
Now we can call classes in the module by appending the class name to the module name with two colons(::):

a = Mammal::Dog.new
b = Mammal::Cat.new

a.speak # "Woof"
b.speak # "Meow"
Try It Yourself

The advantages of namespacing classes:
It becomes easy for us to recognize related classes in our code.
It reduces the likelihood of our classes colliding with other similarly named classes in our code. We can have the same class names across different modules.

Namespacing



Another use for modules is as containers for methods.
This allows us to group together relevant methods and use them in our code.
For example:

module MyMath
PI = 3.14
def self.square(x)
x*x
end
def self.negate(x)
-x
end
def self.factorial(x)
(1..x).inject(:*) || 1
end
end

puts MyMath.factorial(8)
Try It Yourself

The code above defines a module called MyMath, which includes a constant called PI, and three class methods. The methods inside a module are defined as class methods (note the self keyword), and we call them using the dot syntax.
You can call the methods using two colon syntax (::) as well (MyMath::factorial(8)), but the dot syntax is preferred.
Again, the advantages of using modules to group methods includes preventing name collisions, meaning that we can have the same method names across multiple modules.

Structs



In some cases, there is no need for defining a fully structured class. Sometimes we need just a group of attributes bundled together (for example, defining points in a 2D space using x and y coordinates).
We could, of course, create a separate class with all the instance variables and methods, but Ruby provides a shortcut to bundle a number of attributes together called a Struct.Point = Struct.new(:x, :y)
In the code above, Point is a Struct, having two attribute accessors: x and y. Struct automatically creates its initialize method for the defined accessors, so now we can use Points just like a class, instantiating different objects from it.

origin = Point.new(0, 0)
dest = Point.new(15, 42)

puts dest.y # 42
Try It Yourself

Struct is a built-in Ruby class and makes it shorter to define simple classes, accessors, and their initialize methods.

OStruct



OpenStruct (or OStruct) acts very similarly to Struct, except that it doesn't have a defined list of attributes.
To use OStruct, we need to include the corresponding library using the require statement.

require "ostruct"

person = OpenStruct.new
person.name = "John"
person.age = 42
person.salary = 250

puts person.name # John
Try It Yourself

As you can see, we can define any number of attributes on the fly.
OStruct isn't as fast as Struct, but it is more flexible.

OStruct



We can also initialize an OStruct using a hash.
For example:

require "ostruct"

person = OpenStruct.new(name:"John", age:42, salary:250)

puts person.name # John
Try It Yourself

Struct and OStruct provide a simple way to create data structures that have the behavior of a class.

Standard Classes



Ruby provides a number of standard built-in classes that make our life a lot easier by providing useful methods for manipulating data.
Some of the classes in previous lessons include ArrayString and Struct.
Another useful class is the Math class, which provides methods to perform mathematical operations.
For example:

# square root
puts Math.sqrt(9) # 3

# pi constant
puts Math::PI

# trigonometry (sin, cos, tan)
puts Math::cos(0) # 1
Try It Yourself

Tap Try It Yourself to play around with the code!

Time



The Time class represents dates and times in Ruby.

# current time
t = Time.now
puts t

# year, month, day
puts t.year
puts t.month
puts t.day

# custom date
t = Time.new(1988, 6, 10)

# day of week: 0 is Sunday
puts t.wday

# day of year
puts t.yday
Try It Yourself

Tap Try It Yourself to play around with the code!

Procs



Ruby provides the ability to take a block of code, wrap it up in an object (called a proc), store it in a variable, and run the code in the block whenever you feel like (more than once, if you want).
For example:greet = Proc.new do |x|
puts "Welcome #{x}"
end

We created a Proc that takes a parameter and outputs a greeting, and assigned it to the greet variable.
We run the code in the proc using the call method.

greet.call "David"
greet.call "Amy"

# Outputs
# "Welcome David"
# "Welcome Amy"
Try It Yourself

The code between the do and end keywords can include any number of operations.

Procs



Procs are very similar to methods. They perform operations and can include parameters.
What make Procs really powerful and unique is the ability to pass them into methods, because procs are actually objects.
For example:greet = Proc.new do |x|
puts "Welcome #{x}"
end

goodbye = Proc.new do |x|
puts "Goodbye #{x}"
end

def say(arr, proc)
arr.each { |x| proc.call x}
end

We have defined two procs and a method, that takes an array and a proc as its parameters. For each item in the array it calls the proc. Now, greet and goodbye are objects that contain the corresponding blocks of code.
We can call the say method and pass our proc objects as parameters:

people = ["David", "Amy", "John"]
say(people, greet)
say(people, goodbye)
Try It Yourself

We can pass to our methods as many procs as we want.
Using procs gives the added flexibility to be able to reuse code blocks in more than one place without having to type them out every time. Procs basically take blocks of code and embed them in an object, allowing them to be reused and passed around.

Procs



Let's create a program that counts the execution time of a block of code.
We will define a method that takes a proc as its parameter and counts the time it takes to execute the proc.def calc(proc)
start = Time.now
proc.call
dur = Time.now - start
end

Let's see it in action:

someProc = Proc.new do
num = 0
1000000.times do
num = num + 1
end
end

puts calc(someProc)
Try It Yourself

With that tiny calc method, we can now easily calculate the execution time of any Ruby code!

Lambdas



Lambdas are a variation of Procs. A lambda is actually an instance of the Proc class.
To create a lambda in Ruby, you use the following syntax:talk = lambda {puts "Hi"}
Alternatively, the following syntax can be used:talk = ->() {puts "Hi"}
Just like with procs, you use the call method to execute the lambda:

talk.callTry It Yourself

In other programming languages, a lambda is commonly referred to as an anonymous function.

Lambdas



Lambdas as very similar to procs. However, there are some important differences.
The first difference between procs and lambdas is how arguments are handled.
Lambdas check the number of arguments, while procs do not.
For example:

talk = lambda { |x| puts "Hello #{x}" }
talk_proc = Proc.new { |x| puts "Hello #{x}" }

talk.call "David"
# outputs "Hello David"

talk_proc.call "Amy"
# outputs "Hello Amy"
Try It Yourself

As you can see, the lambda and proc worked the same way, when the number of arguments is correct.
However, if we try to call them with incorrect number of arguments:

talk_proc.call
# ouputs Hello

talk.call
# outputs "Error: wrong number of arguments (given 0, expected 1)"
Try It Yourself

As you can see, when a lambda expects an argument, you need to pass those arguments or an Error will occur. However, in the case of the Proc, if the argument is not passed it automatically defaults to nil.
A second difference between a lambda and a Proc is how the return statement is handled.
When a lambda encounters a return statement it returns execution to the enclosing method.
However, when a Proc encounters a return statement it jumps out of itself, as well as the enclosing method.

Creating Files



Until now we have been performing input and output tasks using the gets and puts methods. A more permanent form of input and output is files.
Ruby allows us to easily create and work with files by using the built-in File class.file = File.new("test.txt", "w+")
The code above creates a file named "test.txt" with the mode w+ for read and write access to the file.
Note that the file will be created in the same directory as our program.
Now we can use our file object to perform different operations. After using a file, it must be closed using the close method:file.close
It is necessary to close open files so they no longer continue to occupy space in memory.

File Modes



Ruby supports the following file modes:
r read-only, starts at beginning of file (default mode).
r+ read-write, starts at beginning of file.
w write-only, truncates existing file to zero length or creates a new file for writing.
w+ read-write, truncates existing file to zero length overwriting existing data or creates a new file for reading and writing.
a write-only, appends to end of file if file exists, otherwise creates a new file for writing.
a+ read-write, appends or reads from end of file if file exists, otherwise creates a new file for reading and writing.
When the open mode is read only, the mode cannot be changed to writable. Similarly, the open mode cannot be changed from write only to readable.

Opening Files



To open an existing file you use the File class open method:file = File.open("filename", "w+")
As with the new method, the second argument of the open method specifies the mode.

Writing Files



The puts and write methods can be used for writing content to a file.
The difference between the two is that puts adds a line break to the end of strings, while write does not.
For example:file = File.new("test.txt", "w+")
file.puts("some text")
file.close

Remember to close the file after performing the file operations.

Writing Files



If we want to write to an existing file, we can use a block of code to perform the write operation.
With this code Ruby will automatically close the file.
For example:File.open("file.txt", "w+") {
|file| file.puts("some text")
}

This shorter way makes sure the file closes at the end of the block.

Reading Files



To read the entire contents of a file the File.read method can be used.
For example:

f = File.new("test.txt", "w+")
f.puts("a line of text")
f.puts("another line of text")
f.close

puts File.read("test.txt")

# Outputs:
# a line of text
# another line of text
Try It Yourself

Tap Try It Yourself to play around with the code!

Reading Files



We can also read the file contents line-by-line using the readlines method.
For example:

File.open("test.txt", "a+") {
|file| file.puts("a line of text")
file.puts("another line of text")
}

File.readlines("test.txt").each {
|line| puts " --- #{line}"
}
Try It Yourself

The readlines method reads the entire file based on individual lines and returns those lines in an array.

Deleting Files



We can delete a file using the File.delete method.
For example:File.delete("test.txt")
Be careful, as this command deletes the file permanently.

Working with Files



When working with files, it is important to first check if the file exists in order to prevent an error.
The file? method provides an easy way of checking if the file exists.
For example:File.open("test.txt") if File.file?("text.txt")
The code above tests whether a file exists before opening it.
File.file? returns true if the file exists, and false if it does not.

File Info



Ruby provides some useful methods to get relevant information about a file.
The size method returns the size of the file.
The zero? method returns true if the named file exists and has a zero size (is empty).
For example:

# create a file
f = File.new("test.txt", "w+")
f.puts("some file content")

puts f.size # 19

f.close

puts File.zero?("test.txt") # false
Try It Yourself

Tap Try It Yourself to play around with the code!

File Info



We can also check whether the file is writable, readable or executable:

f = File.new("test.txt", "w+")
f.puts("some content")
f.close

puts File.readable?("test.txt") # true
puts File.writable?("test.txt") # true
puts File.executable?("test.txt") # false
Try It Yourself

Tap Try It Yourself to play around with the code!







1 comment:


  1. Thanks for sharing such a Excellent Blog! We are linking too this particularly great post on our site. Keep up the great writing.
    ruby assignment help

    ReplyDelete