Ruby Programming Language Guide
Table of Contents
- Introduction
- Installation
- Basic Syntax
- Data Types
- Variables
- Control Structures
- Methods
- Classes and Objects
- Modules
- Exception Handling
- File I/O
- Gems and Libraries
- Best Practices
- Advanced Topics
Introduction
Ruby is a dynamic, open-source programming language with a focus on simplicity and productivity. It was created by Yukihiro "Matz" Matsumoto in the mid-1990s. Ruby follows the principle of least surprise and emphasizes human-friendly syntax.
Key Features
- Object-oriented: Everything in Ruby is an object
- Dynamic typing: Variables don't need explicit type declarations
- Garbage collection: Automatic memory management
- Flexible syntax: Multiple ways to accomplish the same task
- Metaprogramming: Code that writes code
- Cross-platform: Runs on Windows, macOS, Linux, and Unix
Installation
Using Version Managers (Recommended)
rbenv
# Install rbenv
curl -fsSL https://github.com/rbenv/rbenv-installer/raw/HEAD/bin/rbenv-installer | bash
# Add to shell profile
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
# Install Ruby
rbenv install 3.2.0
rbenv global 3.2.0
RVM (Ruby Version Manager)
# Install RVM
\curl -sSL https://get.rvm.io | bash -s stable
# Install Ruby
rvm install 3.2.0
rvm use 3.2.0 --default
Package Managers
Ubuntu/Debian
sudo apt update
sudo apt install ruby-full
CentOS/RHEL
sudo yum install ruby ruby-devel
Basic Syntax
Hello World
puts "Hello, World!"
Comments
# Single line comment
=begin
Multi-line comment
This is a block comment
=end
Variables and Constants
# Variables (snake_case)
user_name = "John"
age = 25
# Constants (UPPER_CASE)
PI = 3.14159
MAX_SIZE = 100
Data Types
Numbers
# Integers
integer = 42
negative = -10
big_number = 1_000_000
# Floats
float_number = 3.14
scientific = 1.5e10
# Operations
result = 10 + 5 # 15
division = 10 / 3 # 3 (integer division)
float_div = 10.0 / 3 # 3.333...
Strings
# String creation
single_quoted = 'Hello'
double_quoted = "World"
multiline = <<~TEXT
This is a
multiline string
TEXT
# String interpolation (only in double quotes)
name = "Alice"
greeting = "Hello, #{name}!"
# String methods
text = "ruby programming"
puts text.upcase # "RUBY PROGRAMMING"
puts text.capitalize # "Ruby programming"
puts text.length # 16
puts text.include?("ruby") # true
Arrays
# Array creation
numbers = [1, 2, 3, 4, 5]
mixed = ["hello", 42, true, 3.14]
empty_array = []
# Array methods
numbers.push(6) # Add element
numbers << 7 # Another way to add
numbers.pop # Remove last element
numbers.first # First element
numbers.last # Last element
numbers.length # Array size
# Iteration
numbers.each { |num| puts num }
numbers.map { |num| num * 2 }
Hashes
# Hash creation
person = {
"name" => "John",
"age" => 30,
"city" => "New York"
}
# Symbol keys (preferred)
person = {
name: "John",
age: 30,
city: "New York"
}
# Hash methods
person[:email] = "[email protected]" # Add key-value
person.keys # Get all keys
person.values # Get all values
person.has_key?(:name) # Check if key exists
Symbols
# Symbols are immutable strings
status = :active
direction = :north
# Often used as hash keys
config = {
database: "postgresql",
host: "localhost",
port: 5432
}
Booleans and Nil
# Boolean values
is_valid = true
is_complete = false
# Nil (represents nothing/null)
empty_value = nil
# Truthiness: only false and nil are falsy
puts "truthy" if 0 # prints "truthy"
puts "truthy" if "" # prints "truthy"
puts "falsy" if false # doesn't print
puts "falsy" if nil # doesn't print
Variables
Local Variables
name = "Ruby"
age = 28
Instance Variables
class Person
def initialize(name)
@name = name # Instance variable
end
def greet
puts "Hello, I'm #{@name}"
end
end
Class Variables
class Counter
@@count = 0 # Class variable
def initialize
@@count += 1
end
def self.total_count
@@count
end
end
Global Variables
$global_var = "Available everywhere"
def some_method
puts $global_var # Accessible here
end
Control Structures
Conditional Statements
if/elsif/else
score = 85
if score >= 90
grade = "A"
elsif score >= 80
grade = "B"
elsif score >= 70
grade = "C"
else
grade = "F"
end
# One-line conditional
puts "Pass" if score >= 60
# Unless (opposite of if)
puts "Fail" unless score >= 60
case/when
day = "Monday"
case day
when "Monday", "Tuesday", "Wednesday", "Thursday", "Friday"
puts "Weekday"
when "Saturday", "Sunday"
puts "Weekend"
else
puts "Invalid day"
end
Loops
while
counter = 0
while counter < 5
puts counter
counter += 1
end
until
counter = 0
until counter >= 5
puts counter
counter += 1
end
for
for i in 1..5
puts i
end
# Iterating over arrays
fruits = ["apple", "banana", "orange"]
for fruit in fruits
puts fruit
end
each
# Preferred way to iterate
[1, 2, 3, 4, 5].each do |number|
puts number
end
# One-line version
[1, 2, 3, 4, 5].each { |number| puts number }
times
5.times do |i|
puts "Iteration #{i}"
end
Methods
Method Definition
def greet(name)
"Hello, #{name}!"
end
# Method with default parameters
def greet(name = "World")
"Hello, #{name}!"
end
# Method with multiple parameters
def calculate(a, b, operation = :add)
case operation
when :add
a + b
when :subtract
a - b
when :multiply
a * b
when :divide
a / b
end
end
Return Values
def add(a, b)
a + b # Implicit return (last expression)
end
def subtract(a, b)
return a - b # Explicit return
end
Variable Arguments
def sum(*numbers)
numbers.reduce(0) { |total, num| total + num }
end
puts sum(1, 2, 3, 4, 5) # 15
Keyword Arguments
def create_user(name:, email:, age: 18)
{
name: name,
email: email,
age: age
}
end
user = create_user(name: "Alice", email: "[email protected]")
Classes and Objects
Class Definition
class Person
# Class variable
@@population = 0
# Constructor
def initialize(name, age)
@name = name
@age = age
@@population += 1
end
# Instance method
def introduce
"Hi, I'm #{@name} and I'm #{@age} years old."
end
# Getter method
def name
@name
end
# Setter method
def name=(new_name)
@name = new_name
end
# Class method
def self.population
@@population
end
# Private methods
private
def secret_method
"This is private"
end
end
# Creating objects
person1 = Person.new("Alice", 25)
person2 = Person.new("Bob", 30)
puts person1.introduce
puts Person.population
Attribute Accessors
class Book
# Creates getter and setter methods
attr_accessor :title, :author
# Creates only getter methods
attr_reader :isbn
# Creates only setter methods
attr_writer :price
def initialize(title, author, isbn)
@title = title
@author = author
@isbn = isbn
end
end
book = Book.new("1984", "George Orwell", "978-0-452-28423-4")
book.title = "Animal Farm" # Using setter
puts book.title # Using getter
Inheritance
class Animal
def initialize(name)
@name = name
end
def speak
"Some sound"
end
end
class Dog < Animal
def speak
"Woof!"
end
def fetch
"#{@name} is fetching the ball"
end
end
class Cat < Animal
def speak
"Meow!"
end
end
dog = Dog.new("Buddy")
cat = Cat.new("Whiskers")
puts dog.speak # "Woof!"
puts cat.speak # "Meow!"
puts dog.fetch # "Buddy is fetching the ball"
Modules
Module Definition
module Greetings
def say_hello
"Hello!"
end
def say_goodbye
"Goodbye!"
end
end
# Including module in class
class Person
include Greetings
def initialize(name)
@name = name
end
end
person = Person.new("Alice")
puts person.say_hello # "Hello!"
puts person.say_goodbye # "Goodbye!"
Namespacing
module Math
PI = 3.14159
def self.circle_area(radius)
PI * radius * radius
end
end
puts Math::PI # 3.14159
puts Math.circle_area(5) # 78.53975
Mixins
module Walkable
def walk
"Walking..."
end
end
module Swimmable
def swim
"Swimming..."
end
end
class Human
include Walkable
include Swimmable
end
class Fish
include Swimmable
end
human = Human.new
puts human.walk # "Walking..."
puts human.swim # "Swimming..."
fish = Fish.new
puts fish.swim # "Swimming..."
# fish.walk would cause an error
Exception Handling
Basic Exception Handling
begin
# Code that might raise an exception
result = 10 / 0
rescue ZeroDivisionError => e
puts "Error: #{e.message}"
rescue StandardError => e
puts "General error: #{e.message}"
else
puts "No exceptions occurred"
ensure
puts "This always executes"
end
Custom Exceptions
class CustomError < StandardError
def initialize(message = "Something went wrong")
super(message)
end
end
def risky_operation
raise CustomError, "This is a custom error"
end
begin
risky_operation
rescue CustomError => e
puts "Caught custom error: #{e.message}"
end
Raising Exceptions
def validate_age(age)
raise ArgumentError, "Age must be positive" if age < 0
raise ArgumentError, "Age must be a number" unless age.is_a?(Numeric)
age
end
begin
validate_age(-5)
rescue ArgumentError => e
puts "Validation error: #{e.message}"
end
File I/O
Reading Files
# Reading entire file
content = File.read("example.txt")
puts content
# Reading line by line
File.open("example.txt", "r") do |file|
file.each_line do |line|
puts line
end
end
# Using readlines
lines = File.readlines("example.txt")
lines.each { |line| puts line }
Writing Files
# Writing to file (overwrites existing content)
File.open("output.txt", "w") do |file|
file.write("Hello, World!\n")
file.puts "This is a new line"
end
# Appending to file
File.open("output.txt", "a") do |file|
file.puts "This line is appended"
end
# One-liner for writing
File.write("simple.txt", "Simple content")
File Operations
# Check if file exists
if File.exist?("example.txt")
puts "File exists"
end
# Get file information
file_size = File.size("example.txt")
file_time = File.mtime("example.txt")
# Directory operations
Dir.mkdir("new_directory") unless Dir.exist?("new_directory")
Dir.entries(".").each { |entry| puts entry }
Gems and Libraries
Bundler and Gemfile
# Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 7.0'
gem 'sqlite3', '~> 1.4'
gem 'puma', '~> 5.0'
group :development, :test do
gem 'rspec-rails'
gem 'factory_bot_rails'
end
group :development do
gem 'listen', '~> 3.3'
end
Installing and Using Gems
# Install bundler
gem install bundler
# Install gems from Gemfile
bundle install
# Install individual gem
gem install nokogiri
# Update gems
bundle update
Popular Gems
- Rails: Web application framework
- Sinatra: Lightweight web framework
- RSpec: Testing framework
- Nokogiri: HTML/XML parser
- HTTParty: HTTP client
- Devise: Authentication solution
- Sidekiq: Background job processing
Best Practices
Code Style
# Use snake_case for variables and methods
user_name = "john_doe"
def calculate_total; end
# Use CamelCase for classes and modules
class UserAccount; end
module PaymentProcessor; end
# Use SCREAMING_SNAKE_CASE for constants
MAX_RETRY_COUNT = 3
API_BASE_URL = "https://api.example.com"
# Prefer symbols over strings for hash keys
config = {
database: "postgresql",
host: "localhost"
}
# Use meaningful variable names
users.each do |user|
# Good
puts user.full_name
end
users.each do |u|
# Avoid single letter variables (except for short blocks)
puts u.full_name
end
Method Design
# Keep methods small and focused
def calculate_tax(amount, rate)
amount * rate
end
# Use guard clauses for early returns
def process_user(user)
return unless user
return unless user.active?
# Main logic here
end
# Prefer keyword arguments for methods with multiple parameters
def create_user(name:, email:, age: 18, active: true)
# Implementation
end
Error Handling
# Be specific with exception handling
begin
user = User.find(id)
rescue ActiveRecord::RecordNotFound
# Handle specific case
rescue StandardError => e
# Handle general case
logger.error "Unexpected error: #{e.message}"
end
# Use custom exceptions for domain-specific errors
class InsufficientFundsError < StandardError; end
def withdraw(amount)
raise InsufficientFundsError if amount > balance
# Process withdrawal
end
Advanced Topics
Metaprogramming
define_method
class DynamicMethods
%w[name age email].each do |attribute|
define_method(attribute) do
instance_variable_get("@#{attribute}")
end
define_method("#{attribute}=") do |value|
instance_variable_set("@#{attribute}", value)
end
end
end
method_missing
class FlexibleObject
def initialize
@attributes = {}
end
def method_missing(method_name, *args)
if method_name.to_s.end_with?('=')
attribute = method_name.to_s.chomp('=')
@attributes[attribute] = args.first
else
@attributes[method_name.to_s]
end
end
def respond_to_missing?(method_name, include_private = false)
true
end
end
obj = FlexibleObject.new
obj.name = "Ruby"
puts obj.name # "Ruby"
Blocks, Procs, and Lambdas
Blocks
def with_timing
start_time = Time.now
yield
end_time = Time.now
puts "Execution time: #{end_time - start_time} seconds"
end
with_timing do
sleep(1)
puts "Task completed"
end
Procs
# Creating a Proc
square = Proc.new { |x| x * x }
puts square.call(5) # 25
# Proc with multiple arguments
multiply = Proc.new { |x, y| x * y }
puts multiply.call(3, 4) # 12
# Using & to convert block to proc
numbers = [1, 2, 3, 4, 5]
squared = numbers.map(&square)
Lambdas
# Creating a lambda
double = lambda { |x| x * 2 }
# or
double = ->(x) { x * 2 }
puts double.call(5) # 10
# Lambda with argument checking
strict_divide = lambda { |x, y| x / y }
# This will raise an error if wrong number of arguments
Regular Expressions
# Basic pattern matching
text = "The year is 2023"
if text =~ /\d{4}/
puts "Found a year"
end
# Extracting matches
email = "[email protected]"
if match = email.match(/(\w+)@(\w+\.\w+)/)
username = match[1] # "user"
domain = match[2] # "example.com"
end
# String methods with regex
text = "Hello, World!"
puts text.gsub(/[aeiou]/, '*') # Replace vowels with *
puts text.scan(/\w+/) # Extract all words
Enumerable Methods
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
# select (filter)
evens = numbers.select { |n| n.even? }
# reject (opposite of select)
odds = numbers.reject { |n| n.even? }
# map (transform)
squares = numbers.map { |n| n * n }
# reduce (accumulate)
sum = numbers.reduce(0) { |total, n| total + n }
# or
sum = numbers.reduce(:+)
# find
first_even = numbers.find { |n| n.even? }
# any? and all?
has_evens = numbers.any?(&:even?)
all_positive = numbers.all? { |n| n > 0 }
# group_by
grouped = numbers.group_by { |n| n.even? ? :even : :odd }
This comprehensive guide covers the essential aspects of Ruby programming. Ruby's philosophy of making programmers happy through elegant and readable code makes it an excellent choice for web development, scripting, and general-purpose programming. The language's flexibility and powerful features like metaprogramming make it suitable for both beginners and advanced developers.