Table of Contents
- Introduction to Ruby
- Getting Started: Installing Ruby
- Ruby Syntax Basics
- Variables and Data Types
- Control Structures
- Methods and Blocks
- Object-Oriented Programming (OOP) in Ruby
- Exception Handling
- Modules and Mixins
- Metaprogramming (Optional Advanced Topic)
- Working with Gems
- Testing in Ruby
- Real-World Applications of Ruby
- Conclusion
- References
1. Introduction to Ruby
What Makes Ruby Unique?
- Developer-Centric: Ruby prioritizes human-readable code over strict performance (though it’s fast enough for most use cases).
- Object-Oriented: Everything in Ruby is an object (even numbers and strings), making it easy to model real-world problems.
- Expressive Syntax: Ruby’s syntax is concise and flexible. For example, you can write
[1,2,3].map { |x| x * 2 }to double each element in an array—no boilerplate required. - Rich Ecosystem: Thousands of open-source libraries (“gems”) and frameworks (like Rails) extend Ruby’s capabilities.
2. Getting Started: Installing Ruby
To start coding in Ruby, you’ll need to install it on your system. Here’s how:
Windows
Use RubyInstaller, a pre-packaged installer. Download the latest version (e.g., Ruby 3.2.x) and follow the setup wizard.
macOS
macOS pre-installs Ruby, but it’s often outdated. Use Homebrew for the latest version:
brew install ruby
Linux (Ubuntu/Debian)
Use apt:
sudo apt update
sudo apt install ruby-full
Version Management (Recommended)
For managing multiple Ruby versions, use tools like rbenv or RVM. For example, with rbenv:
# Install rbenv
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc
# Install Ruby 3.2.2
rbenv install 3.2.2
rbenv global 3.2.2 # Set as default
Verify Installation
Open a terminal and run:
ruby -v
You should see output like ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux].
3. Ruby Syntax Basics
Ruby’s syntax is designed to be readable. Let’s cover the essentials:
Comments
Use # for single-line comments, and =begin/=end for multi-line comments:
# This is a single-line comment
=begin
This is a
multi-line comment
=end
Indentation
Ruby uses 2 spaces for indentation (not tabs). Consistent indentation improves readability:
# Good
if 5 > 3
puts "5 is greater than 3"
end
# Bad (inconsistent indentation)
if 5 > 3
puts "This is messy"
end
Semicolons
Semicolons (;) are optional. Use them only to separate multiple statements on one line:
name = "Alice"; age = 30 # Valid but not recommended for readability
Case Sensitivity
Ruby is case-sensitive: name, Name, and NAME are distinct variables.
4. Variables and Data Types
Variables
Variables store data. Ruby has four main variable types, distinguished by prefixes:
| Type | Prefix | Example | Scope |
|---|---|---|---|
| Local Variables | None | count = 10 | Limited to the current method/block |
| Instance Variables | @ | @name = "Alice" | Shared across an object’s methods |
| Class Variables | @@ | @@total_users = 0 | Shared across all instances of a class |
| Global Variables | $ | $debug = true | Available everywhere (use sparingly!) |
Data Types
Ruby has built-in data types for common needs:
Strings
Text enclosed in quotes (" or '). Use #{} for interpolation:
name = "Bob"
greeting = "Hello, #{name}!" # "Hello, Bob!"
multiline = <<~HEREDOC # Heredoc for multi-line strings
This is a
multi-line string.
HEREDOC
Numbers
Integers (123), floats (3.14), and big integers (10**100 for very large numbers):
age = 25
pi = 3.1415
big_num = 10** 100 # 1000000000000000000000000000000...
Booleans and nil
true/false for logic, nil for “no value”:
is_active = true
empty_value = nil
Arrays
Ordered collections of elements (can mix data types):
fruits = ["apple", "banana", "cherry"]
mixed = [1, "hello", true, nil]
# Access elements (0-based index)
fruits[0] # "apple"
fruits[-1] # "cherry" (last element)
# Common methods
fruits.push("date") # Add to end: ["apple", "banana", "cherry", "date"]
fruits.pop # Remove last: "date" (fruits is now ["apple", "banana", "cherry"])
fruits.map { |f| f.upcase } # ["APPLE", "BANANA", "CHERRY"]
Hashes
Key-value pairs (like dictionaries in Python):
person = { name: "Alice", age: 30, hobbies: ["reading", "hiking"] } # Symbol keys (recommended)
# Or old syntax: { :name => "Alice", :age => 30 }
# Access values
person[:name] # "Alice"
person[:hobbies][0] # "reading"
# Add/modify keys
person[:city] = "Paris" # Now { name: "Alice", age: 30, ..., city: "Paris" }
5. Control Structures
Control structures let you dictate the flow of execution.
Conditionals
Use if/else, unless (inverse of if), and case statements:
if/else
age = 18
if age >= 18
puts "Adult"
else
puts "Minor"
end
unless
Use unless for “if not” logic:
unless age < 18
puts "Adult" # Equivalent to: if age >= 18
end
case Statements
Cleaner than nested if/else for multiple conditions:
grade = "B"
case grade
when "A" then puts "Excellent"
when "B" then puts "Good"
when "C" then puts "Fair"
else puts "Needs improvement"
end
Loops
Repeat code with while, until, for, and iterators like each:
while/until
count = 0
while count < 5
puts count
count += 1
end
# until (inverse of while)
count = 5
until count == 0
puts count
count -= 1
end
each Iterator (Most Ruby-like)
Iterate over arrays, hashes, etc., with blocks:
fruits = ["apple", "banana"]
fruits.each do |fruit| # do...end for multi-line blocks
puts "I like #{fruit}s"
end
# Single-line block with {}
(1..5).each { |num| puts num * 2 } # 2, 4, 6, 8, 10
6. Methods and Blocks
Methods
Reusable code defined with def:
def greet(name)
"Hello, #{name}!" # Implicit return (last line is returned)
end
greet("Alice") # "Hello, Alice!"
Parameters and Defaults
Add parameters with defaults for flexibility:
def add(a, b = 1) # b defaults to 1
a + b
end
add(5) # 6 (b=1)
add(5, 3) # 8
Blocks
Anonymous functions passed to methods (Ruby’s “secret sauce”). Use do...end (multi-line) or {} (single-line):
Using Blocks with Built-in Methods
# Example: Array#map transforms elements
numbers = [1, 2, 3]
doubled = numbers.map { |n| n * 2 } # [2, 4, 6]
# Example: Hash#each
person = { name: "Bob", age: 30 }
person.each { |key, value| puts "#{key}: #{value}" }
# Output:
# name: Bob
# age: 30
Custom Methods with Blocks
Use yield to call a block inside a method:
def my_method
yield("Hello from the block!") if block_given? # Check if a block was passed
end
my_method { |msg| puts msg } # "Hello from the block!"
Procs and Lambdas
Save blocks as objects with proc or lambda:
# Proc (less strict with arguments)
greet_proc = proc { |name| "Hello, #{name}!" }
greet_proc.call("Alice") # "Hello, Alice!"
# Lambda (strict with arguments)
greet_lambda = lambda { |name| "Hello, #{name}!" }
greet_lambda.call("Bob") # "Hello, Bob!"
7. Object-Oriented Programming (OOP) in Ruby
Ruby is purely object-oriented: every value is an object, and every operation is a method call.
Classes and Objects
A class is a blueprint for creating objects (instances). Use new to create objects:
class Person
def initialize(name, age) # Constructor (called when new is used)
@name = name
@age = age
end
def introduce
"Hi, I'm #{@name} and I'm #{@age} years old."
end
end
# Create an object
alice = Person.new("Alice", 30)
alice.introduce # "Hi, I'm Alice and I'm 30 years old."
Attributes
Use attr_* shortcuts to define getters/setters for instance variables:
class Person
attr_accessor :name, :age # Creates @name, @age with getters and setters
# attr_reader :name (read-only), attr_writer :age (write-only)
def initialize(name, age)
@name = name
@age = age
end
end
alice = Person.new("Alice", 30)
alice.name = "Alicia" # Setter (from attr_accessor)
alice.age # Getter: 30
Inheritance
Reuse code with class Child < Parent:
class Student < Person # Student inherits from Person
def initialize(name, age, major)
super(name, age) # Call parent's initialize
@major = major
end
def introduce # Override parent method
"#{super} I study #{@major}."
end
end
bob = Student.new("Bob", 22, "Computer Science")
bob.introduce # "Hi, I'm Bob and I'm 22 years old. I study Computer Science."
8. Exception Handling
Handle errors gracefully with begin, rescue, else, and ensure:
begin
result = 10 / 0 # This will raise a ZeroDivisionError
rescue ZeroDivisionError => e # Catch specific error
puts "Error: #{e.message}" # "Error: divided by 0"
else
puts "Result: #{result}" # Runs if no error
ensure
puts "This always runs (e.g., cleanup)"
end
9. Modules and Mixins
Modules group methods for reuse (like “libraries”). They can’t be instantiated but can be included in classes (mixins):
Example: A Swimmable Mixin
module Swimmable
def swim
"#{self.class} is swimming!"
end
end
class Fish
include Swimmable # "Mix in" Swimmable methods
end
class Duck
include Swimmable
end
Fish.new.swim # "Fish is swimming!"
Duck.new.swim # "Duck is swimming!"
10. Metaprogramming (Optional Advanced Topic)
Ruby lets you write code that writes code (metaprogramming). It’s powerful but use it sparingly!
Dynamic Method Definition
Use define_method to create methods on the fly:
class Greeter
["Alice", "Bob", "Charlie"].each do |name|
define_method("greet_#{name.downcase}") do
"Hello, #{name}!"
end
end
end
greeter = Greeter.new
greeter.greet_alice # "Hello, Alice!"
greeter.greet_bob # "Hello, Bob!"
11. Working with Gems
Gems are Ruby’s package manager for libraries. Use gem or Bundler to manage them.
Installing Gems
gem install colorize # Install the "colorize" gem (adds color to output)
Using Bundler
For projects with multiple gems, use Bundler to manage dependencies. Create a Gemfile:
# Gemfile
source "https://rubygems.org"
gem "colorize"
gem "rspec", "~> 3.12" # Version constraint
Install gems:
bundle install # Creates Gemfile.lock (locks versions)
Example: Using colorize
require "colorize"
puts "Hello, World!".red # Red text
puts "Ruby is fun!".green # Green text
12. Testing in Ruby
Testing ensures your code works as expected. RSpec is a popular testing framework.
Example RSpec Test
- Install RSpec:
gem install rspec - Create a file
calculator.rb:
class Calculator
def add(a, b)
a + b
end
end
- Create a test file
spec/calculator_spec.rb:
require "rspec"
require_relative "../calculator"
RSpec.describe Calculator do
describe "#add" do
it "returns the sum of two numbers" do
calculator = Calculator.new
expect(calculator.add(2, 3)).to eq(5)
end
end
end
- Run tests:
rspec spec/calculator_spec.rb
13. Real-World Applications of Ruby
Ruby powers tools across industries:
Web Development
- Ruby on Rails: Full-stack web framework (used by GitHub, Shopify, Airbnb).
- Sinatra: Lightweight micro-framework for small apps.
DevOps
- Chef/Puppet: Configuration management tools.
- Capistrano: Deployment automation.
Data Science
Libraries like Nokogiri (web scraping) and Pandas (via Ruby wrappers) for data processing.
14. Conclusion
Ruby is a joy to learn and use, with a focus on developer happiness and readability. From basics like variables and loops to advanced OOP and metaprogramming, Ruby offers tools for every task. Whether you’re building web apps with Rails or automating workflows, Ruby’s flexibility and community support make it a top choice.
15. References
- Official Docs: Ruby Documentation
- Books: “Programming Ruby” (The Pickaxe Book) by Dave Thomas, “Ruby on Rails Tutorial” by Michael Hartl
- Courses: Codecademy Ruby Course, Udemy: The Complete Ruby Programming Masterclass
- Communities: Stack Overflow Ruby Tag, Reddit r/ruby
Happy coding! 🚀