学习目标:理解 Rails 的整体架构、组件划分和设计理念
1. Rails 是什么?#
Rails 不是一个单一的 gem,而是一个由多个独立但紧密协作的组件组成的 框架集合。每个组件都可以独立使用,但组合在一起时能提供完整的 Web 开发解决方案。
核心理念#
Rails 遵循几个核心设计理念:
约定优于配置(Convention over Configuration)
- 减少开发者需要做的决策
- 提供合理的默认值
不要重复自己(Don’t Repeat Yourself, DRY)
- 信息在系统中只有单一、明确的表示
- 减少重复代码
模块化设计
- 每个组件职责单一
- 组件间低耦合
2. Rails 的目录结构#
让我们先看看 Rails 源代码的顶层结构:
rails/
├── actioncable/ # WebSocket 框架
├── actionmailbox/ # 接收邮件处理
├── actionmailer/ # 发送邮件
├── actionpack/ # 路由和控制器
├── actiontext/ # 富文本编辑器
├── actionview/ # 视图模板
├── activejob/ # 后台任务队列
├── activemodel/ # 模型接口定义
├── activerecord/ # ORM 数据库访问
├── activestorage/ # 文件上传存储
├── activesupport/ # Ruby 核心扩展
├── railties/ # 框架粘合剂
├── guides/ # 官方文档
└── tools/ # 开发工具
每个目录都是一个独立的 gem,都有自己的:
lib/
- 源代码test/
- 测试文件*.gemspec
- gem 规格文件README
- 文档
3. Rails 十大核心组件#
3.1 ActiveSupport - 基础设施#
作用:为 Ruby 核心类提供扩展,是其他所有组件的基础。
核心功能:
- Ruby 核心类扩展(String, Array, Hash 等)
- 回调系统(Callbacks)
- 关注点(Concerns)
- 自动加载(Autoloading)
- 缓存(Caching)
- 时间处理(Time zones)
- I18n 国际化
示例代码位置:
# activesupport/lib/active_support.rb - 主入口
# activesupport/lib/active_support/core_ext/ - 核心扩展
# activesupport/lib/active_support/concern.rb - Concern 模块
为什么重要:ActiveSupport 提供了 Rails 所有其他组件依赖的基础工具。
3.2 ActiveModel - 模型接口#
位置:activemodel/
作用:定义模型对象的标准接口,无需数据库支持。
核心功能:
- 验证(Validations)
- 回调(Callbacks)
- 属性赋值
- 序列化
- 脏检查(Dirty tracking)
- 类型转换
关键类:
ActiveModel::Model
- 让任何类表现得像 ActiveRecordActiveModel::Validations
- 验证框架ActiveModel::Attributes
- 属性系统
代码示例:
# 你可以在不使用数据库的情况下拥有模型功能
class Person
include ActiveModel::Model
attr_accessor :name, :age
validates :name, presence: true
validates :age, numericality: true
end
3.3 ActiveRecord - ORM 数据访问#
作用:实现对象关系映射(ORM),是 Rails 的数据持久化层。
核心功能:
- 数据库抽象层
- 查询接口(Query Interface)
- 关联(Associations)
- 迁移(Migrations)
- 验证
- 回调
关键类:
ActiveRecord::Base
- 所有模型的基类ActiveRecord::Relation
- 查询构建器ActiveRecord::ConnectionAdapters
- 数据库适配器
依赖关系:
ActiveRecord
├── ActiveModel (使用其验证、回调等)
└── ActiveSupport (使用其工具类)
源码入口:activerecord/lib/active_record.rb
3.4 ActionPack - 请求处理#
位置:actionpack/
作用:处理 HTTP 请求,包含路由和控制器。
核心功能:
- 路由系统(Routing)
- 控制器(Controllers)
- HTTP 请求/响应处理
- 会话管理
- Cookie 处理
- 中间件(Middleware)
主要模块:
ActionDispatch
- 路由和请求处理ActionController
- 控制器基类AbstractController
- 控制器抽象层
代码结构:
actionpack/
├── lib/
│ ├── action_dispatch/ # 路由、中间件
│ ├── action_controller/ # 控制器
│ └── abstract_controller/ # 抽象控制器
3.5 ActionView - 视图渲染#
位置:actionview/
作用:处理视图模板的查找、编译和渲染。
核心功能:
- 模板渲染(ERB, Builder 等)
- Helper 辅助方法
- Partial 部分模板
- Layout 布局
- 表单构建器
关键类:
ActionView::Base
- 视图上下文ActionView::Template
- 模板抽象ActionView::Renderer
- 渲染引擎
3.6 ActionMailer - 邮件发送#
作用:发送邮件,API 类似控制器。
核心功能:
- 邮件模板
- 多格式邮件(HTML/文本)
- 附件处理
- 异步发送
示例:
class UserMailer < ApplicationMailer
def welcome_email(user)
@user = user
mail(to: user.email, subject: 'Welcome')
end
end
3.7 ActionMailbox - 邮件接收#
作用:接收和处理入站邮件。
核心功能:
- 邮件路由
- 邮件处理器(Mailbox)
- 支持多种邮件服务(Sendgrid, Mailgun 等)
3.8 ActiveJob - 后台任务#
位置:activejob/
作用:统一的后台任务接口,支持多种队列后端。
核心功能:
- 任务定义
- 任务队列
- 延迟执行
- 重试机制
- 适配器模式(Sidekiq, Resque 等)
代码示例:
class ProcessOrderJob < ApplicationJob
queue_as :default
def perform(order)
# 处理订单
end
end
3.9 ActionCable - WebSocket#
位置:actioncable/
作用:实时双向通信,WebSocket 集成。
核心功能:
- Connection 连接管理
- Channel 频道
- 订阅和广播
- 适配器(Redis, PostgreSQL 等)
3.10 ActiveStorage - 文件存储#
作用:文件上传和云存储集成。
核心功能:
- 文件附件
- 云服务支持(S3, GCS, Azure)
- 图片变体(缩略图等)
- 直传支持
3.11 ActionText - 富文本#
位置:actiontext/
作用:富文本编辑器集成(基于 Trix)。
核心功能:
- 富文本内容存储
- 编辑器集成
- 附件处理
3.12 Railties - 框架核心#
位置:railties/
作用:Rails 框架的粘合剂,负责组装所有组件。
核心功能:
- 应用启动
- 生成器(Generators)
- Rake 任务
- 控制台(Console)
- 初始化系统
关键文件:
- railties/lib/rails.rb - Rails 模块定义
- railties/lib/rails/application.rb - 应用基类
- railties/lib/rails/railtie.rb - 插件系统
4. 组件依赖关系#
Rails 组件之间有清晰的依赖层次:
层级 1(基础层):
ActiveSupport
└─ Ruby 核心扩展、工具类
层级 2(模型层):
ActiveModel ──> ActiveSupport
└─ 模型接口定义
ActiveRecord ──> ActiveModel ──> ActiveSupport
└─ 数据库 ORM
层级 3(请求处理层):
ActionPack ──> ActiveSupport
├─ ActionDispatch(路由)
└─ ActionController(控制器)
ActionView ──> ActionPack ──> ActiveSupport
└─ 视图渲染
层级 4(功能扩展层):
ActionMailer ──> ActionPack ──> ActiveSupport
ActiveJob ──> ActiveSupport
ActionCable ──> ActionPack ──> ActiveSupport
ActiveStorage ──> ActiveRecord ──> ActiveSupport
ActionText ──> ActiveRecord ──> ActiveStorage
层级 5(框架层):
Railties ──> 所有组件
└─ 组装和初始化
关键观察:
- ActiveSupport 是基石:几乎所有组件都依赖它
- ActiveModel 和 ActiveRecord 独立于 Web 层:可以在非 Web 应用中使用
- ActionPack 是 Web 核心:路由和控制器是 Web 应用的基础
- Railties 负责组装:它知道如何将所有组件整合在一起
5. Rails 模块的加载#
让我们看看 Rails 的主入口文件:
# railties/lib/rails.rb
module Rails
extend ActiveSupport::Autoload
extend ActiveSupport::Benchmarkable
class << self
@application = @app_class = nil
attr_writer :application
attr_accessor :app_class, :cache, :logger
def application
@application ||= (app_class.instance if app_class)
end
def root
application && application.config.root
end
def env
@_env ||= ActiveSupport::EnvironmentInquirer.new(
ENV["RAILS_ENV"].presence ||
ENV["RACK_ENV"].presence ||
"development"
)
end
end
end
关键点:
- Rails 是一个模块,不是类
- 使用
ActiveSupport::Autoload
实现延迟加载 Rails.application
是单例,代表你的应用Rails.env
返回当前环境(development/test/production)
6. Gem 的组织方式#
每个 Rails 组件都是一个独立的 gem:
# rails.gemspec(元 gem)
Gem::Specification.new do |s|
s.name = "rails"
s.version = version
# 依赖所有子组件
s.add_dependency "activesupport", version
s.add_dependency "actionpack", version
s.add_dependency "actionview", version
s.add_dependency "activemodel", version
s.add_dependency "activerecord", version
s.add_dependency "actionmailer", version
s.add_dependency "activejob", version
s.add_dependency "actioncable", version
s.add_dependency "activestorage", version
s.add_dependency "actiontext", version
s.add_dependency "actionmailbox", version
s.add_dependency "railties", version
end
这意味着:
- 安装
gem install rails
会安装所有组件 - 你可以单独使用某个组件,如
gem install activerecord
- 每个组件有自己的版本号,但通常保持一致
7. 实战:探索源码#
让我们动手探索一下:
练习 1:查看 ActiveSupport 扩展#
# 进入 Rails 源码目录
cd activesupport/lib/active_support/core_ext
# 查看都扩展了哪些核心类
ls -la
# 你会看到:array/, hash/, string/, integer/ 等
打开一个扩展文件看看:
# activesupport/lib/active_support/core_ext/string/inflections.rb
class String
def pluralize(count = nil, locale = :en)
# 单数变复数
end
def singularize(locale = :en)
# 复数变单数
end
def camelize(first_letter = :upper)
# 转换为驼峰命名
end
end
练习 2:跟踪 ActiveRecord::Base#
# 在 rails console 中
User.ancestors
# => [User, ApplicationRecord, ActiveRecord::Base, ...]
# 查看 User 的祖先链
User.ancestors.take(10).each { |k| puts k }
练习 3:理解路由DSL#
# config/routes.rb
Rails.application.routes.draw do
resources :posts
end
# 这背后发生了什么?
# 1. Rails.application 是你的应用实例
# 2. routes 返回一个 ActionDispatch::Routing::RouteSet
# 3. draw 方法接收一个块
# 4. resources 是一个 DSL 方法,创建 7 个路由
8. 设计模式识别#
Rails 源码中大量使用了经典设计模式:
8.1 模块混入(Mixin)#
module ActiveModel::Validations
extend ActiveSupport::Concern
included do
# 当被 include 时执行
end
def valid?
# 验证逻辑
end
end
8.2 适配器模式#
# ActiveJob 支持多种队列后端
ActiveJob::Base.queue_adapter = :sidekiq # 或 :resque, :delayed_job
8.3 单例模式#
Rails.application # 总是返回同一个实例
8.4 回调模式#
class User < ApplicationRecord
before_save :normalize_email
after_create :send_welcome_email
end
8.5 策略模式#
# ActiveStorage 支持多种存储服务
config.active_storage.service = :amazon # 或 :google, :local
9. 阅读源码的技巧#
技巧 1:从你熟悉的 API 入手#
你知道 User.find(1)
的用法,那就从这里开始:
# 1. 找到 find 方法的定义
# activerecord/lib/active_record/core.rb
def find(*ids)
# ...
end
# 2. 使用 method_source 查看
User.method(:find).source_location
# => ["activerecord/lib/active_record/core.rb", 200]
技巧 2:使用调试器#
# 在源码中插入断点
require 'debug'
binding.break
# 或者使用 pry
require 'pry'
binding.pry
技巧 3:查看测试#
测试是最好的文档:
# activerecord/test/cases/finder_test.rb
test "find by id" do
assert_equal @first, Topic.find(1)
end
技巧 4:绘制类图#
使用工具如 rails-erd
或手动绘制:
ActiveRecord::Base
├── ActiveRecord::Core
├── ActiveRecord::Persistence
├── ActiveRecord::FinderMethods
├── ActiveRecord::Associations
└── ActiveModel::Validations
10. 本章总结#
通过本章学习,你应该了解:
- Rails 是组件集合,不是单一框架
- 十大核心组件及其职责
- 依赖层次:ActiveSupport → ActiveModel → 其他组件
- 模块化设计:每个组件都是独立的 gem
- 设计模式:Mixin、适配器、回调等
- 阅读源码的方法:从熟悉的 API 入手,使用调试工具
11. 下一步#
在下一篇 Rails 启动流程分析 中,我们将深入探讨:
rails new
创建了什么rails server
如何启动应用- Railtie 和 Engine 的作用
- 初始化器的执行顺序
12. 练习题#
在继续下一章之前,尝试回答以下问题:
- ActiveSupport 为什么是所有组件的基础?它提供了哪些关键功能?
- ActiveModel 和 ActiveRecord 的关系是什么?为什么要分离?
- ActionPack 包含哪两个主要模块?它们分别负责什么?
- 为什么 Railties 被称为"粘合剂"?
- Rails 使用了哪些设计模式?各有什么作用?
提示:如果有疑问,可以回到相应章节重新阅读,或者直接查看源码。
下一篇:Rails 启动流程分析
返回:学习指南首页