Ruby is a dynamic, object-oriented language designed for programmer happiness. Created by Yukihiro Matsumoto ("Matz") in 1995, it's known for its elegant syntax and Rails framework.
Why Ruby?
- Elegant — Designed to be enjoyable to write and read
- Object-oriented — Everything is an object (even numbers and booleans)
- Expressive — Closures, blocks, and metaprogramming
- Rails — The most productive web framework for startups
- Convention over configuration — sensible defaults
- Community — Friendly, welcoming, and passionate
Hello World
Ruby
# Your first Ruby program
puts "Hello, World!"
# Everything is an object!
puts 42.class # Integer
puts 3.14.class # Float
puts "hello".class # String
puts true.class # TrueClass
# String methods (everything is an object)
puts "hello".upcase # "HELLO"
puts "hello".reverse # "olleh"
puts "hello".length # 5
puts "hello".include?("ell") # true
puts "hello world".split # ["hello", "world"]Variables and Types
Ruby
# Variables (snake_case by convention)
name = "Alice" # String
age = 28 # Integer
pi = 3.14159 # Float
active = true # Boolean
nothing = nil # Ruby's null
# Constants (start with uppercase — Ruby warns if you reassign)
MAX_RETRIES = 3
PI = 3.14159
# Type hints (optional, Ruby 2.7+)
def greet(name : String) : String
"Hello, #{name}!"
end
# String interpolation (double quotes only)
puts "Hello, #{name}!"
puts "2 + 2 = #{2 + 2}"
puts 'No interpolation here: #{name}' # Single quotes are literalControl Flow
Ruby
# if/elsif/else/end
score = 85
if score >= 90
puts "A"
elsif score >= 80
puts "B"
elsif score >= 70
puts "C"
else
puts "F"
end
# Ternary operator
status = age >= 18 ? "adult" : "minor"
# One-liner if (modifier form)
puts "Pass" if score >= 60
puts "Fail" unless score >= 60 # Unless = if not
# case/when (powerful pattern matching)
grade = case score
when 90..100 then "A"
when 80..89 then "B"
when 70..79 then "C"
when 60..69 then "D"
else "F"
end
# Loops
5.times { |i| puts i } # 0, 1, 2, 3, 4
1.upto(5) { |i| puts i } # 1, 2, 3, 4, 5
10.downto(1) { |i| puts i } # 10, 9, ..., 1
(1..5).each { |i| puts i } # 1, 2, 3, 4, 5
[1, 2, 3].each { |x| puts x * 2 } # 2, 4, 6
# While/until
i = 0
while i < 5
puts i
i += 1
end
until i == 0
puts i
i -= 1
endBlocks, Procs, and Lambdas
Ruby
# Blocks (most common)
5.times do |i|
puts i
end
[1, 2, 3].each do |num|
puts num * 2
end
# Shorthand block (one-liner)
[1, 2, 3].map { |n| n * 2 } # [2, 4, 6]
# Procs (stored blocks)
square = Proc.new { |x| x ** 2 }
puts square.call(5) # 25
# Lambdas (strict procs)
double = lambda { |x| x * 2 }
# Or shorthand
double = -> (x) { x * 2 }
puts double.call(5) # 10
# Yield (call block from method)
def measure
start = Time.now
yield # Execute the block
elapsed = Time.now - start
puts "Completed in #{elapsed}s"
end
measure do
sleep(1)
end
# "Completed in 1.001s"Collections
Ruby
# Arrays
fruits = ["apple", "banana", "cherry"]
fruits.push("date") # Add to end
fruits.pop # Remove from end
fruits[0] # "apple"
fruits[-1] # "cherry" (last element)
fruits.first # "apple"
fruits.last # "cherry"
fruits.length # 4
fruits.include?("apple") # true
# Array methods
[1, 2, 3, 4, 5].map { |n| n * 2 } # [2, 4, 6, 8, 10]
[1, 2, 3, 4, 5].select { |n| n > 3 } # [4, 5]
[1, 2, 3, 4, 5].reject { |n| n > 3 } # [1, 2, 3]
[1, 2, 3, 4, 5].reduce(0, :+) # 15
[3, 1, 4, 1, 5].sort # [1, 1, 3, 4, 5]
[3, 1, 4, 1, 5].uniq # [3, 1, 4, 5]
# Hashes
person = { name: "Alice", age: 28, email: "alice@test.com" }
# Or string keys
person = { "name" => "Alice", "age" => 28 }
puts person[:name] # "Alice"
puts person.fetch(:email) # "alice@test.com"
person[:phone] = "555-1234" # Add
person.delete(:email) # Remove
# Iterating hashes
person.each do |key, value|
puts "#{key}: #{value}"
end
# Ranges
(1..5).to_a # [1, 2, 3, 4, 5]
(1...5).to_a # [1, 2, 3, 4] (exclusive end)
("a".."e").to_a # ["a", "b", "c", "d", "e"]Classes
Ruby
class User
attr_accessor :name, :email # Create getters and setters
attr_reader :created_at # Read-only
def initialize(name, email)
@name = name # Instance variables start with @
@email = email
@created_at = Time.now
end
def greet
"Hello, I'm #{@name}!"
end
def to_s
"#{name} (#{email})"
end
end
user = User.new("Alice", "alice@example.com")
puts user.greet # "Hello, I'm Alice!"
puts user.name # "Alice"
user.email = "new@test.com"
# Inheritance
class Admin < User
attr_accessor :permissions
def initialize(name, email, permissions = [])
super(name, email) # Call parent constructor
@permissions = permissions
end
def can?(permission)
permissions.include?(permission)
end
end
admin = Admin.new("Bob", "bob@test.com", ["edit", "delete"])
puts admin.can?("edit") # true
puts admin.greet # "Hello, I'm Bob!" (inherited)Knowledge Check
4 questions — test your understanding
1
What does `puts` do in Ruby?
2
What is the difference between a Proc and a Lambda in Ruby?
3
What does `[1,2,3].map { |n| n * 2 }` return?
4
What does `attr_accessor` create in a Ruby class?
Next: Ruby Methods