</>
CodeLearn Pro
Start Learning Free โ†’

Ruby โ€” Complete Beginner Tutorial

6 modules ยท 18 examples ยท Click โ–ถ Run Code to see the console output

โœฆ Beginnerโ† Overview

๐Ÿ’ก How to use this tutorial

Read each explanation, study the code, then click โ–ถ Run Code to see the output.

Module 01

Variables & Basics

Ruby is one of the most beginner-friendly languages ever created. There are no semicolons, no curly braces, and no type declarations โ€” you just write what you want to happen and Ruby does it. Variables start with a lowercase letter. puts prints to the screen with a new line. print prints without a new line. String interpolation with #{} lets you embed any expression directly into a string.

Variables and Output

Ruby variables are simple โ€” just write a name, an equals sign, and the value. No var, let, or type needed. puts (put string) prints to the screen and adds a new line. print does the same but without the new line. p prints the raw value including quotes โ€” useful for debugging.

example.rbRuby
# Variables โ€” no keywords needed
name    = "Alice"
age     = 25
height  = 1.72
is_cool = true

# puts โ€” print with a new line
puts "Hello, #{name}!"
puts "Age: #{age}"
puts "Height: #{height}m"
puts "Cool? #{is_cool}"

# print โ€” no new line at end
print "First "
print "Second "
print "Third"
puts   # just a blank line

# p โ€” shows the raw value (great for debugging)
p name
p age
p [1, 2, 3]

# Multiple assignment
x, y, z = 10, 20, 30
puts "#{x}, #{y}, #{z}"
โ–ถ Click Run Code to see output

In Ruby, the # symbol starts a comment โ€” everything after it on that line is ignored. Use comments to explain why you wrote something, not just what it does. Ruby developers also use # for string interpolation inside double-quoted strings: "Hello, #{name}".

Strings and Their Methods

In Ruby, strings are objects with built-in methods. You call methods by putting a dot after the string followed by the method name. This makes string manipulation feel natural and readable. Ruby has dozens of built-in string methods โ€” here are the ones you will use most often.

example.rbRuby
greeting = "Hello, World!"

# Case methods
puts greeting.upcase        # ALL CAPS
puts greeting.downcase      # all lower
puts greeting.swapcase      # sWAP cASE
puts greeting.capitalize    # First letter up

# Checking and searching
puts greeting.length        # character count
puts greeting.include?("World")  # true/false
puts greeting.start_with?("Hello")
puts greeting.end_with?("!")

# Modifying
puts greeting.reverse
puts greeting.gsub("World", "Ruby")  # replace all
puts greeting.sub("Hello", "Hi")     # replace first

# Trimming and splitting
messy = "   too much space   "
puts messy.strip             # remove leading/trailing spaces
puts messy.lstrip            # remove left only

sentence = "one two three"
words = sentence.split(" ")
puts words.inspect
puts words.join(", ")
โ–ถ Click Run Code to see output

Notice that methods ending with ? return true or false โ€” this is a Ruby convention. Methods ending with ! usually modify the object in place (mutate it) rather than returning a new value. For example, str.upcase returns a new string, but str.upcase! changes str itself.

Numbers and Maths

Ruby has Integer (whole numbers) and Float (decimal numbers). Maths operators work as expected. Integer division drops the decimal โ€” 7 / 2 is 3, not 3.5. Use a Float in the division to get a decimal result. The ** operator raises to a power. % is modulo (the remainder after division).

example.rbRuby
# Integer maths
puts 10 + 3    # 13
puts 10 - 3    # 7
puts 10 * 3    # 30
puts 10 / 3    # 3  (integer division โ€” drops decimal!)
puts 10 % 3    # 1  (remainder)
puts 10 ** 3   # 1000 (power)

# Float maths โ€” keeps the decimal
puts 10.0 / 3  # 3.3333...
puts 7.5 + 2.5

# Conversion methods
puts 42.to_f   # convert integer to float -> 42.0
puts 3.9.to_i  # convert float to integer -> 3 (truncates!)
puts "25".to_i # convert string to integer -> 25
puts "3.14".to_f

# Useful number methods
puts -15.abs   # absolute value -> 15
puts 3.14159.round(2)  # round to 2 decimal places -> 3.14
puts 9.7.ceil          # round up -> 10
puts 9.2.floor         # round down -> 9

# Random number
puts rand(100)          # random 0 to 99
โ–ถ Click Run Code to see output

The difference between 10 / 3 (gives 3) and 10.0 / 3 (gives 3.333) catches many beginners off guard. Ruby performs integer division when both operands are integers. Simply make one of them a float โ€” either write it as 10.0, or call .to_f on it โ€” to get the decimal result.

Module 02

Control Flow

Ruby has all the standard control flow structures, but with a clean twist โ€” you can put conditions at the end of a line (called a "trailing conditional"). This often makes code read more like English. Ruby uses elsif (not else if or elseif) for additional branches. The unless keyword is the opposite of if โ€” it runs when the condition is false.

if, elsif, else and unless

The if statement runs code when a condition is true. elsif adds more branches. else catches everything that did not match. unless is like if but inverted โ€” it runs when the condition is false. Trailing conditions let you write a one-liner: puts "hello" if name == "Alice".

example.rbRuby
score = 75

# Standard if / elsif / else
if score >= 90
  puts "Grade: A โ€” Excellent!"
elsif score >= 75
  puts "Grade: B โ€” Good job!"
elsif score >= 60
  puts "Grade: C โ€” Average"
elsif score >= 50
  puts "Grade: D โ€” Just passed"
else
  puts "Grade: F โ€” Try again"
end

# unless โ€” runs when condition is FALSE
temperature = 8
unless temperature > 20
  puts "It's cold today โ€” bring a jacket!"
end

# Trailing conditions โ€” very Ruby-ish
age = 17
puts "You can vote!" if age >= 18
puts "You cannot vote yet." unless age >= 18

# Ternary operator โ€” one-line if/else
status = age >= 18 ? "adult" : "minor"
puts "Status: #{status}"
โ–ถ Click Run Code to see output

The trailing if and unless (called "postfix conditionals") are one of Ruby's most beloved features. puts "done" if success.zero? reads almost like a sentence. Use this style for simple one-liners. For multi-line logic, use the standard block form with end.

case / when โ€” Pattern Matching

Ruby's case/when is more powerful than a simple switch statement. It can match against values, ranges, regular expressions, and even classes. When no condition matches, the else block runs. The case expression returns a value, so you can assign it directly to a variable.

example.rbRuby
# Basic case / when
day = "Wednesday"

case day
when "Monday"
  puts "Start of the week โ€” let's go!"
when "Tuesday"
  puts "Tuesday energy!"
when "Wednesday"
  puts "Midweek already!"
when "Thursday"
  puts "Almost Friday..."
when "Friday"
  puts "TGIF!"
when "Saturday", "Sunday"
  puts "Weekend โ€” rest and recharge!"
else
  puts "Not a valid day"
end

# case with ranges
age = 22
category = case age
when 0..12   then "Child"
when 13..17  then "Teenager"
when 18..64  then "Adult"
when 65..    then "Senior"
end
puts "Age category: #{category}"

# case matching on type (class)
value = 3.14

case value
when Integer
  puts "It's an integer"
when Float
  puts "It's a float"
when String
  puts "It's a string"
end
โ–ถ Click Run Code to see output

Ruby case/when uses the === operator (triple equals) to test each condition. This is why it can match ranges (18..64 === 22 returns true), classes (Integer === 42 returns true), and regex patterns. This is far more flexible than switch statements in other languages.

Module 03

Loops & Iterators

Ruby has traditional loops (while, until, loop) but the Ruby way to repeat things is through iterators โ€” methods that take a block of code and run it multiple times. The most common are times (repeat N times), each (loop over a collection), map (transform a collection), select (filter a collection), and reduce (combine values into one). Blocks are the curly braces or do...end that you pass to these methods.

times and each

The times method is the simplest way to repeat code. Call it on any integer. The each method is how you loop over an array or range โ€” it gives you each element one at a time inside the block variable (the name between the | pipes). Blocks can be written with {} for one-liners or do...end for multiple lines.

example.rbRuby
# times โ€” repeat exactly N times
5.times do |i|
  puts "Iteration #{i}"   # i goes 0, 1, 2, 3, 4
end

puts "---"

# upto and downto
1.upto(5) { |n| print "#{n} " }
puts
5.downto(1) { |n| print "#{n} " }
puts

puts "---"

# each on an array
fruits = ["apple", "banana", "cherry", "mango"]
fruits.each do |fruit|
  puts "I like #{fruit}"
end

puts "---"

# each on a range
(1..5).each { |n| puts n * n }   # squares

puts "---"

# each_with_index โ€” get the element AND its index
fruits.each_with_index do |fruit, index|
  puts "#{index + 1}. #{fruit.capitalize}"
end
โ–ถ Click Run Code to see output

The variable inside the pipes |fruit| is called the block parameter. You can name it anything descriptive. Convention is to use the singular of the collection name โ€” if your collection is called users, name the block param user. If it is numbers, name it num or number.

map, select and reduce

These three methods are the most powerful iterators in Ruby. map transforms every element and returns a new array. select keeps only elements where the block returns true. reject is the opposite of select. reduce combines all elements into a single value using an accumulator.

example.rbRuby
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# map โ€” transform every element
doubled = numbers.map { |n| n * 2 }
puts "Doubled: #{doubled.inspect}"

squared = numbers.map { |n| n ** 2 }
puts "Squared: #{squared.inspect}"

# select โ€” keep only matching elements
evens = numbers.select { |n| n.even? }
puts "Evens: #{evens.inspect}"

over_five = numbers.select { |n| n > 5 }
puts "Over 5: #{over_five.inspect}"

# reject โ€” remove matching elements (opposite of select)
odds = numbers.reject { |n| n.even? }
puts "Odds: #{odds.inspect}"

# reduce / inject โ€” combine all into one value
total = numbers.reduce(0) { |sum, n| sum + n }
puts "Total: #{total}"

product = numbers.reduce(1) { |prod, n| prod * n }
puts "Product: #{product}"

# Method shorthand โ€” even cleaner!
puts numbers.sum            # built-in sum
puts numbers.min
puts numbers.max
puts numbers.sort.inspect
โ–ถ Click Run Code to see output

You can chain these methods together. numbers.select { |n| n.even? }.map { |n| n * 10 } first keeps only even numbers, then multiplies each by 10. This chaining style is one of the most elegant things about Ruby and is used constantly in real code.

Module 04

Methods

In Ruby, functions are called methods and are defined with the def keyword. Every method in Ruby returns a value โ€” the last evaluated expression is the implicit return value (you do not need to write return unless you want to exit early). Methods can have positional arguments, default values, and keyword arguments. Question mark methods (?) return booleans. Bang methods (!) modify in place.

Defining and Calling Methods

Use def to start a method and end to close it. Call a method by just writing its name. Ruby does not require parentheses when calling methods without arguments. The last expression in the method is automatically returned โ€” no return keyword needed.

example.rbRuby
# Simple method โ€” no parameters
def say_hello
  "Hello, World!"   # this is returned automatically
end

puts say_hello

# Method with parameters
def greet(name, greeting = "Hello")
  "#{greeting}, #{name}!"
end

puts greet("Alice")
puts greet("Bob", "Good morning")

# Multiple return values (returns an array)
def min_and_max(numbers)
  [numbers.min, numbers.max]
end

low, high = min_and_max([3, 1, 9, 2, 7])
puts "Min: #{low}, Max: #{high}"

# Method with a question mark โ€” returns boolean
def adult?(age)
  age >= 18
end

puts adult?(20)
puts adult?(15)

# Calculating with a method
def bmi(weight_kg, height_m)
  (weight_kg / height_m ** 2).round(1)
end

puts "BMI: #{bmi(70, 1.75)}"
โ–ถ Click Run Code to see output

The implicit return (last expression is returned automatically) is one of Ruby's nicest features. You will quickly stop writing return and rely on this. The only time to use explicit return is when you want to exit a method early โ€” for example inside an if statement before reaching the end.

Keyword Arguments and Blocks

Keyword arguments let you pass arguments by name, making method calls much clearer. You can also define methods that accept a block using yield โ€” this is how Ruby's iterators like each and map work internally.

example.rbRuby
# Keyword arguments
def create_user(name:, age:, role: "member")
  "#{role.upcase}: #{name} (#{age})"
end

puts create_user(name: "Alice", age: 25)
puts create_user(name: "Bob", age: 30, role: "admin")

# Order does not matter with keyword args!
puts create_user(age: 22, name: "Carol")

# Method that accepts a block with yield
def repeat(times)
  times.times { yield }
end

repeat(3) { puts "Ruby is great!" }

# Block with a value passed to it
def transform(value)
  result = yield(value)
  "Transformed: #{result}"
end

puts transform(5) { |n| n * 10 }
puts transform("hello") { |s| s.upcase }

# Check if a block was given
def optional_block(value)
  if block_given?
    yield(value)
  else
    value
  end
end

puts optional_block(10) { |n| n + 5 }
puts optional_block(10)
โ–ถ Click Run Code to see output

Keyword arguments (name: value) are preferred over positional arguments when a method has more than two or three parameters. They make the call site self-documenting โ€” create_user(name: "Alice", age: 25) is much clearer than create_user("Alice", 25) where you have to check the method definition to know the order.

Module 05

Classes & Objects

Everything in Ruby is an object โ€” even integers and strings. You can create your own object types using class. The initialize method runs when you create a new object with ClassName.new. Instance variables (starting with @) belong to that specific object. attr_accessor is a shortcut that automatically creates getter and setter methods for your instance variables.

Creating Classes

Define a class with the class keyword. The initialize method is the constructor โ€” it runs when you call ClassName.new. Instance variables start with @. attr_reader gives read-only access. attr_writer gives write-only access. attr_accessor gives both. Methods defined inside the class become the object's behaviour.

example.rbRuby
class Person
  # Creates getter and setter for name and age
  attr_accessor :name, :age

  # initialize runs when Person.new is called
  def initialize(name, age)
    @name = name   # @ means instance variable
    @age  = age
  end

  def introduce
    "Hi! I'm #{@name} and I'm #{@age} years old."
  end

  def birthday!
    @age += 1
    puts "Happy birthday #{@name}! Now #{@age}."
  end

  def adult?
    @age >= 18
  end

  # to_s โ€” called when object is converted to string
  def to_s
    "Person(#{@name}, #{@age})"
  end
end

# Create objects
alice = Person.new("Alice", 25)
bob   = Person.new("Bob", 16)

puts alice.introduce
puts bob.introduce

# Using attr_accessor
puts alice.name     # getter
alice.name = "Alice Smith"  # setter
puts alice.name

puts alice.adult?
puts bob.adult?

alice.birthday!

puts alice   # calls to_s automatically
โ–ถ Click Run Code to see output

attr_accessor :name creates two methods behind the scenes: a getter (def name; @name; end) and a setter (def name=(value); @name = value; end). This is called metaprogramming โ€” Ruby generating code for you. It is one of the most powerful and elegant features of the language.

Inheritance with <

Use < to inherit from another class. The child class gets all the parent's methods automatically. Override a parent method by defining it again in the child class. Call the parent's version of a method using super. Class variables (@@) are shared across all instances of a class and its subclasses.

example.rbRuby
class Animal
  attr_reader :name

  def initialize(name)
    @name = name
  end

  def speak
    "..."
  end

  def describe
    "#{@name} says: #{speak}"
  end
end

# Dog inherits from Animal using <
class Dog < Animal
  def speak
    "Woof!"
  end

  def fetch(item)
    "#{@name} fetches the #{item}!"
  end
end

class Cat < Animal
  def speak
    "Meow!"
  end

  def purr
    "Purrrrr..."
  end
end

class Duck < Animal
  def speak
    "Quack!"
  end
end

# Create objects
dog  = Dog.new("Buddy")
cat  = Cat.new("Whiskers")
duck = Duck.new("Donald")

# describe is inherited from Animal
puts dog.describe
puts cat.describe
puts duck.describe

# Methods specific to each class
puts dog.fetch("ball")
puts cat.purr

# Polymorphism โ€” same method, different behaviour
animals = [dog, cat, duck]
animals.each { |a| puts a.speak }
โ–ถ Click Run Code to see output

The last three lines demonstrate polymorphism โ€” calling the same method (speak) on different types of objects and getting different results. This is one of the key benefits of inheritance. The animals array can hold Dogs, Cats, and Ducks and treat them all the same through the Animal interface.

Module 06

Arrays & Hashes

Arrays hold ordered lists of items (indexed from 0). Hashes hold key-value pairs โ€” like a dictionary. Ruby arrays and hashes have dozens of built-in methods that make working with collections elegant and concise. In modern Ruby, hash keys are usually symbols (:name) rather than strings ("name") โ€” symbols are faster and use less memory.

Arrays โ€” Ordered Lists

Arrays are created with square brackets. Access elements by index (starting from 0). Negative indexes count from the end (-1 is the last element). Ruby arrays have methods for adding, removing, searching, sorting, and transforming data built right in.

example.rbRuby
# Creating arrays
fruits  = ["apple", "banana", "cherry"]
numbers = [3, 1, 4, 1, 5, 9, 2, 6]
mixed   = [1, "hello", true, 3.14]

# Accessing elements
puts fruits[0]      # first element
puts fruits[-1]     # last element
puts fruits.first
puts fruits.last
puts fruits[1, 2].inspect  # 2 elements starting at index 1

# Adding and removing
fruits.push("mango")         # add to end
fruits << "grape"            # same as push
fruits.unshift("avocado")    # add to beginning
puts fruits.inspect

removed = fruits.pop         # remove from end
puts "Removed: #{removed}"
puts fruits.inspect

# Finding and checking
puts fruits.include?("banana")
puts fruits.index("cherry")
puts fruits.length

# Sorting and transforming
puts numbers.sort.inspect
puts numbers.sort.reverse.inspect
puts numbers.uniq.inspect    # remove duplicates
puts numbers.min
puts numbers.max
puts numbers.sum
โ–ถ Click Run Code to see output

The << operator (shovel operator) for adding to arrays is very Ruby. You will see it everywhere โ€” array << item, string << "more text". It is faster than + for adding to arrays because it modifies the array in place rather than creating a new one.

Hashes โ€” Key-Value Pairs

Hashes store data as key-value pairs. Modern Ruby uses symbols as keys (:name) which is faster and cleaner. Access a value with hash[:key]. Use each to iterate over all pairs. Hashes have many useful methods like keys, values, merge, and select.

example.rbRuby
# Creating a hash with symbol keys
person = {
  name:  "Alice",
  age:   25,
  city:  "Cape Town",
  skill: "Ruby"
}

# Accessing values
puts person[:name]
puts person[:age]
puts person[:city]

# Adding and updating
person[:email] = "alice@example.com"
person[:age]   = 26     # update existing
puts person.inspect

# Checking
puts person.key?(:name)
puts person.key?(:phone)
puts person.value?("Alice")

# Iterating
person.each do |key, value|
  puts "  #{key}: #{value}"
end

# Useful methods
puts person.keys.inspect
puts person.values.inspect
puts person.length

# Merge two hashes
defaults = { role: "user", active: true }
full     = defaults.merge(person)
puts "Role: #{full[:role]}"

# select / reject on hashes
young_fields = person.select { |k, v| v.is_a?(Integer) }
puts young_fields.inspect
โ–ถ Click Run Code to see output

When you define a hash with symbol keys you can use either { name: "Alice" } (new style) or { :name => "Alice" } (old style with hash rocket). The new style (name:) is preferred in modern Ruby because it is shorter and easier to read. Both work identically.

You finished the Ruby tutorial!

You can now write Ruby with variables, control flow, loops, iterators, methods, classes and collections. You have the foundation to start learning Ruby on Rails and build real web applications.