ruby
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRuby
Ruby
Overview
概述
Ruby programming patterns including blocks, metaprogramming, and idiomatic Ruby code.
Ruby编程模式包括块、元编程以及Ruby惯用代码写法。
Core Ruby Patterns
核心Ruby编程模式
Classes and Modules
类与模块
ruby
undefinedruby
undefinedClass definition
Class definition
class User
attr_accessor :name, :email
attr_reader :id
attr_writer :password
Class variable
@@count = 0
Class method
def self.count
@@count
end
Initialize
def initialize(name, email)
@id = SecureRandom.uuid
@name = name
@email = email
@@count += 1
end
Instance method
def display_name
"#{name} <#{email}>"
end
Private methods
private
def validate_email
email.include?('@')
end
end
class User
attr_accessor :name, :email
attr_reader :id
attr_writer :password
Class variable
@@count = 0
Class method
def self.count
@@count
end
Initialize
def initialize(name, email)
@id = SecureRandom.uuid
@name = name
@email = email
@@count += 1
end
Instance method
def display_name
"#{name} <#{email}>"
end
Private methods
private
def validate_email
email.include?('@')
end
end
Inheritance
Inheritance
class Admin < User
attr_accessor :permissions
def initialize(name, email, permissions = [])
super(name, email)
@permissions = permissions
end
def has_permission?(perm)
permissions.include?(perm)
end
end
class Admin < User
attr_accessor :permissions
def initialize(name, email, permissions = [])
super(name, email)
@permissions = permissions
end
def has_permission?(perm)
permissions.include?(perm)
end
end
Modules for mixins
Modules for mixins
module Timestampable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def timestamped_attrs
[:created_at, :updated_at]
end
end
def touch
@updated_at = Time.now
end
def created_at
@created_at ||= Time.now
end
end
module Timestampable
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def timestamped_attrs
[:created_at, :updated_at]
end
end
def touch
@updated_at = Time.now
end
def created_at
@created_at ||= Time.now
end
end
Including module
Including module
class Document
include Timestampable
include Comparable
attr_accessor :title, :content
def <=>(other)
title <=> other.title
end
end
class Document
include Timestampable
include Comparable
attr_accessor :title, :content
def <=>(other)
title <=> other.title
end
end
Module for namespacing
Module for namespacing
module MyApp
module Services
class UserService
def create(params)
# ...
end
end
end
end
undefinedmodule MyApp
module Services
class UserService
def create(params)
# ...
end
end
end
end
undefinedBlocks, Procs, and Lambdas
块、Proc与Lambda
ruby
undefinedruby
undefinedBlock usage
Block usage
[1, 2, 3].each { |n| puts n }
[1, 2, 3].map do |n|
n * 2
end
[1, 2, 3].each { |n| puts n }
[1, 2, 3].map do |n|
n * 2
end
Yield to block
Yield to block
def with_timing
start = Time.now
result = yield
elapsed = Time.now - start
puts "Elapsed: #{elapsed}s"
result
end
with_timing { sleep(0.1) }
def with_timing
start = Time.now
result = yield
elapsed = Time.now - start
puts "Elapsed: #{elapsed}s"
result
end
with_timing { sleep(0.1) }
Block with arguments
Block with arguments
def transform_items(items)
items.map { |item| yield(item) }
end
transform_items([1, 2, 3]) { |n| n * 2 }
def transform_items(items)
items.map { |item| yield(item) }
end
transform_items([1, 2, 3]) { |n| n * 2 }
Check if block given
Check if block given
def optional_block
if block_given?
yield
else
"No block provided"
end
end
def optional_block
if block_given?
yield
else
"No block provided"
end
end
Convert block to proc
Convert block to proc
def with_block(&block)
block.call(42)
end
def with_block(&block)
block.call(42)
end
Proc
Proc
my_proc = Proc.new { |x| x * 2 }
my_proc.call(21) # => 42
my_proc = Proc.new { |x| x * 2 }
my_proc.call(21) # => 42
Lambda
Lambda
my_lambda = ->(x) { x * 2 }
my_lambda.call(21) # => 42
my_lambda = ->(x) { x * 2 }
my_lambda.call(21) # => 42
Proc vs Lambda differences
Proc vs Lambda differences
proc_example = Proc.new { |x, y| x }
proc_example.call(1) # Works, y is nil
lambda_example = ->(x, y) { x }
proc_example = Proc.new { |x, y| x }
proc_example.call(1) # Works, y is nil
lambda_example = ->(x, y) { x }
lambda_example.call(1) # ArgumentError
lambda_example.call(1) # ArgumentError
Symbol to proc
Symbol to proc
['a', 'b', 'c'].map(&:upcase)
['a', 'b', 'c'].map(&:upcase)
Equivalent to: ['a', 'b', 'c'].map { |s| s.upcase }
Equivalent to: ['a', 'b', 'c'].map { |s| s.upcase }
undefinedundefinedEnumerable and Iterators
Enumerable与迭代器
ruby
undefinedruby
undefinedArray operations
Array operations
numbers = [1, 2, 3, 4, 5]
numbers = [1, 2, 3, 4, 5]
Map/collect
Map/collect
doubled = numbers.map { |n| n * 2 }
doubled = numbers.map { |n| n * 2 }
Select/filter
Select/filter
evens = numbers.select(&:even?)
evens = numbers.select(&:even?)
Reject
Reject
odds = numbers.reject(&:even?)
odds = numbers.reject(&:even?)
Reduce/inject
Reduce/inject
sum = numbers.reduce(0) { |acc, n| acc + n }
sum = numbers.reduce(:+)
sum = numbers.reduce(0) { |acc, n| acc + n }
sum = numbers.reduce(:+)
Each with index
Each with index
numbers.each_with_index do |n, i|
puts "#{i}: #{n}"
end
numbers.each_with_index do |n, i|
puts "#{i}: #{n}"
end
Find
Find
found = numbers.find { |n| n > 3 }
found = numbers.find { |n| n > 3 }
Any/all/none
Any/all/none
numbers.any?(&:even?) # true
numbers.all? { |n| n > 0 } # true
numbers.none? { |n| n > 10 } # true
numbers.any?(&:even?) # true
numbers.all? { |n| n > 0 } # true
numbers.none? { |n| n > 10 } # true
Group by
Group by
users = [
{ name: 'Alice', role: 'admin' },
{ name: 'Bob', role: 'user' },
{ name: 'Charlie', role: 'admin' }
]
grouped = users.group_by { |u| u[:role] }
users = [
{ name: 'Alice', role: 'admin' },
{ name: 'Bob', role: 'user' },
{ name: 'Charlie', role: 'admin' }
]
grouped = users.group_by { |u| u[:role] }
=> { 'admin' => [...], 'user' => [...] }
=> { 'admin' => [...], 'user' => [...] }
Partition
Partition
passed, failed = scores.partition { |s| s >= 60 }
passed, failed = scores.partition { |s| s >= 60 }
Flat map
Flat map
nested = [[1, 2], [3, 4]]
flat = nested.flat_map { |arr| arr.map { |n| n * 2 } }
nested = [[1, 2], [3, 4]]
flat = nested.flat_map { |arr| arr.map { |n| n * 2 } }
Lazy evaluation
Lazy evaluation
(1..Float::INFINITY).lazy
.select(&:even?)
.map { |n| n * 2 }
.take(10)
.to_a
(1..Float::INFINITY).lazy
.select(&:even?)
.map { |n| n * 2 }
.take(10)
.to_a
Custom iterator
Custom iterator
class Countdown
include Enumerable
def initialize(start)
@start = start
end
def each
@start.downto(0) { |n| yield n }
end
end
Countdown.new(5).to_a # => [5, 4, 3, 2, 1, 0]
---class Countdown
include Enumerable
def initialize(start)
@start = start
end
def each
@start.downto(0) { |n| yield n }
end
end
Countdown.new(5).to_a # => [5, 4, 3, 2, 1, 0]
---Metaprogramming
元编程
ruby
undefinedruby
undefinedMethod missing
Method missing
class DynamicProxy
def initialize(target)
@target = target
end
def method_missing(method, *args, &block)
puts "Calling #{method} with #{args}"
@target.send(method, *args, &block)
end
def respond_to_missing?(method, include_private = false)
@target.respond_to?(method) || super
end
end
class DynamicProxy
def initialize(target)
@target = target
end
def method_missing(method, *args, &block)
puts "Calling #{method} with #{args}"
@target.send(method, *args, &block)
end
def respond_to_missing?(method, include_private = false)
@target.respond_to?(method) || super
end
end
Define method dynamically
Define method dynamically
class User
ROLES = %w[admin moderator user]
ROLES.each do |role|
define_method("#{role}?") do
@role == role
end
end
end
class User
ROLES = %w[admin moderator user]
ROLES.each do |role|
define_method("#{role}?") do
@role == role
end
end
end
Class macro
Class macro
class MyModel
def self.attribute(name, type)
define_method(name) do
instance_variable_get("@#{name}")
end
define_method("#{name}=") do |value|
instance_variable_set("@#{name}", value)
endend
attribute :name, :string
attribute :age, :integer
end
class MyModel
def self.attribute(name, type)
define_method(name) do
instance_variable_get("@#{name}")
end
define_method("#{name}=") do |value|
instance_variable_set("@#{name}", value)
endend
attribute :name, :string
attribute :age, :integer
end
Hook methods
Hook methods
class Base
def self.inherited(subclass)
puts "#{subclass} inherits from #{self}"
end
def self.method_added(method_name)
puts "Method #{method_name} added"
end
end
class Base
def self.inherited(subclass)
puts "#{subclass} inherits from #{self}"
end
def self.method_added(method_name)
puts "Method #{method_name} added"
end
end
Instance eval / class eval
Instance eval / class eval
class Config
def self.configure(&block)
instance_eval(&block)
end
def self.setting(name, value)
define_singleton_method(name) { value }
end
end
Config.configure do
setting :api_key, 'abc123'
setting :timeout, 30
end
class Config
def self.configure(&block)
instance_eval(&block)
end
def self.setting(name, value)
define_singleton_method(name) { value }
end
end
Config.configure do
setting :api_key, 'abc123'
setting :timeout, 30
end
Send / public_send
Send / public_send
obj.send(:private_method) # Can call private
obj.public_send(:public_method) # Only public
obj.send(:private_method) # Can call private
obj.public_send(:public_method) # Only public
Refinements (safer monkey patching)
Refinements (safer monkey patching)
module StringExtensions
refine String do
def to_slug
downcase.gsub(/\s+/, '-')
end
end
end
class MyClass
using StringExtensions
def process(title)
title.to_slug
end
end
---module StringExtensions
refine String do
def to_slug
downcase.gsub(/\s+/, '-')
end
end
end
class MyClass
using StringExtensions
def process(title)
title.to_slug
end
end
---Error Handling
错误处理
ruby
undefinedruby
undefinedBasic exception handling
Basic exception handling
begin
risky_operation
rescue StandardError => e
puts "Error: #{e.message}"
puts e.backtrace.first(5).join("\n")
ensure
cleanup
end
begin
risky_operation
rescue StandardError => e
puts "Error: #{e.message}"
puts e.backtrace.first(5).join("\n")
ensure
cleanup
end
Multiple rescue clauses
Multiple rescue clauses
begin
parse_file(path)
rescue Errno::ENOENT
puts "File not found"
rescue JSON::ParserError => e
puts "Invalid JSON: #{e.message}"
rescue => e
puts "Unknown error: #{e.class}"
end
begin
parse_file(path)
rescue Errno::ENOENT
puts "File not found"
rescue JSON::ParserError => e
puts "Invalid JSON: #{e.message}"
rescue => e
puts "Unknown error: #{e.class}"
end
Retry
Retry
attempts = 0
begin
attempts += 1
connect_to_server
rescue ConnectionError
retry if attempts < 3
raise
end
attempts = 0
begin
attempts += 1
connect_to_server
rescue ConnectionError
retry if attempts < 3
raise
end
Custom exceptions
Custom exceptions
class AppError < StandardError
attr_reader :code
def initialize(message, code: nil)
super(message)
@code = code
end
end
class ValidationError < AppError
attr_reader :errors
def initialize(errors)
super("Validation failed")
@errors = errors
end
end
class AppError < StandardError
attr_reader :code
def initialize(message, code: nil)
super(message)
@code = code
end
end
class ValidationError < AppError
attr_reader :errors
def initialize(errors)
super("Validation failed")
@errors = errors
end
end
Raise with custom exception
Raise with custom exception
raise ValidationError.new({ email: ['is invalid'] })
raise ValidationError.new({ email: ['is invalid'] })
Re-raise with context
Re-raise with context
begin
process_order(order)
rescue => e
raise "Failed to process order #{order.id}: #{e.message}"
end
begin
process_order(order)
rescue => e
raise "Failed to process order #{order.id}: #{e.message}"
end
Result object pattern
Result object pattern
class Result
attr_reader :value, :error
def self.success(value)
new(value: value)
end
def self.failure(error)
new(error: error)
end
def initialize(value: nil, error: nil)
@value = value
@error = error
end
def success?
error.nil?
end
def failure?
!success?
end
def then
return self if failure?
yield(value)
end
end
class Result
attr_reader :value, :error
def self.success(value)
new(value: value)
end
def self.failure(error)
new(error: error)
end
def initialize(value: nil, error: nil)
@value = value
@error = error
end
def success?
error.nil?
end
def failure?
!success?
end
def then
return self if failure?
yield(value)
end
end
Usage
Usage
def create_user(params)
return Result.failure('Email required') unless params[:email]
user = User.create(params)
Result.success(user)
rescue ActiveRecord::RecordInvalid => e
Result.failure(e.message)
end
---def create_user(params)
return Result.failure('Email required') unless params[:email]
user = User.create(params)
Result.success(user)
rescue ActiveRecord::RecordInvalid => e
Result.failure(e.message)
end
---Testing with RSpec
使用RSpec进行测试
ruby
undefinedruby
undefinedspec/models/user_spec.rb
spec/models/user_spec.rb
require 'rails_helper'
RSpec.describe User, type: :model do
describe 'validations' do
it { is_expected.to validate_presence_of(:email) }
it { is_expected.to validate_uniqueness_of(:email) }
end
describe 'associations' do
it { is_expected.to have_many(:posts) }
it { is_expected.to belong_to(:organization) }
end
describe '#display_name' do
subject(:user) { build(:user, name: 'John', email: 'john@example.com') }
it 'returns formatted name with email' do
expect(user.display_name).to eq('John <john@example.com>')
endend
describe '.active' do
let!(:active_user) { create(:user, active: true) }
let!(:inactive_user) { create(:user, active: false) }
it 'returns only active users' do
expect(User.active).to contain_exactly(active_user)
endend
context 'when user is admin' do
subject(:admin) { build(:user, :admin) }
it 'has admin privileges' do
expect(admin).to be_admin
endend
end
require 'rails_helper'
RSpec.describe User, type: :model do
describe 'validations' do
it { is_expected.to validate_presence_of(:email) }
it { is_expected.to validate_uniqueness_of(:email) }
end
describe 'associations' do
it { is_expected.to have_many(:posts) }
it { is_expected.to belong_to(:organization) }
end
describe '#display_name' do
subject(:user) { build(:user, name: 'John', email: 'john@example.com') }
it 'returns formatted name with email' do
expect(user.display_name).to eq('John <john@example.com>')
endend
describe '.active' do
let!(:active_user) { create(:user, active: true) }
let!(:inactive_user) { create(:user, active: false) }
it 'returns only active users' do
expect(User.active).to contain_exactly(active_user)
endend
context 'when user is admin' do
subject(:admin) { build(:user, :admin) }
it 'has admin privileges' do
expect(admin).to be_admin
endend
end
spec/services/user_service_spec.rb
spec/services/user_service_spec.rb
RSpec.describe UserService do
describe '#create' do
subject(:service) { described_class.new }
let(:params) { { email: 'test@example.com', name: 'Test' } }
let(:email_service) { instance_double(EmailService) }
before do
allow(EmailService).to receive(:new).and_return(email_service)
allow(email_service).to receive(:send_welcome)
end
it 'creates a user' do
expect { service.create(params) }.to change(User, :count).by(1)
end
it 'sends welcome email' do
service.create(params)
expect(email_service).to have_received(:send_welcome)
end
context 'with invalid params' do
let(:params) { { email: '' } }
it 'raises validation error' do
expect { service.create(params) }.to raise_error(ValidationError)
end
endend
end
---RSpec.describe UserService do
describe '#create' do
subject(:service) { described_class.new }
let(:params) { { email: 'test@example.com', name: 'Test' } }
let(:email_service) { instance_double(EmailService) }
before do
allow(EmailService).to receive(:new).and_return(email_service)
allow(email_service).to receive(:send_welcome)
end
it 'creates a user' do
expect { service.create(params) }.to change(User, :count).by(1)
end
it 'sends welcome email' do
service.create(params)
expect(email_service).to have_received(:send_welcome)
end
context 'with invalid params' do
let(:params) { { email: '' } }
it 'raises validation error' do
expect { service.create(params) }.to raise_error(ValidationError)
end
endend
end
---Concurrency
并发
ruby
undefinedruby
undefinedThreads
Threads
threads = []
results = []
mutex = Mutex.new
5.times do |i|
threads << Thread.new do
result = heavy_computation(i)
mutex.synchronize { results << result }
end
end
threads.each(&:join)
threads = []
results = []
mutex = Mutex.new
5.times do |i|
threads << Thread.new do
result = heavy_computation(i)
mutex.synchronize { results << result }
end
end
threads.each(&:join)
Thread pool with Concurrent Ruby
Thread pool with Concurrent Ruby
require 'concurrent'
pool = Concurrent::FixedThreadPool.new(5)
futures = urls.map do |url|
Concurrent::Future.execute(executor: pool) do
fetch_url(url)
end
end
results = futures.map(&:value)
require 'concurrent'
pool = Concurrent::FixedThreadPool.new(5)
futures = urls.map do |url|
Concurrent::Future.execute(executor: pool) do
fetch_url(url)
end
end
results = futures.map(&:value)
Async/await with Async gem
Async/await with Async gem
require 'async'
Async do
results = urls.map do |url|
Async do
fetch_url(url)
end
end.map(&:wait)
end
require 'async'
Async do
results = urls.map do |url|
Async do
fetch_url(url)
end
end.map(&:wait)
end
Fiber (cooperative concurrency)
Fiber (cooperative concurrency)
fiber = Fiber.new do
puts "Start"
Fiber.yield 1
puts "Middle"
Fiber.yield 2
puts "End"
end
fiber.resume # "Start", returns 1
fiber.resume # "Middle", returns 2
fiber.resume # "End", returns nil
fiber = Fiber.new do
puts "Start"
Fiber.yield 1
puts "Middle"
Fiber.yield 2
puts "End"
end
fiber.resume # "Start", returns 1
fiber.resume # "Middle", returns 2
fiber.resume # "End", returns nil
Ractor (Ruby 3.0+ parallel execution)
Ractor (Ruby 3.0+ parallel execution)
ractor = Ractor.new do
val = Ractor.receive
val * 2
end
ractor.send(21)
result = ractor.take # => 42
---ractor = Ractor.new do
val = Ractor.receive
val * 2
end
ractor.send(21)
result = ractor.take # => 42
---Related Skills
相关技能
- [[backend]] - Ruby on Rails
- [[testing]] - RSpec, Minitest
- [[automation-scripts]] - Ruby scripting
- [[后端开发]] - Ruby on Rails
- [[测试保障]] - RSpec、Minitest
- [[自动化]] - Ruby脚本编写