首页
/ Ruby on Rails 2.3 发布说明精读:Rack 集成、嵌套事务与统一渲染的演进

Ruby on Rails 2.3 发布说明精读:Rack 集成、嵌套事务与统一渲染的演进

2026-09-05 18:29:49作者:瞿蔚英Wynne

本文基于 Rails 官方发布说明文档 guides/source/2_3_release_notes.md,完整梳理 Rails 2.3 这一里程碑版本的核心变化:Rack 全面集成与中间件栈、Rails 引擎复兴、Active Record 的嵌套属性与嵌套事务、动态/默认作用域、find_each 批处理、统一 render、HTTP Digest 认证、Rails Metal、应用模板与安静回溯(quiet backtraces)等。结合当前 Rails 仓库源码中的对应实现(如 batches.rbnested_attributes.rb),读者既能掌握 2.3 各特性的用法与迁移要点,也能理解这些 API 在当前代码库中的演进脉络。

一、应用架构:Rack 全面集成

Rails 2.3 的架构层面有两个大变化:与模块化 Web 服务器接口 Rack 的完全集成,以及对 Rails 引擎(可嵌入其他应用的 Rails 应用)的支持重新加强。官方提醒:由于内部改动巨大,升级到 2.3 后应在本地与生产环境分别测试会话、Cookie、文件上传和 JSON/XML API。

Rack 集成要点

2.3 的 Rack 相关变化(继承自原文档的完整清单):

  • script/server 改为基于 Rack,因此支持任意 Rack 兼容服务器;它会自动查找 rackup 配置文件(默认 config.ru,可用 -c 开关覆盖)。
  • FCGI 处理器改为走 Rack。
  • ActionController::Dispatcher 维护自己的默认中间件栈,中间件可注入、重排和移除,栈在启动时编译成链;可在 environment.rb 中配置。
  • 新增 rake middleware 任务用于查看中间件栈,方便调试中间件顺序。
  • 集成测试运行器改为执行整个中间件与应用栈,使集成测试成为测试 Rack 中间件的最佳场景。
  • ActionController::CGIHandler 是对 Rack 的向后兼容 CGI 封装,负责把旧 CGI 对象的环境信息转换为 Rack 形式。
  • CgiRequestCgiResponse 被移除。
  • 会话存储改为懒加载:请求中若从不访问 session 对象,就不会解析 cookie、读 memcache 或查 ActiveRecord 记录。
  • 测试中设置 cookie 不再需要 CGI::Cookie.new,直接给 request.cookies["foo"] 赋值字符串即可。
  • 会话存储类迁移:CGI::Session::CookieStoreActionController::Session::CookieStoreCGI::Session::MemCacheStoreActionController::Session::MemCacheStoreCGI::Session::ActiveRecordStoreActiveRecord::SessionStore;仍然可以用 ActionController::Base.session_store = :active_record_store 切换。
  • 会话默认选项仍通过 ActionController::Base.session = { :key => "..." } 设置,但 :session_domain 选项改名为 :domain
  • 原本包裹整个请求的互斥锁移入中间件 ActionController::Lock
  • ActionController::AbstractRequestActionController::Request 合并,新的 ActionController::Request 继承自 Rack::Request;这影响测试请求中 response.headers['type'] 的访问,应改用 response.content_type
  • 若加载了 ActiveRecord,ActiveRecord::QueryCache 中间件会自动插入中间件栈,负责建立并清空按请求隔离的查询缓存。
  • 路由器与控制器类遵循 Rack 规范,可以直接用 SomeController.call(env) 调用控制器;路由参数存放在 rack.routing_args 中。
  • 配置会话键的写法从 config.action_controller.session = { :session_key => 'foo', ... } 改为 { :key => 'foo', ... }
  • ParamsParser 中间件会预处理 XML、JSON 或 YAML 请求体,使后续任何 Rack::Request 都能正常读取。

引擎(Engines)支持的复兴

Rails 2.3 为引擎提供了几项新能力:引擎中的路由文件现在会和 routes.rb 一样被自动加载与重新加载(其他插件的路由文件同理);若插件带有 app 文件夹,app/[models|controllers|helpers] 会自动加入 Rails 加载路径;引擎还支持添加视图路径,Action Mailer 与 Action View 都会使用来自引擎及其他插件的视图。

二、Ruby 1.9.1 支持

Rails 2.3 在 Ruby 1.8 与当时刚发布的 Ruby 1.9.1 下都应通过全部自有测试。但迁移到 1.9.1 需要自行核查所有数据适配器、插件和依赖代码对 1.9.1 的兼容性,而不只是 Rails 核心本身。

三、Active Record:嵌套属性、嵌套事务与批处理

嵌套属性(Nested Attributes)

Active Record 可以在你显式声明后,直接更新嵌套模型的属性:

class Book < ActiveRecord::Base
  has_one :author
  has_many :pages

  accepts_nested_attributes_for :author, :pages
end

开启嵌套属性带来几项能力:记录与其关联子记录的自动(且原子的)一起保存、感知子记录的校验,以及嵌套表单(见 Action View 部分)。还可以用 :reject_if 选项为新记录的加入设置过滤条件:

accepts_nested_attributes_for :author,
  :reject_if => proc { |attributes| attributes['name'].blank? }

从当前仓库源码看,nested_attributes.rb 中的 accepts_nested_attributes_for 至今仍是这套签名:默认 allow_destroy: false, update_only: false,合法选项为 allow_destroyreject_iflimitupdate_only,且 reject_if: :all_blank 会被映射到内置的“全空即拒绝”过程对象——2.3 引入的选项空间在十余年后几乎原样保留。

嵌套事务(Nested Transactions)

2.3 实现了长期呼声很高的嵌套事务,你可以写出这样的代码:

User.transaction do
  User.create(:username => 'Admin')
  User.transaction(:requires_new => true) do
    User.create(:username => 'Regular')
    raise ActiveRecord::Rollback
  end
end

User.find(:all)  # => 只返回 Admin

嵌套事务允许回滚内层事务而不影响外层事务状态。必须显式加上 :requires_new 选项才会产生嵌套,否则内层事务只是并入父事务(与 Rails 2.2 行为一致)。底层机制是 savepoint,因此即使数据库不支持真正的嵌套事务也能工作;此外,还有一套机制保证嵌套事务在测试的事务性 fixture 下正常工作。

这一点在当前源码中依然成立:transaction.rb 中的 SavepointTransaction 通过 connection.create_savepoint(savepoint_name) / rollback_to_savepoint / release_savepoint 管理保存点,与 2.3 发布说明描述的“用 savepoint 模拟嵌套事务”的机制一脉相承。

动态作用域(Dynamic Scopes)

在动态查找器(find_by_color_and_flavor 这类即兴方法)与命名作用域(把可复用查询条件封装成 currently_active 这样的好名字)之外,2.3 新增了动态作用域方法:允许边链式调用边即时过滤。

Order.scoped_by_customer_id(12)
Order.scoped_by_customer_id(12).find(:all,
  :conditions => "status = 'open'")
Order.scoped_by_customer_id(12).scoped_by_status("open")

使用动态作用域无需任何定义,直接可用。(从源码结构看,这种基于 method_missingscoped_by_* 约定式写法在后来的版本中被 where 链式 API 取代,但 2.3 的历史定位是“让即时过滤具备方法链能力”。)

默认作用域(Default Scopes)

2.3 引入与命名作用域相似、但作用于模型内所有查询的“默认作用域”概念。例如 default_scope :order => 'name ASC' 会让该模型取出的记录始终按 name 排序(除非显式覆盖)。

批处理(Batch Processing)

2.3 提供 find_in_batches 以减小大表遍历时的内存压力:

Customer.find_in_batches(:conditions => {:active => true}) do |customer_group|
  customer_group.each { |customer| customer.update_account_balance! }
end

可以把大部分 find 选项传给 find_in_batches,但有两个约束:不能指定返回顺序(始终按主键升序,且主键必须是整数),也不能使用 :limit 选项;应使用 :batch_size 选项(默认 1000)控制每批记录数。

find_eachfind_in_batches 的包装,逐条返回记录,查询本身仍按批(默认 1000 条)进行:

Customer.find_each do |customer|
  customer.update_account_balance!
end

官方建议仅在批处理场景使用此方法:少于 1000 条的小数据量直接用常规 find 加自己的循环即可。

当前仓库中这两个方法依然活跃,位于 batches.rbbatches.rb:签名演变为关键字参数 start:finish:batch_size: 1000cursor:(可为单列或列名数组)、order:find_each 内部实现正是逐批调用 find_in_batches 再对每批 records.each(&block)——与 2.3 发布说明描述的“find_each 是 find_in_batches 的逐条包装”完全一致。新增的 start/finish 参数正是当年多 worker 并行处理队列场景的自然延续;源码注释同样保留了“批处理天然存在竞态、游标列须可排序”的提醒。

回调支持多条件

Active Record 回调现在允许在同一回调上同时组合 :if:unless,并以数组形式提供多个条件:

before_save :update_credit_rating, :if => :active,
  :unless => [:admin, :cash_only]

find 支持 having

find 新增 :having 选项(has_manyhas_and_belongs_to_many 关联同样支持),用于对分组结果过滤:

developers = Developer.find(:all, :group => "salary",
  :having => "sum(salary) > 10000", :select => "salary")

MySQL 连接自动重连

MySQL 客户端支持重连标志:设为 true 时,连接丢失后客户端会先尝试重连再放弃。2.3 起可以在 database.yml 中为 MySQL 连接设置 reconnect = true 获得该行为;默认 false,不改变现有应用行为。

其他 Active Record 变化

  • 移除了 has_and_belongs_to_many 预加载生成 SQL 中多余的 AS,对部分数据库更友好。
  • ActiveRecord::Base#new_record? 对已存在的记录返回 false 而非 nil
  • 修复了某些 has_many :through 关联中表名引用的 bug。
  • 可以指定 updated_at 的时间戳:cust = Customer.create(:name => "ABC Industries", :updated_at => 1.day.ago)
  • find_by_attribute! 失败时给出更好的错误信息。
  • to_xml 增加 :camelize 选项。
  • 修复了从 before_updatebefore_create 中取消回调的 bug。
  • 新增通过 JDBC 测试数据库的 rake 任务。
  • validates_length_of 在提供 :in:within 时会使用自定义错误消息。
  • 带作用域的 select 上的 count 现在正常工作,例如 Account.scoped(:select => "DISTINCT credit_limit").count
  • ActiveRecord::Base#invalid? 现在表现为 valid? 的反义。

四、Action Controller:统一渲染与路由效率

统一渲染(Unified Rendering)

ActionController::Base#render 变得更智能:你只需告诉它“渲染什么”,它会推断出“怎么渲染”。旧版通常需要显式给出选项:

render :file => '/tmp/random_file.erb'
render :template => 'other_controller/action'
render :action => 'show'

2.3 可以直接给出目标:

render '/tmp/random_file.erb'
render 'other_controller/action'
render 'show'
render :show

Rails 依据渲染目标的特征在 file、template、action 之间选择:以斜杠开头视为文件路径,内嵌斜杠视为模板路径,完全没有斜杠则视为 action;渲染 action 时符号与字符串等价。其他渲染风格(:inline:text:update:nothing:json:xml:js)仍需显式选项。

Application Controller 更名

一直让 application.rb 特殊命名困扰的人可以欣慰了:2.3 将其重构为 application_controller.rb。同时新增 rake 任务 rake rails:update:application_controller 自动完成迁移,该任务也会作为常规 rake rails:update 流程的一部分运行。

HTTP Digest 认证支持

2.3 内置了 HTTP Digest 认证。调用 authenticate_or_request_with_http_digest 并传入一个返回用户密码的块(密码会被哈希后与传来的凭据比较):

class PostsController < ApplicationController
  Users = {"dhh" => "secret"}
  before_filter :authenticate

  def secret
    render :text => "Password Required!"
  end

  private
  def authenticate
    realm = "Application"
    authenticate_or_request_with_http_digest(realm) do |name|
      Users[name]
    end
  end
end

这个方法在当前仓库中仍然存在,见 http_authentication.rb:实现依旧是“先尝试认证,失败则发起 Digest 认证挑战”的两段式结构,2.3 引入时的形态延续至今。

更高效的路由

2.3 的路由有两大变化:

  • formatted_ 路由助手被移除,改为直接传 :format 选项。这让任一资源的 route 生成过程减少 50%,还能显著节省内存(大型应用最多可达 100MB)。旧的 formatted_ 助手暂时可用,但已被弃用。
  • Rails 现在支持多个路由文件,不再只有 routes.rb。可以用 RouteSet#add_configuration_file 随时引入更多路由,且不清除已加载的路由。这一变化对引擎最有用,但任何需要分批加载路由的应用都能受益。

基于 Rack 的懒加载会话

2.3 把 Action Controller 的会话存储底层推到了 Rack 层面。这一改动对 Rails 应用完全透明(顺带清除了围绕旧 CGI 会话处理器的一些补丁),但意义在于:非 Rails 的 Rack 应用也能访问与 Rails 应用相同的会话存储处理器(从而共享同一会话)。会话同时改为懒加载,与整个框架的加载改进保持一致——不再需要显式禁用会话,不引用即不加载。

MIME 类型处理变化

  • MIME::Type 现在实现了 =~ 运算符,检查带同义类型的存在性更简洁:
if content_type && Mime::JS =~ content_type
  # do something cool
end

Mime::JS =~ "text/javascript"        => true
Mime::JS =~ "application/javascript" => true
  • 框架内部在多处检查 JavaScript 时改用 Mime::JS,统一处理这些别名。

respond_to 优化

Rails 与 Merb 团队合并后的首批成果之一:respond_to 方法经过消除一次 method_missing 调用与若干调优后,在一个在三种格式间切换的简单 respond_to 场景下,每秒可处理的请求数提升约 8%。应用代码无需任何改动即可享受这一加速。

缓存性能改进

Rails 现在维护一份按请求隔离的本地缓存,缓存从远程缓存存储读取过的内容,减少不必要的读操作。这项工作最初限于 MemCacheStore,但任何实现了所需方法的远程存储都可用。

本地化视图

Rails 可以根据已设置的 locale 提供本地化视图:Posts 控制器的 show 动作默认渲染 app/views/posts/show.html.erb;若设置了 I18n.locale = :da,则渲染 app/views/posts/show.da.html.erb;本地化模板不存在时回退到无修饰版本。Rails 还提供 I18n#available_localesI18n::SimpleBackend#available_locales,返回当前项目中可用的翻译数组。

同样的机制也适用于 public 目录下的救援页:public/500.da.htmlpublic/404.en.html 均可生效。

翻译的局部作用域

翻译 API 的改动让在 partial 内书写键翻译更简洁:从 people/index.html.erb 模板调用 translate(".foo"),实际等价于 I18n.translate("people.index.foo");键前不加句号则保持原有行为,不做作用域处理。

其他 Action Controller 变化

  • ETag 处理更干净:响应无 body 或使用 send_file 发送文件时不再发送 ETag 头。
  • 移动端流量大的站点常因代理设置不当被 IP 伪造检测误伤,可以设置 ActionController::Base.ip_spoofing_check = false 完全禁用该检查。
  • ActionController::Dispatcher 实现自己的中间件栈,可通过 rake middleware 查看。
  • Cookie 会话拥有持久会话标识符,并与此前服务端存储的 API 保持兼容。
  • send_filesend_data:type 选项现在可接受符号,如 send_file("fabulous.png", :type => :png)
  • map.resources:only:except 选项不再被嵌套资源继承。
  • 捆绑的 memcached 客户端更新至 1.6.4.99。
  • expires_instale?fresh_when 新增 :public 选项,便于代理缓存。
  • :requirements 选项在额外的 RESTful member 路由上正常工作。
  • 浅层路由(shallow routes)正确尊重命名空间。
  • polymorphic_url 更好地处理不规则复数名的对象。

五、Action View:嵌套表单与更聪明的 render

嵌套对象表单

只要父模型为子对象开启了嵌套属性(见 Active Record 部分),就可以用 form_forfields_for 创建嵌套表单。表单可以任意深度嵌套,从而在单个视图中编辑复杂的对象层级而无需大量代码。给定模型:

class Customer < ActiveRecord::Base
  has_many :orders

  accepts_nested_attributes_for :orders, :allow_destroy => true
end

即可写出这样的视图:

<% form_for @customer do |customer_form| %>
  <div>
    <%= customer_form.label :name, 'Customer Name:' %>
    <%= customer_form.text_field :name %>
  </div>

  <!-- 在 customer_form 构建器实例上调用 fields_for,
   块会为 orders 集合的每个成员执行一次。 -->
  <% customer_form.fields_for :orders do |order_form| %>
    <p>
      <div>
        <%= order_form.label :number, 'Order Number:' %>
        <%= order_form.text_field :number %>
      </div>

      <!-- 模型中的 allow_destroy 选项启用子记录的删除。 -->
      <% unless order_form.object.new_record? %>
        <div>
          <%= order_form.label :_delete, 'Remove:' %>
          <%= order_form.checkbox :_delete %>
        </div>
      <% end %>
    </p>
  <% end %>

  <%= customer_form.submit %>
<% end %>

分片(Partial)的智能渲染

render 变得更进一步聪明:当你有一个对象或集合,且命名与某个 partial 匹配时,直接渲染对象即可。在“命名合理”的前提下:

# 等价于 render :partial => 'articles/_article',
# :object => @article
render @article

# 等价于 render :partial => 'articles/_article',
# :collection => @articles
render @articles

日期选择辅助方法的提示(Prompts)

2.3 允许为各日期选择辅助方法(date_selecttime_selectdatetime_select)提供自定义提示,用法与集合选择辅助方法一致:可以传提示字符串,或为各组件分别提供提示的 hash;也可以直接把 :prompt 设为 true 使用通用提示。

select_datetime(DateTime.now, :prompt => true)

select_datetime(DateTime.now, :prompt => "Choose date and time")

select_datetime(DateTime.now, :prompt =>
  {:day => 'Choose day', :month => 'Choose month',
   :year => 'Choose year', :hour => 'Choose hour',
   :minute => 'Choose minute'})

资产时间戳缓存

Rails 向来在静态资产路径上加时间戳作为“缓存破坏器”,避免修改服务器上的图片/样式表后浏览器继续命中旧缓存。2.3 新增 Action View 配置项 cache_asset_timestamps 来改变这一行为:启用后,Rails 只在首次提供某资产时计算一次时间戳并保存该值——减少昂贵的文件系统调用,代价是服务器运行期间修改资产不会被客户端感知。

资产主机(Asset Host)可以是对象

资产主机支持声明为一个响应 call 的具体对象,使你可以为资产托管实现任意复杂逻辑。

grouped_options_for_select 辅助方法

Action View 已有的 select 辅助方法家族再添一员:grouped_options_for_select。它接受字符串数组或 hash,转换为用 optgroup 标签包裹的 option 标签串。例如:

grouped_options_for_select([["Hats", ["Baseball Cap","Cowboy Hat"]]],
  "Cowboy Hat", "Choose a product...")

返回:

<option value="">Choose a product...</option>
<optgroup label="Hats">
  <option value="Baseball Cap">Baseball Cap</option>
  <option selected="selected" value="Cowboy Hat">Cowboy Hat</option>
</optgroup>

表单 select 辅助方法的禁用选项

表单 select 辅助方法(如 selectoptions_for_select)现在支持 :disabled 选项,可取单个值或值的数组:

select(:post, :category, Post::CATEGORIES, :disabled => 'private')

返回:

<select name="post[category]">
<option>story</option>
<option>joke</option>
<option>poem</option>
<option disabled="disabled">private</option>
</select>

还可以用匿名函数在运行时决定集合中哪些选项被选中和/或禁用:

options_from_collection_for_select(@product.sizes, :name, :id, :disabled => lambda{|size| size.out_of_stock?})

模板加载缓存说明

2.3 允许按环境开启或关闭模板缓存。缓存模板带来速度提升(渲染时不再检查模板文件是否更新),但意味着不重启服务器就无法“即时”替换模板。多数情况下你希望生产环境开启,在 production.rb 中设置:

config.action_view.cache_template_loading = true

新建的 2.3 应用会默认生成这行配置;从旧版本升级的应用则默认在生产与测试环境缓存模板、开发环境不缓存。

其他 Action View 变化

  • CSRF 令牌生成简化:改用 ActiveSupport::SecureRandom 生成的随机字符串,不再摆弄 session ID。
  • auto_link 现在正确地把 :target:class 等选项应用到生成的邮件链接上。
  • autolink 辅助方法重构,更整洁直观。
  • current_page? 在 URL 含多个查询参数时也能正常工作。

六、Active Support:Object#try 与可替换的 XML 解析器

Object#try

很多人已经习惯用 try() 来尝试对对象执行操作,在视图中尤为有用——可以避开 nil 检查:<%= @person.try(:name) %>。2.3 把它做进了 Rails 核心:对私有方法会抛出 NoMethodError,对象为 nil 时恒返回 nil

当前仓库中该实现位于 try.rb:无参带块时执行块,respond_to? 命中时用 public_send 调用(私有方法自然不可达,行为与 2.3 的描述一致),nil 上的 try 亦直接返回 niltry.rb)。

Object#tap 回填

Object#tap 是 Ruby 1.9(及 1.8.7)新增的方法,与 Rails 一直提供的 returning 类似:把对象交给块执行,然后返回被 yield 的对象。2.3 起 Rails 为旧版本 Ruby 补齐了这一能力。

XmlMini 可替换解析器

Active Support 的 XML 解析支持改为可插拔:默认使用标准 REXML 实现,但若安装了相应 gem,可以切换到更快的 LibXML 或 Nokogiri 实现:

XmlMini.backend = 'LibXML'

这一设计在当前源码中完整保留,见 xml_mini.rbbackend= 把名称转成对应模块赋值给 @backend,还支持线程级别的 with_backend 临时切换——2.3 引入的“可替换解析器”思路在后续版本里被进一步细化为线程局部后端。

TimeWithZone 的分数秒

TimeTimeWithZone 都提供 xmlschema 方法返回 XML 友好的时间字符串。2.3 起 TimeWithZone 支持与 Time 相同的参数,用于指定返回字符串中分数秒部分的位数:

Time.zone.now.xmlschema(6) # => "2009-01-16T13:00:06.13653Z"

JSON 键引用

按 JSON 规范,结构中所有键都必须是带双引号的字符串。自 2.3 起,Rails 对数字键也做了正确引用处理。

其他 Active Support 变化

  • Enumerable#none? 用于检查没有元素匹配给定块。
  • Active Support 委托的 :allow_nil 新选项:目标对象为 nil 时返回 nil 而不是抛异常。
  • ActiveSupport::OrderedHash 新增 each_keyeach_value
  • ActiveSupport::MessageEncryptor 提供一种简单方式,为存放到不可信位置(如 cookie)的信息加密。
  • from_xml 不再依赖 XmlSimple,Rails 自带仅含所需功能的 XmlMini 实现,摆脱了长期捆绑的 XmlSimple。
  • 被 memoize 的私有方法,其结果现在也是私有的。
  • String#parameterize 接受可选分隔符:"Quick Brown Fox".parameterize('_') => "quick_brown_fox"
  • number_to_phone 现在接受 7 位电话号码。
  • ActiveSupport::Json.decode 现在能处理 \u0000 样式的转义序列。

七、Railties:Rails Metal、应用模板与安静回溯

Rails Metal

Rails Metal 是一种在 Rails 应用内提供超快端点的新机制。Metal 类绕过路由与 Action Controller,以放弃 Action Controller 全部功能为代价换取原始速度。它建立在“让 Rails 成为具有暴露中间件栈的 Rack 应用”这一系列基础工作之上。Metal 端点可以从应用或插件中加载。

应用模板(Application Templates)

2.3 吸收了 Jeremy McAnally 的 rg 应用生成器:模板化应用生成直接内置进 Rails。如果你有每个应用都要包含的一组插件(以及众多其他用途),只需配置一次模板,之后运行 rails 命令即可反复使用。另有 rake 任务把模板应用到已有应用:

$ rake rails:template LOCATION=~/template.rb

模板的变更会叠加在项目现有代码之上。

安静的回溯(Quiet Backtraces)

基于 thoughtbot 的 Quiet Backtrace 插件(可选择性移除 Test::Unit 回溯中的行),2.3 在核心实现了 ActiveSupport::BacktraceCleanerRails::BacktraceCleaner。它同时支持过滤器(filter,对回溯行做正则替换)与静默器(silencer,整行删除)。新应用中 Rails 会自动加入一组静默器以消除最常见噪音,并生成 config/backtrace_silencers.rb 文件供你追加自定义项;该特性还让任意 gem 在回溯中都能更漂亮地打印。

开发模式下更快的启动:懒加载/自动加载

大量工作确保了 Rails(及其依赖)的部件只在真正需要时才进入内存。核心框架——Active Support、Active Record、Action Controller、Action Mailer 与 Action View——现在使用 autoload 惰性加载各自的类,有助于压低内存占用并提升整体性能。

新增 preload_frameworks 选项可控制核心库是否在启动时全部自动加载,默认 false(Rails 按片段自加载);但有些场景仍需一次性载入全部——Passenger 与 JRuby 都要求看到整体加载的 Rails。

rake gem 任务重写

rake gem 任务内部被大幅重构:系统现在能区分开发依赖与运行期依赖,解包系统更健壮,查询 gem 状态时信息更清晰,冷启动时更少遇到“鸡生蛋”依赖问题;还修复了 JRuby 下 gem 命令的问题,以及依赖试图引入已 vendored 的外部 gem 副本的问题。

其他 Railties 变化

  • 更新并扩充了 CI 服务器构建 Rails 的说明。
  • Rails 内部测试从 Test::Unit::TestCase 切换到 ActiveSupport::TestCase,Rails 核心测试需要 Mocha。
  • 默认的 environment.rb 被清理精简。
  • dbconsole 脚本现在支持全数字密码而不崩溃。
  • Rails.root 现在返回 Pathname 对象,可直接配合 join 方法使用,简化原先用 File.join 的代码。
  • 处理 CGI/FCGI 分发的若干 public 文件默认不再为每个应用生成;需要时可加 --with-dispatchers 运行 rails 命令,或事后用 rake rails:update:generate_dispatchers 补上。
  • Rails Guides 从 AsciiDoc 转换为 Textile 标记(本仓库的 guides/ 目录即是这一文档体系的延续)。
  • Scaffold 生成的视图与控制器略有清理。
  • script/server 新增 --path 参数,可从特定路径挂载 Rails 应用。
  • 若配置中的 gem 有缺失,gem rake 任务会跳过加载大部分环境,缓解“缺少 gem 导致 rake gems:install 无法运行”的鸡生蛋问题。
  • gem 只会被解包一次,修复了以只读权限打包文件(如 hoe)导致的 gem 问题。

八、弃用清单

本版本弃用(或移除)了若干旧代码,迁移时需要留意:

  • 依赖 inspector、reaper、spawner 脚本部署的开发者需注意:这些脚本不再包含在核心 Rails 中,需要时可从 irs_process_scripts 插件获取副本。
  • render_component 从“弃用”变成“不存在”。仍需要它的话,安装 render_component 插件。
  • Rails 组件(components)的支持被移除。
  • script/performance/request 从核心移除;可安装 request_profiler 插件获得完全相同的功能。
  • ActionController::Base#session_enabled? 弃用,因为会话已改为懒加载。
  • protect_from_forgery:digest:secret 选项弃用且无效。
  • 部分集成测试助手被移除:response.headers["Status"]headers["Status"] 不再返回任何值(Rack 不允许返回头中出现 "Status"),但 statusstatus_message 助手仍可用;response.headers["cookie"]headers["cookie"] 不再返回 CGI cookie,可检查 headers["Set-Cookie"] 查看原始 cookie 头,或用 cookies 助手获取发给客户端的 cookie hash。
  • formatted_polymorphic_url 弃用,改用带 :formatpolymorphic_url
  • ActionController::Response#set_cookie:http_only 选项更名为 :httponly
  • to_sentence:connector:skip_last_comma 选项被 :words_connector:two_words_connector:last_word_connector 取代。
  • 提交含空 file_field 控件的 multipart 表单,旧行为是向控制器提交空字符串;由于 Rack multipart 解析器与旧 Rails 解析器的差异,现在提交 nil。

九、结语:2.3 的遗产在当前仓库中的映射

Rails 2.3 发布说明所确立的多项机制,在当前的 Rails 仓库中仍能找到清晰延续,这也印证了该版本的技术含金量:

  • 批处理 API:find_each/find_in_batches 保留于 batches.rb,默认批大小仍为 1000,并扩展出 start/finish/cursor/order 等参数。
  • 嵌套属性:accepts_nested_attributes_for 保留于 nested_attributes.rbreject_if/allow_destroy 选项语义与 2.3 一致。
  • 嵌套事务:基于 savepoint 的 SavepointTransaction 仍见诸 transaction.rb
  • HTTP Digest 认证:authenticate_or_request_with_http_digest 保留于 http_authentication.rb
  • 可替换 XML 解析器:XmlMini.backend= 保留并增强于 xml_mini.rb
  • Object#try 保留并扩展了 try!,见 try.rb

需要说明的是:本文档描述的是 Rails 2.3(2009 年发布)的行为与 API,其中动态/默认作用域、formatted_ 助手、CGI 处理器等细节在后继版本中已被替换或弃用。阅读时应以“理解演进脉络”为目的,具体使用请以目标版本的官方文档为准;而本文引用的当前仓库路径,仅用于展示 2.3 特性在现代代码库中的存续形态。

登录后查看全文
热门项目推荐
相关项目推荐