Ruby 教程
Ruby 教程
Ruby 是一种简单快捷的面向对象脚本语言,由日本人松本行弘(Yukihiro Matsumoto)于 1995 年开发。Ruby 的设计哲学强调程序员的生产力与幸福感,遵循"最小惊讶原则"——即让代码的表现符合直觉,减少学习与使用中的意外。
本教程内容
本教程将从 Ruby 的基础语法开始,循序渐进地讲解变量、运算符、条件判断、循环、方法、类与对象、模块、字符串、数组、哈希等核心知识,再深入文件操作、异常处理、正则表达式、数据库访问、网络编程与多线程等进阶主题。
Ruby 的特点
- 完全面向对象:一切皆对象,包括数字和布尔值
- 简洁优雅的语法,接近自然语言
- 动态类型,无需声明变量类型
- 内置垃圾回收,无需手动管理内存
- 丰富的标准库与活跃的社区生态(gems)
学习前提
建议具备一定的编程基础(任意语言),对 Web 开发感兴趣的话可以搭配 Ruby on Rails 一起学习。准备好 Ruby 环境后,就可以开始第一章的学习了。
Ruby 简介
Ruby 简介
Ruby 是一门开源的动态编程语言,专注于简洁与生产力。它的语法优雅而富有表达力,常用于 Web 开发(如知名的 Ruby on Rails 框架)、脚本编写、自动化任务与数据处理。
发展历史
1995 年,松本行弘发布了 Ruby 的第一个公开版本。其设计融合了 Perl 的实用、Smalltalk 的纯粹面向对象以及 Lisp 的函数式思想,形成了一种独特而富有魅力的语言风格。
Ruby 能做什么
- Web 应用开发:Ruby on Rails、Sinatra 等框架
- 自动化脚本与系统管理
- 数据处理与文本解析
- 原型开发与快速迭代
- 运维工具(如 Chef、Puppet 的配置语言)
Hello World
puts "Hello, Ruby!"
# 输出: Hello, Ruby!
为什么学习 Ruby
Ruby 的代码可读性极高,几乎接近英语的自然表达。它鼓励开发者编写优雅、可维护的代码,是学习面向对象编程和函数式思想的绝佳入门语言。社区氛围友好,文档完善,学习资源丰富。
Ruby 环境
Ruby 环境
在开始编写 Ruby 代码之前,需要先在计算机上安装 Ruby 解释器。Ruby 支持主流操作系统:Linux、macOS 与 Windows。本节介绍环境准备的整体思路与常用工具。
环境构成
- Ruby 解释器:执行 Ruby 代码的核心程序
- gem 包管理器:安装与管理第三方库(RubyGems)
- IRB:交互式命令行环境,适合快速试验代码
- 文本编辑器或 IDE:编写代码的工具(如 VS Code、RubyMine)
版本选择
建议安装当前稳定的 Ruby 3.x 版本。旧项目可能需要特定版本,推荐使用版本管理工具:
- Linux/macOS:
rbenv或rvm - Windows:
RubyInstaller
验证安装
ruby -v
# 输出示例: ruby 3.2.2 (2023-03-30 revision e51014f9c0) [x86_64-linux]
gem -v
# 输出示例: 3.4.6
第一个程序
# 保存为 hello.rb
puts "Hello, World!"
# 运行
# ruby hello.rb
环境就绪后,即可按照下一节的指引在具体操作系统上安装 Ruby。
Ruby 安装 - Linux
Ruby 安装 - Linux
Linux 系统安装 Ruby 有多种方式,推荐使用系统包管理器快速安装,或使用 rbenv/rvm 实现多版本管理。
方式一:系统包管理器
Debian/Ubuntu 系列:
sudo apt update
sudo apt install ruby-full
CentOS/RHEL/Fedora 系列:
sudo yum install ruby # CentOS 7
sudo dnf install ruby # Fedora / CentOS 8+
方式二:使用 rbenv(推荐)
rbenv 可以自由切换 Ruby 版本,适合开发环境:
# 安装依赖
sudo apt install git curl libssl-dev libreadline-dev zlib1g-dev autoconf bison build-essential libyaml-dev libncurses5-dev libffi-dev libgdbm-dev
# 安装 rbenv
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
source ~/.bashrc
# 安装 ruby-build 插件
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
# 安装 Ruby
rbenv install 3.2.2
rbenv global 3.2.2
验证
ruby -v
gem -v
安装完成后,建议同时安装 bundler 用于管理项目依赖:gem install bundler。
Ruby 安装 - Windows
Ruby 安装 - Windows
Windows 上安装 Ruby 最便捷的方式是使用 RubyInstaller,它是一个官方推荐的安装包,内置 Ruby 与 MSYS2 开发环境。
安装步骤
- 访问 RubyInstaller 官网(rubyinstaller.org)下载最新版本安装包
- 选择与系统匹配的版本(64 位建议使用 x64)
- 运行安装程序,勾选"Add Ruby executables to your PATH"
- 安装完成后,安装程序会提示运行
ridk install,选择 1 安装 MSYS2 基础组件(用于编译原生扩展)
验证安装
ruby -v
# 输出示例: ruby 3.2.2-1 [x64-mingw-ucrt]
gem -v
在 Windows 上使用 Ruby
- 命令提示符 / PowerShell:直接运行
ruby 文件名.rb - IRB:输入
irb进入交互式环境 - 编辑器:推荐 VS Code + Ruby 扩展
常见问题
- 命令找不到:检查 PATH 是否包含 Ruby 安装目录
- 原生扩展安装失败:确认 MSYS2 已通过
ridk install安装
Windows 下部分 gem(如 eventmachine)需要编译环境,MSYS2 能解决绝大多数问题。
Ruby 命令行选项
Ruby 命令行选项
Ruby 解释器支持丰富的命令行选项,用于控制执行方式、调试级别与编码等行为。掌握这些选项可以更高效地使用 Ruby。
常用选项
| 选项 | 说明 |
|---|---|
-e '代码' | 直接执行给定的 Ruby 代码 |
-c | 只检查语法,不执行 |
-w | 开启警告信息 |
-d | 开启调试模式(设置 $DEBUG) |
-v | 显示版本号并进入详细模式 |
-I 路径 | 添加库搜索路径 |
-r 库名 | 执行前先 require 指定库 |
-K 编码 | 指定源文件编码 |
使用示例
# 直接执行代码
ruby -e 'puts 1 + 2'
# 检查语法
ruby -c hello.rb
# 带警告运行
ruby -w hello.rb
# 引入 json 库后执行
ruby -r json -e 'puts JSON.generate({a: 1})'
调试相关
# 开启调试模式
ruby -d script.rb
调试模式下脚本会输出更多运行时信息,配合 binding.irb 或 byebug 可以在运行时打断点检查变量状态。
Ruby 环境变量
Ruby 环境变量
Ruby 运行时依赖一些环境变量来配置行为,例如库加载路径、gem 安装位置与编码设置等。本节介绍最常用的几个。
核心环境变量
| 变量 | 作用 |
|---|---|
RUBYOPT | 启动时自动附加的命令行选项 |
RUBYLIB | 额外的库搜索路径(类似 -I) |
GEM_HOME | gem 的安装目录 |
GEM_PATH | gem 的搜索路径列表 |
BUNDLE_GEMFILE | 指定 Bundler 使用的 Gemfile |
RUBY_YJIT_ENABLE | 开启 YJIT 即时编译器 |
在脚本中读取环境变量
# 读取
puts ENV["HOME"]
puts ENV["PATH"]
# 设置(仅影响当前进程)
ENV["MY_VAR"] = "hello"
puts ENV["MY_VAR"]
RUBYOPT 示例
# 让所有 Ruby 命令自动加载 warning 抑制
export RUBYOPT="-W0"
# 设置默认编码
export RUBYOPT="-Eutf-8"
查看当前环境
ruby -e 'ENV.each { |k, v| puts "#{k}=#{v}" }'
合理配置环境变量可以统一团队开发环境,避免编码、库路径不一致导致的问题。
Ruby 语法
Ruby 语法
Ruby 的语法以简洁著称:不需要分号结尾(可用换行分隔语句)、缩进不是语法要求(但强烈建议使用)、方法调用常可省略括号。本节快速浏览核心语法元素。
注释
# 单行注释
=begin
这是多行注释
=end
输出与输入
puts "输出并换行"
print "不换行"
printf("%d 和 %s\n", 100, "Ruby")
# 获取用户输入
name = gets.chomp
puts "你好, #{name}"
变量与常量
name = "Ruby" # 局部变量
$global = 1 # 全局变量
@instance = 2 # 实例变量
@@class_var = 3 # 类变量
CONST = 100 # 常量(大写开头)
# 常量可被警告式重新赋值
CONST = 200
方法定义
def add(a, b)
a + b # 最后一行自动作为返回值
end
if 与 unless
x = 10
if x > 5
puts "大"
end
puts "小" if x < 5 # 后置写法
puts "大" unless x < 5
Ruby 语法强调可读性,几乎每种控制结构都有简洁的替代写法,学习时注意体会这种"表达力"。
Ruby 类和对象
Ruby 类和对象
Ruby 是纯粹的面向对象语言,一切皆对象。类是对象的模板,对象是类的实例。本节介绍如何定义类、创建对象与访问属性。
定义类
class Person
def initialize(name, age)
@name = name # 实例变量
@age = age
end
def intro
"我是 #{@name},今年 #{@age} 岁"
end
end
创建对象
p1 = Person.new("小明", 18)
puts p1.intro # 我是 小明,今年 18 岁
属性读写
class Person
attr_reader :name # 只读
attr_writer :age # 只写
attr_accessor :city # 读写
end
p = Person.new
p.city = "北京"
puts p.city
方法访问控制
class Demo
def public_method; end
private
def secret; end
protected
def inner; end
end
继承
class Student < Person
def intro
super + ",是一名学生"
end
end
类方法用 self.method 定义,单例方法可用 def obj.method 定义。Ruby 的类体系高度灵活,是学习 OOP 的优秀语言。
Ruby 类案例
Ruby 类案例
本节通过一个完整的案例——"银行账户管理系统",综合运用类、对象、属性访问、方法封装与继承等知识。
需求
实现一个 BankAccount 类:支持开户、存款、取款、查询余额;余额不足时抛出异常;并提供定期账户(SavingsAccount)子类,享有利率。
实现代码
class BankAccount
attr_reader :owner, :balance
def initialize(owner, balance = 0)
@owner = owner
@balance = balance
end
def deposit(amount)
raise "金额必须为正" if amount <= 0
@balance += amount
self
end
def withdraw(amount)
raise "余额不足" if amount > @balance
@balance -= amount
self
end
end
class SavingsAccount < BankAccount
def initialize(owner, balance = 0, rate = 0.02)
super(owner, balance)
@rate = rate
end
def add_interest
@balance += (@balance * @rate).round(2)
end
end
使用示例
acc = BankAccount.new("张三", 1000)
acc.deposit(500).withdraw(200)
puts acc.balance # 1300
sav = SavingsAccount.new("李四", 10000)
sav.add_interest
puts sav.balance # 10200.0
案例要点
attr_reader提供只读访问- 方法链式调用:返回
self - 异常用于业务规则校验
- 继承 +
super复用父类逻辑
这个小案例展示了 Ruby 面向对象的核心套路,掌握后可以应对更复杂的建模需求。
Ruby 变量
Ruby 变量
Ruby 变量根据作用域分为四种:局部变量、全局变量、实例变量与类变量。变量名首字符决定了其类型,这是 Ruby 的独特设计。
局部变量
name = "Ruby" # 小写字母或下划线开头
age = 30
_local = true
局部变量在定义它的作用域内有效(方法、块、循环内)。
全局变量
$global = 10 # $ 开头,整个程序可见
def show
puts $global
end
全局变量会破坏封装,应谨慎使用。Ruby 内置了一些特殊全局变量如 $stdin、$! (最近异常)。
实例变量
class Point
def initialize(x, y)
@x = x
@y = y
end
def info
"(#{@x}, #{@y})"
end
end
实例变量以 @ 开头,属于单个对象,未初始化时为 nil。
类变量
class Counter
@@count = 0
def initialize
@@count += 1
end
def self.count
@@count
end
end
类变量以 @@ 开头,被类及其所有实例共享。
常量与伪变量
MAX = 100 # 常量,大写开头
self # 当前对象
nil / true / false # 伪变量
理解变量类型是掌握 Ruby 作用域规则的第一步,建议动手实验每种变量的可见范围。
Ruby 运算符
Ruby 运算符
Ruby 提供算术、比较、赋值、逻辑、位运算等丰富的运算符,并支持运算符重载(实际上运算符都是方法调用)。
算术运算符
a, b = 10, 3
puts a + b # 13
puts a - b # 7
puts a * b # 30
puts a / b # 3 (整数除法)
puts a % b # 1 (取余)
puts a ** b # 1000 (幂)
比较运算符
puts 1 == 1 # true (相等)
puts 1 != 2 # true
puts 2 > 1 # true
puts 1 <= 2 # true
puts 1 <=> 2 # -1 (宇宙飞船运算符:小于-1,等于0,大于1)
puts 1.eql?(1.0) # false (类型也相同才 true)
赋值运算符
x = 5
x += 3 # x = 8
x -= 2 # x = 6
x *= 4 # x = 24
x /= 3 # x = 8
x %= 5 # x = 3
x **= 2 # x = 9
# 并行赋值
a, b = 1, 2
a, b = b, a # 交换
逻辑运算符
true and false # false (and / or 优先级低)
true && false # false (&& / || 优先级高)
!true # false
true || nil # true
# 常用技巧:短路赋值
name ||= "默认值" # 为空才赋值
三元与范围
x = 10
msg = x > 5 ? "大" : "小"
puts (1..5).to_a # 包含结尾 [1,2,3,4,5]
puts (1...5).to_a # 不含结尾 [1,2,3,4]
运算符优先级注意
and/or 优先级低于 &&/||,混用时建议加括号避免歧义。Ruby 还支持 defined? 与 === 等特殊运算符,后者在 case 语句中扮演重要角色。
Ruby 注释
Ruby 注释
注释是写给开发者看的说明文字,不会被执行。良好的注释能显著提升代码可维护性,Ruby 支持单行、多行以及文档注释三种形式。
单行注释
# 这是单行注释
puts "Hello" # 行尾注释
多行注释
=begin
这是多行注释的第一行
第二行
=end
puts "继续执行"
文档注释(RDoc)
Ruby 社区习惯用文档注释生成 API 文档:
# 计算两数之和
#
# == 参数
# a, b :: 两个数值
# == 返回值
# 返回 a + b 的结果
def add(a, b)
a + b
end
注释规范建议
- 解释"为什么"而不是"是什么"(代码本身说明怎么做的)
- 复杂算法标注思路与复杂度
- TODO/FIXME 标记待办事项
- 方法/类的文档注释保持简洁一致
用注释调试
# 临时注释掉某行排查问题
# puts result
# 标记未完成
# TODO: 支持负数输入
注意:=begin/=end 必须顶格书写,前面不能有空格,否则会被当作普通字符串处理。
Ruby 条件判断
Ruby 条件判断
Ruby 提供 if、unless、case 三种条件判断结构,每种都有后置写法,让代码更加简洁自然。
if / elsif / else
score = 85
if score >= 90
puts "优秀"
elsif score >= 60
puts "及格"
else
puts "不及格"
end
后置 if
puts "成年" if age >= 18
# 一行三目
status = age >= 18 ? "成年" : "未成年"
unless(除非)
unless user.nil?
puts "用户存在"
end
# 后置写法
puts "禁止访问" unless logged_in?
case / when
day = "周一"
case day
when "周六", "周日"
puts "休息日"
when "周一".."周五"
puts "工作日"
else
puts "未知"
end
case 使用 === 进行匹配,支持范围、正则、类等多种匹配方式,非常灵活。
条件表达式注意
# 在 Ruby 中只有 nil 和 false 为假,0 和空字符串都是真
puts "真" if 0 # 输出 真
puts "真" if "" # 输出 真
puts "假" if nil # 不输出
理解 Ruby 的"真值"规则(仅 nil/false 为假)对编写条件判断至关重要。
Ruby 循环
Ruby 循环
Ruby 提供了 while、until、for 以及基于迭代器的 each、times、loop 等多种循环方式,推荐优先使用迭代器风格。
while 循环
i = 0
while i < 5
puts i
i += 1
end
until 循环
i = 0
until i >= 5
puts i
i += 1
end
for 循环
for i in 0..4
puts i
end
迭代器(推荐)
5.times { |i| puts i }
(1..5).each { |n| puts n }
["a", "b", "c"].each_with_index do |v, idx|
puts "#{idx}: #{v}"
end
控制语句
(1..10).each do |n|
next if n.even? # 跳过偶数
break if n > 7 # 大于7退出
puts n # 输出 1 3 5 7
end
loop 与 retry
count = 0
loop do
count += 1
puts count
break if count >= 3
end
Ruby 中 each、map 等迭代器比 for 更符合习惯,配合块(block)使用是 Ruby 编程的核心模式。
Ruby 方法
Ruby 方法
方法是 Ruby 中组织逻辑的基本单元。Ruby 的方法支持默认参数、可变参数、块参数、关键字参数等丰富特性,最后一条表达式的值自动作为返回值。
基本定义
def greet(name)
"Hello, #{name}!"
end
puts greet("Ruby") # Hello, Ruby!
# 调用时可省略括号
puts greet "Ruby"
返回值
def max(a, b)
a > b ? a : b # 最后一行自动返回
end
默认参数与可变参数
def power(x, n = 2)
x ** n
end
def sum(*nums)
nums.sum
end
puts power(3) # 9
puts sum(1, 2, 3, 4) # 10
关键字参数
def create_user(name:, age: 18, admin: false)
{ name: name, age: age, admin: admin }
end
create_user(name: "张三", admin: true)
块参数与 yield
def repeat(times)
times.times { yield }
end
repeat(3) { puts "hi" }
方法别名与可见性
alias old_max max
def max(a, b)
puts "调用中"
old_max(a, b)
end
方法名可包含 ?、!、= 等后缀,如 empty?、sort!,用于表达语义约定。
Ruby 块
Ruby 块(Block)
块是 Ruby 最具特色的语法之一:一段可以传递给方法的代码片段,用 { } 或 do...end 包裹。块不是对象,但可以转换为 Proc 对象。
基本用法
[1, 2, 3].each { |n| puts n * 2 }
[1, 2, 3].each do |n|
puts n * 2
end
约定:单行用 { },多行用 do...end。
yield 调用块
def my_each(arr)
arr.each { |x| yield x }
end
my_each([1, 2]) { |x| puts "值: #{x}" }
块参数 block_given?
def log(msg)
if block_given?
yield msg
else
puts msg
end
end
Proc 与 Lambda
p = Proc.new { |x| x * 2 }
l = lambda { |x| x * 2 }
puts p.call(5) # 10
puts l.call(5) # 10
区别:lambda 检查参数个数,return 只退出 lambda;Proc 不检查参数,return 会退出整个方法。
块转 Proc 与符号调用
# 用 & 捕获块
def capture(&block)
block
end
b = capture { puts "hi" }
b.call
# 符号转块(常用技巧)
["a", "b"].map(&:upcase) # ["A", "B"]
块是迭代器模式的基石,理解块与 Proc/lambda 的区别是进阶 Ruby 的关键。
Ruby 模块(Module)
Ruby 模块(Module)
模块在 Ruby 中有两大作用:作为命名空间组织方法,以及作为 Mixin 向类混入方法。Ruby 不支持多继承,模块提供了类似的能力。
定义模块
module MathUtils
PI = 3.14159
def self.square(x)
x * x
end
def area
self.class::PI
end
end
puts MathUtils.square(4) # 16
作为命名空间
module Payment
class Alipay
def pay; "支付宝支付"; end
end
class Wechat
def pay; "微信支付"; end
end
end
Payment::Alipay.new.pay
Mixin 混入
module Greetable
def hello
"你好,我是 #{name}"
end
end
class Person
include Greetable # 实例方法
attr_reader :name
def initialize(name); @name = name; end
end
class Team
extend Greetable # 类方法
def self.name; "团队"; end
end
Person.new("小明").hello
Team.hello
include vs prepend vs extend
include:模块方法成为实例方法(插入继承链中间)prepend:插入继承链最前面,可覆盖类方法extend:模块方法成为类方法
模块嵌套与 require
module A
module B
class C
end
end
end
A::B::C.new
模块还可以使用 module_function 同时提供实例与模块方法,是工具类的常见模式。
Ruby 字符串(String)
Ruby 字符串(String)
字符串是 Ruby 中使用最频繁的数据类型。Ruby 字符串可变,支持单双引号、插值、编码处理与丰富的方法库。
创建字符串
s1 = "双引号"
s2 = '单引号'
s3 = %q(不插值)
s4 = %Q(插值 #{1 + 1})
s5 = <<~EOF
多行字符串
heredoc 语法
EOF
插值与转义
name = "Ruby"
puts "Hello #{name}" # 双引号支持插值
puts 'Hello #{name}' # 单引号原样输出
常用方法
s = "Hello, Ruby"
s.length # 11
s.upcase # "HELLO, RUBY"
s.downcase # "hello, ruby"
s.capitalize # "Hello, ruby"
s.strip # 去首尾空白
s.include?("Ruby") # true
s.start_with?("He") # true
s.split(", ") # ["Hello", "Ruby"]
s.gsub("Ruby", "World") # 替换
# 拼接
"a" + "b" # "ab"
"a" * 3 # "aaa"
索引与切片
s = "Ruby"
s[0] # "R"
s[0, 2] # "Ru"
s[-1] # "y"
s[1..2] # "ub"
编码处理
s = "中文"
s.encoding # UTF-8
s.bytesize # 6 (UTF-8 下一个中文3字节)
s.length # 2 (字符数)
s.encode("GBK") # 转编码
Ruby 3 默认 UTF-8 编码,处理中文等 Unicode 字符时按字符操作,非常友好。
Ruby 数组(Array)
Ruby 数组(Array)
Ruby 数组是有序、可变的元素集合,可以混合存放任意类型,支持丰富的查询与变换方法,是日常开发的主力数据结构。
创建数组
arr1 = [1, 2, 3]
arr2 = %w[a b c] # 字符串数组 ["a","b","c"]
arr3 = Array.new(3) # [nil, nil, nil]
arr4 = Array.new(3, 0) # [0, 0, 0]
访问元素
a = [10, 20, 30, 40]
a[0] # 10
a[-1] # 40
a.first # 10
a.last # 40
a[1, 2] # [20, 30]
a[1..2] # [20, 30]
增删元素
a = [1, 2]
a.push(3) # [1,2,3] 末尾添加
a << 4 # [1,2,3,4] 语法糖
a.unshift(0) # [0,1,2,3,4] 头部添加
a.pop # 删除末尾
a.shift # 删除头部
a.delete(2) # 删除指定值
a.uniq! # 去重(就地修改)
遍历与变换
[1, 2, 3].each { |n| puts n }
[1, 2, 3].map { |n| n * 2 } # [2,4,6]
[1, 2, 3, 4].select(&:even?) # [2,4]
[1, 2, 3, 4].reject(&:even?) # [1,3]
[1, 2, 3].reduce(0) { |sum, n| sum + n } # 6
[3, 1, 2].sort # [1,2,3]
[1, 2, 3].reverse # [3,2,1]
常用查询
a = [1, 2, 3]
a.size # 3
a.empty? # false
a.include?(2) # true
a.max # 3
a.sum # 6
a.join("-") # "1-2-3"
数组方法大多有带 ! 的就地版本(map!、sort!),不修改原数组的版本更安全,按需选择。
Ruby 哈希(Hash)
Ruby 哈希(Hash)
哈希(Hash)是键值对的集合,类似于其他语言的字典/映射。Ruby 哈希键值顺序保持插入顺序,Ruby 1.9+ 默认有序。
创建哈希
h1 = { "name" => "Ruby", "age" => 30 }
h2 = { name: "Ruby", age: 30 } # 符号键简写
h3 = Hash.new # 默认值为 nil
h4 = Hash.new(0) # 默认值为 0
访问与修改
h = { name: "Ruby" }
h[:name] # "Ruby"
h[:age] = 30 # 新增/修改
h.fetch(:age) # 30,键不存在抛异常
h.fetch(:x, 0) # 0,提供默认值
h.key?(:name) # true
遍历
h = { a: 1, b: 2 }
h.each { |k, v| puts "#{k}=#{v}" }
h.each_key { |k| puts k }
h.each_value { |v| puts v }
h.map { |k, v| "#{k}:#{v}" }
变换与合并
h1 = { a: 1, b: 2 }
h2 = { b: 3, c: 4 }
h1.merge(h2) # {a:1, b:3, c:4}
h1.keys # [:a, :b]
h1.values # [1, 2]
h1.select { |k, v| v > 1 } # {b: 2}
# 默认值块
h = Hash.new { |hash, key| hash[key] = [] }
h[:users] << "张三"
符号键 vs 字符串键
h = { name: "Ruby" }
h[:name] # "Ruby"
h["name"] # nil (不同键)
# 从外部数据(JSON)解析来的哈希是字符串键
require "json"
data = JSON.parse('{"name":"Ruby"}')
data["name"] # "Ruby"
哈希配合数组是 Ruby 数据处理的基础,each、map、select 等方法的组合几乎可以完成所有数据变换需求。
Ruby 日期 & 时间
Ruby 日期 & 时间
Ruby 通过内置的 Time 类和标准库 Date 提供日期时间处理能力。Time 处理时间戳与时刻,Date 处理日历日期,DateTime 两者兼顾。
Time 基本用法
require "time"
t = Time.now
puts t # 2026-08-17 10:30:00 +0800
t.year # 2026
t.month # 8
t.day # 17
t.hour # 10
t.strftime("%Y-%m-%d %H:%M") # 格式化输出
# 创建指定时间
t2 = Time.new(2026, 1, 1, 0, 0, 0)
t3 = Time.at(1700000000) # 时间戳转换
日期运算
t = Time.now
t + 3600 # 一小时后
t - 86400 # 一天前
t2 - t # 时间差(秒)
Date 类
require "date"
d = Date.today
d.year # 2026
d + 1 # 明天
d - 7 # 一周前
d.leap? # 是否闰年
Date.new(2026, 12, 25) # 创建日期
Date.parse("2026-08-17") # 字符串解析
格式化与解析
require "time"
# 格式化
Time.now.strftime("%Y年%m月%d日 %H:%M")
# 解析
Time.parse("2026-08-17 10:30:00")
Time.iso8601("2026-08-17T10:30:00+08:00")
时区处理
t = Time.now
t.utc # 转 UTC
t.localtime # 转本地
t.zone # 时区名
t.utc_offset # 与 UTC 的秒差
注意:require "time" 提供扩展方法,Date 类需要 require "date"。商业应用中建议统一使用 UTC 存储、展示时转本地时区。
Ruby 范围(Range)
Ruby 范围(Range)
范围(Range)表示一个区间,例如 1..10。Ruby 范围可用于序列生成、条件判断、数组切片等场景,语法简洁而强大。
创建范围
r1 = 1..5 # 包含结尾 1,2,3,4,5
r2 = 1...5 # 不含结尾 1,2,3,4
r3 = "a".."e" # 字符范围
r4 = 1.0..2.0 # 浮点范围
范围转数组
(1..5).to_a # [1,2,3,4,5]
(1...5).to_a # [1,2,3,4]
("a".."e").to_a # ["a","b","c","d","e"]
(1..5).to_a.reverse
遍历与步长
(1..10).each { |n| print n } # 12345678910
(0..10).step(2) { |n| print n } # 0246810
成员判断
(1..5).include?(3) # true
(1...5).cover?(4.5) # true (范围包含该值)
(1..5) === 3 # true (case 中常用)
用于数组切片
arr = ["a", "b", "c", "d"]
arr[1..2] # ["b","c"]
arr[1...3] # ["b","c"]
arr[0..-1] # 全部
case 与范围
score = 85
case score
when 90..100 then "A"
when 80...90 then "B"
when 70...80 then "C"
else "D"
end # "B"
无起点/终点范围(Ruby 2.6+)
arr = [10, 20, 30, 40]
arr[2..] # [30, 40]
arr[..1] # [10, 20]
范围在 Ruby 中扮演重要角色:配合 case 做分级判断、配合数组做切片、配合 step 做步进遍历,是提升代码简洁度的利器。
Ruby 迭代器
Ruby 迭代器
迭代器是 Ruby 的"一等公民"设计:几乎所有容器(数组、哈希、范围)都通过迭代器方法进行遍历,配合块(block)形成优雅的循环模式。
each 系列
[1, 2, 3].each { |n| puts n }
{ a: 1, b: 2 }.each { |k, v| puts "#{k}=#{v}" }
# 带索引
["a", "b"].each_with_index { |v, i| puts "#{i}:#{v}" }
["a", "b"].each_index { |i| puts i }
# 连续多个元素
(1..10).each_slice(3) { |slice| p slice }
# [1,2,3] [4,5,6] [7,8,9] [10]
times / upto / downto
3.times { puts "hi" }
1.upto(5) { |n| print n } # 12345
5.downto(1) { |n| print n } # 54321
变换类迭代器
[1, 2, 3].map { |n| n * 2 } # 变换
[1, 2, 3, 4].select(&:even?) # 过滤
[1, 2, 3].reduce(0, :+) # 归约 = 6
[1, 2, 3].inject { |sum, n| sum + n }
# 链式
(1..10).select(&:even?).map { |n| n * n } # [4,16,36,64,100]
group_by / partition
["apple", "banana", "avocado"].group_by { |w| w[0] }
# {"a"=>["apple","avocado"], "b"=>["banana"]}
[1, 2, 3, 4].partition(&:even?)
# [[2,4], [1,3]]
自定义迭代器
class Range
def each_square
each { |n| yield n * n }
end
end
(1..3).each_square { |sq| puts sq } # 1 4 9
掌握迭代器意味着告别显式索引循环,代码更简洁、更少出错。理解"块 + 方法"的协作方式是 Ruby 进阶的分水岭。
Ruby 文件的输入与输出
Ruby 文件的输入与输出
Ruby 通过 File 类与 IO 类提供完整的文件读写能力。推荐使用块形式的 open,文件会自动关闭,避免资源泄漏。
写入文件
# w: 覆盖写入
File.open("data.txt", "w") do |f|
f.puts "第一行"
f.puts "第二行"
end
# a: 追加
File.open("data.txt", "a") { |f| f.puts "追加行" }
读取文件
# 全部读入
content = File.read("data.txt")
# 逐行读取
File.foreach("data.txt") { |line| puts line }
# 逐行读取(带行号)
File.open("data.txt") do |f|
f.each_with_index { |line, i| puts "#{i+1}: #{line}" }
end
二进制与编码
# 二进制模式
File.open("img.png", "rb") { |f| data = f.read }
# 指定编码
File.open("data.txt", "r:UTF-8") { |f| puts f.read }
IO 标准流
$stdin.readline # 读取标准输入一行
$stdout.puts "输出"
$stderr.puts "错误信息"
# 管道
IO.popen("ls -la") { |io| puts io.read }
路径与权限
File.exist?("data.txt") # 是否存在
File.file?("data.txt") # 是否文件
File.directory?("dir") # 是否目录
File.size("data.txt") # 字节数
File.writable?("data.txt") # 是否可写
File.mtime("data.txt") # 修改时间
错误处理
begin
File.read("不存在.txt")
rescue Errno::ENOENT
puts "文件不存在"
rescue Errno::EACCES
puts "无权限"
end
文件操作务必注意:优先使用块形式确保关闭文件;根据业务选择 UTF-8 编码;处理可能抛出的异常(ENOENT 等)。
Ruby File 类和方法
Ruby File 类和方法
File 类封装了文件系统操作,包括创建、删除、重命名、查询属性等。本节系统介绍 File 类的常用方法。
创建与删除
File.new("a.txt", "w") # 创建文件(需关闭)
File.write("a.txt", "内容") # 一步写入
File.delete("a.txt") # 删除文件
File.unlink("a.txt") # 同上
File.rename("a.txt", "b.txt") # 重命名
目录操作
Dir.mkdir("mydir") # 创建目录
Dir.rmdir("mydir") # 删除空目录
Dir.exist?("mydir") # 是否存在
Dir.glob("*.rb") # 通配符匹配
Dir["*.rb"] # 同上
Dir.entries(".") # 目录条目
Dir.mkdir_p("a/b/c") # 递归创建
文件属性
f = File.new("data.txt")
f.path # 路径
f.size # 大小
f.atime # 访问时间
f.ctime # 状态变更时间
f.mtime # 修改时间
f.close # 关闭
权限与模式
File.readable?("data.txt") # 可读
File.writable?("data.txt") # 可写
File.executable?("data.sh") # 可执行
File.chmod(0755, "data.sh") # 修改权限
File.umask # 查看默认掩码
常用文件判断
File.exist?("path") # 存在(文件或目录)
File.file?("path") # 是普通文件
File.directory?("path") # 是目录
File.symlink?("path") # 是符号链接
File.zero?("path") # 是否空文件
逐字节/逐行读取
File.open("big.log") do |f|
f.each_line { |line| process(line) }
end
处理大文件时优先逐行读取而非 File.read 整体加载;跨平台路径建议使用 File.join 拼接。
Ruby Dir 类和方法
Ruby Dir 类和方法
Dir 类用于目录操作:遍历目录、创建删除目录、匹配文件路径等。它与 File 类配合可完成完整的文件系统管理。
遍历目录
Dir.foreach(".") { |entry| puts entry }
Dir.entries(".") # 返回数组
Dir.children(".") # 不含 . 和 ..
Dir.each_child(".") { |d| puts d }
glob 模式匹配
Dir.glob("*.rb") # 当前目录所有 rb
Dir.glob("**/*.txt") # 递归所有 txt
Dir.glob("src/**/*.{rb,js}") # 多扩展名
Dir.glob("lib/**/").each { |d| puts d } # 目录
# 等价写法
Dir["*.rb"]
创建与删除
Dir.mkdir("new_dir")
Dir.mkdir("a/b", 0755) # 指定权限
Dir.rmdir("new_dir") # 只能删空目录
# 递归删除(慎用)
require "fileutils"
FileUtils.rm_rf("dir")
当前目录操作
puts Dir.pwd # 当前工作目录
Dir.chdir("/tmp") # 切换目录
Dir.home # 用户主目录
Dir.home("root") # 指定用户主目录
目录属性
Dir.exist?("path") # 是否存在
Dir.empty?("path") # 是否为空
Dir["path/*"].size # 条目数
FileUtils 增强
require "fileutils"
FileUtils.cp("a.txt", "b.txt") # 复制
FileUtils.mv("a.txt", "c.txt") # 移动
FileUtils.mkdir_p("x/y/z") # 递归创建
FileUtils.rm_r("dir") # 递归删除
FileUtils.touch("new.txt") # 创建空文件
批量处理文件时,Dir.glob + FileUtils 是 Ruby 社区的标准组合,简洁高效。
Ruby 异常
Ruby 异常
Ruby 使用 begin/rescue/ensure 结构处理异常。异常是对象,继承自 Exception 类。良好的异常处理让程序在错误场景下依然健壮。
基本结构
begin
result = 10 / 0
rescue ZeroDivisionError => e
puts "捕获异常: #{e.message}"
ensure
puts "无论成败都会执行"
end
多类型捕获
begin
File.read("missing.txt")
rescue Errno::ENOENT => e
puts "文件不存在"
rescue Errno::EACCES
puts "权限不足"
rescue StandardError => e
puts "其他错误: #{e.class}: #{e.message}"
end
抛出异常
def divide(a, b)
raise "除数不能为 0" if b.zero?
a / b
end
# 指定类型
raise ArgumentError, "参数不合法"
raise ArgumentError.new("参数不合法")
# 带 cause
begin
divide(1, 0)
rescue => e
raise RuntimeError, "计算失败", e.backtrace
end
自定义异常类
class MyAppError < StandardError; end
class ValidationError < MyAppError; end
raise ValidationError, "邮箱格式错误"
retry 与 else
attempts = 0
begin
attempts += 1
risky_call
rescue
retry if attempts < 3 # 重试最多3次
ensure
puts "执行完毕"
end
异常层级要点
StandardError 是 rescue 的默认捕获范围;只捕获 Exception 会吞掉 SystemExit、Interrupt 等系统级异常,通常应避免。Ruby 3 还引入了 raise ... cause 链与 Exception#cause 查看异常来源。
Ruby 面向对象
Ruby 面向对象
Ruby 是"纯粹"的面向对象语言:一切皆为对象,包括整数、布尔值、nil。本节系统梳理 Ruby 的 OOP 特性与独到之处。
一切皆对象
1.class # Integer
true.class # TrueClass
nil.class # NilClass
"a".class # String
# 甚至类本身也是对象
Integer.class # Class
Integer.superclass # Numeric
封装
class Account
attr_reader :balance
def initialize(balance = 0)
@balance = balance
end
def deposit(n)
@balance += n if n > 0
end
private
def validate
@balance >= 0
end
end
继承与多态
class Animal
def speak; "..." ; end
end
class Dog < Animal
def speak; "汪汪"; end
end
class Cat < Animal
def speak; "喵喵"; end
end
[Dog.new, Cat.new].each { |a| puts a.speak } # 多态
单例方法
str = "hello"
def str.shout
upcase + "!"
end
str.shout # "HELLO!"
# 更常见:定义类方法
class Foo
def self.bar; end # 类方法 = Class 对象的单例方法
end
方法查找链
obj.singleton_class # 单例类
obj.class # 类
obj.class.ancestors # 继承链(含模块)
Open Class 与 Monkey Patch
class String
def shout
upcase + "!"
end
end
"hi".shout # "HI!"
# 安全做法:带命名空间的补丁
module StringExt
refine String do
def shout; upcase + "!"; end
end
end
Ruby OOP 灵活度极高,但也需要自律:合理使用访问控制、优先组合而非继承、补丁使用 Refinements 限定作用域。
Ruby 正则表达式
Ruby 正则表达式
Ruby 内置正则表达式,语法类似 Perl。正则用于文本匹配、提取、替换,是处理字符串的利器。
基本语法
# 字面量写法
re = /hello/
re = %r{hello}
# 匹配
"hello world" =~ /hello/ # 返回位置 0
/hello/.match("hello world")# MatchData 对象
"hello" === /hello/ # true
常用模式
/a.c/ # . 匹配任意字符
/ab*/ # * 前一个字符0次或多次
/ab+/ # + 一次或多次
/a?b/ # ? 0次或1次
/[abc]/ # 字符类
/[a-z]/ # 范围
/[^0-9]/ # 取反
/\d/ # 数字 = [0-9]
/\w/ # 单词字符
/\s/ # 空白
/^start/ # 行首
/end$/ # 行尾
捕获与替换
m = /(\d{4})-(\d{2})-(\d{2})/.match("2026-08-17")
m[1] # "2026"
m[2] # "08"
m[3] # "17"
"Hello Ruby".gsub(/(Ruby)/, '[]') # 分组引用
"abc123".sub(/\d+/, "X") # 替换第一处
正则选项
/ruby/i # 忽略大小写
/ruby/m # 多行模式
/ruby/x # 忽略空白(可加注释)
/ruby/u # UTF-8
命名捕获
m = /(?<year>\d{4})-(?<month>\d{2})/.match("2026-08")
m[:year] # "2026"
m["month"] # "08"
scan 与 split
"a1b2c3".scan(/\d/) # ["1","2","3"]
"a,b;c".split(/[,;]/) # ["a","b","c"]
"Hello World".scan(/\w+/) # ["Hello","World"]
复杂正则建议用 /x 模式加注释分段,并配合在线工具测试。正则虽强大,过度使用反而降低可读性,简单场景用字符串方法即可。
Ruby 数据库访问 - DBI 教程
Ruby 数据库访问 - DBI 教程
DBI(Database Interface)是 Ruby 早期统一的数据库访问接口,类似 Java 的 JDBC。现代开发更推荐使用 mysql2、pg、sqlite3 等驱动或 ActiveRecord ORM,但理解 DBI 有助于掌握数据库编程思路。
DBI 架构
DBI 分为两层:上层 DBI 提供统一 API,下层 DBD(数据库驱动)对接具体数据库(MySQL、PostgreSQL、SQLite 等)。
安装
gem install dbi
gem install dbd-mysql # MySQL 驱动
gem install dbd-pg # PostgreSQL 驱动
连接数据库
require "dbi"
dbh = DBI.connect("DBI:Mysql:database=test;host=localhost",
"username", "password")
执行查询
# 查询
sth = dbh.prepare("SELECT id, name FROM users WHERE age > ?")
sth.execute(18)
while row = sth.fetch
puts "#{row[0]} #{row[1]}"
end
sth.finish
# 简便方式
dbh.select_all("SELECT * FROM users").each do |row|
puts row["name"]
end
插入与更新
dbh.do("INSERT INTO users(name, age) VALUES(?, ?)", "张三", 25)
dbh.do("UPDATE users SET age = ? WHERE id = ?", 26, 1)
事务处理
dbh.transaction do
dbh.do("UPDATE accounts SET balance = balance - 100 WHERE id = 1")
dbh.do("UPDATE accounts SET balance = balance + 100 WHERE id = 2")
end
现代替代方案
# mysql2 驱动示例
require "mysql2"
client = Mysql2::Client.new(host: "localhost",
username: "root",
database: "test")
results = client.query("SELECT * FROM users")
results.each { |row| puts row["name"] }
使用 ? 占位符预编译语句可有效防止 SQL 注入,任何数据库访问都应遵循这一原则。
Ruby CGI 编程
Ruby CGI 编程
CGI(Common Gateway Interface)是最早的 Web 动态页面技术:Web 服务器把请求转给 CGI 程序,程序输出 HTML 返回客户端。Ruby 内置 CGI 库,适合学习 Web 工作原理。
最小 CGI 程序
#!/usr/bin/env ruby
require "cgi"
cgi = CGI.new
puts cgi.header
puts "<html><body><h1>你好, Ruby CGI</h1></body></html>"
读取参数
require "cgi"
cgi = CGI.new
# GET/POST 参数
name = cgi["name"]
age = cgi["age"]
# 所有参数
cgi.params.each { |k, v| puts "#{k}=#{v}" }
处理表单
require "cgi"
cgi = CGI.new
if cgi.request_method == "POST"
user = cgi["user"]
puts cgi.header
puts "<p>提交的用户:#{CGI.escapeHTML(user)}</p>"
else
puts cgi.header
puts "<form method='post'>"
puts "<input name='user'>"
puts "<button>提交</button></form>"
end
Cookie 与 Session
cgi = CGI.new
cookie = CGI::Cookie.new("name" => "user_id",
"value" => "12345",
"expires" => Time.now + 3600)
puts cgi.header("cookie" => cookie)
输出 HTML 与转义
CGI.escapeHTML("<script>") # 防止 XSS
CGI.escape("a b&c") # URL 编码
CGI.unescape("a%20b")
服务器配置
将 CGI 程序放到服务器 cgi-bin 目录并赋予执行权限,Apache/Nginx 配置 CGI 处理器后即可通过 URL 访问。
现代开发中 CGI 已被 Rack(以及 Rails/Sinatra 等框架)取代,但 CGI 知识仍是理解 HTTP 请求-响应模型的最佳起点。
Ruby CGI方法
Ruby CGI 方法
CGI 类提供了一系列方法用于处理 HTTP 请求:参数解析、Header 生成、Cookie 管理等。本节梳理常用方法。
初始化与请求信息
cgi = CGI.new
cgi.request_method # "GET" / "POST"
cgi.query_string # 查询字符串
cgi.path_info # 路径信息
cgi.remote_addr # 客户端 IP
cgi.user_agent # 浏览器 UA
cgi.http_accept_language
参数访问
cgi["name"] # 单个参数(字符串)
cgi.params # 全部参数 Hash
cgi.params["name"] # 参数数组
cgi.has_key?("name") # 是否存在
cgi.keys # 所有参数名
cgi.master_key # 密码保护的多文件
Header 方法
cgi.header # 默认 text/html
cgi.header("type" => "text/plain")
cgi.header("status" => "404 Not Found")
cgi.header("location" => "http://example.com/")
cgi.header("cookie" => cookie)
输出辅助
cgi.out { "HTML 内容" } # 输出 header + 内容
cgi.print("直接输出")
CGI.escapeHTML(str) # HTML 转义
CGI.escape(str) / CGI.unescape(str)
文件上传
cgi = CGI.new
if cgi.params["file"]&.first.is_a?(StringIO)
file = cgi.params["file"].first
File.open("upload.txt", "w") { |f| f.write(file.read) }
end
multipart 与编码
cgi.accept_charset # 客户端字符集
cgi.charset # 当前字符集
CGI.new(charset: "UTF-8")
CGI 方法虽多,核心就是三件事:取参数、读环境、输出响应。理解这些方法后,过渡到 Rack/Sinatra 会非常自然。
Ruby CGI Cookie
Ruby CGI Cookie
Cookie 是服务器保存在客户端的小段数据,用于维持会话状态(如登录态、购物车)。CGI 库提供 Cookie 类的完整封装。
创建 Cookie
require "cgi"
cookie = CGI::Cookie.new(
"name" => "user_id",
"value" => "12345",
"expires" => Time.now + 3600, # 过期时间
"path" => "/", # 作用路径
"domain" => "example.com",
"secure" => true, # 仅 HTTPS
"httponly" => true # 禁止 JS 读取
)
发送 Cookie
cgi = CGI.new
cookie = CGI::Cookie.new("name" => "login", "value" => "ok")
puts cgi.header("cookie" => cookie)
puts "<html>...</html>"
读取 Cookie
cgi = CGI.new
user_id = cgi.cookies["user_id"]&.first
puts "用户ID: #{user_id}"
多个 Cookie
c1 = CGI::Cookie.new("name" => "a", "value" => "1")
c2 = CGI::Cookie.new("name" => "b", "value" => "2")
puts cgi.header("cookie" => [c1, c2])
删除 Cookie
cookie = CGI::Cookie.new(
"name" => "user_id",
"value" => "",
"expires" => Time.at(0) # 过去时间即删除
)
Cookie 属性说明
| 属性 | 说明 |
|---|---|
| expires | 过期时间,不设则会话结束失效 |
| path | 生效路径,默认当前路径 |
| domain | 生效域名 |
| secure | 仅 HTTPS 传输 |
| httponly | 禁止脚本访问(防 XSS 窃取) |
| max_age | 存活秒数(现代替代 expires) |
安全注意事项
- 敏感信息不要明文存 Cookie,建议只存会话 ID
- 启用 HttpOnly 与 Secure 属性
- 会话 ID 应随机生成并设置过期时间
- 校验 Cookie 时防止伪造(加签名或服务端校验)
Cookie 与 Session 配合是现代 Web 会话管理的基础,理解 Cookie 机制对排查登录问题非常有帮助。
Ruby CGI Session
Ruby CGI Session
Session(会话)是服务端保存的用户状态数据,通过 Cookie 中的 session_id 关联。相比直接存 Cookie,Session 更安全,可存任意大小数据。
创建 Session
require "cgi"
require "cgi/session"
cgi = CGI.new
session = CGI::Session.new(cgi,
"session_key" => "session_id",
"session_id" => nil, # 自动生成
"new_session" => true,
"database_manager" => CGI::Session::FileStore, # 存储方式
"tmpdir" => "/tmp/cgi_sessions" # 文件目录
)
写入与读取
session["user"] = "张三"
session["login_time"] = Time.now.to_s
puts session["user"] # 读取
session.key?("user") # 判断存在
输出 session_id
# Session 首次创建后需要把 session_id 写回浏览器
puts cgi.header("cookie" => session.update)
删除 Session
session.delete # 删除数据
session.close # 关闭(保持数据)
session.update # 刷新输出
完整登录示例
require "cgi"
require "cgi/session"
cgi = CGI.new
session = CGI::Session.new(cgi,
"database_manager" => CGI::Session::FileStore)
if cgi.request_method == "POST" && cgi["pwd"] == "secret"
session["login"] = "true"
puts cgi.header("cookie" => session.update)
puts "<p>登录成功</p>"
elsif session["login"] == "true"
puts cgi.header
puts "<p>欢迎回来</p>"
else
puts cgi.header
puts "<form method='post'><input type='password' name='pwd'><button>登录</button></form>"
end
session.close
存储方式选择
FileStore:文件存储,简单可靠,适合小流量MemoryStore:内存存储,快但进程重启丢失PStore:PStore 文件存储,支持对象
生产环境建议使用数据库或 Redis 存储 Session,并设置合理的过期时间,定期清理过期会话文件。
Ruby 发送邮件 - SMTP
Ruby 发送邮件 - SMTP
Ruby 的 net/smtp 库提供 SMTP 协议支持,可以发送纯文本与 HTML 邮件;配合 mail 等 gem 使用更便捷。本节演示核心用法。
简单发送
require "net/smtp"
message = <<~MESSAGE_END
From: 发件人 <from@example.com>
To: 收件人 <to@example.com>
Subject: 测试邮件
你好,这是一封来自 Ruby 的测试邮件!
MESSAGE_END
Net::SMTP.start("smtp.example.com", 25,
"localhost", "username", "password", :plain) do |smtp|
smtp.send_message(message, "from@example.com", ["to@example.com"])
end
发送 HTML 邮件
html = <<~HTML
<html><body><h2>欢迎!</h2><p>注册成功</p></body></html>
HTML
message = <<~MSG
From: noreply@example.com
To: user@example.com
Subject: 注册确认
MIME-Version: 1.0
Content-Type: text/html; charset=UTF-8
#{html}
MSG
带附件(使用 mail gem 更简单)
require "mail"
mail = Mail.new do
from "noreply@example.com"
to "user@example.com"
subject "月报"
body "请查收附件"
add_file "report.pdf"
end
mail.delivery_method :smtp, address: "smtp.example.com",
port: 587,
user_name: "user",
password: "pass",
authentication: :plain,
enable_starttls_auto: true
mail.deliver
TLS/SSL 支持
Net::SMTP.start("smtp.example.com", 465,
"localhost", "user", "pass", :login,
ssl_context: OpenSSL::SSL::SSLContext.new) do |smtp|
smtp.send_message(msg, "from@example.com", ["to@example.com"])
end
常见邮箱服务器参数
- QQ 邮箱:SMTP 587/TLS,授权码代替密码
- 163 邮箱:SMTP 465/SSL 或 25
- Gmail:SMTP 587,需应用专用密码
生产环境注意:不要硬编码密码,使用环境变量;发送失败时捕获 Net::SMTPError 并重试。
Ruby Socket 编程
Ruby Socket 编程
Socket 是网络编程的基础抽象。Ruby 的 socket 库提供 TCP/UDP 客户端与服务器开发能力,是理解网络应用原理的重要一课。
TCP 服务器
require "socket"
server = TCPServer.new("0.0.0.0", 2000)
puts "服务器启动,监听 2000 端口"
loop do
client = server.accept # 等待连接
Thread.new(client) do |c|
c.puts "连接成功"
while line = c.gets
c.puts "回显: #{line}"
end
c.close
end
end
TCP 客户端
require "socket"
client = TCPSocket.new("localhost", 2000)
puts client.gets # 连接成功
client.puts "你好,服务器"
puts client.gets # 回显: 你好,服务器
client.close
UDP 通信
require "socket"
# 服务器
udp = UDPSocket.new
udp.bind("0.0.0.0", 2001)
data, addr = udp.recvfrom(1024)
puts "收到: #{data} 来自 #{addr[3]}"
# 客户端
udp = UDPSocket.new
udp.send("hello", 0, "localhost", 2001)
HTTP 请求(基于 Socket)
require "socket"
sock = TCPSocket.new("www.example.com", 80)
sock.write("GET / HTTP/1.1
Host: www.example.com
Connection: close
")
puts sock.read
sock.close
获取主机信息
require "socket"
Socket.gethostname # 主机名
Socket.getaddrinfo("example.com", 80) # DNS 解析
IPSocket.getaddress("example.com") # 获取 IP
异常处理
begin
client = TCPSocket.new("example.com", 80, 5) # 5秒超时
rescue Errno::ECONNREFUSED
puts "连接被拒绝"
rescue Errno::ETIMEDOUT
puts "连接超时"
end
Socket 编程是理解 HTTP、WebSocket、数据库驱动等一切网络通信的基础,掌握 TCP 流程后进阶异步框架会事半功倍。
Ruby XML, XSLT 和 XPath 教程
Ruby XML、XSLT 和 XPath 教程
XML 是结构化数据的经典格式。Ruby 提供 REXML 解析 XML,XSLT 用于样式转换,XPath 用于节点查询。本节介绍核心用法(JSON 已逐渐取代 XML,但配置文件、旧系统集成仍常见)。
REXML 解析
require "rexml/document"
include REXML
xml = <<~XML
<bookstore>
<book id="1">
<title>Ruby 入门</title>
<price>59.0</price>
</book>
<book id="2">
<title>Ruby 进阶</title>
<price>79.0</price>
</book>
</bookstore>
XML
doc = Document.new(xml)
XPath 查询
include REXML
doc = Document.new(xml)
# 查询所有书名
XPath.each(doc, "//title") { |el| puts el.text }
# 按条件查询
book = XPath.first(doc, "//book[@id='2']")
puts book.elements["title"].text # Ruby 进阶
# 属性
XPath.first(doc, "//book/@id")
遍历与修改
doc.root.each_element do |book|
puts "#{book.attributes['id']}: #{book.elements['title'].text}"
end
# 修改
book.elements["price"].text = "99.0"
doc.write($stdout)
创建 XML
include REXML
doc = Document.new
root = doc.add_element("users")
user = root.add_element("user")
user.add_attribute("id", "1")
user.add_element("name").text = "张三"
doc.write($stdout)
XSLT 转换(需安装 nokogiri)
require "nokogiri"
xml = Nokogiri::XML(File.read("data.xml"))
xslt = Nokogiri::XSLT(File.read("style.xsl"))
html = xslt.transform(xml)
puts html
现代建议
新项目优先使用 JSON/YAML;处理 XML 时 Nokogiri 性能与功能远强于 REXML,解析不可信 XML 注意 XXE 防护(禁用外部实体)。
Ruby Web Service
Ruby Web Service
Web Service 是跨语言、跨平台的远程调用标准,主要有 SOAP 与 REST 两种风格。Ruby 通过 soap4r(SOAP)与标准 HTTP 库(REST)提供服务与消费能力。
RESTful API 消费
require "net/http"
require "json"
require "uri"
uri = URI("https://api.example.com/users")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
# GET
resp = http.get(uri)
users = JSON.parse(resp.body)
# POST
req = Net::HTTP::Post.new(uri, "Content-Type" => "application/json")
req.body = { name: "张三" }.to_json
resp = http.request(req)
使用 Faraday / HTTParty
require "httparty"
resp = HTTParty.get("https://api.github.com/users/octocat")
puts resp["login"]
HTTParty.post("https://api.example.com/users",
body: { name: "李四" }.to_json,
headers: { "Content-Type" => "application/json" })
SOAP 服务(soap4r)
require "soap/wsdlDriver"
soap = SOAP::WSDLDriverFactory.new("http://example.com/service?wsdl").create_rpc_driver
result = soap.getUser("1001")
puts result
创建简单 REST 服务
require "sinatra"
get "/api/users" do
content_type :json
[{ id: 1, name: "张三" }].to_json
end
post "/api/users" do
params.to_json
end
GraphQL 与 gRPC(现代方向)
# graphql-ruby 提供 GraphQL 服务
# grpc gem 提供 gRPC 支持
require "grpc"
选择建议
- 公开 API:优先 REST/GraphQL(JSON)
- 遗留系统对接:SOAP(WSDL)
- 微服务内部:gRPC(高性能)
无论哪种风格,核心都是"序列化 + 传输 + 反序列化"。理解 HTTP 状态码、鉴权方式与错误处理是调用任何 Web Service 的基础。
Ruby 多线程
Ruby 多线程
Ruby 通过 Thread 类实现多线程。MRI(标准 Ruby)受 GVL(全局虚拟机锁)限制,CPU 密集任务无法真正并行,但 I/O 密集任务(网络请求、文件读写)可以显著提速。
创建线程
threads = []
5.times do |i|
threads << Thread.new(i) do |n|
sleep(rand(0.1..0.3))
puts "线程 #{n} 完成"
end
end
threads.each(&:join) # 等待所有线程结束
puts "全部完成"
线程变量与参数
t = Thread.new do
Thread.current[:name] = "worker"
sleep 0.1
end
t.join
puts t[:name] # 线程局部变量
共享数据与锁
counter = 0
mutex = Mutex.new
threads = 10.times.map do
Thread.new do
1000.times do
mutex.synchronize { counter += 1 }
end
end
end
threads.each(&:join)
puts counter # 10000(不加锁可能小于此值)
线程状态与控制
t = Thread.new { sleep 5 }
t.alive? # true
t.status # "sleep"
t.wakeup
t.exit # 强制终止(慎用)
t.kill
异常处理
t = Thread.new { raise "线程内错误" }
begin
t.join # 异常会在 join 时抛出
rescue => e
puts "捕获: #{e.message}"
end
队列实现生产者消费者
require "thread"
queue = Queue.new
producer = Thread.new { 5.times { |i| queue << i; sleep 0.1 } }
consumer = Thread.new { 5.times { puts "消费: #{queue.pop}" } }
[producer, consumer].each(&:join)
JRuby / TruffleRuby 并行
JRuby 与 TruffleRuby 没有 GVL,可以真正利用多核。对 CPU 密集场景,或使用 Ractors(Ruby 3 的并行抽象):
ractor = Ractor.new { 1 + 1 }
puts ractor.take # 2
多线程核心原则:最小化共享状态、用 Mutex/Queue 保护共享资源、避免死锁。I/O 密集用线程,CPU 密集用 Ractor 或进程。
Ruby JSON
Ruby JSON
JSON 是现代 Web 数据交换的事实标准。Ruby 标准库 json 提供完整的解析与生成能力,还支持 JSON Schema 风格的扩展。
解析 JSON
require "json"
str = '{"name":"Ruby","age":30,"tags":["web","script"]}'
data = JSON.parse(str)
data["name"] # "Ruby"
data["age"] # 30
data["tags"] # ["web","script"]
生成 JSON
require "json"
h = { name: "Ruby", age: 30, tags: %w[web script] }
JSON.generate(h) # {"name":"Ruby",...}
h.to_json # 等价
# 美化输出
JSON.pretty_generate(h)
符号键处理
data = JSON.parse(str, symbolize_names: true)
data[:name] # "Ruby"
# 嵌套对象
user = JSON.parse('{"user":{"name":"张三"}}')
user["user"]["name"]
对象序列化
require "json"
class Person
attr_accessor :name, :age
def initialize(name, age)
@name, @age = name, age
end
def to_json(*)
{ name: @name, age: @age }.to_json
end
def self.from_json(json)
d = JSON.parse(json)
new(d["name"], d["age"])
end
end
p = Person.new("张三", 25)
json = p.to_json
p2 = Person.from_json(json)
文件读写
require "json"
# 写文件
File.write("data.json", JSON.pretty_generate(data))
# 读文件
data = JSON.parse(File.read("data.json"))
错误处理
begin
JSON.parse("invalid json")
rescue JSON::ParserError => e
puts "JSON 解析失败: #{e.message}"
end
JSON 使用注意:嵌套层级深的大文档解析较慢;不可信 JSON 可能包含超大数组,可用 JSON.parse(str, max_nesting: 10) 限制深度防 DoS。
Ruby RubyGems
Ruby RubyGems
RubyGems 是 Ruby 的包管理器,提供 gem 的搜索、安装、发布与依赖管理。Bundler 在此基础上实现项目的依赖锁定与可复现环境。
gem 常用命令
gem list # 已安装的 gem
gem search rails # 搜索
gem install rails # 安装
gem uninstall rails # 卸载
gem update --system # 升级 gem 本身
gem env # 查看环境信息
gem sources -l # 查看源
国内源配置
gem sources --add https://gems.ruby-china.com/
gem sources --remove https://rubygems.org/
gem sources -l
Bundler 使用
# Gemfile
source "https://gems.ruby-china.com"
gem "rails", "~> 7.0"
gem "puma"
gem "sqlite3", group: :development
# 安装与锁定
bundle install
bundle exec rails server # 在锁定环境运行
创建自己的 gem
gem build mygem.gemspec
gem install mygem-0.1.0.gem
# 发布
gem push mygem-0.1.0.gem
gemspec 基础结构:
Gem::Specification.new do |s|
s.name = "mygem"
s.version = "0.1.0"
s.summary = "示例 gem"
s.authors = ["Your Name"]
s.files = Dir["lib/**/*.rb"]
s.require_paths = ["lib"]
end
gem 结构约定
mygem/
├── lib/mygem.rb # 入口
├── lib/mygem/ # 模块文件
├── spec/ # 测试
├── Gemfile
└── mygem.gemspec
常用高质量 gem
- Web:rails、sinatra、rack
- HTTP:httparty、faraday
- 数据库:mysql2、pg、sqlite3、activerecord
- 测试:rspec、minitest
- 其他:nokogiri(HTML/XML)、sidekiq(后台任务)
安装 gem 前建议查看其维护状态与依赖;使用 Bundler 管理版本,避免环境漂移问题。