ruby
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRuby Language Skill
Ruby 语言技能
Overview
概述
Opinionated Ruby conventions and idioms for writing idiomatic Ruby 3.x+ code. Focuses on patterns agents miss by default — the Weirich raise/fail distinction, safe nil-aware memoization, result objects over exceptions for expected failures, and performance-conscious enumeration.
这是一套带有明确倾向性的Ruby约定与惯用写法,用于编写符合Ruby规范的3.x+版本代码。重点关注Agent默认容易遗漏的模式:Weirich提出的raise/fail用法区分、可安全处理nil的memoization、针对预期故障优先使用结果对象而非异常,以及考虑性能的枚举方式。
Error Handling Conventions
错误处理约定
Weirich raise/fail Convention
Weirich raise/fail 约定
Use for first-time exceptions, only for re-raising:
failraiseruby
def process(order)
fail ArgumentError, "Order cannot be nil" if order.nil?
begin
gateway.charge(order)
rescue PaymentError => e
logger.error("Payment failed: #{e.message}")
raise # re-raise with raise
end
end首次抛出异常时使用,仅在重新抛出异常时使用:
failraiseruby
def process(order)
fail ArgumentError, "Order cannot be nil" if order.nil?
begin
gateway.charge(order)
rescue PaymentError => e
logger.error("Payment failed: #{e.message}")
raise # re-raise with raise
end
endCustom Exception Hierarchies
自定义异常层级
Group domain exceptions under a base error:
ruby
module MyApp
class Error < StandardError; end
class PaymentError < Error; end
class InsufficientFundsError < PaymentError; end
end将业务域异常统一继承自一个基础错误类:
ruby
module MyApp
class Error < StandardError; end
class PaymentError < Error; end
class InsufficientFundsError < PaymentError; end
endRescue at any granularity:
Rescue at any granularity:
rescue MyApp::InsufficientFundsError # specific
rescue MyApp::PaymentError # category
rescue MyApp::Error # all app errors
undefinedrescue MyApp::InsufficientFundsError # specific
rescue MyApp::PaymentError # category
rescue MyApp::Error # all app errors
undefinedResult Objects for Expected Failures
预期故障使用结果对象
Use result objects instead of exceptions for expected failure paths:
ruby
class Result
attr_reader :value, :error
def self.success(value) = new(value: value)
def self.failure(error) = new(error: error)
def initialize(value: nil, error: nil) = (@value, @error = value, error)
def success? = error.nil?
def failure? = !success?
end针对预期会出现的故障路径,使用结果对象而非异常处理:
ruby
class Result
attr_reader :value, :error
def self.success(value) = new(value: value)
def self.failure(error) = new(error: error)
def initialize(value: nil, error: nil) = (@value, @error = value, error)
def success? = error.nil?
def failure? = !success?
endCaller-Supplied Fallback
调用方提供降级逻辑
Let callers define error handling via blocks:
ruby
def fetch_user(id, &fallback)
User.find(id)
rescue ActiveRecord::RecordNotFound => e
fallback ? fallback.call(e) : raise
end
user = fetch_user(999) { |_| User.new(name: "Guest") }See for full patterns and retry strategies.
references/error_handling.md允许调用方通过代码块自定义错误处理逻辑:
ruby
def fetch_user(id, &fallback)
User.find(id)
rescue ActiveRecord::RecordNotFound => e
fallback ? fallback.call(e) : raise
end
user = fetch_user(999) { |_| User.new(name: "Guest") }完整模式与重试策略可查看。
references/error_handling.mdModern Ruby (3.x+)
现代Ruby(3.x+版本)
Pattern Matching
模式匹配
ruby
case response
in { status: 200, body: { users: [{ name: }, *] } }
"First user: #{name}"
in { status: (400..), error: message }
"Error: #{message}"
endruby
case response
in { status: 200, body: { users: [{ name: }, *] } }
"First user: #{name}"
in { status: (400..), error: message }
"Error: #{message}"
endFind pattern
Find pattern
case array
in [*, String => str, *]
"Found string: #{str}"
end
case array
in [*, String => str, *]
"Found string: #{str}"
end
Pin operator
Pin operator
expected = 200
case response
in { status: ^expected, body: }
process(body)
end
undefinedexpected = 200
case response
in { status: ^expected, body: }
process(body)
end
undefinedOther 3.x+ Features
其他3.x+版本特性
ruby
undefinedruby
undefinedEndless methods (3.0+)
Endless methods (3.0+)
def square(x) = x * x
def admin? = role == "admin"
def square(x) = x * x
def admin? = role == "admin"
Numbered block parameters (2.7+)
Numbered block parameters (2.7+)
[1, 2, 3].map { _1 * 2 }
[1, 2, 3].map { _1 * 2 }
Data class - immutable value objects (3.2+)
Data class - immutable value objects (3.2+)
Point = Data.define(:x, :y)
p = Point.new(x: 1, y: 2)
p.with(x: 3) # => Point(x: 3, y: 2)
Point = Data.define(:x, :y)
p = Point.new(x: 1, y: 2)
p.with(x: 3) # => Point(x: 3, y: 2)
Hash#except (3.0+)
Hash#except (3.0+)
params.except(:password, :admin)
params.except(:password, :admin)
filter_map (2.7+) - select + map in one pass
filter_map (2.7+) - select + map in one pass
users.filter_map { |u| u.email if u.active? }
users.filter_map { |u| u.email if u.active? }
tally (2.7+)
tally (2.7+)
%w[a b a c b a].tally # => {"a"=>3, "b"=>2, "c"=>1}
See `references/modern_ruby.md` for ractors, fiber scheduler, RBS types, and advanced pattern matching.%w[a b a c b a].tally # => {"a"=>3, "b"=>2, "c"=>1}
有关ractor、纤程调度器、RBS类型与高级模式匹配的内容可查看`references/modern_ruby.md`。Performance Quick Wins
性能优化快速指南
Frozen String Literals
冻结字符串字面量
ruby
undefinedruby
undefinedfrozen_string_literal: true
frozen_string_literal: true
Add to top of every file. Prevents mutation, reduces allocations.
Add to top of every file. Prevents mutation, reduces allocations.
When you need mutable: String.new("hello") or +"hello"
When you need mutable: String.new("hello") or +"hello"
undefinedundefinedEfficient Enumeration
高效枚举
ruby
undefinedruby
undefinedeach_with_object for building results (avoids intermediate arrays)
each_with_object for building results (avoids intermediate arrays)
totals = items.each_with_object(Hash.new(0)) do |item, hash|
hash[item.category] += item.amount
end
totals = items.each_with_object(Hash.new(0)) do |item, hash|
hash[item.category] += item.amount
end
Lazy enumerables for large/infinite sequences
Lazy enumerables for large/infinite sequences
(1..Float::INFINITY).lazy.select(&:odd?).map { _1 ** 2 }.first(10)
undefined(1..Float::INFINITY).lazy.select(&:odd?).map { _1 ** 2 }.first(10)
undefinedMemoization with nil/false Caveat
支持nil/false的安全memoization
ruby
undefinedruby
undefinedSimple (only works if result is truthy)
Simple (only works if result is truthy)
def users = @users ||= User.all.to_a
def users = @users ||= User.all.to_a
Safe (handles nil/false results)
Safe (handles nil/false results)
def feature_enabled?
return @feature_enabled if defined?(@feature_enabled)
@feature_enabled = expensive_check
end
undefineddef feature_enabled?
return @feature_enabled if defined?(@feature_enabled)
@feature_enabled = expensive_check
end
undefinedString Building
字符串构建
ruby
undefinedruby
undefinedBad: O(n^2) with +=
Bad: O(n^2) with +=
result = ""; items.each { |i| result += i.to_s }
result = ""; items.each { |i| result += i.to_s }
Good: O(n) with <<
Good: O(n) with <<
result = String.new; items.each { |i| result << i.to_s }
result = String.new; items.each { |i| result << i.to_s }
Best: join
Best: join
items.map(&:to_s).join
See `references/performance.md` for YJIT, GC tuning, benchmarking, and profiling tools.items.map(&:to_s).join
有关YJIT、GC调优、基准测试与性能分析工具的内容可查看`references/performance.md`。Ruby Idioms to Prefer
推荐使用的Ruby惯用写法
Guard Clauses
卫语句
ruby
def process(value)
return unless value
return unless value.valid?
# main logic here
endruby
def process(value)
return unless value
return unless value.valid?
# main logic here
endLiteral Array Constructors
字面量数组构造器
ruby
STATES = %w[draft published archived] # word array
FIELDS = %i[name email created_at] # symbol arrayruby
STATES = %w[draft published archived] # word array
FIELDS = %i[name email created_at] # symbol arrayHash#fetch for Required Keys
使用Hash#fetch获取必填键
ruby
config.fetch(:api_key) # raises KeyError if missing
config.fetch(:timeout, 30) # default value
config.fetch(:handler) { build_handler } # lazy defaultruby
config.fetch(:api_key) # raises KeyError if missing
config.fetch(:timeout, 30) # default value
config.fetch(:handler) { build_handler } # lazy defaultSafe Navigation
安全导航
ruby
user&.profile&.avatar_url # returns nil if any link is nilruby
user&.profile&.avatar_url # returns nil if any link is nilPredicate and Bang Conventions
谓词方法与Bang方法约定
- suffix: returns boolean (
?,empty?,valid?)admin? - suffix: dangerous version - mutates receiver or raises on failure (
!,save!)sort! - Always provide a non-bang alternative when defining bang methods
- 后缀:返回布尔值(
?、empty?、valid?等)admin? - 后缀:危险版本方法,会修改调用对象本身或在失败时抛出异常(
!、save!等)sort! - 定义Bang方法时,务必提供对应的非Bang版本
Common Mistakes
常见错误
| Mistake | Fix |
|---|---|
| Use |
| Use |
String concatenation with | Use |
| Rescue |
Deep | Extract to a method or use explicit nil check |
Missing | Add to top of every file |
| 错误写法 | 修复方案 |
|---|---|
使用 | 遵循Weirich约定,使用 |
当返回结果可能为 | |
循环中使用 | 使用 |
捕获 | 仅捕获 |
过长的 | 抽取为独立方法或使用显式nil判断 |
缺少 | 在每个文件顶部添加该声明 |
References
参考资料
- - Pattern matching, ractors, fiber scheduler, RBS types
references/modern_ruby.md - - Exception hierarchies, result objects, retry patterns
references/error_handling.md - - YJIT, GC tuning, benchmarking, profiling
references/performance.md - - OOD principles, naming, SOLID, TRUE heuristic, Law of Demeter
references/ood-philosophy.md
- - 模式匹配、ractor、纤程调度器、RBS类型
references/modern_ruby.md - - 异常层级、结果对象、重试模式
references/error_handling.md - - YJIT、GC调优、基准测试、性能分析
references/performance.md - - 面向对象设计原则、命名规范、SOLID、TRUE启发式规则、迪米特法则
references/ood-philosophy.md