Introduction to Ruby & Setup
What is Ruby?
Ruby is a dynamic, open-source, object-oriented programming language created by Yukihiro "Matz" Matsumoto in the mid-90s. Unlike many other languages, Ruby was designed specifically to make programmers happy. It focuses on human-readable syntax rather than machine-readable efficiency.
If you've ever looked at code and felt it looked like a "secret code" of brackets and symbols, Ruby is the cure. It reads almost like English, which makes it one of the best languages for beginners to learn the logic of programming.
Why Learn Ruby?
- Elegant Syntax: Ruby minimizes "boilerplate" code. You can do in 3 lines of Ruby what might take 10 lines in Java or C++.
- Extreme Productivity: Because the language is so flexible, developers can build prototypes and full applications incredibly fast.
- Ruby on Rails: Ruby powers Rails, one of the most influential web frameworks in history (used by Shopify, Airbnb, and GitHub).
- Everything is an Object: This is a core concept. In Ruby, every single piece of data (even a number) is an "object," which makes the language consistent and powerful.
Installation & Environment
Installing Ruby can be tricky because most operating systems (like macOS) come with a "System Ruby" pre-installed. Warning: Never use the System Ruby for development, as it can break your OS!
Instead, we use Version Managers. These allow you to install different versions of Ruby and switch between them easily.
- rbenv: A lightweight tool to manage Ruby versions (Highly Recommended).
- RVM (Ruby Version Manager): A more powerful, feature-rich manager.
- Homebrew: The standard package manager for macOS to install the tools above.
# 1. Check if Ruby is already installed ruby --version # 2. Enter the Interactive Ruby shell (The Playground) irb
Meeting IRB: The Interactive Shell
Before writing full files, Ruby provides a tool called irb
(Interactive Ruby). This is a "Read-Eval-Print Loop" (REPL). It allows you to type a line of
code and get an instant result without having to save a file and run it.
It is the perfect place to experiment with new ideas or test a mathematical formula before putting it into your main program.
Your First Ruby Program
In Ruby, the most common way to output text to the screen is using puts (short for "put string"). Unlike print, puts automatically adds a new
line after the text, making your output clean and readable.
# Any line starting with # is a comment and is ignored by Ruby puts "Hello, Ruby Mastery!" puts "Ruby is fun and productive!" puts "Current Date: " + Time.now.to_s
Ruby is fun and productive!
Current Date: 2024-05-20 14:30:00 +0000
๐ป Try It Yourself - Multi-Language Compiler
Practice Ruby and many other programming languages right here in your browser! Switch between languages, modify the code, and click "Run" to see results instantly.
๐ก Practice Tips:
- Switch to Ruby in the language selector and try the web development examples
- Experiment with Ruby's dynamic typing and metaprogramming features
- Try other scripting languages like Python, JavaScript, or compare with PHP
- Use the "Load Example" button to see Ruby-specific code samples
- Use Ctrl+Enter to quickly run your code
How to run your code
To run a Ruby file, you go to your terminal and use the command ruby followed by the filename:
ruby hello.rb
Complete the following steps to ensure your environment is ready:
- 1. Install Ruby using
rbenvorRVM. - 2. Open your terminal and type
irb. Inside the shell, calculate123 * 456and then typeexitto leave. - 3. Create a file named
about_me.rb. - 4. Write a program that uses
putsto print your name, your favorite hobby, and a "Fun Fact" about yourself on three separate lines. - 5. Run the file using the command
ruby about_me.rb.
Variables & Data Types
1. Understanding Variables
Think of a variable as a labeled box. You put a piece of data inside the box, and you give the box a name so you can find that data later in your program.
Ruby uses Dynamic Typing. This means you don't have to tell Ruby if a variable is a number or a piece of text; Ruby is smart enough to figure it out automatically based on the value you assign to it.
Naming Conventions
In Ruby, we use snake_case for variable names. This means all
letters are lowercase, and words are separated by underscores.
# โ Correct: snake_case user_name = "Alex" user_age = 25 is_learning_ruby = true # โ Avoid: camelCase or PascalCase (Reserved for other things in Ruby) userName = "Alex" UserAge = 25 # Note: Starting with a Capital letter creates a Constant!
2. Primary Data Types
Every value in Ruby belongs to a "class" or data type. Here are the most common ones you will use:
Numbers (Integers & Floats)
Ruby handles numbers in two main ways: Integers (whole numbers) and Floats (decimal numbers).
age = 30 # Integer pi = 3.14159 # Float price = 19.99 # Float
Strings (Text)
Strings are sequences of characters wrapped in quotes. But Ruby has a "superpower" called String Interpolation. By using double quotes "" and the #{ } syntax, you can
insert variables directly into a string.
name = "Sarah" # Simple concatenation (adding strings together) puts "Hello, " + name # String Interpolation (The better way!) puts "Hello, #{name}! Welcome to Ruby."
Booleans (True/False)
Booleans represent one of two states: true or false. These are used primarily for making decisions in your code
(if/else statements).
is_logged_in = true has_paid_subscription = false
Symbols
Symbols look like strings but start with a colon (e.g., :id).
While a string is "data" that can change, a symbol is a unique
label. They are faster for the computer to process and are used everywhere in Ruby
(especially in Rails) as keys in hashes.
# String (Can be changed) name = "Admin" # Symbol (Immutable/Permanent label) role = :admin
Nil (The Absence of Value)
In Ruby, nil represents "nothing" or "no value." It is not the
same as zero or an empty string; it is a complete lack of a value.
user_phone_number = nil # The user didn't provide a phone number
Create a script called profile.rb that does the following:
- Assign your name to a variable (String).
- Assign your age to a variable (Integer).
- Assign your height to a variable (Float).
- Create a variable called
is_studentand set it totrueorfalse. - Use String Interpolation to print a sentence like:
"My name is Alex, I am 25 years old, and my height is 5.9 feet." - Create a symbol called
:ruby_learnerand print it to the console.
Control Statements
What are Control Statements?
Up until now, your code has run line-by-line from top to bottom. Control Statements allow you to change that flow. They let your program make decisions: "If the user is logged in, show the dashboard; otherwise, show the login page."
This is the foundation of all logic in programming.
1. The If/Elsif/Else Structure
The if statement is the most common way to control logic. It evaluates a
condition: if that condition is true, the code inside the block
runs.
- if: The primary check.
- elsif: (Short for "else if") used to check a second or third condition if the first one was false.
- else: The "catch-all" that runs if none of the above conditions were met.
score = 85 if score >= 90 puts "Grade: A - Excellent!" elsif score >= 80 puts "Grade: B - Great job!" elsif score >= 70 puts "Grade: C - You passed!" else puts "Grade: F - Keep practicing!" end
2. The "Unless" Statement (The Ruby Way)
Ruby provides a unique keyword called unless. It is the exact opposite of
if. It reads as: "Do this UNLESS this condition is true."
Using unless makes your code read more like a natural English sentence,
which is a core goal of Ruby.
is_admin = false # This reads: "Print warning UNLESS the user is an admin" unless is_admin puts "Access Denied: You do not have admin privileges." end
3. Case Statements
When you have a single variable that could be one of many different values, using ten elsif statements becomes messy. The case statement
is a cleaner, more organized alternative.
weather = "Rainy" case weather when "Sunny" puts "Wear sunscreen!" when "Rainy" puts "Bring an umbrella!" when "Snowy" puts "Wear a heavy coat!" else puts "Check the forecast again." end
4. The Ternary Operator (Shorthand)
For very simple "either-or" decisions, Ruby developers use the Ternary
Operator. It allows you to write an if/else block in a
single line.
Syntax: condition ? true_result : false_result
age = 20 # Traditional way: if age >= 18 status = "Adult" else status = "Minor" end # Ternary way: status = age >= 18 ? "Adult" : "Minor" puts status
Create a script called smart_home.rb that simulates a smart
home system:
- 1. Create a variable
temperature(Integer) andis_raining(Boolean). - 2. Use an
if/elsif/elseblock to print:- "Turn on Heater" if temp is below 15.
- "Turn on AC" if temp is above 25.
- "Temperature is perfect" otherwise.
- 3. Use an
unlessstatement to print "Close the windows!" only if it is raining. - 4. Create a variable
time_of_day(e.g., "Morning", "Afternoon", "Night") and use acasestatement to print a matching greeting. - 5. Use a Ternary Operator to check if the temperature is exactly 20 and print "Ideal" or "Not Ideal".
Loops & Iterators
What are Loops?
Loops allow you to run the same block of code multiple times. Instead of writing the same line of code 10 times, you can tell Ruby to "repeat this until a condition is met" or "do this for every item in this list."
In Ruby, we divide this into two categories: Traditional Loops and Iterators.
1. Traditional Loops: While & Until
These loops are based on a condition. They keep running as long as that condition is true (or false).
- while: Runs as long as the condition is true.
- until: Runs until the condition becomes true (it's like a "while not" loop).
# WHILE LOOP: Keep going while count is less than 3 count = 1 while count <= 3 puts "While Loop: Iteration #{count}" count += 1 # Increment is vital to avoid infinite loops! end # UNTIL LOOP: Keep going until count is 6 count = 1 until count == 6 puts "Until Loop: Iteration #{count}" count += 1 end
2. The "Ruby Way": Iterators
In professional Ruby code, you will rarely see while loops.
Instead, Ruby uses Iterators. Iterators are methods that can be called on a
collection (like an Array or a Range) to perform an action on every element.
The .times Method
If you just want to repeat a block of code a specific number of times, use .times.
5.times do |i| puts "This is repeat number #{i}" end # Note: The |i| is the index, which starts at 0
The .each Method (Most Important)
The .each method is the most used iterator in Ruby. It visits
every element in a collection and passes that element into the block.
friends = ["Alice", "Bob", "Charlie"] friends.each do |friend| puts "Hello #{friend}, welcome to the party!" end
Ranges and Iteration
You can combine a Range (like 1..5) with the .each method to
create a loop that mimics the "for" loop from other languages.
# This will print numbers 1 through 5 (1..5).each do |num| puts "Processing number: #{num}" end
3. Loop Control: Break & Next
Sometimes you need to stop a loop early or skip a specific part of it. We use break and next for this.
- break: Exits the loop immediately.
Methods & Blocks
1. Ruby Methods
A Method is a named collection of code that performs a specific task. Instead of writing the same logic five times in your program, you wrap it in a method and "call" it whenever you need it.
This makes your code DRY (Don't Repeat Yourself), which is a core principle of professional software development.
Defining and Calling Methods
In Ruby, we define a method using the def keyword and end it with the
end keyword.
# Defining a simple method with no arguments def greet_world puts "Hello! Welcome to the Ruby world!" end # Calling the method greet_world # Output: Hello! Welcome to the Ruby world!
Arguments & Parameters
Methods become powerful when you pass data into them. These are called arguments.
# 'name' and 'time_of_day' are parameters def personalized_greet(name, time_of_day) puts "Good #{time_of_day}, #{name}!" end personalized_greet("Sarah", "Morning") # Output: Good Morning, Sarah! personalized_greet("Alex", "Evening") # Output: Good Evening, Alex!
Implicit Returns
One of Ruby's most famous features is the Implicit Return. In
many languages, you must use a return statement to send a value back. In
Ruby, the last line of a method is automatically returned as the result.
# This method calculates a total def add_tax(price) total = price * 1.15 total # This is the last line, so it is returned automatically end result = add_tax(100) puts result # Output: 115.0
2. Understanding Blocks
A Block is a chunk of code that you can pass into a method as an argument. It's like giving a method a "mini-program" to run inside itself.
Blocks are usually wrapped in do ... end or curly braces { ... }.
The Magic of yield
If you want to create a method that accepts a block, you use the yield
keyword. When Ruby hits yield, it pauses the method, runs the code
inside the block, and then comes back to finish the method.
# A method that accepts a block def repeat_action puts "Starting the action..." yield # This is where the block code will be executed puts "Action completed!" end # Calling the method and passing a block repeat_action do puts "I am currently inside the block!" puts "Doing some hard work..." end
Passing Data to Blocks
You can pass data from the method into the block by adding values to yield. The block receives these values inside pipes |variable|.
# This is essentially how .each works behind the scenes! def custom_looper(items) items.each do |item| yield(item) # Send the current item into the block end end my_list = ["Ruby", "Python", "Java"] custom_looper(my_list) do |lang| puts "I am learning #{lang}!" end
Create a script called calculator_pro.rb and complete these
tasks:
- 1. Create a method called
calculate_totalthat takespriceandquantityand returns the total cost. - 2. Create a method called
apply_operation. This method should take two numbers and a block. Inside the method, useyieldto pass those two numbers into the block. - 3. Call
apply_operationthree different times, passing a different block each time to:- Sum the two numbers.
- Multiply the two numbers.
- Subtract the second number from the first.
- 4. Print the results of all three operations using
puts.
Arrays & Hashes
Arrays and Hashes are two of the most important data structures in Ruby. They help developers organize, store, and manage data efficiently in applications.
Arrays are used for storing ordered collections of values, while Hashes store data in key-value pairs. Both are heavily used in Ruby programming, web development, APIs, and database operations.
Arrays
An Array is a collection of multiple values stored inside a single variable.
Each element inside the array has an index position starting from 0.
Arrays can store numbers, strings, objects, or even other arrays.
arr = [1, 2, 3, 4, 5]
arr << 6
arr.each { |n| puts n }
arr.map { |n| n * 2 }
arr.select { |n| n.even? }
In this example:
- [1, 2, 3, 4, 5] creates an array
- << adds a new element to the array
- each loops through every element
- map transforms array values
- select filters elements based on conditions
Accessing Array Elements
Array elements can be accessed using index positions. Remember that indexing starts from zero.
fruits = ["Apple", "Banana", "Orange"] puts fruits[0] puts fruits[1]
Output:
Apple Banana
Here:
- fruits[0] accesses the first element
- fruits[1] accesses the second element
Adding and Removing Elements
Ruby provides several methods for modifying arrays dynamically.
numbers = [1, 2, 3] numbers.push(4) numbers.pop puts numbers.inspect
Explanation:
- push() adds an element to the end of the array
- pop removes the last element
- inspect displays the complete array
Looping Through Arrays
Looping allows us to process each element individually. Ruby provides elegant iteration methods for arrays.
colors = ["Red", "Green", "Blue"] colors.each do |color| puts color end
The each method is one of the most commonly used iteration methods in Ruby.
Transforming Arrays with map()
The map() method creates a new array by modifying each element.
numbers = [1, 2, 3]
doubled = numbers.map { |n| n * 2 }
puts doubled.inspect
Output:
[2, 4, 6]
Filtering Arrays with select()
The select() method filters elements based on conditions.
numbers = [1, 2, 3, 4, 5]
evens = numbers.select { |n| n.even? }
puts evens.inspect
Output:
[2, 4]
Hashes
A Hash is a collection of key-value pairs. Instead of numeric indexes, data is accessed using unique keys.
Hashes are extremely useful for storing structured data such as user profiles, configuration settings, and JSON-like objects.
user = { name: "Ahmed", age: 25 }
user[:city] = "Cairo"
user.each { |k, v| puts "#{k}: #{v}" }
In this example:
- name and age are keys
- "Ahmed" and 25 are values
- user[:city] adds a new key-value pair
- each iterates through the hash
Accessing Hash Values
Hash values are accessed using keys.
student = {
name: "Sara",
marks: 92
}
puts student[:name]
puts student[:marks]
Output:
Sara 92
Updating Hashes
Hash values can easily be updated or modified.
user = { name: "Ali", age: 20 }
user[:age] = 21
puts user.inspect
The age value changes from 20 to 21.
Nested Arrays and Hashes
Ruby allows arrays and hashes to be nested inside each other for more complex data structures.
students = [
{ name: "Ahmed", grade: 90 },
{ name: "Sara", grade: 85 }
]
puts students[0][:name]
This structure is commonly used in APIs and database records.
Advantages of Arrays & Hashes
- Efficient data organization
- Easy data access and modification
- Flexible storage structures
- Useful for real-world applications
- Essential for Ruby and Rails development
Best Practices
- Use arrays for ordered collections
- Use hashes for structured key-value data
- Choose meaningful key names
- Avoid deeply nested structures when possible
- Use iteration methods for cleaner code
Create a hash of students with grades, find all with grade > 80.
Try adding:
- At least 5 students
- A loop to display student names
- A condition to filter high grades
- An average grade calculator
Summary
In this lecture, we learned about Arrays and Hashes in Ruby. We explored how arrays store ordered collections and how hashes store data using key-value pairs.
We also learned how to access, modify, transform, and filter data using methods like each, map, and select.
Arrays and Hashes are foundational concepts used throughout Ruby programming and Ruby on Rails development.
Strings & Regular Expressions
Strings are one of the most commonly used data types in programming. Almost every application works with text in some form โ usernames, passwords, emails, search queries, messages, logs, and file content.
Ruby provides powerful built-in string methods that make text processing simple and efficient. Along with strings, Ruby also supports Regular Expressions (Regex), which allow developers to search, validate, and manipulate text patterns.
In this lecture, we will learn:
- How to work with strings in Ruby
- Important string methods
- How string manipulation works
- What regular expressions are
- How regex is used for validation and searching
String Methods
Ruby strings come with many built-in methods for modifying and analyzing text. These methods help developers process data quickly without writing complex logic.
s = "Hello Ruby" # Convert to uppercase puts s.upcase # Convert to lowercase puts s.downcase # Reverse string puts s.reverse # String length puts s.length # Replace text puts s.gsub("Ruby", "World") # Split into array p s.split(" ")
Understanding Important String Methods
- upcase converts text to uppercase
- downcase converts text to lowercase
- reverse reverses characters
- length returns total characters
- gsub replaces text globally
- split breaks string into arrays
More Useful String Methods
Ruby includes many additional methods for checking and manipulating text.
text = " Ruby Language " # Remove spaces puts text.strip # Check substring puts text.include?("Ruby") # Start and end checks puts text.start_with?(" ") puts text.end_with?(" ") # Repeat string puts "Hi! " * 3 # Access characters puts text[2] puts text[0, 5]
String Interpolation
String interpolation allows variables to be inserted directly inside strings using #{ }.
This is cleaner and easier than concatenation.
name = "Ahmed"
age = 22
puts "My name is #{name}"
puts "I am #{age} years old"
Regular Expressions (Regex)
Regular Expressions are patterns used to search, match, and validate text. Regex is extremely useful for checking formats like emails, phone numbers, passwords, URLs, and dates.
Ruby provides excellent built-in support for regex.
# Match digits puts "abc123" =~ /\d+/ # Email validation puts "a@b.com".match?(/\A[\w.]+@[\w]+\.\w+\z/) # Find all matches p "hello".scan(/l/)
Understanding Regex Symbols
- \d โ Any digit
- + โ One or more matches
- \A โ Start of string
- \z โ End of string
- [] โ Character group
- . โ Any character
Regex Matching Methods
Ruby provides several methods for regex operations.
text = "Ruby 123 Programming" # Check match puts text.match?(/\d+/) # Return first match p text.match(/\d+/) # Find all matches p text.scan(/\w+/) # Replace using regex puts text.gsub(/\d+/, "***")
Email Validation Example
Regex is commonly used for validating user input in forms and applications.
email = "user@example.com" pattern = /\A[\w.]+@[\w]+\.\w+\z/ if email.match?(pattern) puts "Valid Email" else puts "Invalid Email" end
Phone Number Validation
Regex can also validate phone number formats.
phone = "9876543210"
if phone.match?(/\A\d{10}\z/)
puts "Valid Number"
else
puts "Invalid Number"
end
Common Regex Patterns
# Only digits /\d+/ # Only alphabets /[a-zA-Z]+/ # Whitespace /\s/ # Email format /\A[\w.]+@[\w]+\.\w+\z/ # 10-digit phone number /\A\d{10}\z/
Best Practices
- Use meaningful regex patterns
- Keep regex readable and simple
- Use string methods before complex regex when possible
- Validate user input carefully
- Test regex patterns with multiple examples
Create Ruby programs that:
- Validate phone numbers using regex
- Validate email addresses
- Count words inside a sentence
- Replace digits with symbols
- Check if a password contains numbers
OOP in Ruby
Object-Oriented Programming (OOP) is a programming paradigm that organizes code using objects and classes. Ruby is a fully object-oriented language, meaning almost everything in Ruby is treated as an object.
OOP helps developers write cleaner, reusable, scalable, and maintainable code. It is widely used in Ruby on Rails applications and modern software development.
What is a Class?
A class is a blueprint or template used to create objects. It defines the properties (attributes) and behaviors (methods) that objects will have.
For example:
- A Car class may contain color, speed, and brand
- A User class may contain name, email, and password
- A BankAccount class may contain balance and account number
Classes
class Person
attr_accessor :name, :age
def initialize(name, age)
@name = name
@age = age
end
def greet = "Hi, I'm #{@name}"
end
p = Person.new("Ahmed", 25)
puts p.greet
In this example:
- class Person defines a new class
- attr_accessor creates getter and setter methods
- initialize is the constructor method
- @name and @age are instance variables
- Person.new() creates a new object
- greet is an instance method
Understanding Objects
An object is an instance of a class. When we create an object, Ruby allocates memory and stores the object's data separately.
car1 = Person.new("Ali", 22)
car2 = Person.new("Sara", 21)
puts car1.name
puts car2.name
Each object maintains its own independent data.
Instance Variables
Instance variables store object-specific data.
They begin with the @ symbol.
class Student
def initialize(name)
@name = name
end
def show
puts @name
end
end
The variable @name belongs to each individual object.
Methods in Ruby
Methods define the behavior of objects. They allow objects to perform actions.
class Calculator
def add(a, b)
a + b
end
end
calc = Calculator.new
puts calc.add(5, 3)
Output:
8
Accessors in Ruby
Ruby provides special helper methods for managing object attributes.
- attr_reader โ Read-only access
- attr_writer โ Write-only access
- attr_accessor โ Read and write access
class User
attr_accessor :username
def initialize(username)
@username = username
end
end
Inheritance
Inheritance allows one class to inherit properties and methods from another class. This promotes code reusability and reduces duplication.
class Animal def speak = "..." end class Dog < Animal def speak = "Woof!" end
In this example:
- Dog inherits from Animal
- The speak method is overridden inside Dog
- This demonstrates polymorphism in Ruby
Method Overriding
Method overriding occurs when a child class provides its own version of a parent class method.
class Vehicle
def move
puts "Moving..."
end
end
class Bike < Vehicle
def move
puts "Bike is moving"
end
end
Using super
The super keyword calls a method from the parent class.
class Animal
def speak
puts "Animal sound"
end
end
class Cat < Animal
def speak
super
puts "Meow"
end
end
Output:
Animal sound Meow
Encapsulation
Encapsulation means hiding internal object details and exposing only necessary functionality. It improves security and protects object data.
class Account
def show_balance
puts calculate_balance
end
private
def calculate_balance
5000
end
end
Private methods can only be accessed inside the same class.
Advantages of OOP
- Improves code organization
- Encourages code reusability
- Makes large applications easier to manage
- Supports modular development
- Reduces duplicate code
Real-World Uses of OOP
- Web applications
- Game development
- Banking systems
- E-commerce platforms
- Social media applications
- Desktop software
Best Practices
- Use meaningful class names
- Keep classes focused on one responsibility
- Avoid unnecessary inheritance
- Use encapsulation to protect data
- Write reusable methods
Create a BankAccount class with deposit/withdraw.
Try adding:
- A balance instance variable
- A method to check account balance
- Error handling for insufficient funds
- A transaction history array
Summary
In this lecture, we learned the fundamentals of Object-Oriented Programming in Ruby. We explored classes, objects, methods, instance variables, and accessors.
We also studied inheritance, method overriding, encapsulation, and the use of the super keyword.
OOP is a foundational concept in Ruby and is heavily used in Ruby on Rails and modern software development.
Modules & Mixins
As Ruby applications grow larger, organizing code becomes extremely important. Writing everything inside a single file or class quickly becomes difficult to manage.
Ruby provides Modules and Mixins to help developers organize reusable code efficiently. Modules help group related functionality together, while mixins allow classes to share methods without inheritance.
These concepts are widely used in Ruby and Ruby on Rails applications because they improve:
- Code organization
- Reusability
- Maintainability
- Scalability
In this lecture, we will learn:
- What modules are
- How modules work as namespaces
- How to create reusable mixins
- How include works in Ruby
- How modules improve application structure
What is a Module?
A module is a container used to group related methods, constants, and classes together. Unlike classes, modules cannot create objects directly.
Modules are mainly used for:
- Creating namespaces
- Sharing reusable functionality
- Avoiding naming conflicts
Modules as Namespaces
Namespaces prevent naming conflicts between classes and methods. In large applications, different files may contain methods or classes with the same name.
Modules solve this problem by grouping related functionality together.
module MathTools
# Module method
def self.square(x)
x * x
end
end
puts MathTools.square(5)
Understanding the Example
- module MathTools creates a namespace
- self.square defines a module method
- MathTools.square(5) calls the method
Another Namespace Example
Multiple classes with the same name can exist safely inside different modules.
module Admin
class User
def info
puts "Admin User"
end
end
end
module Customer
class User
def info
puts "Customer User"
end
end
end
Admin::User.new.info
Customer::User.new.info
Why Namespaces Are Important
- Avoid duplicate names
- Improve project organization
- Group related functionality
- Keep code cleaner
Mixins
Mixins allow methods from a module to be included inside a class. This is one of Rubyโs most powerful features.
Instead of inheriting from another class, Ruby allows classes to reuse functionality through modules.
module Loggable
def log(msg)
puts "[LOG] #{msg}"
end
end
class Service
include Loggable
def run
log("Started")
end
end
Service.new.run
Understanding Mixins
- Loggable contains reusable methods
- include adds module methods to the class
- Service can directly use the log() method
Benefits of Mixins
- Reduce duplicate code
- Share reusable behaviors
- Support cleaner architecture
- Avoid deep inheritance chains
Including Multiple Modules
A Ruby class can include multiple modules at the same time.
module Printer
def print_data
puts "Printing Data"
end
end
module Scanner
def scan_data
puts "Scanning Data"
end
end
class Machine
include Printer
include Scanner
end
m = Machine.new
m.print_data
m.scan_data
Using extend
Ruby also provides the extend keyword. While include adds methods as instance methods, extend adds methods as class methods.
module Greeting
def hello
puts "Hello"
end
end
class Test
extend Greeting
end
Test.hello
include vs extend
- include โ Adds instance methods
- extend โ Adds class methods
Ruby Built-in Mixins
Ruby includes several built-in modules that provide useful functionality.
class Product
include Comparable
attr_accessor :price
def initialize(price)
@price = price
end
def <=>(other)
price <=> other.price
end
end
p1 = Product.new(100)
p2 = Product.new(200)
puts p1 < p2
What is Comparable?
The Comparable module allows objects to be compared using operators like:
- <
- >
- <=
- >=
- ==
To use Comparable, a class must define the <=> spaceship operator.
Best Practices
- Use modules to organize related functionality
- Use mixins for reusable behaviors
- Avoid putting too many responsibilities in one module
- Prefer mixins over unnecessary inheritance
- Use namespaces in large projects
Create the following:
- A Comparable mixin for a Book class
- A Logger module with reusable log methods
- Two namespaces containing classes with the same name
- A class that includes multiple mixins
File I/O & Exceptions
Reading & Writing
File.read("input.txt")
File.readlines("input.txt").each { |l| puts l }
File.open("out.txt", "w") { |f| f.puts "Hello" }
File.open("out.txt", "a") { |f| f.puts "World" }Exceptions
begin
File.read("missing.txt")
rescue Errno::ENOENT => e
puts "Not found: #{e}"
ensure
puts "Done."
endWrite a CSV parser that handles missing files gracefully.
Metaprogramming
Dynamic Methods
class User
[:name, :age, :email].each do |attr|
define_method(attr) { instance_variable_get("@#{attr}") }
define_method("#{attr}=") { |v| instance_variable_set("@#{attr}", v) }
end
endmethod_missing
class Ghost
def method_missing(method, *args)
puts "Called #{method} with #{args}"
end
endBuild find_by_name, find_by_age dynamic methods.
Gems & Bundler
Gemfile
source 'https://rubygems.org' gem 'rails', '~> 7.0' gem 'rspec', group: :test
Commands
gem install nokogiri bundle install bundle update bundle exec rake test
Create a Bundler project, install httparty, fetch a URL.
Testing with RSpec
Basic Spec
require 'rspec'
describe Calculator do
it "adds two numbers" do
expect(Calculator.add(2, 3)).to eq(5)
end
endMocks
api = double("API")
allow(api).to receive(:fetch).and_return("data")
expect(api.fetch).to eq("data")Write 5 specs for a Stack class (push, pop, peek, empty?, size).
Rails Basics
Create a Rails App
gem install rails rails new blog cd blog rails server
MVC Pattern
Rails follows Model-View-Controller: Models talk to the DB, Controllers handle requests, Views render HTML/JSON.
Scaffold a Resource
rails generate scaffold Post title:string body:text rails db:migrate rails server
Scaffold a Book resource with title and author.
Models & ActiveRecord
Model Definition
class Post < ApplicationRecord
belongs_to :user
has_many :comments
validates :title, presence: true, length: { minimum: 5 }
endQueries
Post.all Post.find(1) Post.where(published: true).order(created_at: :desc) Post.create(title: "Hello", body: "World") post.update(title: "New") post.destroy
Migrations
class AddAuthorToPosts < ActiveRecord::Migration[7.0]
def change
add_column :posts, :author, :string
end
endCreate a User-Post-Comment relationship with validations.
Controllers & Routes
Routes
Rails.application.routes.draw do root "posts#index" resources :posts get "/about", to: "pages#about" end
Controller
class PostsController < ApplicationController
def index = @posts = Post.all
def show = @post = Post.find(params[:id])
def create
@post = Post.new(post_params)
@post.save ? redirect_to(@post) : render(:new)
end
private
def post_params
params.require(:post).permit(:title, :body)
end
endBuild full CRUD routes and controller for a Book resource.
APIs & Deployment
API Mode
rails new myapi --api # Controllers return JSON automatically
class Api::PostsController < ApplicationController
def index
render json: Post.all
end
def create
post = Post.create!(post_params)
render json: post, status: :created
end
endDeploy to Heroku/Render
git push heroku main heroku run rails db:migrate heroku logs --tail
Build a JSON API for a Todo list, deploy to Render.com.
Capstone Project: Rails Blog
Project: Full Blog Platform
Build a complete Rails blog with authentication, posts, comments, tags, and admin dashboard.
Features
- User auth (Devise gem)
- Posts with categories & tags
- Comments with nested replies
- Admin dashboard
- Rich text editor (ActionText)
- Image uploads (ActiveStorage)
Stack
gem 'rails', '~> 7.0' gem 'devise' gem 'pg' gem 'image_processing' gem 'rspec-rails', group: :test
Bonus
- Add markdown support with Redcarpet
- Add full-text search with pg_search
- Build a JSON API for mobile
- Deploy with Docker + CI/CD
Build the full blog. Deploy it. Share the GitHub repo!