Rails 6 特性:新特性及其重要性

已发表: 2022-03-11

大多数 Ruby on Rails 粉丝可能都知道,Rails 6 即将推出,并带来了许多期待已久的特性和变化。 本文的目的是让您熟悉 Rails 6 中添加的关键特性,并概述它们如何帮助您改进应用程序,从而节省宝贵的开发时间。

对于初学者,请记住 Rails 6 需要 Ruby 2.5+ 和升级的数据库。 因此,请确保您有相应升级系统的计划,以防您尚未这样做。

那么这些新功能是什么? 以下是您可能会使用的主要 Rails 6 功能的快速回顾:

在 Rails 6 中测试

作为专业的 Ruby on Rails 开发人员,我们的目标是确保最大限度地覆盖我们的代码。 然而,当我们的测试用例变得“繁重”并且我们必须等待几分钟甚至几个小时才能执行测试用例时,测试就变成了一项乏味的活动。

并行测试

好吧,Rails 6 在这里给出了答案。 它向ActiveSupport::TestCase添加了一个parallelize方法,允许您将测试套件与分叉进程并行化。

因此,您需要做的是并行化测试流程,将其添加到您的test_helper.rb

 parallelize(workers: 2)

或者,我们可以替换之前使用的命令来运行测试。 例如, bin/rails test OR bin/rspec spec现在可以替换为PARALLEL_WORKERS=15 rails test OR PARALLEL_WORKERS=15 rspec spec

因此,您可以更改在 Travis、Gitlab、CircleCI 等不同 CI 平台上运行测试套件的命令。

每个进程创建/销毁时也有钩子,可以使用如下:

 class ActiveSupport::TestCase parallelize_setup do |worker| # setup databases end parallelize_teardown do |worker| # cleanup databases end parallelize(workers: :number_of_processors) end

注意:如果您想了解更多信息,可以查看 Rails 指南以获取更多详细信息。

动作电缆测试

既然我们在谈论高效测试,那么让我们也了解一下作为 Rails 5 最显着的特性之一的 Action Cable 是如何改进的。 现在可以在任何级别测试 Action Cable:连接频道广播

连接测试旨在检查连接的标识符是否被正确分配或任何不正确的连接请求被拒绝:

 class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase test "connects with params" do connect params: { user_id: 42 } OR cookies.signed[:user_id] = "42" connect assert_equal connection.user_id, "42" end test "rejects connection without params" do assert_reject_connection { connect } end end

可以编写频道测试来检查用户是否可以订阅频道以及频道是否有流:

 class ChatChannelTest < ActionCable::Channel::TestCase test "subscribes and stream for room" do # Simulate a subscription creation by calling `subscribe` subscribe room: "15" # You can access the Channel object via `subscription` in tests assert subscription.confirmed? assert_has_stream "chat_15" end end

可以像这样测试广播到频道

 # app/jobs/chat_relay_job.rb class ChatRelayJob < ApplicationJob def perform_later(room, message) ChatChannel.broadcast_to room, text: message end end # test/jobs/chat_relay_job_test.rb require 'test_helper' class ChatRelayJobTest < ActiveJob::TestCase include ActionCable::TestHelper test "broadcast message to room" do room = rooms(:all) assert_broadcast_on(ChatChannel.broadcasting_for(room), text: "Hi!") do ChatRelayJob.perform_now(room, "Hi!") end end end

注意:可以在此处找到有关如何测试的更多提示。

批量插入和更新插入

在某些时候,我们都需要一次插入多条记录,并且在这样做时找到了许多解决方法。 好吧,Rails 6 提供了一个开箱即用的新方法—— insert_all ,类似于update_all

它不会触发任何回调并将执行单个 SQL 查询。 还有一个额外的方法upsert_all允许您使用许多现代数据库(如 Postgres)公开的 upsert 操作。 因此,现在您可以减少插入查询并使代码更加优化。 另外,告别以前使用过的 gem,比如activerecord-import

单个INSERT SQL 查询由这些方法准备,单个 SQL 语句被发送到数据库,无需实例化模型,或调用 Active Record 回调和验证。 还可以在违反主键(唯一索引或唯一约束)时定义标准,并可选择跳过或运行 upsert 查询。

一些例子如下:

 result = Article.insert_all( [ { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' }, #...snip... ], returning: %w[ id title ], unique_by: :index_articles_on_title_and_author ) result = Article.upsert_all( [ { id: 1, title: 'Handling 1M Requests Per Second', author: 'John', slug: '1m-req-per-second' }, { id: 1, .... }, # duplicate 'id' here { id: 2, .... }, { id: 3, .... }, # duplicate 'title' and 'author' here { id: 4, .... }, { id: 5, .... }, # duplicate 'slug' here { id: 6, .... } ] )

方法insertinsert!upsertinsert_allinsert_all!upsert_all ,分别。

注意:有一篇非常好的文章讨论了关于不同数据库的批量查询。 如果您需要更多信息,请务必查看。

在多个数据库之间切换

许多大型应用程序都会欣赏的主要功能之一是:Rails 6 终于为您的应用程序添加了对多个数据库的支持,内置并准备就绪,开箱即用!

数据库切换示意图

当然,设计选择仍然是您的选择,无论您是将应用程序分解为多个微服务,每个微服务都有一个单独的数据库,还是采用单片路由,或者为您的应用程序添加多个只读副本。

但是,能够以如此简单的方式完成它有可能在开发方面节省大量时间。

因此,这就是您的新database.yml文件的外观:

 development: primary: database: my_primary_db user: root primary_replica: database: my_primary_db user: ro_user replica: true animals: database: my_animals_db user: root animals_replica database: my_animals_db user: ro_user replica: true

以下是指定如何切换到不同数据库的有趣方法:

 class AnimalsModel < ApplicationRecord self.abstract_class = true connects_to database: { writing: :animals_primary, reading: :animals_replica } end class Dog < AnimalsModel # connected to both the animals_primary db for writing and the animals_replica for reading end

这是官方的 GitHub 页面,它也有很好的文档记录。 就个人而言,我期待在未来的 Rails 更新中也有数据库分片功能(类似这样)。

操作邮箱

Rails 6 另一个有趣的特性是增加了 Action Mailbox,它增加了将传入电子邮件路由到控制器的能力,就像在 Rails 中处理的邮箱一样。

Action Mailbox 具有 Mailgun、Mandrill、Postmark 和 SendGrid 的入口。 您还可以通过内置的 Exim、Postfix 和 Qmail 入口直接处理入站电子邮件。 现在,您无需详细说明即可想象潜在的好处。 它可能会直接处理来自帮助台的邮件以自动处理支持工单——Rails 6 允许客户直接通过电子邮件进行回复,等等。 您可以探索此功能并提出适合您的应用的方法。

下面是一个小例子来了解如何使用 Action Mailbox:

 COMMENTS_REGEX = /^comment\+(.+)@example\.com/i # app/mailboxes/application_mailbox.rb class ApplicationMailbox < ActionMailbox::Base routing COMMENTS_REGEX => :comments end # app/mailboxes/comments_mailbox.rb class CommentsMailbox < ApplicationMailbox def process user = User.find_by(email: mail.from) post_uuid = COMMENTS_REGEX.match(mail.to)[1] post = Post.find_by(uuid: post_uuid) post.comments.create(user: user, content: mail.body) end end

另外,新的邮件配置方式如下(以Sendgrid为例):

 # config/environments/production.rb config.action_mailbox.ingress = :sendgrid

使用rails credentials:edit将密码添加到action_mailbox.ingress_password下的应用程序的加密凭据中,Action Mailbox 会在其中自动找到它:

 action_mailbox: ingress_password: …

配置 SendGrid 入站解析以使用用户名actionmailbox和您之前生成的密码将入站电子邮件转发到/rails/action_mailbox/sendgrid/inbound_emails 。 如果您的应用程序位于https://example.com ,您将使用以下 URL 配置 SendGrid:

 https://actionmailbox:[email protected]/rails/action_mailbox/sendgrid/i

如果您想进一步探索,Rails 已经在此处提供了相关指南。

时代周刊

Zeitwerk 是 Ruby 的新代码加载器。 给定传统的文件结构,Zeitwerk 会按需加载项目的类和模块,这意味着您无需为自己的文件编写 require 调用。 要在 Rails 6 中启用它,您可以执行以下操作:

 config.autoloader = :zeitwerk

您可以在此处阅读有关 Zeitwerk 的更多信息。

优化器提示

您担心您的某些查询执行时间过长? 好吧,现在您也可以为查询定义超时了。

如果查询的执行时间比正常时间长,则以下语句将引发StatementTimeout异常:

 User.optimizer_hints("MAX_EXECUTION_TIME(5000)").all

它由 MySQL 支持,您必须探索您的数据库是否支持它。

截断数据库

播种数据呢? 以下语句将截断所有数据库表,然后您可以继续播种数据:

 rails db:truncate_all

不再需要删除您的数据库来播种。 您可能会同意这是一个优雅而快速的解决方案。

动作文本

对于许多使用 WYSIWYG 编辑器的应用程序来说,另一个值得注意的特性可能是在 Rails 6 应用程序中添加了对原生 Trix 编辑器的支持。 对于许多项目来说,这肯定是一个很好的升级/添加。

大多数 WYSIWYG HTML 编辑器的范围都很大——每个浏览器的实现都有自己的一组错误和怪癖,JavaScript 开发人员需要解决不一致的问题。 Trix 通过将contenteditable视为 I/O 设备来回避这些不一致:当输入进入编辑器时,Trix 将该输入转换为对其内部文档模型的编辑操作,然后将该文档重新呈现回编辑器。 这使 Trix 可以完全控制每次击键后发生的事情。

安装:

 rails action_text:install # app/models/message.rb class Message < ApplicationRecord has_rich_text :content end

您可以在此处的官方文档中更详细地探索操作文本。

安全

没有一些安全增强功能,就没有完成真正的升级。 Rails 6 在安全方面也没有让人失望。 第一个值得注意的安全升级是增加了对Host Authorization的支持。

主机授权是一种新的中间件,它通过明确允许可以向主机发送请求来防止 DNS 重新绑定攻击。 这意味着您可以定义可以访问您的应用程序的主机。

另一个安全升级旨在阻止试图复制 cookie 的签名/加密值并将其用作另一个 cookie 的值的攻击。 它通过将 cookie 名称存储在用途字段中来实现,然后将其与 cookie 值一起签名/加密。 然后,在服务器端读取时,我们验证 cookie 名称并丢弃任何受到攻击的 cookie。 启用action_dispatch.use_cookies_with_metadata以使用此功能,该功能会写入嵌入新用途和过期元数据的 cookie。

Webpack 作为默认 Bundler

作为许多现代 JavaScript 前端开发框架的事实标准,Rails 6 通过 webpacker gem 添加了 Webpack 作为默认的 JavaScript 捆绑器,取代了 Rails 资产管道。 这是一个相对简单的添加,我们不会详细介绍。 可以说,Webpack 将为过度劳累的前端开发人员带来一些缓解。

防止比赛条件

Rails 6 有一个新方法,用于防止我们的代码中出现 SELECT/INSERT 竞争条件(我相信很多读者在扩展项目时遇到过竞争条件的不幸)。 如果您需要更多信息,这里是 GitHub 线程。

基础表必须具有使用唯一约束定义的相关列。 虽然我们避免了 SELECT → INSERT from #find_or_create_by之间的竞争条件,但实际上我们在 INSERT → SELECT 之间还有另一个竞争条件,如果这两个语句之间的 DELETE 由另一个客户端运行,则可以触发该竞争条件。 但是,对于大多数应用程序来说,这是我们不太可能达到的条件。

Rails 6 中的凭据

自 Rails 5.2 以来,凭证已被命名为一种新的“Rails 方式”来处理敏感信息,并承诺一劳永逸地摆脱臭名昭著的.env文件。 使用凭据,第三方服务的加密密钥可以直接检查到源代码控制中。

然而,到目前为止,Rails 对所有环境都使用相同的加密文件,这使得在开发和生产中处理不同的密钥有点棘手,尤其是在处理大型项目和遗留代码时。

在 Rails 6 中,这最终通过支持每个环境的凭据来解决。 同样,可以在官方 GitHub 线程上探索更多细节。

Rails 6 是一个好的更新吗?

是的,事实上 Rails 6 可以说是一次重大更新,尽管很少有人会称它为游戏规则改变者。 由于 Ruby on Rails 已经存在多年,很少有人期待革命性的变化,但它的第六次化身带来了很多。

Rails 6 中推出的一些特性似乎是微小的改进,而其他特性则有可能节省大量开发时间、提高安全性、健壮性等。 底线:Rails 已经成熟,许多开发人员仍然对它的未来充满热情,随着 Rails 6 的发布,它变得更好了。

当然,这个 Rails 6 特性列表是不完整的,要查看完整的更改集,您需要查看更改日志。 此外,您应该考虑很多弃用。 最后,如果您坚持检查每一个更改并更新自己,请阅读完整的发行说明。