👑 Premium Sign in Sign up

Language lab · Beginner → Web

💎 Ruby

Concept → trace → write code → run tests → explain why → improve → mini-project. Start in the browser if you like; set up your own computer when you are ready.

💻 Runs on your computerOfficial download & docs ↗

📖 Topics with real code (14)

Every verified output below was produced by running the code with ruby 3.3.13 on 6 Nov 2026. Open a topic to edit the code and run it right here in your browser.

🌱 Beginner (5)

Variables and data types

Everything in Ruby is an object, even numbers. Integer, Float, String, true/false and nil are the basic types; .class shows the type and to_i, to_f, to_s convert.

age = 15
height = 1.62
student = true
name = "Asha"
puts "#{name} #{age} #{height} #{student}"
puts [age.class, height.class, student.class, name.class, nil.class].join(" ")
puts "88".to_i + 2, 7 / 2, 7.0 / 2, 9.99.to_i

✅ Verified output

Asha 15 1.62 true
Integer Float TrueClass String NilClass
90
3
3.5
9
Operators and boolean logic

&& || ! work as in other languages; == compares values, .equal? compares identity. Integer division drops the fraction; ** is power.

a, b = 17, 5
puts [a + b, a - b, a * b, a / b, a % b, a ** 2].join(" ")
has_ticket, vip = true, false
puts "enter: #{has_ticket && !vip}, either: #{has_ticket || vip}"
puts "abc" == "abc", "abc".equal?("abc")
puts 5.between?(1, 10), (1..10).include?(11)

✅ Verified output

22 12 85 3 2 289
enter: true, either: true
true
false
true
false
Conditionals: if, unless, case

if/elsif/else picks a branch, unless is "if not", and case/when matches values or ranges. Every expression returns a value.

marks = 78
grade = if marks >= 90 then "A+"
        elsif marks >= 75 then "A"
        elsif marks >= 35 then "Pass"
        else "Fail"
        end
puts "Grade: #{grade}"
day = "sat"
puts case day when "sat", "sun" then "Weekend" else "School day" end
puts "try again" unless marks >= 35
puts(marks >= 35 ? "passed" : "failed")

✅ Verified output

Grade: A
Weekend
passed
Loops and iterators

Ruby prefers iterators: each, times, upto, step. while and until exist too; next skips and break stops.

3.times { |i| print i, " " }
puts
1.upto(5) do |i|
  next if i == 3
  puts "upto #{i}"
end
n = 10
until n < 4
  puts "until #{n}"
  n -= 3
end
(1..3).each { |r| puts (1..3).map { |c| (r * c).to_s.rjust(3) }.join }

✅ Verified output

0 1 2 
upto 1
upto 2
upto 4
upto 5
until 10
until 7
until 4
  1  2  3
  2  4  6
  3  6  9
Methods and blocks

def defines a method; the last expression is returned. Default and keyword arguments are common, and a block can be passed to a method and called with yield.

def area(length, width = 1) = length * width
def greet(name, greeting: "Namaste") = "#{greeting}, #{name}!"
def twice
  yield
  yield
end
puts area(5, 3), area(7)
puts greet("Ravi"), greet("Meena", greeting: "Hello")
twice { puts "block ran" }

✅ Verified output

15
7
Namaste, Ravi!
Hello, Meena!
block ran
block ran

🌿 Intermediate (6)

Arrays

Arrays are ordered and grow. map, select, reduce, sort and each_slice transform them; negative indexes count from the end.

marks = [72, 95, 64, 88]
marks << 91
puts marks.sort.inspect, "first #{marks.min} last #{marks.max} top2 #{marks.max(2).inspect}"
puts (1..5).select(&:odd?).map { |x| x * x }.inspect
puts marks.sum / marks.size.to_f
puts marks.each_slice(2).to_a.inspect

✅ Verified output

[64, 72, 88, 91, 95]
first 64 last 95 top2 [95, 91]
[1, 9, 25]
82.0
[[72, 95], [64, 88], [91]]
Strings

Strings are mutable and support interpolation #{...}. Useful methods: upcase, sub/gsub, split, center, reverse, scan.

t = "  GayaGnan learning  ".strip
puts t.upcase, t.sub("learning", "labs"), t.length
puts t.split.join("-")
puts format("Total: %.2f for %d items", 49.5 * 3, 3)
puts "level" == "level".reverse
puts "a1b22c333".scan(/\d+/).inspect

✅ Verified output

GAYAGNAN LEARNING
GayaGnan labs
17
GayaGnan-learning
Total: 148.50 for 3 items
true
["1", "22", "333"]
Hashes and symbols

A Hash maps keys to values; symbols (:pen) are light, fixed names often used as keys. tally counts items in one call.

stock = { pen: 40, book: 12 }
stock[:eraser] = 25
stock[:pen] -= 5
stock.sort.each { |item, count| puts "#{item} #{count}" }
puts stock.key?(:ruler), stock.fetch(:ruler, 0)
puts "the cat and the hat and the bat".split.tally.inspect

✅ Verified output

book 12
eraser 25
pen 35
false
0
{"the"=>3, "cat"=>1, "and"=>2, "hat"=>1, "bat"=>1}
Classes, objects and attr_reader

class defines a type; initialize runs on new; attr_reader makes read-only accessors; raise signals an error.

class BankAccount
  attr_reader :owner, :balance
  def initialize(owner, balance = 0)
    @owner, @balance = owner, balance
  end
  def deposit(amount)
    raise ArgumentError, "amount must be positive" unless amount.positive?
    @balance += amount
    self
  end
end
acc = BankAccount.new("Asha", 500).deposit(250)
puts "#{acc.owner} #{acc.balance}"

✅ Verified output

Asha 750
Inheritance and modules (mixins)

A class inherits with <; modules add shared behaviour to many classes with include (Comparable gives <, >, between? from <=>).

class Shape
  include Comparable
  def <=>(other) = area <=> other.area
  def to_s = "#{self.class.name} #{format('%.2f', area)}"
end
class Circle < Shape
  def initialize(r) = @r = r
  def area = Math::PI * @r**2
end
class Rect < Shape
  def initialize(w, h) = (@w, @h = w, h)
  def area = @w * @h
end
shapes = [Rect.new(3, 4), Circle.new(1)]
puts shapes.sort.map(&:to_s)
puts shapes.max.to_s

✅ Verified output

Circle 3.14
Rect 12.00
Rect 12.00
Exceptions

begin/rescue/ensure handles errors; retry tries again; custom errors subclass StandardError.

class InsufficientFunds < StandardError; end
def withdraw(balance, amount)
  raise InsufficientFunds, "need #{amount - balance} more" if amount > balance
  balance - amount
end
begin
  puts 10 / 0
rescue ZeroDivisionError => e
  puts "ZeroDivisionError: #{e.message}"
ensure
  puts "ensure always runs"
end
begin
  withdraw(100, 150)
rescue InsufficientFunds => e
  puts "Custom: #{e.message}"
end
puts Integer("abc", exception: false).inspect

✅ Verified output

ZeroDivisionError: divided by 0
ensure always runs
Custom: need 50 more
nil

🌳 Advanced (3)

Recursion and memoization

A Hash with a default block caches results automatically, turning slow recursive Fibonacci into a fast one. Ruby integers grow without overflow.

fib = Hash.new { |h, n| h[n] = n < 2 ? n : h[n - 1] + h[n - 2] }
puts (0..9).map { |i| fib[i] }.inspect
puts fib[100]
def bsearch(a, t) = a.bsearch_index { |x| x >= t }.then { |i| i && a[i] == t ? i : -1 }
puts bsearch([2, 5, 8, 12, 16, 23, 38], 23)

✅ Verified output

[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
354224848179261915075
5
Procs, lambdas and Enumerable chains

Lambdas are function values; compose with >> and <<. Lazy enumerators handle infinite sequences.

square = ->(x) { x * x }
inc = ->(x) { x + 1 }
puts (square >> inc).call(5), (square << inc).call(5)
students = [["Asha", 88], ["Ravi", 95], ["Meena", 72]]
puts students.sort_by { |_, m| -m }.map(&:first).inspect
puts (1..Float::INFINITY).lazy.map { |x| x * 3 }.select(&:even?).first(4).inspect

✅ Verified output

26
36
["Ravi", "Asha", "Meena"]
[6, 12, 18, 24]
Structs, Data and pattern matching

Data.define makes immutable value objects; case/in destructures arrays and hashes (pattern matching).

Point = Data.define(:x, :y)
p1 = Point.new(x: 3, y: 4)
puts p1.inspect, p1.with(y: 0).inspect
event = { type: "login", user: { name: "Asha", role: "student" } }
case event
in { type: "login", user: { name: String => who, role: "student" } }
  puts "student login: #{who}"
in { type: "login" }
  puts "other login"
end

✅ Verified output

#<data Point x=3, y=4>
#<data Point x=3, y=0>
student login: Asha

🛠️ Set up and verify

  1. Install
    winget install RubyInstallerTeam.Ruby.3.3
    brew install ruby
    sudo apt install ruby-full
  2. Editor: Visual Studio Code with Ruby LSP (Shopify).
  3. Create hello.rb
    puts 'Hello, GayaGnan!'
  4. Run it and check the version
    hello.rb
    ruby --version
  5. Health check: paste what your terminal printed.

Install one language at a time. These commands only install or print versions; nothing here deletes files. Versions change: check the official page and note the version you installed.

🧭 The 14-lab path

  • 00 · Setup and verify — install the runtime and editor, run hello world, print the version, fix PATH
  • 01 · Syntax and trace — follow each line and the value of every variable
  • 02 · Logic — conditionals and decision trees
  • 03 · Loops — trace tables and loop challenges
  • 04 · Functions — break problems down and test each piece
  • 05 · Data structures — lists, maps, stacks, queues and trees you can see
  • 06 · Debugging — bug cases with a hint ladder
  • 07 · Algorithms — search, sort and Big-O comparison
  • 08 · Files and data — read and write fixture files
  • 09 · API or database — talk to a mock API or a sandbox database
  • 10 · Testing — write tests and read failing assertions
  • 11 · Security — secure coding and dependency hygiene for this language
  • 12 · Project — one portfolio mini-project
  • 13 · Assessment — quiz, coding test, rubric and an explain-your-answer prompt

📚 Modules

  1. variables
  2. operators
  3. conditionals
  4. iterators
  5. methods and blocks
  6. arrays
  7. strings
  8. hashes and symbols
  9. classes
  10. modules and mixins
  11. exceptions
  12. memoization
  13. lambdas and lazy
  14. Data and pattern matching

🏗️ Mini-projects

  • Quiz game
  • Expense tracker
  • Rails-style mini blog (concepts)

Projects use synthetic data only.