Table of Contents
- What is Ruby? A Brief Overview
- Basic Syntax Fundamentals
- Core Data Types
- Control Structures
- Methods and Functions
- Object-Oriented Programming (OOP) in Ruby
- Error Handling
- Unique Ruby Features
- Conclusion
- References
1. What is Ruby? A Brief Overview
Ruby is a dynamic, interpreted, object-oriented programming language released in 1995. Key traits include:
- Dynamic typing: No need to declare variable types (e.g.,
x = 5works for integers,x = "hello"for strings). - Everything is an object: Even primitive values like
5or"hello"are objects with methods (e.g.,5.times { puts "Hi" }). - Readable syntax: Designed to resemble natural language, making code easy to write and understand.
- Flexible: Supports multiple programming paradigms (OOP, functional, procedural).
Ruby’s popularity surged with the rise of the Ruby on Rails framework (2004), which revolutionized web development with its “convention over configuration” philosophy. Today, Ruby powers apps like GitHub, Shopify, and Airbnb.
2. Basic Syntax Fundamentals
Ruby’s syntax is designed for clarity. Let’s break down its core rules:
2.1 Readability and Indentation
Ruby uses indentation (typically 2 spaces) to improve readability, but unlike Python, it’s not enforced by the interpreter. For example:
# Readable with indentation (recommended)
if x > 5
puts "x is large"
else
puts "x is small"
end
# Works but is messy (avoid)
if x>5
puts"x is large"
else
puts"x is small"
end
2.2 Comments
- Single-line comments: Start with
#. - Multi-line comments: Enclosed in
=beginand=end(must start at the beginning of the line).
# This is a single-line comment
=begin
This is a multi-line comment.
It can span multiple lines!
=end
2.3 Semicolons
Semicolons (;) separate multiple statements on a single line (optional for single statements).
name = "Alice"; age = 30 # Two statements on one line
puts name # No semicolon needed here
2.4 Variables
Ruby has four main variable types, distinguished by prefixes:
| Type | Prefix | Example | Scope |
|---|---|---|---|
| Local | None | score = 100 | Limited to the current block/method |
| Instance | @ | @name = "Alice" | Shared across an object’s methods |
| Class | @@ | @@count = 0 | Shared across all instances of a class |
| Global | $ | $debug = true | Accessible everywhere (use sparingly!) |
Constants: Named in CamelCase and cannot be reassigned (though Ruby only warns, doesn’t error).
PI = 3.14159 # Constant
PI = 3 # Warning: already initialized constant PI
3. Core Data Types
Ruby includes built-in data types for common use cases:
3.1 Numbers
- Integers: Whole numbers (e.g.,
42,-7). - Floats: Decimal numbers (e.g.,
3.14,-0.001). - BigDecimals: High-precision decimals (for financial calculations, avoid floating-point errors).
age = 25 # Integer
pi = 3.14 # Float
require 'bigdecimal'
tax = BigDecimal("0.075") # High-precision decimal
3.2 Strings
Sequences of characters, enclosed in single (') or double (") quotes. Double quotes support interpolation (#{}) and escape sequences (\n, \t).
single_quoted = 'Hello, \nWorld' # No interpolation/escape
double_quoted = "Hello, \nWorld" # Escape sequences work
name = "Alice"
greeting = "Hello, #{name}!" # Interpolation: "Hello, Alice!"
Strings are objects with useful methods:
"hello".upcase # "HELLO"
" test ".strip # "test" (trims whitespace)
"abc".reverse # "cba"
3.3 Symbols
Immutable identifiers prefixed with :, often used as hash keys or to represent fixed values. Unlike strings, symbols are unique and memory-efficient (only one copy exists).
:name # A symbol
:age # Another symbol
# Symbols vs. strings:
"name" == "name" # true (two separate string objects)
:name == :name # true (same symbol object)
3.4 Arrays
Ordered collections of elements (can mix types). Created with [] or Array.new.
fruits = ["apple", "banana", "cherry"] # Array literal
empty_array = Array.new(3, 0) # [0, 0, 0] (3 elements, all 0)
# Access elements (0-indexed)
fruits[0] # "apple"
fruits[-1] # "cherry" (last element)
# Common methods
fruits << "date" # Add to end: ["apple", "banana", "cherry", "date"]
fruits.length # 4
fruits.join(", ") # "apple, banana, cherry, date"
3.5 Hashes
Key-value pairs (dictionaries). Prior to Ruby 1.9, hashes used =>; modern Ruby uses symbol: value syntax for symbol keys.
# Old syntax (still valid)
person = { "name" => "Alice", "age" => 30 }
# Modern syntax (symbol keys)
person = { name: "Alice", age: 30 } # Equivalent to { :name => "Alice", :age => 30 }
# Access values
person[:name] # "Alice"
person[:city] = "Paris" # Add a new key-value pair
3.6 Booleans and nil
trueandfalse: Represent truth values.nil: Represents “nothing” or “absence of value” (similar tonullin other languages).
Note: In Ruby, only false and nil are “falsy.” All other values (including 0, "", and empty arrays/hashes) are “truthy.”
if 0 # 0 is truthy!
puts "This will run"
end
if nil # nil is falsy
puts "This will NOT run"
end
3.7 Ranges
Represent sequences of values with start..end (inclusive) or start...end (exclusive).
1..5 # 1, 2, 3, 4, 5 (inclusive)
1...5 # 1, 2, 3, 4 (exclusive)
(1..5).to_a # [1, 2, 3, 4, 5]
('a'..'d').to_a # ["a", "b", "c", "d"]
4. Control Structures
Control structures dictate the flow of execution. Ruby’s syntax is concise and readable:
4.1 Conditionals
if/else/elsif
score = 85
if score >= 90
puts "A"
elsif score >= 80
puts "B"
else
puts "C"
end
# Output: B
unless
The inverse of if (runs code only if the condition is false).
unless score < 60 # Equivalent to: if !(score < 60)
puts "Passed!"
else
puts "Failed"
end
Modifier Form
Conditional logic can be appended to a statement (reads like natural language).
puts "You win!" if score == 100 # Runs only if score is 100
puts "Try again" unless score > 50 # Runs only if score <= 50
case Statements
Simplify multi-condition checks (uses === for comparison under the hood).
day = "Monday"
case day
when "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"
puts "Weekday"
when "Saturday", "Sunday"
puts "Weekend"
else
puts "Invalid day"
end
# Output: Weekday
4.2 Loops
while and until
while condition: Runs code as long asconditionis true.until condition: Runs code as long asconditionis false.
count = 1
while count <= 3
puts "Count: #{count}"
count += 1
end
# Output: Count: 1, Count: 2, Count: 3
count = 3
until count == 0
puts "Count: #{count}"
count -= 1
end
# Output: Count: 3, Count: 2, Count: 1
Iterators
Ruby favors iterators (methods that loop over collections) over traditional for loops. Common iterators:
each: Loop over elements in an array/hash.times: Run a blockntimes.map: Transform elements and return a new array.
# each (arrays)
[1, 2, 3].each { |num| puts num * 2 } # Output: 2, 4, 6
# each (hashes)
{ name: "Alice", age: 30 }.each do |key, value|
puts "#{key}: #{value}"
end
# Output: name: Alice, age: 30
# times
5.times { puts "Hello" } # Prints "Hello" 5 times
# map (transform elements)
doubled = [1, 2, 3].map { |num| num * 2 } # [2, 4, 6]
5. Methods and Functions
Methods are reusable blocks of code. In Ruby, they’re defined with def and implicitly return the value of the last expression.
5.1 Defining Methods
def greet(name)
"Hello, #{name}!" # Implicit return (no need for `return` keyword)
end
puts greet("Bob") # Output: Hello, Bob!
5.2 Parameters
Methods can have optional parameters with default values.
def greet(name = "Guest") # Default value for `name`
"Hello, #{name}!"
end
greet() # "Hello, Guest!"
greet("Alice") # "Hello, Alice!"
5.3 Method Calls
Parentheses are optional in method calls (use them for clarity with complex arguments).
greet "Alice" # Valid (no parentheses)
greet("Alice") # Also valid (parentheses for clarity)
5.4 Blocks
Blocks are anonymous code snippets passed to methods (enclosed in do...end or { ... }). Use yield to execute the block inside a method.
def my_method
puts "Before block"
yield # Execute the block
puts "After block"
end
my_method do
puts "Inside the block!"
end
# Output: Before block, Inside the block!, After block
Blocks can take arguments (use |args|):
def calculate(a, b)
result = yield(a, b) # Pass a and b to the block
"Result: #{result}"
end
puts calculate(5, 3) { |x, y| x + y } # "Result: 8"
puts calculate(5, 3) { |x, y| x * y } # "Result: 15"
6. Object-Oriented Programming (OOP) in Ruby
Ruby is a purely object-oriented language: every value is an object, and every operation is a method call.
6.1 Classes and Objects
A class is a blueprint for creating objects. Objects are instances of classes.
# Define a class
class Person
# Initialize method: runs when a new object is created
def initialize(name, age)
@name = name # Instance variable: unique to each object
@age = age
end
# Instance method: access or modify instance variables
def greet
"Hello, I'm #{@name} and I'm #{@age} years old."
end
end
# Create an object (instance of Person)
alice = Person.new("Alice", 30)
puts alice.greet # Output: Hello, I'm Alice and I'm 30 years old.
6.2 Inheritance
Classes can inherit behavior from parent classes using <. Use super to call the parent class’s method.
class Student < Person # Student inherits from Person
def initialize(name, age, major)
super(name, age) # Call Person's initialize method
@major = major
end
# Override the greet method
def greet
"#{super} I study #{@major}." # Reuse parent logic with super
end
end
bob = Student.new("Bob", 22, "Computer Science")
puts bob.greet # Output: Hello, I'm Bob and I'm 22 years old. I study Computer Science.
6.3 Modules (Mixins)
Modules group related methods and can be “mixed into” classes to share behavior (Ruby doesn’t support multiple inheritance, so modules solve this).
module Greetable
def greet
"Hello from #{self.class}!"
end
end
class Dog
include Greetable # Mix in the Greetable module
end
dog = Dog.new
puts dog.greet # Output: Hello from Dog!
7. Error Handling
Ruby uses begin/rescue blocks to handle errors gracefully, preventing crashes.
7.1 Basic Error Handling
begin
# Code that might fail
result = 10 / 0 # Division by zero!
rescue ZeroDivisionError => e # Catch specific error
puts "Error: #{e.message}" # Output: Error: divided by 0
else
# Runs if no error occurred
puts "Result: #{result}"
ensure
# Runs ALWAYS (e.g., clean up resources)
puts "This runs no matter what!"
end
7.2 Raising Exceptions
Use raise to manually trigger errors (useful for validations).
def validate_age(age)
if age < 0
raise ArgumentError, "Age cannot be negative!" # Custom error message
end
end
begin
validate_age(-5)
rescue ArgumentError => e
puts e.message # Output: Age cannot be negative!
end
8. Unique Ruby Features
Ruby’s flexibility and expressiveness come from unique features:
8.1 Open Classes
You can modify existing classes (even built-in ones!) to add/override methods (called “monkeypatching”). Use with caution—changing core classes can break code!
# Add a method to the built-in String class
class String
def shout
upcase + "!!!"
end
end
"hello".shout # "HELLO!!!"
8.2 method_missing
A special method that runs when an undefined method is called. Useful for dynamic behavior (e.g., ORMs like ActiveRecord).
class Magic
def method_missing(method_name, *args)
"You called: #{method_name} with args: #{args.join(', ')}"
end
end
magic = Magic.new
puts magic.foo(1, 2, 3) # Output: You called: foo with args: 1, 2, 3
8.3 Symbols vs. Strings
Symbols are immutable and memory-efficient, making them ideal for hash keys or fixed identifiers. Strings are mutable and better for dynamic text.
9. Conclusion
Ruby’s syntax and semantics prioritize developer happiness, readability, and flexibility. From its human-readable control structures to its “everything is an object” philosophy, Ruby makes programming enjoyable and productive.
To deepen your skills:
- Experiment with Ruby’s standard library (e.g.,
Array,Hashmethods). - Explore Ruby on Rails for web development.
- Dive into metaprogramming (advanced Ruby features like
method_missing).
10. References
- Ruby Official Documentation
- Programming Ruby: The Pragmatic Programmer’s Guide (Dave Thomas, Chad Fowler, Andy Hunt)
- Ruby on Rails Guides
- Matz’s Blog (Ruby’s creator)
Happy coding! 🎉