Standard library
The Ruby standard library admits substantial breadth — file system, networking, date and time, JSON, YAML, CSV, regular expressions, cryptography, logging, configuration, plus the core classes (Array, Hash, String, Integer, Float, etc.). The library is layered: core (always available — Array, String, etc.), standard (loaded via require — json, csv, set), bundled gems (shipped with Ruby but loaded as gems — pp, mutex_m), and the RubyGems ecosystem (third-party). The combination — a substantial core, a rich standard library, the conventional Bundled and Default gems, the discipline of require for the substantial features — is the substance of Ruby’s runtime library.
This tour points out the principal built-in classes and standard-library modules.
Core classes
The core classes are always available — no require needed:
String, Integer, Float, Numeric, Rational, Complex, BigDecimal
Symbol, Array, Hash, Range, Proc, Method
TrueClass, FalseClass, NilClass
Object, Class, Module
Comparable, Enumerable
File, IO, Dir, Pathname (loaded as needed)
Time, Date, DateTime
Regexp, MatchData
Exception, StandardError, RuntimeError, ...
Treated in Types, Strings, Data structures, Enumerable.
Object and Kernel
Object is the base class of (almost) all Ruby objects. Kernel is mixed into Object, providing global-style methods accessible everywhere:
puts "hello" # Kernel#puts
print "hello" # Kernel#print
p obj # Kernel#p — debug print
require "json" # Kernel#require
require_relative "lib/foo"
load "config.rb"
raise "error" # Kernel#raise
catch(:label) { ... } # Kernel#catch
throw :label, value # Kernel#throw
at_exit { puts "goodbye" } # registers exit handler
exit # Kernel#exit
exit!(0) # without finalisers
sleep 1 # Kernel#sleep
Integer("42") # strict conversion
Float("3.14")
String(123)
Array(nil) # []
Array([1, 2]) # [1, 2]
The mechanism admits substantial convenience — puts, p, etc. are everywhere.
pp (pretty print)
Pretty-printed output:
require "pp"
complex = { name: "Alice", roles: ["admin", "user"], settings: { theme: "dark" } }
pp complex
# {:name=>"Alice",
# :roles=>["admin", "user"],
# :settings=>{:theme=>"dark"}}
Since Ruby 3.0, pp is loaded by default; explicit require "pp" is rarely needed.
File system: File, Dir, Pathname
File.read("file.txt") # whole file as string
File.write("file.txt", "content") # write
File.binread("file.bin") # binary read
File.exist?("file.txt")
File.directory?("path")
File.size("file.txt")
File.basename("/etc/hosts") # "hosts"
File.dirname("/etc/hosts") # "/etc"
File.extname("file.txt") # ".txt"
File.join("a", "b", "c") # "a/b/c"
File.expand_path("~/foo") # absolute path
File.foreach("file.txt") { |line| puts line } # streaming line iteration
Dir.entries(".") # all entries
Dir.glob("*.rb") # ["foo.rb", "bar.rb"]
Dir["*.{rb,txt}"] # alternative glob
Dir.mkdir("new_dir")
Dir.exist?("path")
Dir.chdir("/tmp") do
# work in /tmp; returns to original dir at block end
end
require "pathname"
p = Pathname.new("/etc/hosts")
p.basename # #<Pathname:hosts>
p.dirname # #<Pathname:/etc>
p.exist?
p.read
p + "subdir" # joined Pathname
Treated in I/O.
Time and Date
Time.now # current time
Time.new(2026, 1, 15, 10, 0, 0) # specific
Time.parse("2026-01-15T10:00:00") # require "time"
Time.now.year
Time.now.month
Time.now.day
Time.now.hour
Time.now.to_i # Unix epoch seconds
# Arithmetic:
Time.now + 60 # 60 seconds later
Time.now - 86400 # 1 day ago
# Formatting:
Time.now.strftime("%Y-%m-%d %H:%M:%S") # "2026-01-15 10:00:00"
Time.now.iso8601
# Date:
require "date"
Date.today
Date.parse("2026-01-15")
Date.new(2026, 1, 15)
(Date.today + 7) # one week later
(Date.today - Date.new(2026, 1, 1)).to_i # days difference
DateTime.now # combined date and time
For substantial date manipulation, the conventional alternatives:
- ActiveSupport —
1.day.ago,2.weeks.from_now, etc. - date-fns equivalents — minimal in Ruby.
JSON
require "json"
# Parse:
data = JSON.parse('{"name": "Alice", "age": 30}')
# => {"name"=>"Alice", "age"=>30}
JSON.parse(json_string, symbolize_names: true)
# => {:name=>"Alice", :age=>30}
# Generate:
JSON.dump({ name: "Alice", age: 30 })
# => '{"name":"Alice","age":30}'
JSON.pretty_generate(complex_data)
# pretty-printed output
# To/from object:
"hello".to_json # "\"hello\""
[1, 2, 3].to_json # "[1,2,3]"
{ a: 1 }.to_json # "{\"a\":1}"
JSON.parse(File.read("data.json"))
File.write("out.json", JSON.pretty_generate(data))
YAML
require "yaml"
data = YAML.load_file("config.yml")
data = YAML.safe_load(File.read("config.yml")) # safer; restricts class loading
YAML.dump(complex_data)
File.write("out.yml", YAML.dump(data))
The conventional contemporary discipline uses YAML.safe_load over YAML.load for untrusted input — load admits arbitrary class instantiation.
CSV
require "csv"
# Parse:
CSV.parse("a,b,c\n1,2,3")
# => [["a", "b", "c"], ["1", "2", "3"]]
CSV.parse(text, headers: true)
# => CSV::Table
# From file:
CSV.foreach("data.csv", headers: true) do |row|
puts row["name"], row["age"]
end
# Write:
CSV.open("out.csv", "w") do |csv|
csv << ["name", "age"]
csv << ["Alice", 30]
csv << ["Bob", 25]
end
# Generate to string:
csv_text = CSV.generate do |csv|
csv << ["a", "b", "c"]
csv << [1, 2, 3]
end
URI and Net::HTTP
require "uri"
uri = URI.parse("https://example.com/path?q=1&r=2")
uri.host # "example.com"
uri.path # "/path"
uri.query # "q=1&r=2"
URI.encode_www_form(name: "Alice", age: 30)
# => "name=Alice&age=30"
URI.decode_www_form_component("Hello%20World")
# => "Hello World"
require "net/http"
# Simple GET:
response = Net::HTTP.get(URI("https://example.com"))
# With response object:
uri = URI("https://example.com/api")
response = Net::HTTP.get_response(uri)
response.code # "200"
response.body
# POST with options:
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.path)
request["Content-Type"] = "application/json"
request.body = data.to_json
response = http.request(request)
For substantial HTTP clients, the conventional gem alternatives: Faraday, HTTParty, Typhoeus, httpx.
OpenStruct
Dynamic attribute objects:
require "ostruct"
o = OpenStruct.new(name: "Alice", age: 30)
o.name # "Alice"
o.email = "a@b.c" # admits setting any attribute
o.unknown # nil
Conventional caveats: OpenStruct uses method_missing internally, admits substantial overhead. Struct or plain Hash is conventionally preferred for performance-sensitive code.
Logger
require "logger"
logger = Logger.new($stdout) # to stdout
logger = Logger.new("app.log") # to file
logger = Logger.new("app.log", 5, 10 * 1024 * 1024) # rotate at 10 MB, keep 5
logger.level = Logger::INFO # DEBUG, INFO, WARN, ERROR, FATAL
logger.debug "debug message"
logger.info "info message"
logger.warn "warning"
logger.error "error"
logger.fatal "fatal"
logger.info("my_app") { "structured message" } # with progname
# Custom format:
logger.formatter = ->(severity, datetime, progname, msg) {
"[#{datetime}] #{severity}: #{msg}\n"
}
ENV and ARGV
ENV["HOME"] # environment variable
ENV.fetch("DATABASE_URL") # raises if missing
ENV.fetch("PORT", "3000") # default
ENV["KEY"] = "value" # set
ENV.delete("KEY")
ENV.each { |k, v| puts "#{k}=#{v}" }
ARGV # command-line arguments
# ruby script.rb foo bar
# ARGV = ["foo", "bar"]
For substantial CLI parsing, the conventional gems: OptionParser (standard), Thor, commander, dry-cli.
require "optparse"
options = {}
OptionParser.new do |parser|
parser.banner = "Usage: my_script [options]"
parser.on("-v", "--verbose", "Verbose output") { options[:verbose] = true }
parser.on("-h", "--help") { puts parser; exit }
parser.on("-n NAME", "--name=NAME", "Name to use") { |n| options[:name] = n }
end.parse!
puts options
Set
The Set class admits unique-value collections:
require "set"
s = Set.new([1, 2, 3])
s.add(4)
s << 5
s.include?(3)
s & other # intersection
s | other # union
Treated in Data structures.
Tempfile and tmpdir
require "tempfile"
Tempfile.create("prefix") do |f|
f.write("temporary data")
f.flush
process(f.path)
end # automatically cleaned up
require "tmpdir"
Dir.mktmpdir do |dir|
# use dir; cleaned up at block end
end
FileUtils
Higher-level file operations:
require "fileutils"
FileUtils.mkdir_p("a/b/c") # like mkdir -p
FileUtils.rm_rf("dir") # like rm -rf
FileUtils.cp("src", "dest")
FileUtils.cp_r("src_dir", "dest_dir")
FileUtils.mv("old", "new")
FileUtils.touch("file.txt")
FileUtils.chmod(0644, "file.txt")
FileUtils.chmod_R(0755, "dir")
Open3 and process spawning
require "open3"
# Capture all:
stdout, stderr, status = Open3.capture3("ls", "-la")
# Streaming:
Open3.popen3("long_command") do |stdin, stdout, stderr, wait_thr|
stdin.close
stdout.each_line { |line| puts line }
wait_thr.value
end
# Just standard output:
output = `ls -la` # backtick form (kernel)
output = %x{ls -la} # alternative
system "command" # returns true/false
Digest and securerandom
require "digest"
Digest::SHA256.hexdigest("hello")
# => "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
Digest::SHA1.hexdigest("hello")
Digest::MD5.hexdigest("hello")
require "securerandom"
SecureRandom.uuid # UUID v4
SecureRandom.hex(16) # 32 hex chars
SecureRandom.base64(32)
SecureRandom.random_number(100) # 0..99
SecureRandom.random_bytes(16) # raw bytes
Base64
require "base64"
Base64.encode64("hello") # "aGVsbG8=\n"
Base64.strict_encode64("hello") # "aGVsbG8="
Base64.decode64("aGVsbG8=") # "hello"
Base64.urlsafe_encode64("hello") # URL-safe variant
Zlib and compression
require "zlib"
Zlib::Deflate.deflate("data to compress")
Zlib::Inflate.inflate(compressed)
# GZip:
Zlib::GzipReader.open("file.gz") do |gz|
puts gz.read
end
Zlib::GzipWriter.open("file.gz") do |gz|
gz.write("data")
end
Net::SMTP for email
require "net/smtp"
message = <<~MESSAGE
From: sender@example.com
To: recipient@example.com
Subject: Hello
Body of the message.
MESSAGE
Net::SMTP.start("smtp.example.com", 25) do |smtp|
smtp.send_message(message, "sender@example.com", "recipient@example.com")
end
For substantial email handling, the conventional gem: Mail, Pony.
OptionParser for CLI
require "optparse"
options = {}
parser = OptionParser.new do |opts|
opts.banner = "Usage: my_script.rb [options]"
opts.on("-vLEVEL", "--verbose=LEVEL", "Set verbosity (1-5)") do |level|
options[:verbose] = level.to_i
end
opts.on("-q", "--quiet", "Suppress output") do
options[:quiet] = true
end
opts.on("-h", "--help", "Print help") do
puts opts
exit
end
end
parser.parse!(ARGV)
puts options
puts ARGV # remaining positional args
Test::Unit and Minitest
The standard testing frameworks:
require "minitest/autorun"
class TestExample < Minitest::Test
def test_addition
assert_equal 4, 2 + 2
end
def test_string
assert "hello".include?("ll")
end
def test_raises
assert_raises(ArgumentError) { do_invalid }
end
end
For BDD-style testing, the conventional gem: RSpec.
Benchmark
require "benchmark"
Benchmark.bm do |x|
x.report("a") { 1_000_000.times { something } }
x.report("b") { 1_000_000.times { other_thing } }
end
# Or:
time = Benchmark.realtime { expensive_operation }
puts "took #{time.round(3)}s"
Forwardable
Method delegation:
require "forwardable"
class TodoList
extend Forwardable
def_delegators :@items, :each, :size, :empty?, :first, :last
def_delegator :@items, :length, :count # rename
def initialize
@items = []
end
def add(item)
@items << item
end
end
Treated in Modules and mixins.
Common patterns
Read JSON file
require "json"
data = JSON.parse(File.read("config.json"), symbolize_names: true)
Parse command-line arguments
require "optparse"
options = {}
OptionParser.new { |o|
o.on("-v", "--verbose") { options[:verbose] = true }
}.parse!
run(ARGV, **options)
Generate a UUID
require "securerandom"
id = SecureRandom.uuid
Hash a string
require "digest"
Digest::SHA256.hexdigest("password")
Make HTTP request
require "net/http"
require "uri"
require "json"
uri = URI("https://api.example.com/data")
response = Net::HTTP.get(uri)
data = JSON.parse(response)
Run a subprocess
require "open3"
output, status = Open3.capture2("git", "status")
puts output if status.success?
Iterate over file lines
File.foreach("large.log") do |line|
puts line if line.include?("ERROR")
end
Format a date
Time.now.strftime("%Y-%m-%d") # "2026-01-15"
Time.now.iso8601 # ISO 8601
A note on the conventional discipline
The contemporary Ruby standard-library advice:
- Use the standard library before reaching for gems.
- Use
JSONfor the conventional structured data. - Use
YAMLfor configuration;safe_loadfor untrusted input. - Use
CSVfor tabular data. - Use
URIandNet::HTTPfor simple HTTP; gems for elaborate needs. - Use
Pathnamefor path manipulation. - Use
FileUtilsfor higher-level file operations. - Use
Open3for spawning subprocesses. - Use
SecureRandomfor cryptographic randomness. - Use
Digest::SHA256for hashing. - Use
OptionParserfor CLI argument parsing. - Use
Loggerfor application logging. - Use
Minitest(built-in) or RSpec (gem) for testing.
The combination — substantial core classes, the rich standard library, the conventional require for substantial features, the gem ecosystem layered on top — is the substance of Ruby’s runtime library. The discipline produces concise, expressive code with substantial built-in functionality; the conventional Ruby program leans heavily on the standard library.