ruby-conventions

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Ruby Conventions

Ruby 约定规范

Service objects, strong params, behavior-focused tests.
服务对象、Strong Params、以行为为核心的测试。

Architecture

架构设计

Service objects for business logic:
ruby
undefined
使用服务对象处理业务逻辑:
ruby
undefined

app/services/user_registration_service.rb

app/services/user_registration_service.rb

class UserRegistrationService def initialize(user_params:, mailer: UserMailer) @user_params = user_params @mailer = mailer end
def call user = User.create!(@user_params) @mailer.welcome(user).deliver_later user end end

**Thin controllers:**
```ruby
def create
  user = UserRegistrationService.new(user_params: user_params).call
  render json: UserSerializer.new(user), status: :created
rescue ActiveRecord::RecordInvalid => e
  render json: { errors: e.record.errors }, status: :unprocessable_entity
end
class UserRegistrationService def initialize(user_params:, mailer: UserMailer) @user_params = user_params @mailer = mailer end
def call user = User.create!(@user_params) @mailer.welcome(user).deliver_later user end end

**轻量控制器:**
```ruby
def create
  user = UserRegistrationService.new(user_params: user_params).call
  render json: UserSerializer.new(user), status: :created
rescue ActiveRecord::RecordInvalid => e
  render json: { errors: e.record.errors }, status: :unprocessable_entity
end

Strong Parameters

Strong Parameters(强参数)

Always use strong params. Never mass-assign directly:
ruby
undefined
始终使用强参数,切勿直接批量赋值:
ruby
undefined

Good

Good

def user_params params.require(:user).permit(:email, :name, :password) end
def user_params params.require(:user).permit(:email, :name, :password) end

Never

Never

User.create(params[:user]) # SQL injection risk
undefined
User.create(params[:user]) # SQL injection risk
undefined

Query Safety

查询安全性

Always parameterize queries:
ruby
undefined
始终使用参数化查询:
ruby
undefined

Good

Good

User.where(email: email) User.where("email = ?", email)
User.where(email: email) User.where("email = ?", email)

Never

Never

User.where("email = '#{email}'") # SQL injection

**Prevent N+1 queries:**
```ruby
User.where("email = '#{email}'") # SQL injection

**避免N+1查询问题:**
```ruby

Good

Good

User.includes(:posts).each { |u| u.posts.count }
User.includes(:posts).each { |u| u.posts.count }

Bad (N+1)

Bad (N+1)

User.all.each { |u| u.posts.count }
undefined
User.all.each { |u| u.posts.count }
undefined

Testing with RSpec

使用RSpec进行测试

ruby
RSpec.describe UserRegistrationService do
  describe "#call" do
    it "creates active user with welcome email" do
      mailer = instance_double(UserMailer)
      allow(mailer).to receive(:welcome).and_return(double(deliver_later: true))

      service = described_class.new(
        user_params: { email: "test@example.com", name: "Test" },
        mailer: mailer
      )

      user = service.call

      expect(user).to be_persisted
      expect(user.email).to eq("test@example.com")
      expect(mailer).to have_received(:welcome).with(user)
    end
  end
end
Use FactoryBot, not fixtures:
ruby
FactoryBot.define do
  factory :user do
    email { Faker::Internet.email }
    name { Faker::Name.name }
  end
end

let(:user) { create(:user) }
ruby
RSpec.describe UserRegistrationService do
  describe "#call" do
    it "creates active user with welcome email" do
      mailer = instance_double(UserMailer)
      allow(mailer).to receive(:welcome).and_return(double(deliver_later: true))

      service = described_class.new(
        user_params: { email: "test@example.com", name: "Test" },
        mailer: mailer
      )

      user = service.call

      expect(user).to be_persisted
      expect(user.email).to eq("test@example.com")
      expect(mailer).to have_received(:welcome).with(user)
    end
  end
end
使用FactoryBot,而非fixtures:
ruby
FactoryBot.define do
  factory :user do
    email { Faker::Internet.email }
    name { Faker::Name.name }
  end
end

let(:user) { create(:user) }

API Design

API 设计

ruby
undefined
ruby
undefined

Consistent serialization

Consistent serialization

class UserSerializer include JSONAPI::Serializer attributes :id, :email, :name, :created_at end
class UserSerializer include JSONAPI::Serializer attributes :id, :email, :name, :created_at end

Versioned routes

Versioned routes

namespace :api do namespace :v1 do resources :users end end
undefined
namespace :api do namespace :v1 do resources :users end end
undefined

Language Patterns

语言模式

ruby
undefined
ruby
undefined

Keyword arguments for 2+ params

Keyword arguments for 2+ params

def send_email(to:, subject:, body:) ... end
def send_email(to:, subject:, body:) ... end

Safe navigation

Safe navigation

user&.profile&.avatar_url
user&.profile&.avatar_url

Frozen strings (file header)

Frozen strings (file header)

frozen_string_literal: true

frozen_string_literal: true

undefined
undefined

Anti-Patterns

反模式

  • Fat models (business logic in ActiveRecord)
  • Business logic in controllers
  • Fixtures instead of factories
  • Testing implementation (mocking internals)
  • String interpolation in SQL
  • N+1 queries without includes
  • Synchronous email in request cycle
  • 臃肿模型(将业务逻辑放在ActiveRecord中)
  • 在控制器中编写业务逻辑
  • 使用fixtures而非工厂模式
  • 测试实现细节(模拟内部逻辑)
  • 在SQL中使用字符串插值
  • 未使用includes导致N+1查询
  • 在请求周期中同步发送邮件

References

参考资料

  • rails-performance.md - Caching, background jobs, profiling
  • rails-performance.md - 缓存、后台任务、性能分析