1. 方法定义
Ruby 用 def...end 定义方法,方法名使用蛇形命名法(snake_case)。
def greet(name)
"Hello, #{name}!"
end
puts greet("Ruby") # => "Hello, Ruby!"
默认参数与关键字参数
def connect(host, port: 3306, ssl: false)
puts "连接 #{host}:#{port},SSL: #{ssl}"
end
connect("db.example.com") # 使用默认值
connect("db.example.com", port: 5432, ssl: true)
def log(message, level: :info, timestamp: Time.now)
puts "[#{timestamp}] #{level.upcase}: #{message}"
end
可变参数(Splat)
def sum(*numbers)
numbers.reduce(0, :+)
end
puts sum(1, 2, 3, 4, 5) # => 15
def create_user(name, **options)
puts "用户:#{name}"
options.each { |k, v| puts " #{k}: #{v}" }
end
create_user("Alice", role: "admin", active: true)
方法命名约定
def empty? # 以 ? 结尾 —— 返回布尔值(谓词方法)
@items.length == 0
end
def save! # 以 ! 结尾 —— 危险操作(修改自身或可能抛异常)
raise "验证失败" unless valid?
perform_save
end
def name=(value) # 以 = 结尾 —— setter 方法
@name = value.strip
end
隐式返回:Ruby 方法自动返回最后一个表达式的值,无需写 return。只有需要提前退出方法时才使用 return。这让代码更简洁,也是 Ruby 风格的标志。
🔄 函数/方法定义对比
| 语言 | 语法 |
| Ruby | def greet(name) ... end |
| Python | def greet(name): ... |
| PHP | function greet(string $name): string { ... } |
| Java | public String greet(String name) { ... } |
Ruby 和 Python 用 def,但 Ruby 用 end 替代缩进来界定方法体。Ruby 的 ?/! 命名约定是其他语言没有的特色。
2. Block、Proc 与 Lambda
Block 是 Ruby 最独特的特性之一,它是一段可以传递给方法的代码块。
Block 基础
# 两种写法:do...end 适合多行,{} 适合单行
[1, 2, 3].each do |n|
puts n * 10
end
[1, 2, 3].each { |n| puts n * 10 }
yield 调用 Block
def with_logging
puts "[开始] #{Time.now}"
result = yield
puts "[结束] #{Time.now}"
result
end
with_logging do
sleep(1)
"操作完成"
end
def repeat(n)
n.times { |i| yield(i) }
end
repeat(3) { |i| puts "第 #{i + 1} 次" }
&block 参数
def transform(data, &block)
data.map(&block)
end
result = transform([1, 2, 3]) { |n| n ** 2 }
puts result.inspect # => [1, 4, 9]
def execute(&block)
block.call if block # 显式调用
puts "没有 Block" unless block
end
execute { puts "有 Block" }
execute
Proc vs Lambda
square = Proc.new { |n| n ** 2 }
square = proc { |n| n ** 2 } # 简写
puts square.call(5) # => 25
puts square.(5) # => 25(简写调用)
puts square[5] # => 25(另一种简写)
double = lambda { |n| n * 2 }
double = ->(n) { n * 2 } # 箭头 Lambda(推荐写法)
puts double.call(5) # => 10
# Proc 与 Lambda 的两个关键区别:
# 1. 参数检查:Lambda 严格检查参数数量,Proc 宽松(多余忽略,不足补 nil)
# 2. return 行为:Lambda 的 return 返回到 Lambda 自身,Proc 的 return 返回到外层方法
validator = ->(age) { age >= 18 }
[15, 20, 12, 25].select(&validator) # => [20, 25]
Method 对象
def triple(n)
n * 3
end
m = method(:triple)
puts m.call(5) # => 15
[1, 2, 3].map(&m) # => [3, 6, 9]
puts method(:puts).arity # => -1(可变参数)
Block 是 Ruby 的灵魂:几乎所有 Ruby 的迭代、资源管理、DSL 都建立在 Block 之上。File.open(path) { |f| ... } 会自动关闭文件,ActiveRecord 的作用域和回调也是 Block 的应用。理解 Block/Proc/Lambda 是掌握 Ruby 的关键。
3. 类 (Class)
class User
attr_accessor :name, :email # 同时生成 getter 和 setter
attr_reader :id # 只生成 getter
attr_writer :password # 只生成 setter
@@user_count = 0
def initialize(name, email)
@id = SecureRandom.uuid rescue rand(10000)
@name = name
@email = email
@@user_count += 1
end
def to_s
"#{@name} <#{@email}>"
end
def self.count
@@user_count
end
def admin?
@email.end_with?("@admin.com")
end
private
def encrypt_password(raw)
Digest::SHA256.hexdigest(raw) rescue raw.reverse
end
end
user = User.new("Alice", "alice@example.com")
puts user.name # => "Alice"(attr_accessor 生成的 getter)
user.name = "Bob" # attr_accessor 生成的 setter
puts user # => "Bob "(调用 to_s)
puts user.admin? # => false
puts User.count # => 1(类方法)
继承
class Admin < User
attr_reader :permissions
def initialize(name, email, permissions = [])
super(name, email)
@permissions = permissions
end
def admin?
true
end
def can?(action)
@permissions.include?(action)
end
end
admin = Admin.new("Charlie", "charlie@admin.com", [:manage_users, :edit_posts])
puts admin.admin? # => true
puts admin.can?(:manage_users) # => true
puts admin.is_a?(User) # => true
puts admin.is_a?(Admin) # => true
puts Admin.superclass # => User
访问控制
class Account
def deposit(amount) # public(默认)
@balance += amount
end
protected
def balance # protected:同类或子类的实例可以调用
@balance
end
def >(other)
balance > other.balance
end
private
def audit_log(action) # private:只能在实例内部调用,不能有显式接收者
puts "[审计] #{action}"
end
end
4. 模块与 Mixin
Ruby 是单继承语言,但通过模块(Module)的 Mixin 机制实现了多重继承的效果。
模块作为 Mixin
module Loggable
def log(message)
puts "[#{self.class}] #{message}"
end
end
module Serializable
def to_json
instance_variables.each_with_object({}) do |var, hash|
hash[var.to_s.delete("@")] = instance_variable_get(var)
end.to_s
end
end
class Order
include Loggable # 作为实例方法混入
include Serializable
def initialize(id, total)
@id = id
@total = total
end
def process
log("处理订单 ##{@id}")
end
end
order = Order.new(1, 99.9)
order.process # => "[Order] 处理订单 #1"
puts order.to_json # => {"id"=>1, "total"=>99.9}
include vs extend vs prepend
module Greetable
def hello
"Hello from #{self.class}!"
end
end
class MyClass
include Greetable # hello 成为实例方法
end
MyClass.new.hello # => "Hello from MyClass!"
class MyClass2
extend Greetable # hello 成为类方法
end
MyClass2.hello # => "Hello from MyClass2!"
module Logging
def save
puts "保存前记录日志..."
super # 调用原始的 save
puts "保存后记录日志..."
end
end
class Record
def save
puts "保存数据"
end
end
class LoggedRecord < Record
prepend Logging # prepend 插入到方法查找链的前面
end
LoggedRecord.new.save
# 输出:
# 保存前记录日志...
# 保存数据
# 保存后记录日志...
模块作为命名空间
module Payment
class Gateway
def charge(amount)
puts "收费 #{amount}"
end
end
class Receipt
def generate
puts "生成收据"
end
end
end
gateway = Payment::Gateway.new
gateway.charge(100)
🔄 接口/Trait/Mixin 对比
| 语言 | 机制 | 特点 |
| Ruby | Module (Mixin) | 可包含具体实现,通过 include/extend/prepend 混入 |
| Java | Interface | Java 8+ 支持 default 方法,但主要是契约定义 |
| PHP | Trait | 类似 Ruby Mixin,用 use 引入,支持冲突解决 |
| Python | 多重继承 | 直接支持多继承,通过 MRO 解决方法查找顺序 |
5. 常用内置模块
Enumerable
只要类实现了 each 方法并 include Enumerable,就能获得数十个集合操作方法:
class NumberSet
include Enumerable
def initialize(*numbers)
@data = numbers
end
def each(&block)
@data.each(&block)
end
end
set = NumberSet.new(3, 1, 4, 1, 5, 9)
puts set.sort.inspect # => [1, 1, 3, 4, 5, 9]
puts set.min # => 1
puts set.max # => 9
puts set.select(&:odd?).inspect # => [3, 1, 1, 5, 9]
puts set.any? { |n| n > 8 } # => true
puts set.all? { |n| n > 0 } # => true
puts set.find { |n| n.even? } # => 4
puts set.flat_map { |n| [n, -n] }.inspect # => [3, -3, 1, -1, ...]
puts set.group_by(&:even?).inspect # => { false: [3,1,1,5,9], true: [4] }
puts set.tally.inspect # => {3=>1, 1=>2, 4=>1, 5=>1, 9=>1}
Comparable
class Temperature
include Comparable
attr_reader :degrees
def initialize(degrees)
@degrees = degrees
end
def <=>(other)
@degrees <=> other.degrees
end
end
temps = [Temperature.new(30), Temperature.new(15), Temperature.new(25)]
puts temps.sort.map(&:degrees).inspect # => [15, 25, 30]
puts temps.min.degrees # => 15
puts Temperature.new(20).between?(Temperature.new(10), Temperature.new(30)) # => true
Kernel 常用方法
puts "标准输出(自动换行)"
print "不换行"
p [1, 2, 3] # 输出 inspect 结果:[1, 2, 3](调试用)
pp({ a: 1, b: { c: [2, 3] } }) # 格式化输出复杂对象
name = gets.chomp # 读取用户输入
system("echo hello") # 执行 shell 命令
result = `ls -la` # 反引号捕获命令输出
sleep(2) # 暂停 2 秒
rand(100) # 0-99 随机整数
Array(nil) # => [](安全转换)
Array("hello") # => ["hello"]
Array([1, 2]) # => [1, 2]
p vs puts vs pp:puts 调用对象的 to_s 方法输出人类可读的文本。p 调用 inspect 方法,输出带引号的字符串和结构信息,适合调试。pp(pretty print)对复杂嵌套对象做格式化缩进输出。
6. Struct 与 Data
Struct —— 轻量数据容器
Point = Struct.new(:x, :y)
p1 = Point.new(3, 4)
puts p1.x # => 3
puts p1.y # => 4
puts p1.to_a # => [3, 4]
p1.x = 10 # Struct 默认可修改
Point = Struct.new(:x, :y) do
def distance_to(other)
Math.sqrt((x - other.x) ** 2 + (y - other.y) ** 2)
end
end
p1 = Point.new(0, 0)
p2 = Point.new(3, 4)
puts p1.distance_to(p2) # => 5.0
Data —— 不可变值对象(Ruby 3.2+)
Color = Data.define(:r, :g, :b)
red = Color.new(r: 255, g: 0, b: 0)
puts red.r # => 255
# red.r = 100 # NoMethodError! Data 对象不可变
blue = Color.new(r: 0, g: 0, b: 255)
puts red == Color.new(r: 255, g: 0, b: 0) # => true(值比较)
Config = Data.define(:host, :port) do
def url
"http://#{host}:#{port}"
end
end
cfg = Config.new(host: "localhost", port: 3000)
puts cfg.url # => "http://localhost:3000"
OpenStruct —— 动态属性
require "ostruct"
person = OpenStruct.new(name: "Alice", age: 30)
person.email = "alice@example.com" # 随时添加新属性
puts person.name # => "Alice"
puts person.email # => "alice@example.com"
puts person.phone # => nil(不存在的属性返回 nil)
选择建议:需要简单的数据载体用 Struct;需要不可变的值对象用 Data(Ruby 3.2+);需要动态灵活的属性用 OpenStruct(性能较差,避免在热路径使用)。需要完整业务逻辑用 Class。
7. 本章要点
🔧 方法定义
def...end 定义方法,支持默认参数、关键字参数、*args/**kwargs。?/!/= 命名约定表达语义。隐式返回。
📦 Block/Proc/Lambda
Block 是 Ruby 的核心闭包机制,用 yield 或 &block 接收。Lambda 严格检查参数,Proc 宽松。箭头 Lambda ->(x) { } 是推荐写法。
🏗️ 类与继承
class...end 定义类,initialize 是构造方法。attr_accessor 生成 getter/setter。< 实现继承,super 调用父类。
🧩 模块与 Mixin
include 混入实例方法,extend 混入类方法,prepend 插入方法链前端。模块也用作命名空间。
🔢 Enumerable/Comparable
实现 each + include Enumerable 获得全套集合操作。实现 <=> + include Comparable 获得排序能力。
📋 Struct/Data
Struct 快速创建数据容器,Data(3.2+)创建不可变值对象,OpenStruct 支持动态属性。按需选用。
方法和面向对象是 Ruby 的核心。掌握 Block/Proc/Lambda 和 Mixin 机制后,你就拥有了阅读和编写地道 Ruby 代码的能力。接下来可以深入 Ruby 标准库和 Rails 框架。