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.
📖 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
- Install
winget install RubyInstallerTeam.Ruby.3.3
brew install ruby
sudo apt install ruby-full
- Editor: Visual Studio Code with Ruby LSP (Shopify).
- Create
hello.rbputs 'Hello, GayaGnan!'
- Run it and check the version
hello.rb ruby --version
- 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
- variables
- operators
- conditionals
- iterators
- methods and blocks
- arrays
- strings
- hashes and symbols
- classes
- modules and mixins
- exceptions
- memoization
- lambdas and lazy
- Data and pattern matching
🏗️ Mini-projects
- Quiz game
- Expense tracker
- Rails-style mini blog (concepts)
Projects use synthetic data only.