ruby

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Ruby 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
fail
for first-time exceptions,
raise
only for re-raising:
ruby
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
首次抛出异常时使用
fail
,仅在重新抛出异常时使用
raise
ruby
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

Custom 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
end

Rescue at any granularity:

Rescue at any granularity:

rescue MyApp::InsufficientFundsError # specific rescue MyApp::PaymentError # category rescue MyApp::Error # all app errors
undefined
rescue MyApp::InsufficientFundsError # specific rescue MyApp::PaymentError # category rescue MyApp::Error # all app errors
undefined

Result 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?
end

Caller-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
references/error_handling.md
for full patterns and retry strategies.
允许调用方通过代码块自定义错误处理逻辑:
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.md

Modern 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}"
end
ruby
case response
in { status: 200, body: { users: [{ name: }, *] } }
  "First user: #{name}"
in { status: (400..), error: message }
  "Error: #{message}"
end

Find 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
undefined
expected = 200 case response in { status: ^expected, body: } process(body) end
undefined

Other 3.x+ Features

其他3.x+版本特性

ruby
undefined
ruby
undefined

Endless 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
undefined
ruby
undefined

frozen_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"

undefined
undefined

Efficient Enumeration

高效枚举

ruby
undefined
ruby
undefined

each_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)
undefined

Memoization with nil/false Caveat

支持nil/false的安全memoization

ruby
undefined
ruby
undefined

Simple (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
undefined
def feature_enabled? return @feature_enabled if defined?(@feature_enabled) @feature_enabled = expensive_check end
undefined

String Building

字符串构建

ruby
undefined
ruby
undefined

Bad: 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
end
ruby
def process(value)
  return unless value
  return unless value.valid?
  # main logic here
end

Literal Array Constructors

字面量数组构造器

ruby
STATES = %w[draft published archived]      # word array
FIELDS = %i[name email created_at]         # symbol array
ruby
STATES = %w[draft published archived]      # word array
FIELDS = %i[name email created_at]         # symbol array

Hash#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 default
ruby
config.fetch(:api_key)                     # raises KeyError if missing
config.fetch(:timeout, 30)                 # default value
config.fetch(:handler) { build_handler }   # lazy default

Safe Navigation

安全导航

ruby
user&.profile&.avatar_url  # returns nil if any link is nil
ruby
user&.profile&.avatar_url  # returns nil if any link is nil

Predicate 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

常见错误

MistakeFix
raise
for new exceptions
Use
fail
; reserve
raise
for re-raising (Weirich convention)
@var ||= compute
when result can be
nil
/
false
Use
defined?(@var)
check instead
String concatenation with
+=
in loops
Use
<<
or
.join
+=
is O(n²)
rescue Exception
Rescue
StandardError
Exception
catches
SignalException
,
NoMemoryError
Deep
&.
chains (3+ links)
Extract to a method or use explicit nil check
Missing
# frozen_string_literal: true
Add to top of every file
错误写法修复方案
使用
raise
抛出新异常
遵循Weirich约定,使用
fail
抛出新异常,
raise
仅用于重新抛出
当返回结果可能为
nil
/
false
时使用`@var
循环中使用
+=
拼接字符串
使用
<<
.join
——
+=
的时间复杂度为O(n²)
捕获
Exception
仅捕获
StandardError
——
Exception
会捕获
SignalException
NoMemoryError
等系统级异常
过长的
&.
链式调用(超过3层)
抽取为独立方法或使用显式nil判断
缺少
# frozen_string_literal: true
声明
在每个文件顶部添加该声明

References

参考资料

  • references/modern_ruby.md
    - Pattern matching, ractors, fiber scheduler, RBS types
  • references/error_handling.md
    - Exception hierarchies, result objects, retry patterns
  • references/performance.md
    - YJIT, GC tuning, benchmarking, profiling
  • references/ood-philosophy.md
    - OOD principles, naming, SOLID, TRUE heuristic, Law of Demeter
  • references/modern_ruby.md
    - 模式匹配、ractor、纤程调度器、RBS类型
  • references/error_handling.md
    - 异常层级、结果对象、重试模式
  • references/performance.md
    - YJIT、GC调优、基准测试、性能分析
  • references/ood-philosophy.md
    - 面向对象设计原则、命名规范、SOLID、TRUE启发式规则、迪米特法则