Ruby Programming Language Guide

Table of Contents

  1. Introduction
  2. Installation
  3. Basic Syntax
  4. Data Types
  5. Variables
  6. Control Structures
  7. Methods
  8. Classes and Objects
  9. Modules
  10. Exception Handling
  11. File I/O
  12. Gems and Libraries
  13. Best Practices
  14. 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

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
  • 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.