Ruby on Rails 4.2 发布记
Ruby on Rails 4.2 发布记
Rails 4.2 精华摘要:
- Active Job
- 异步邮件
- Adequate Record
- Web 终端
- 外键支持
本篇仅记录主要的变化。要了解关于已修复的 Bug、特性变更等,请参考 Rails GitHub 主页上各个 Gem 的 CHANGELOG 或是 Rails 的提交历史。
1 升级至 Rails 4.2
如果您正试着升级现有的应用,应用最好要有足够的测试。第一步先升级至 4.1,确保应用仍正常工作,接着再升上 4.2。升级需要注意的事项在 Ruby on Rails 升级指南可以找到。
2 重要新特性
2.1 Active Job
Active Job 是 Rails 4.2 新搭载的框架。是队列系统(Queuing systems)的统一接口,用来连接像是 Resque、Delayed Job、Sidekiq 等队列系统。
采用 Active Job API 撰写的任务程序(Background jobs),便可在任何支持的队列系统上运行而无需对代码进行任何修改。Active Job 缺省会即时执行任务。
任务通常需要传入 Active Record 对象作为参数。Active Job 将传入的对象作为 URI(统一资源标识符),而不是直接对对象进行 marshal。新增的 GlobalID 函式库,给对象生成统一资源标识符,并使用该标识符来查找对象。现在因为内部使用了 Global ID,任务只要传入 Active Record 对象即可。
譬如,trashable
是一个 Active Record 对象,则下面这个任务无需做任何序列化,便可正常完成任务:
class TrashableCleanupJob < ActiveJob::Base def perform(trashable, depth) trashable.cleanup(depth) end end
参考 Active Job 基础指南来进一步了解。
2.2 异步邮件
构造于 Active Job 之上,Action Mailer 新增了 #deliver_later
方法,通过队列来发送邮件,若开启了队列的异步特性,便不会拖慢控制器或模型的运行(缺省队列是即时执行任务)。
想直接发送信件仍可以使用 deliver_now
。
2.3 Adequate Record
Adequate Record 是对 Active Record find
和 find_by
方法以及其它的关联查询方法所进行的一系列重构,查询速度最高提升到了两倍之多。
工作原理是在执行 Active Record 调用时,把 SQL 查询语句缓存起来。有了查询语句的缓存之后,同样的 SQL 查询就无需再次把调用转换成 SQL 语句。更多细节请参考 Aaron Patterson 的博文。
Adequate Record 已经合并到 Rails 里,所以不需要特别启用这个特性。多数的 find
和 find_by
调用和关联查询会自动使用 Adequate Record,比如:
Post.find(1) # First call generates and cache the prepared statement Post.find(2) # Subsequent calls reuse the cached prepared statement Post.find_by_title('first post') Post.find_by_title('second post') post.comments post.comments(true)
有一点特别要说明的是,如上例所示,缓存的语句不会缓存传入的数值,只是缓存语句的模版而已。
下列场景则不会使用缓存:
- 当 model 有缺省作用域时
- 当 model 使用了单表继承时
- 当
find
查询一组 ID 时:
# not cached Post.find(1, 2, 3) Post.find([1,2])
- 以 SQL 片段执行
find_by
:
Post.find_by('published_at < ?', 2.weeks.ago)
2.4 Web 终端
用 Rails 4.2 新产生的应用程序,缺省搭载了 Web 终端。Web 终端给错误页面添加了一个互动式 Ruby 终端,并提供视图帮助方法 console
,以及一些控制器帮助方法。
错误页面的互动式的终端,让你可以在异常发生的地方执行代码。插入 console
视图帮助方法到任何页面,便可以在页面的上下文里,在页面渲染结束后启动一个互动式的终端。
最后,可以执行 rails console
来启动一个 VT100 终端。若需要建立或修改测试资料,可以直接从浏览器里执行。
2.5 外键支持
迁移 DSL 现在支持新增、移除外键,外键也会导出到 schema.rb
。目前只有 mysql
、mysql2
以及 postgresql
的适配器支持外键。
# add a foreign key to `articles.author_id` referencing `authors.id` add_foreign_key :articles, :authors # add a foreign key to `articles.author_id` referencing `users.lng_id` add_foreign_key :articles, :users, column: :author_id, primary_key: "lng_id" # remove the foreign key on `accounts.branch_id` remove_foreign_key :accounts, :branches # remove the foreign key on `accounts.owner_id` remove_foreign_key :accounts, column: :owner_id
完整说明请参考 API 文档:add_foreign_key 和 remove_foreign_key。
3 Rails 4.2 向下不兼容的部份
前版弃用的特性已全数移除。请参考文后下列各 Rails 部件来了解 Rails 4.2 新弃用的特性有那些。
以下是升级至 Rails 4.2 所需要立即采取的行动。
3.1 render
字串参数
4.2 以前在 Controller action 调用 render "foo/bar"
时,效果等同于:render file: "foo/bar"
;Rails 4.2 则改为 render template: "foo/bar"
。如需 render
文件,请将代码改为 render file: "foo/bar"
。
3.2 respond_with
/ class-level respond_to
respond_with
以及对应的类别层级 respond_to
被移到了 responders
gem。要使用这个特性,把 gem 'responders', '~> 2.0'
加入到 Gemfile:
# app/controllers/users_controller.rb class UsersController < ApplicationController respond_to :html, :json def show @user = User.find(params[:id]) respond_with @user end end
而实例层级的 respond_to
则不受影响:
# app/controllers/users_controller.rb class UsersController < ApplicationController def show @user = User.find(params[:id]) respond_to do |format| format.html format.json { render json: @user } end end end
3.3 rails server
的缺省主机(host)变更
由于 Rack 的一项修正,rails server
现在缺省会监听 localhost
而不是 0.0.0.0
。http://127.0.0.1:3000 和 http://localhost:3000 仍可以像先前一般使用。
但这项变更禁止了从其它机器访问 Rails 服务器(譬如开发环境位于虚拟环境里,而想要从宿主机器上访问),则需要用 rails server -b 0.0.0.0
来启动,才能像先前一样使用。
若是使用了 0.0.0.0
,记得要把防火墙设置好,改成只有信任的机器才可以存取你的开发服务器。
3.4 HTML Sanitizer
HTML sanitizer 换成一个新的、更加安全的实现,基于 Loofah 和 Nokogiri。新的 Sanitizer 更安全,而 sanitization 更加强大与灵活。
有了新的 sanitization 算法之后,某些 pathological 输入的输出会和之前不太一样。
若真的需要使用旧的 sanitizer,可以把 rails-deprecated_sanitizer
加到 Gemfile,便会用旧的 sanitizer 取代掉新的。而因为这是自己选择性加入的 gem,所以并不会抛出弃用警告。
Rails 4.2 仍会维护 rails-deprecated_sanitizer
,但 Rails 5.0 之后便不会再进行维护。
参考这篇文章来了解更多关于新的 sanitizer 的变更内容细节。
3.5 assert_select
assert_select
测试方法现在用 Nokogiri 改写了。
不再支援某些先前可用的选择器。若应用程式使用了以下的选择器,则会需要进行更新:
属性选择器的数值需要用双引号包起来。
a[href=/] => a[href="/"] a[href$=/] => a[href$="/"]
含有错误嵌套的 HTML 所建出来的 DOM 可能会不一样
譬如:
# content: <div><i><p></i></div> # before: assert_select('div > i') # => true assert_select('div > p') # => false assert_select('i > p') # => true # now: assert_select('div > i') # => true assert_select('div > p') # => true assert_select('i > p') # => false
之前要比较含有 HTML entities 的元素要写未经转译的 HTML,现在写转译后的即可
# content: <p>AT&T</p> # before: assert_select('p', 'AT&T') # => true assert_select('p', 'AT&T') # => false # now: assert_select('p', 'AT&T') # => true assert_select('p', 'AT&T') # => false
4 Railties
请参考 CHANGELOG 来了解更多细节。
4.1 移除
--skip-action-view
选项从 app generator 移除。 (Pull Request)移除
rails application
命令。 (Pull Request)
4.2 弃用
生产环境新增
config.log_level
设置。 (Pull Request)弃用
rake test:all
,请改用rake test
来执行test
目录下的所有测试。 (Pull Request)弃用
rake test:all:db
,请改用rake test:db
。 (Pull Request)弃用
Rails::Rack::LogTailer
,没有替代方案。 (Commit)
4.3 值得一提的变化
web-console
导入为应用内建的 Gem。 (Pull Request)Model 用来产生关联的 generator 添加
required
选项。 (Pull Request)导入
after_bundle
回调到 Rails 模版。 (Pull Request)导入
x
命名空间,可用来自订设置选项:# config/environments/production.rb config.x.payment_processing.schedule = :daily config.x.payment_processing.retries = 3 config.x.super_debugger = true
这些选项都可以从设置对象里获取:
Rails.configuration.x.payment_processing.schedule # => :daily Rails.configuration.x.payment_processing.retries # => 3 Rails.configuration.x.super_debugger # => true
(Commit)
导入
Rails::Application.config_for
,用来给当前的环境载入设置# config/exception_notification.yml: production: url: http://127.0.0.1:8080 namespace: my_app_production development: url: http://localhost:3001 namespace: my_app_development # config/production.rb Rails.application.configure do config.middleware.use ExceptionNotifier, config_for(:exception_notification) end
产生器新增
--skip-turbolinks
选项,可在新建应用时拿掉 turbolink。 (Commit)导入
bin/setup
脚本来启动(bootstrapping)应用。 (Pull Request)config.assets.digest
在开发模式的缺省值改为true
。 (Pull Request)导入给
rake notes
注册新扩充功能的 API。 (Pull Request)导入
Rails.gem_version
作为返回Gem::Version.new(Rails.version)
的便捷方法。 (Pull Request)
5 Action Pack
请参考 CHANGELOG 来了解更多细节。
5.1 移除
将
respond_with
以及类别层级的respond_to
从 Rails 移除,移到responders
gem(版本 2.0)。要继续使用这个特性,请在 Gemfile 添加:gem 'responders', '~> 2.0'
。(Pull Request)移除弃用的
AbstractController::Helpers::ClassMethods::MissingHelperError
, 改用AbstractController::Helpers::MissingHelperError
取代。 (Commit)
5.2 弃用
弃用
*_path
帮助方法的only_path
选项。 (Commit)弃用
assert_tag
、assert_no_tag
、find_tag
以及find_all_tag
,请改用assert_select
。 (Commit)弃用路由的
:to
选项里,:to
可以指向符号或不含井号的字串这两个功能。get '/posts', to: MyRackApp => (No change necessary) get '/posts', to: 'post#index' => (No change necessary) get '/posts', to: 'posts' => get '/posts', controller: :posts get '/posts', to: :index => get '/posts', action: :index
(Commit)
弃用 URL 帮助方法不再支持使用字串作为键:
# bad root_path('controller' => 'posts', 'action' => 'index') # good root_path(controller: 'posts', action: 'index')
5.3 值得一提的变化
*_filter
方法已经从文件中移除,已经不鼓励使用。偏好使用*_action
方法:after_filter => after_action append_after_filter => append_after_action append_around_filter => append_around_action append_before_filter => append_before_action around_filter => around_action before_filter => before_action prepend_after_filter => prepend_after_action prepend_around_filter => prepend_around_action prepend_before_filter => prepend_before_action skip_after_filter => skip_after_action skip_around_filter => skip_around_action skip_before_filter => skip_before_action skip_filter => skip_action_callback
若应用程式依赖这些
*_filter
方法,应该使用*_action
方法替换。 因为*_filter
方法最终会从 Rails 里拿掉。 (Commit 1, 2)render nothing: true
或算绘nil
不再加入一个空白到响应主体。 (Pull Request)Rails 现在会自动把模版的 digest 加入到 ETag。 (Pull Request)
传入 URL 辅助方法的片段现在会自动 Escaped。 (Commit)
导入
always_permitted_parameters
选项,用来设置全局允许赋值的参数。 缺省值是['controller', 'action']
。 (Pull Request)从 RFC 4791 新增 HTTP 方法
MKCALENDAR
。 (Pull Request)*_fragment.action_controller
通知消息的 Payload 现在会带有控制器和动作名称。 (Pull Request)改善路由错误页面,搜索路由支持模糊搜寻。 (Pull Request)
新增关掉记录 CSRF 失败的选项。 (Pull Request)
当使用 Rails 服务器来提供静态资源时,若客户端支持 gzip,则会自动传送预先产生好的 gzip 静态资源。Asset Pipeline 缺省会给所有可压缩的静态资源产生
.gz
文件。传送 gzip 可将所需传输的数据量降到最小,并加速静态资源请求的存取。当然若要在 Rails 生产环境提供静态资源,最好还是使用 CDN。(Pull Request)在整合测试里调用
process
帮助方法时,路径开始需要有/
。以前可以忽略开头的/
,但这是实作所产生的副产品,而不是有意新增的特性,譬如:test "list all posts" do get "/posts" assert_response :success end
6 Action View
请参考 CHANGELOG 来了解更多细节。
6.1 弃用
弃用
AbstractController::Base.parent_prefixes
。想修改寻找视图的位置, 请覆盖AbstractController::Base.local_prefixes
。 (Pull Request)弃用
ActionView::Digestor#digest(name, format, finder, options = {})
,现在参数改用 Hash 传入。 (Pull Request)
6.2 值得一提的变化
render "foo/bar"
现在等同render template: "foo/bar"
而不是render file: "foo/bar"
。(Pull Request)隐藏栏位的表单辅助方法不再产生含有行内样式表的
<div>
元素。 (Pull Request)导入一个特别的
#{partial_name}_iteration
局部变量,给在 collection 里渲染的部分视图(Partial)使用。这个变量可以通过#index
、#size
、first?
以及last?
等方法来获得目前迭代的状态。(Pull Request)Placeholder I18n 遵循和
label
I18n 一样的惯例。 (Pull Request)
7 Action Mailer
请参考 CHANGELOG 来了解更多细节。
7.1 弃用
Mailer 弃用所有
*_path
的帮助方法。请全面改用*_url
。 (Pull Request)弃用
deliver
与deliver!
,请改用deliver_now
或deliver_now!
。 (Pull Request)
7.2 值得一提的变化
link_to
和url_for
在模版里缺省产生绝对路径,不再需要传入only_path: false
。 (Commit)导入
deliver_later
方法,将邮件加到应用的队列里,用来异步发送邮件。 (Pull Request)新增
show_previews
选项,用来在开发环境之外启用邮件预览特性。 (Pull Request)
8 Active Record
请参考 CHANGELOG 来了解更多细节。
8.1 移除
移除
cache_attributes
以及其它相关的方法,所有的属性现在都会缓存了。 (Pull Request)移除已弃用的方法
ActiveRecord::Base.quoted_locking_column
. (Pull Request)移除已弃用的方法
ActiveRecord::Migrator.proper_table_name
。 请改用ActiveRecord::Migration
的实例方法:proper_table_name
。 (Pull Request)移除了未使用的
:timestamp
类型。把所有timestamp
类型都改为:datetime
的别名。 修正在ActiveRecord
之外,栏位类型不一致的问题,譬如 XML 序列化。 (Pull Request)
8.2 弃用
弃用
after_commit
和after_rollback
会吃掉错误的行为。 (Pull Request)弃用对
has_many :through
自动侦测 counter cache 的支持。要自己对has_many
和belongs_to
关联,给through
的记录手动设置。 (Pull Request)弃用
.find
或.exists?
可传入 Active Record 对象。请先对对象调用#id
。 (Commit 1, 2)弃用仅支持一半的 PostgreSQL 范围数值(不包含起始值)。目前我们把 PostgreSQL 的范围对应到 Ruby 的范围。但由于 Ruby 的范围不支持不包含起始值,所以无法完全转换。
目前的解决方法是将起始数递增,这是不对的,已经弃用了。关于不知如何递增的子类型(比如没有定义
#succ
)会对不包含起始值的抛出ArgumentError
。(Commit)
弃用无连接调用
DatabaseTasks.load_schema
。请改用DatabaseTasks.load_schema_current
来取代。 (Commit)弃用
sanitize_sql_hash_for_conditions
,没有替代方案。使用Relation
来进行查询或更新是推荐的做法。 (Commit)弃用
add_timestamps
和t.timestamps
可不用传入:null
选项的行为。Rails 5 将把缺省null: true
改为null: false
。 (Pull Request)弃用
Reflection#source_macro
,没有替代方案。Active Record 不再需要这个方法了。 (Pull Request)弃用
serialized_attributes
,没有替代方案。 (Pull Request)弃用了当栏位不存在时,还会从
column_for_attribute
返回nil
的情况。 Rails 5.0 将会返回 Null Object。 (Pull Request)弃用了
serialized_attributes
,没有替代方案。 (Pull Request)弃用依赖实例状态(有定义接受参数的作用域)的关联可以使用
.joins
、.preload
以及.eager_load
的行为 (Commit)
8.3 值得一提的变化
SchemaDumper
对create_table
使用force: :cascade
。这样就可以重载加入外键的纲要文件。单数关联增加
:required
选项,用来定义关联的存在性验证。 (Pull Request)ActiveRecord::Dirty
现在会侦测可变数值的变化。序列化过的属性只在有变更时才会保存。 修复了像是 PostgreSQL 不会侦测到字串或 JSON 栏位改变的问题。 (Pull Requests 1, 2, 3)导入
bin/rake db:purge
任务,用来清空当前环境的数据库。 (Commit)导入
ActiveRecord::Base#validate!
,若记录不合法时会抛出RecordInvalid
。 (Pull Request)引入
#validate
作为#valid?
的别名。 (Pull Request)#touch
现在可一次对多属性操作。 (Pull Request)PostgreSQL 适配器现在支持 PostgreSQL 9.4+ 的
jsonb
数据类型。 (Pull Request)新增 PostgreSQL 适配器的
citext
支持。 (Pull Request)PostgreSQL 与 SQLite 适配器不再默认限制字串只能 255 字符。 (Pull Request)
新增 PostgreSQL 适配器的使用自建的范围类型支持。 (Commit)
sqlite3:///some/path
现在可以解析系统的绝对路径/some/path
。 相对路径请使用sqlite3:some/path
。(先前是sqlite3:///some/path
会解析成some/path
。这个行为已在 Rails 4.1 被弃用了。 Rails 4.1.) (Pull Request)新增 MySQL 5.6 以上版本的 fractional seconds 支持。 (Pull Request 1, 2)
新增
ActiveRecord::Base
对象的#pretty_print
方法。 (Pull Request)ActiveRecord::Base#reload
现在的行为同m = Model.find(m.id)
,代表不再给自定的select
保存额外的属性。 (Pull Request)ActiveRecord::Base#reflections
现在返回的 hash 的键是字串类型,而不是符号。 (Pull Request)迁移的
references
方法支持type
选项,用来指定外键的类型,比如:uuid
。 (Pull Request)
9 Active Model
请参考 CHANGELOG 来了解更多细节。
9.1 移除
- 移除了
Validator#setup
,没有替代方案。 (Pull Request)
9.2 弃用
弃用
reset_#{attribute}
,请改用restore_#{attribute}
。 (Pull Request)弃用
ActiveModel::Dirty#reset_changes
,请改用#clear_changes_information
。 (Pull Request)
9.3 值得一提的变化
引入
#validate
作为#valid?
的别名。 (Pull Request)ActiveModel::Dirty
导入restore_attributes
方法,用来回复已修改的属性到先前的数值。 (Pull Request 1, 2)has_secure_password
现在缺省允许空密码(只含空白的密码也算空密码)。 (Pull Request)验证启用时,
has_secure_password
现在会检查密码是否少于 72 个字符。 (Pull Request)
10 Active Support
请参考 CHANGELOG 来了解更多细节。
10.1 移除
移除弃用的
Numeric#ago
、Numeric#until
、Numeric#since
以及Numeric#from_now
. (Commit)移除弃用
ActiveSupport::Callbacks
基于字串的终止符。 (Pull Request)
10.2 弃用
弃用
Kernel#silence_stderr
、Kernel#capture
以及Kernel#quietly
方法,没有替代方案。(Pull Request)弃用
Class#superclass_delegating_accessor
,请改用Class#class_attribute
。 (Pull Request)弃用
ActiveSupport::SafeBuffer#prepend!
请改用ActiveSupport::SafeBuffer#prepend
(两者功能相同)。 (Pull Request)
10.3 值得一提的变化
导入新的设置选项:
active_support.test_order
,用来指定测试执行的顺序,预设是:sorted
,在 Rails 5.0 将会改成:random
。(Commit)Object#try
和Object#try!
方法现在不需要消息接收者也可以使用。 (Commit, Pull Request)travel_to
测试辅助方法现在会把usec
部分截断为 0。 (Commit)Object#with_options
方法现在不需要消息接收者也可以使用。 (Pull Request)导入
String#truncate_words
方法,可指定要单词截断至几个单词。 (Pull Request)新增
Hash#transform_values
与Hash#transform_values!
方法,来简化 Hash 值需要更新、但键保留不变这样的常见模式。 (Pull Request)humanize
现在会去掉前面的底线。 (Commit)导入
Concern#class_methods
来取代module ClassMethods
以及Kernel#concern
,来避免使用module Foo; extend ActiveSupport::Concern; end
这样的样板。 (Commit)新增一篇指南,关于常量的载入与重载。
11 致谢
许多人花费宝贵的时间贡献至 Rails 项目,使 Rails 成为更稳定、更强韧的网络框架,参考完整的 Rails 贡献者清单,感谢所有的贡献者!