Ruby
Programming Language
Ruby
Overview
Ruby is an object-oriented scripting language developed in Japan.
Details
Ruby is an object-oriented scripting language developed in Japan by Yukihiro Matsumoto (Matz) in 1995. Based on the design philosophy that emphasizes "programmer happiness," it features highly readable, natural syntax and powerful expressiveness. With the consistent design principle that "everything is an object," it enables flexible and intuitive programming. The explosive popularity in web development came with David Heinemeier Hansson's release of the Ruby on Rails framework in 2004. Following the principle of "Convention over Configuration," it enables rapid development and has been adopted by famous services like GitHub, Shopify, Airbnb, and Twitter (early version). It continues to evolve through an active community.
Code Examples
Hello World
# Basic output
puts "Hello, World!"
# Multiple output methods
print "Hello, "
print "Ruby!\n"
# Output using variables
message = "Hello, Ruby!"
puts message
# String interpolation
name = "John"
age = 25
puts "My name is #{name} and I am #{age} years old."
# Multi-line strings
multiline = <<~TEXT
This is a multi-line
string.
Very Ruby-like way to write.
TEXT
puts multiline
# p method (for debugging)
p "This is p method output" # Shows with quotes
# printf style
printf "My name is %s and I am %d years old.\n", name, age
Variables and Data Types
# Numbers
number = 42
float_number = 3.14159
big_number = 123_456_789 # Underscore for digit separation
binary = 0b1010 # Binary
octal = 0o755 # Octal
hex = 0xFF # Hexadecimal
# Strings
single_quoted = 'Single quoted string'
double_quoted = "Double quoted string"
name = "Smith"
interpolated = "Hello, #{name}!" # String interpolation
# Symbols
symbol = :hello
status = :active
http_method = :get
# Arrays
fruits = ["Apple", "Banana", "Orange"]
numbers = [1, 2, 3, 4, 5]
mixed = [1, "string", :symbol, true]
# Easy array creation
words = %w[red green blue yellow] # ["red", "green", "blue", "yellow"]
symbols = %i[start pause stop] # [:start, :pause, :stop]
# Hashes (associative arrays)
person = {
"name" => "John Smith",
"age" => 30,
"city" => "New York"
}
# Hash with symbol keys (modern syntax)
person_modern = {
name: "Jane Doe",
age: 25,
city: "San Francisco"
}
# Ranges
range1 = 1..10 # 1 to 10 (inclusive)
range2 = 1...10 # 1 to 10 (exclusive)
alphabet = 'a'..'z' # a to z
# Boolean values
is_active = true
is_complete = false
nothing = nil # Ruby's null value
# Regular expressions
pattern = /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/
email = "[email protected]"
# Check variable types
puts number.class # Integer
puts float_number.class # Float
puts name.class # String
puts symbol.class # Symbol
puts fruits.class # Array
puts person.class # Hash
puts pattern.class # Regexp
# Type conversion
str_number = "123"
puts str_number.to_i # String to integer
puts number.to_s # Integer to string
puts number.to_f # Integer to float
puts fruits.join(", ") # Array to string
# Constants
TAX_RATE = 0.1
COMPANY_NAME = "ABC Corporation"
puts "Number: #{number}"
puts "String: #{double_quoted}"
puts "Interpolated string: #{interpolated}"
puts "Array: #{fruits}"
puts "Hash: #{person}"
puts "Range includes 5: #{range1.include?(5)}"
puts "Email matches: #{email.match?(pattern)}"
Methods and Blocks
# Basic method definition
def add(a, b)
a + b # Ruby automatically returns the last expression
end
# Explicit return
def subtract(a, b)
return a - b
end
# Default arguments
def greet(name, greeting = "Hello")
"#{greeting}, #{name}!"
end
# Keyword arguments
def create_user(name:, age:, email: nil)
user = {
name: name,
age: age,
email: email
}
puts "User created: #{user}"
user
end
# Variable arguments
def sum(*numbers)
numbers.sum
end
# Keyword argument splat
def log_info(message, **options)
puts "Message: #{message}"
puts "Options: #{options}" unless options.empty?
end
# Method with block
def repeat_task(times)
times.times do |i|
yield(i + 1) if block_given?
end
end
# Method accepting block
def process_data(data, &block)
data.map(&block)
end
# Method call examples
puts add(5, 3)
puts subtract(10, 4)
puts greet("Smith")
puts greet("Johnson", "Good morning")
create_user(name: "Brown", age: 30)
create_user(name: "Wilson", age: 25, email: "[email protected]")
puts sum(1, 2, 3, 4, 5)
log_info("Process started", level: :info, timestamp: Time.now)
# Block usage examples
repeat_task(3) do |count|
puts "Executing task #{count}..."
end
# Array methods
numbers = [1, 2, 3, 4, 5]
# each (iterate over each element)
numbers.each { |n| puts "Number: #{n}" }
# map (transform)
doubled = numbers.map { |n| n * 2 }
puts "Doubled: #{doubled}"
# select (filter)
evens = numbers.select { |n| n.even? }
puts "Even numbers: #{evens}"
# reject (exclude)
odds = numbers.reject { |n| n.even? }
puts "Odd numbers: #{odds}"
# reduce (aggregate)
total = numbers.reduce(0) { |sum, n| sum + n }
puts "Total: #{total}"
# Shorthand
sum_short = numbers.sum
puts "Total (shorthand): #{sum_short}"
# Hash methods
person = { name: "Smith", age: 30, city: "New York" }
person.each do |key, value|
puts "#{key}: #{value}"
end
# Method chaining
result = numbers
.select { |n| n.odd? }
.map { |n| n ** 2 }
.sum
puts "Sum of odd squares: #{result}"
# proc and lambda
square_proc = proc { |x| x ** 2 }
square_lambda = lambda { |x| x ** 2 }
puts "proc result: #{square_proc.call(5)}"
puts "lambda result: #{square_lambda.call(5)}"
# Convert block to proc
def use_block(&block)
block.call("Hello from proc!")
end
use_block { |msg| puts msg }
Classes and Objects
# Basic class
class Person
# Automatic accessor methods
attr_reader :name, :age # Read-only
attr_writer :email # Write-only
attr_accessor :phone # Read and write
# Class variable
@@population = 0
# Constant
SPECIES = "Homo sapiens"
# Initialize method (constructor)
def initialize(name, age)
@name = name # Instance variable
@age = age
@id = generate_id
@@population += 1
end
# Instance method
def introduce
"My name is #{@name} and I am #{@age} years old."
end
def birthday
@age += 1
puts "Happy #{@age}th birthday, #{@name}!"
end
# Class method
def self.population
@@population
end
def self.create_anonymous
new("Anonymous", 0)
end
# Private method
private
def generate_id
"ID_#{Time.now.to_i}_#{rand(1000)}"
end
end
# Inheritance
class Student < Person
attr_accessor :school, :grade
def initialize(name, age, school)
super(name, age) # Call parent's initialize
@school = school
@grade = "Not set"
end
def introduce
super + " I'm a student at #{@school}."
end
def study(subject)
puts "#{@name} is studying #{subject}."
end
end
# Module (mixin)
module Greetable
def say_hello
"Hello! I'm #{name}."
end
def say_goodbye
"Goodbye from #{name}!"
end
end
# Using module
class Employee < Person
include Greetable # Mixin
attr_accessor :company, :position
def initialize(name, age, company, position)
super(name, age)
@company = company
@position = position
end
def work
puts "#{@name} works at #{@company} as a #{@position}."
end
end
# Usage examples
puts "=== Class Usage Examples ==="
# Create instances
person1 = Person.new("John Smith", 25)
person2 = Person.new("Jane Doe", 30)
puts person1.introduce
puts person2.introduce
# Using accessors
person1.phone = "090-1234-5678"
puts "Phone: #{person1.phone}"
# Birthday
person1.birthday
# Class method call
puts "Total population: #{Person.population}"
anonymous = Person.create_anonymous
puts anonymous.introduce
# Inheritance example
student = Student.new("Bob Wilson", 20, "MIT")
puts student.introduce
student.study("Computer Science")
# Mixin example
employee = Employee.new("Alice Brown", 35, "Tech Corp", "Engineer")
puts employee.introduce
employee.work
puts employee.say_hello
puts employee.say_goodbye
puts "Total population: #{Person.population}"
# Object inspection
puts "\n=== Object Inspection ==="
puts "person1's class: #{person1.class}"
puts "student's class: #{student.class}"
puts "student is a Person: #{student.is_a?(Person)}"
puts "student is a Student: #{student.is_a?(Student)}"
puts "employee includes Greetable: #{employee.class.include?(Greetable)}"
# Metaprogramming example
puts "\n=== Metaprogramming ==="
# Dynamic method definition
class DynamicClass
["create", "update", "delete"].each do |action|
define_method("#{action}_user") do |user_id|
puts "#{action.capitalize}ing user #{user_id}"
end
end
end
dynamic = DynamicClass.new
dynamic.create_user(123)
dynamic.update_user(456)
dynamic.delete_user(789)
# Using method_missing
class FlexibleClass
def initialize
@data = {}
end
def method_missing(method_name, *args)
if method_name.to_s.end_with?('=')
# Setter
attribute = method_name.to_s.chop
@data[attribute] = args.first
else
# Getter
@data[method_name.to_s]
end
end
def respond_to_missing?(method_name, include_private = false)
true
end
end
flexible = FlexibleClass.new
flexible.name = "Dynamic Object"
flexible.value = 42
puts "Name: #{flexible.name}"
puts "Value: #{flexible.value}"
Exception Handling and Error Management
# Custom exception classes
class ValidationError < StandardError
attr_reader :field
def initialize(message, field = nil)
super(message)
@field = field
end
end
class AgeError < ValidationError; end
class EmailError < ValidationError; end
# Methods that raise exceptions
def validate_age(age)
raise AgeError.new("Age must be 0 or greater", :age) if age < 0
raise AgeError.new("Age must be 150 or less", :age) if age > 150
true
end
def validate_email(email)
pattern = /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}\z/
unless email.match?(pattern)
raise EmailError.new("Invalid email address: #{email}", :email)
end
true
end
def divide(a, b)
raise ZeroDivisionError, "Cannot divide by zero" if b == 0
a.to_f / b
end
# Exception handling examples
puts "=== Exception Handling ==="
# begin-rescue-end
begin
result = divide(10, 2)
puts "10 ÷ 2 = #{result}"
rescue ZeroDivisionError => e
puts "Math error: #{e.message}"
end
# Catching multiple exceptions
begin
validate_age(-5)
rescue AgeError => e
puts "Age error: #{e.message} (field: #{e.field})"
rescue ValidationError => e
puts "Validation error: #{e.message}"
rescue StandardError => e
puts "Unexpected error: #{e.message}"
end
# ensure clause (always executed)
begin
puts "Starting process"
validate_email("invalid-email")
rescue EmailError => e
puts "Email error: #{e.message}"
ensure
puts "Cleanup process (always executed)"
end
# else clause (executed when no exception occurs)
begin
validate_age(25)
rescue AgeError => e
puts "Age error: #{e.message}"
else
puts "Age validation successful"
ensure
puts "Validation process completed"
end
# retry (retry execution)
attempt = 0
begin
attempt += 1
puts "Attempt #{attempt}"
if attempt < 3
raise "Temporary error"
end
puts "Process successful!"
rescue => e
puts "Error: #{e.message}"
if attempt < 3
puts "Retrying..."
retry
else
puts "Maximum attempts reached"
end
end
# raise (re-raise exception)
def process_data(data)
validate_age(data[:age])
rescue AgeError => e
puts "Error during data processing: #{e.message}"
raise # Re-raise exception
end
begin
process_data(age: -10)
rescue AgeError
puts "Error caught in caller"
end
# Using custom exceptions
class UserService
def create_user(name, age, email)
validate_age(age)
validate_email(email)
# User creation process
user = { name: name, age: age, email: email, id: rand(1000) }
puts "User creation successful: #{user}"
user
rescue ValidationError => e
puts "User creation failed: #{e.message}"
nil
end
end
service = UserService.new
service.create_user("Smith", 25, "[email protected]")
service.create_user("Johnson", -5, "[email protected]")
service.create_user("Brown", 30, "invalid-email")
# throw/catch (non-local exit, not exception)
def find_in_nested_array(array, target)
catch(:found) do
array.each do |subarray|
subarray.each do |item|
throw(:found, item) if item == target
end
end
nil # Not found
end
end
nested = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
result = find_in_nested_array(nested, 5)
puts "Search result: #{result}"
File Operations and I/O
# File read/write operations
puts "=== File Operations ==="
# Write to file
File.open("sample.txt", "w") do |file|
file.puts "This is a sample file."
file.puts "We're doing file operations in Ruby."
file.puts "Using blocks automatically closes the file."
end
# Read from file
content = File.read("sample.txt")
puts "File content:"
puts content
# Read line by line
puts "\nReading line by line:"
File.foreach("sample.txt") do |line|
puts "Line: #{line.strip}"
end
# Read as array
lines = File.readlines("sample.txt")
puts "\nRead as array: #{lines.map(&:strip)}"
# Append to file
File.open("sample.txt", "a") do |file|
file.puts "This line was appended."
end
# CSV file operations
require 'csv'
# Create CSV data
csv_data = [
["Name", "Age", "Occupation"],
["John Smith", 30, "Engineer"],
["Jane Doe", 25, "Designer"],
["Bob Wilson", 35, "Manager"]
]
# Write to CSV file
CSV.open("people.csv", "w") do |csv|
csv_data.each { |row| csv << row }
end
# Read from CSV file
puts "\nCSV file content:"
CSV.foreach("people.csv", headers: true) do |row|
puts "#{row['Name']} (#{row['Age']} years old) - #{row['Occupation']}"
end
# JSON file operations
require 'json'
# Convert hash to JSON
user_data = {
name: "Alice Johnson",
age: 28,
hobbies: ["Reading", "Movies", "Programming"],
address: {
city: "San Francisco",
state: "California"
}
}
# Write to JSON file
File.open("user.json", "w") do |file|
file.write(JSON.pretty_generate(user_data))
end
# Read from JSON file
loaded_user = JSON.parse(File.read("user.json"), symbolize_names: true)
puts "\nData loaded from JSON:"
puts "Name: #{loaded_user[:name]}"
puts "Hobbies: #{loaded_user[:hobbies].join(', ')}"
# Directory operations
puts "\n=== Directory Operations ==="
# Current directory
puts "Current directory: #{Dir.pwd}"
# Create directory
Dir.mkdir("test_dir") unless Dir.exist?("test_dir")
# Get file list
files = Dir.glob("*.txt")
puts "txt files: #{files}"
# Process files in directory
Dir.foreach(".") do |file|
next if file.start_with?(".")
puts "File: #{file} (size: #{File.size(file)} bytes)" if File.file?(file)
end
# File information
if File.exist?("sample.txt")
stat = File.stat("sample.txt")
puts "\nsample.txt information:"
puts "Size: #{stat.size} bytes"
puts "Last modified: #{stat.mtime}"
puts "Permissions: #{sprintf('%o', stat.mode)}"
end
# Path operations
require 'pathname'
path = Pathname.new("test_dir/subdir/file.txt")
puts "\nPath operations:"
puts "Directory name: #{path.dirname}"
puts "File name: #{path.basename}"
puts "Extension: #{path.extname}"
puts "File name without extension: #{path.basename(path.extname)}"
# Temporary files
require 'tempfile'
Tempfile.create("ruby_temp") do |temp|
temp.write("Temporary data")
temp.rewind
puts "\nTemporary file content: #{temp.read}"
puts "Temporary file path: #{temp.path}"
end
# Cleanup
File.delete("sample.txt") if File.exist?("sample.txt")
File.delete("people.csv") if File.exist?("people.csv")
File.delete("user.json") if File.exist?("user.json")
Dir.rmdir("test_dir") if Dir.exist?("test_dir")
puts "\nFile operations examples completed."
Versions
Version | Release Date | Major Features |
---|---|---|
Ruby 3.3 | 2023-12 | Prism parser, YJIT improvements |
Ruby 3.2 | 2022-12 | WASI support, Anonymous rest arguments |
Ruby 3.1 | 2021-12 | YJIT JIT compiler, IRB improvements |
Ruby 3.0 | 2020-12 | Pattern matching, Keyword arguments separation |
Ruby 2.7 | 2019-12 | Pattern matching (experimental), Compaction GC |
Ruby 2.6 | 2018-12 | JIT compiler, Endless range |
Ruby 2.5 | 2017-12 | rescue/else/ensure in do/end blocks |
Reference Links
Official Documentation
- Ruby Official Site - Ruby official website
- Ruby Documentation - Official documentation
- RubyGems - Ruby library repository
Learning Resources
- Ruby User's Guide - Official user guide
- Ruby Style Guide - Ruby style guide
- Ruby Koans - Ruby learning exercises
Frameworks and Tools
- Ruby on Rails - Web application framework
- Sinatra - Lightweight web framework
- RSpec - Testing framework
- Bundler - Dependency management tool