Ruby on Rails 之 Action Text 富文本指南:Trix 编辑器、RichText 模型与 Active Storage 的完整实战解析
【免费下载链接】railsRuby on Rails项目地址: https://gitcode.com/GitHub_Trending/rai/rails
Action Text 是 Ruby on Rails 官方仓库中的富文本解决方案,为 Rails 应用带来"开箱即用"的富文本内容编辑与渲染能力。本文以 actiontext/README.md 为核心骨架,结合本仓库内 Action Text 的模型、引擎、辅助方法、生成器源码与视图实现,讲解其端到端工作方式、核心 API、底层数据流与工程实践。读完本文,你将掌握has_rich_text的完整用法、RichText 的存取与渲染链路、附件/加密等高级配置,并能够在自己的 Rails 应用中正确安装与定制 Action Text。
Action Text 是什么:把富文本编辑变成 Rails 的一等公民
README 的第一句话概括了它的定位:Action Text 为 Rails 带来富文本内容与编辑能力。它把「富文本编辑器」和「服务端内容模型」打通,形成一条完整的链路:
- 前端编辑:内置 Trix 编辑器,负责格式化、超链接、引用、列表、嵌入式图片与图片画廊等一切编辑交互;
- 后端存储:Trix 产生的富文本内容被保存到独立的
RichText模型中,并与应用中任意已有的 Active Record 模型关联; - 文件托管:富文本中嵌入的图片(及其他附件)自动交由 Active Storage 存储,并与上述
RichText模型关联。
也就是说,你不需要自己维护编辑器集成、HTML 清洗、附件上传三套各自为政的方案。README 也提示,官方还维护了一份更完整的 Action Text Overview 指南,本仓库的对应文档源位于该路径,可进一步深入。
端到端架构:从一次点击到一行 SQL 记录
先看一张「内容从编辑器流到数据库」的链路图(文本示意):
用户在 Trix 中编辑/拖入图片 │ ▼ Trix 输出含 <action-text-attachment sgid="..."> 的 HTML │ ▼ has_rich_text :content 写入 Message#content │ ▼ RichText 记录:serialize :body → ActionText::Content(保存 HTML) │ before_validation 自动提取 sgid → has_many_attached :embeds ▼ 嵌入文件 → Active Storage(Blob / Attachment 表)前端:Trix 编辑器生成的 HTML 形态
编辑器输出不是普通文本,而是一段可被 Action Text 理解、包含附件引用的 HTML。例如一条消息的 content 被存储为:
<h1>Funny Times!</h1> <p>看这张照片:</p> <action-text-attachment sgid="BAh7CEkiCG…" caption="一辆赛车"></action-text-attachment>其中action-text-attachment标签是服务端识别附件的统一约定,sgid(Signed Global ID)指向某个可附着的对象(通常是 Active Storage Blob)。
服务端:has_rich_text建立关联
要让某个 Active Record 模型拥有富文本字段,只需一行声明。来自 attribute.rb 文档中的经典示例:
class Message < ActiveRecord::Base has_rich_text :content end message = Message.create!(content: "<h1>Funny times!</h1>") message.content? #=> true message.content #=> #<ActionText::RichText ...> message.content.to_s #=> "<h1>Funny times!</h1>" message.content.to_plain_text #=> "Funny times!"源码中has_rich_text通过class_eval为你生成两个实例方法:content返回「懒加载的关联 RichText 记录(必要时即时构建)」,content?判断是否存在对应记录;底层再声明一个has_one :rich_text_content多态关联,autosave: true、dependent: :destroy,实现「改动后自动保存、父记录删除时级联清理」。
has_rich_text支持的选项,汇总自 attribute.rb:
| 选项 | 默认值 | 作用 |
|---|---|---|
encrypted | false | 为true时富文本字段使用非确定性加密存储,底层换用ActionText::EncryptedRichText |
strict_loading | strict_loading_by_default(默认 false) | 为true时对该关联强制 strict loading |
store_if_blank | true | 为false时,若赋空值则不创建空白的 RichText 记录,而是标记销毁已有记录 |
一个更完整的声明示例:
class Article < ApplicationRecord has_rich_text :body, encrypted: true, strict_loading: true, store_if_blank: true end防 N+1:预加载作用域
has_rich_text同时为每个字段生成两个预加载作用域,另有一个全局作用域,见 attribute.rb:
Message.all.with_rich_text_content # 只预载 body Message.all.with_rich_text_content_and_embeds # 预载 body + 嵌入附件 Message.all.with_all_rich_text # 预载该模型全部富文本字段其中with_rich_text_content实为includes("rich_text_content"),_and_embeds版本进一步includes("rich_text_content": { embeds_attachments: :blob }),能显著降低列表页的查询次数。
RichText 记录:body 的序列化、附件提取与纯文本/多格式输出
ActionText::RichText是这套体系的数据核心,位于 rich_text.rb,继承自抽象基类 record.rb(ActionText::Record < ActiveRecord::Base)。它做了三件关键的事:
1. body 的序列化:HTML 片段以ActionText::Content形式存库
serialize :body, coder: ActionText::Content belongs_to :record, polymorphic: true, touch: truebody列在数据库中保存的是 Trix/编辑器产生的 HTML 字符串;落库时经由ActionText::Content编解码。ActionText::Content(见 content.rb)本质上是对 HTML 片段的包装器,负责规范化、解析、渲染与序列化。构造时它会做一次 canonicalization,例如把编辑器形态的附件标签、附件画廊、Markdown 原始标签统一收敛为action-text-attachment的规范 HTML。
2. 附件自动提取:embeds与 Active Storage 对接
has_many_attached :embeds before_validation do self.embeds = body.attachables.grep(ActiveStorage::Blob).uniq if body.present? end在每次校验前,RichText 都会扫描body中所有 attachable(通过ActionText::Content#attachables,解析 sgid 得到对象),过滤出ActiveStorage::Blob去重后挂到embeds上。这正是 README 所述「嵌入图片等附件自动使用 Active Storage 存储并关联到 RichText 模型」的代码落点。
3. 内容输出:面向展示与消费的多种形态
RichText 对body做了方法委托,并内置三种输出:
| 方法 | 作用 | 示例 |
|---|---|---|
to_s | 安全渲染为带布局的 HTML 字符串 | <h1>Funny times!</h1> |
to_plain_text | 去掉标签、实体被转码为普通文本 | "Funny times!" |
to_markdown(attachment_links: false) | 转换为 Markdown,附件默认输出转义方括号文本 | "# Funny times!" |
to_editor_html | 输出可在编辑器内继续编辑的 HTML(含附件内联预览),旧名to_trix_html已废弃 | 见下文说明 |
注意to_plain_text/to_markdown的返回结果不是 HTML-safe 的,直接渲染到浏览器前需另行消毒。另外在to_markdown(attachment_links: true)时,附件会被渲染成带 URL 的 Markdown 链接,这会依赖渲染上下文(Controller 或 Mailer 动作),URL 生成失败时会抛出异常——设计上是为了避免在无法生成完整 URL 的场合(如队列任务)误用。
to_editor_html内部经由RichText.editor.as_editable(canonical_fragment)把规范内容还原为可编辑形态(当前默认编辑器为 Trix,见下文引擎配置),并将附件预览图渲染为可回显的<img>。
干净安全的渲染是默认项
to_s之所以"安全",是因为渲染路径经过了消毒。渲染辅助定义在 content_helper.rb:
mattr_accessor(:sanitizer, default: Rails::HTML4::Sanitizer.safe_list_sanitizer.new)render_action_text_content先渲染附件局部模板,再对结果做安全列表消毒;action-text-attachment、figure、figcaption标签及附件的属性集合会被加入白名单(见sanitizer_allowed_tags与sanitizer_allowed_attributes)。应用可通过ActionText::ContentHelper.allowed_tags、allowed_attributes、scrubber或引擎的config.action_text.sanitizer_vendor自定义策略。
附件体系:action-text-attachment与 attachable 契约
附件标签与属性白名单
附件在 HTML 中的落点是一个统一标签。默认标签名可在引擎中配置(见下),源码默认值在 attachment.rb:
mattr_accessor :tag_name, default: "action-text-attachment" ATTRIBUTES = %w( sgid content-type url href filename filesize width height previewable presentation caption content ).freezeActionText::Attachment包装「一个标签节点 + 一个可附着对象」,提供to_html、to_plain_text、to_markdown等方法。对象自身可通过实现attachable_plain_text_representation/attachable_markdown_representation方法自定义其在纯文本、Markdown 中的呈现:
class Person < ApplicationRecord include ActionText::Attachable def attachable_plain_text_representation(caption) "[#{name}]" end end附件画廊(Gallery)同样是富文本中常见的能力:ActionText::AttachmentGallery会把连排的action-text-attachment识别为画廊,并按figure.gallery结构渲染。
Active Storage Blob 的默认 attachable 行为
引擎初始化器(见 engine.rb)在:active_storage_blob加载完成后include ActionText::Attachable,因此所有 Blob 天然可嵌入富文本:
ActiveSupport.on_load(:active_storage_blob) do include ActionText::Attachable def previewable_attachable? representable? end def attachable_plain_text_representation(caption = nil) "[#{caption || filename}]" end # ... endAttachable模块(attachable.rb)通常需要对象实现to_attachable_sgid/ 具备sgid能力,服务端据此在解析 HTML 时把标签还原成真实对象(缺失时降级为Attachables::MissingAttachable,见 attachables 目录)。这也是「富文本内容天然携带可解析的附件引用、而非死板的<img src>」的关键设计。
附件的渲染局部模板
附件在页面上的呈现通过局部模板完成,仓库中内置以下视图(安装时会被复制/引用到应用):
- attachment_galleries/_attachment_gallery.html.erb:画廊容器;
- attachables/_content_attachment.html.erb、
_remote_image.html.erb、_missing_attachable.html.erb:不同类型的 attachable; - contents/_content.html.erb 与对应的布局 partial:
to_s渲染时套用的默认布局; - active_storage/blobs/_blob.html.erb:Blob 附件(图片预览 / 文件下载)的呈现模板,安装生成器会把它复制进应用供覆盖定制。
引擎接入方式:Action Text 如何成为 Rails 的一部分
ActionText::Engine(engine.rb)作为 Rails Engine 装配了所有能力:
- 依赖
active_record、active_storage、action_controller等 railtie; - 初始化器把
ActionText::Attributeinclude 进 Active Record、把ActionText::Encryptionprepend 进去(支持encrypted: true),并为ActiveStorage::Blob、Controller/Mailer、System Test Case 注入对应能力; - 通过
isolate_namespace ActionText提供action_text命名空间。
引擎配置项一览(可在config/application.rb或环境配置中设置)
| 配置项 | 默认值 | 说明 |
|---|---|---|
config.action_text.editor | :trix | 当前生效的编辑器名,从editors注册表中取出 |
config.action_text.editors | { trix: {} } | 编辑器注册表(继承式配置),对应 editor/registry.rb |
config.action_text.attachment_tag_name | "action-text-attachment" | 附件在 HTML 中的标签名,会被写入ActionText::Attachment.tag_name |
config.action_text.sanitizer_vendor | nil | 指定消毒器实现厂商(klass.safe_list_sanitizer) |
编辑器是可扩展的:config.action_text.editor = :trix默认指向 Trix,引擎通过Editor::Registry在 RichText 加载时完成解析。渲染内容回编辑器时,RichText.editor.as_editable(fragment)负责把存储态 HTML 转换成对应编辑器的可编辑格式(Trix 编辑器逻辑见 trix_editor.rb 与 trix_conversion.rb)。
加密富文本:EncryptedRichText
只要has_rich_text传encrypted: true,关联就会指向ActionText::EncryptedRichText:
class EncryptedRichText < RichText encrypts :body end见 encrypted_rich_text.rb。它通过ActiveRecord::Encryption的非确定性加密对body列整体加密,其余渲染/附件能力与普通 RichText 完全一致——适合正文需要静态加密的业务(如草稿、隐私内容)。
安装与实践:从零接入一个 Rails 应用
仓库的安装生成器位于 install_generator.rb,并暴露为action_text:install任务(任务定义见 tasks/actiontext.rake)。典型执行方式:
bin/rails action_text:install生成器实际完成的步骤:
- 安装 JS 依赖:通过当前 JS 包管理器安装
@rails/actiontext以及编辑器依赖(默认 Trix),并把import "@rails/actiontext"、import "trix"追加到app/javascript/application.js;若应用使用 importmap,则向config/importmap.rb追加pin "@rails/actiontext", to: "actiontext.esm.js"等条目。 - 复制样式与视图:生成
app/assets/stylesheets/actiontext.css(内含.trix-content等排版/画廊样式),并把 active_storage/blobs/_blob.html.erb 与 contents/_content.html.erb 布局 复制到应用内,便于按项目定制附件呈现。 - 复制迁移:执行
rails_command "railties:install:migrations FROM=active_storage,action_text",把 Active Storage 与 Action Text 的迁移(源位于 actiontext/db/migrate)灌入应用后运行bin/rails db:migrate建表。 - 可选测试夹具模板:若启用了 TestUnit,还会为富文本字段生成
fixtures.yml生成器(见 test_unit 安装生成器)。
如需更换编辑器,安装时指定:
bin/rails action_text:install --editor=trix注意:多态关联会把类名存入数据库,即action_text_rich_texts.record_type列。若日后重命名使用了has_rich_text的模型类,必须同步更新该列中的类名(源码注释在 attribute.rb 中有明确提醒)。
接入后,典型的完整用法是:模型has_rich_text+ 表单提供富文本输入 + 视图展示富文本字段。例如控制器把参数直接写入关联:
# app/controllers/messages_controller.rb def create @message = Message.create!(message_params) end private def message_params params.require(:message).permit(:content) # content 即富文本 HTML(含附件标签) end展示时@message.content已能安全输出渲染好的 HTML(经 sanitizer + 附件 partial),直接作为视图内容使用即可。
前端资源与开发工作流:npm、资源管线与构建同步
README 的 Development 一节交代了 Action Text 前端资源的发布策略,这部分对贡献者尤其重要:
- Action Text 的 JavaScript同时以两种形态分发:作为 npm 模块
@rails/actiontext(配置见 package.json,打包脚本由 rollup.config.js 定义),以及通过资产管线作为actiontext.js提供;Trix 也被镜像为trix.js。 - 为保证两者始终一致,每次改动 JavaScript 源码或升级 Trix 依赖后,都必须运行
yarn build并提交构建产物(即app/assets/javascripts/下的actiontext.js、actiontext.esm.js等)。引擎初始化器也会把这些文件加入assets.precompile列表(见 engine.rb)。 - CSS 改动必须人工同步到
app/assets/stylesheets/trix.css(README 明确指出不会自动生成)。
如果你只是使用 Action Text,正常依赖 npm 版@rails/actiontext+ 应用构建流程即可;只有当你修改 Action Text 的 JS 源码(或升级 Trix)时才需要关心yarn build与提交产物的约定。
系统测试与测试数据
Action Text 为 Rails 系统测试提供了一个辅助模块 system_test_helper.rb,引擎初始化器会自动把它 include 进ActionDispatch::SystemTestCase,方便在端到端测试中驱动 Trix 编辑器交互。本仓库 actiontext/test 下的集成/系统测试(如 integration)是理解真实用法的活文档。
小结:Action Text 的设计要点回顾
围绕 actiontext/README.md 的核心承诺,结合本仓库源码可归纳出四条要点:
- 编辑端到存储端的整链闭环:Trix 编辑 → HTML 含
action-text-attachment(sgid)→ 任意 Active Record 模型的has_rich_text字段 →RichText的body(序列化为ActionText::Content)。 - 附件统一走 Active Storage:
before_validation自动把 body 中的 Blob 提取到has_many_attached :embeds,存储、预览、下载都由 Active Storage 及其 Blob 局部模板负责。 - 渲染安全是内建默认:输出路径统一经 safe-list sanitizer 消毒,
figure/figcaption/附件标签及白名单属性被显式放行。 - 可配置与可扩展:编辑器注册表、附件标签名、消毒器厂商、加密存储等均有明确的引擎配置项与源码落点。
对于需要在 Rails 应用中交付「所见即所得正文 + 图片/文件嵌入」场景的开发者,Action Text 是一条覆盖模型层、视图层与资产层的完整官方路径。更多进阶内容可继续阅读仓库内的 Action Text Overview 指南,以及 actiontext 测试目录 中的用例。
【免费下载链接】railsRuby on Rails项目地址: https://gitcode.com/GitHub_Trending/rai/rails
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考