作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
Avant Mittal's profile image

Avant Mittal

Working as a consultant, Avant帮助Chipotle等客户开发了应用程序, Sebamed USA, HealthEngine, DealDey, and many more.

Expertise

Previously At

Chipotle
Share

As most Ruby on Rails fans might be aware, Rails 6 is coming soon, 并带来了许多期待已久的功能和变化. 本文的目的是让您熟悉Rails 6中添加的关键特性,并概述它们如何帮助您改进应用程序, 从而节省了宝贵的开发时间.

对于初学者,请记住Rails 6需要Ruby 2.5+ and upgraded databases. So, 确保你有一个相应升级系统的计划, 如果你还没有这样做的话.

那么这些新功能是什么呢? 下面是你可能会用到的Rails 6关键特性的快速回顾:

Testing in Rails 6

As professional Ruby on Rails developers,我们的目标是确保我们代码的最大覆盖率. However, 当我们的测试用例变得“繁重”并且我们不得不等待几分钟时,测试就变成了一项乏味的活动, or even hours, 只是为了执行测试用例.

Parallel Testing

Rails 6在这里给出了答案. It has added a parallelize method to the ActiveSupport::TestCase 哪一种方法允许您将测试套件与分支进程并行化.

所以,你需要做的是将测试过程并行化,将这个添加到 test_helper.rb:

parallelize(workers: 2)

或者,我们可以替换之前使用的命令来运行测试. For example, bin/rails test或bin/rspec can now be replaced by PARALLEL_WORKERS=15 rails test或PARALLEL_WORKERS=15 rspec规范.

Accordingly, 您可以更改命令,以便在不同的CI平台(如Travis)上运行测试套件, Gitlab, CircleCI, and others.

当每个进程被创建/销毁时也有钩子,可以这样使用:

class ActiveSupport::TestCase
  parallelize_setup do |worker|
    # setup databases
  end
 
  parallelelize_teardown do |worker
    # cleanup databases
  end
 
  并行化(工人::number_of_processors)
end

Note: 如果你想了解更多,你可以点击查看 Rails Guides for additional details.

Action Cable Testing

因为我们在讨论有效的测试, 让我们来了解一下Action Cable, Rails 5最显著的特性之一, has improved. 现在可以在任何级别测试Action Cable: connections, channels, and broadcasts.

Connection tests 目的是检查连接的标识符是否被正确分配,或者任何不适当的连接请求是否被拒绝:

class ApplicationCable::ConnectionTest < ActionCable::Connection::TestCase
  测试“连接参数”

    连接参数:{user_id: 42}
    OR
    cookies.signed[:user_id] = "42"
    connect

    assert_equal connection.user_id, "42"
  end
 
  测试"拒绝不带参数的连接
    Assert_reject_connection {connect}
  end
end

Channel tests 可以编写检查用户是否可以订阅频道以及频道是否有流:

class ChatChannelTest < ActionCable::Channel::TestCase
  测试“订阅和流为房间”做
    #通过调用' subscribe '模拟订阅创建
    subscribe room: "15"
 
    #您可以在测试中通过“订阅”访问Channel对象
    assert subscription.confirmed?
    assert_has_stream "chat_15"
  end
end

Broadcasting to channels can be tested like this:

# app/jobs/chat_relay_job.rb
class ChatRelayJob < ApplicationJob
  Def perform_later(房间,消息)
    ChatChannel.Broadcast_to room, text: message
  end
end
 
#测试/工作/ chat_relay_job_test.rb
require 'test_helper'
 
class ChatRelayJobTest < ActiveJob::TestCase
  包括ActionCable:: TestHelper
 
  测试“向房间广播消息”做
    room = rooms(:all)
 
    assert_broadcast_on (ChatChannel.broadcasting_for(房间),text:“嗨!") do
      ChatRelayJob.perform_now(room, "Hi!")
    end
  end
end

Note: 更多关于如何测试的提示可以找到 here.

Bulk Insert and Upsert

At some point, 我们都需要一次插入多个记录,并且在这样做时找到了许多解决方法. 好吧,Rails 6带来了一个开箱即用的新方法insert_all, similar to update_all.

它不会触发任何回调,只会执行单个SQL查询. There is an additional method upsert_all which allows you to use the upsert operation 这是许多现代数据库如Postgres所暴露的. 因此,现在您可以减少插入查询并使代码更加优化. 此外,告别以前使用的宝石,如 activerecord-import.

A single INSERT SQL查询是由这些方法准备的, 并向数据库发送一条SQL语句, 不实例化模型, 或调用活动记录回调和验证. 还可以在违反主键-唯一索引或唯一约束时定义标准,并提供跳过或运行upsert查询的选项.

Some examples are below:

result = Article.insert_all(
  [
    { id: 1,
      标题:“每秒处理1M个请求”
      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,标题:'每秒处理1M个请求',作者:'John',属性:' 1M - request - Per - Second'},
    { id: 1, .... }, # duplicate 'id' here
    { id: 2, .... },
    { id: 3, .... }, #复制'title'和'author'
    { id: 4, .... },
    { id: 5, .... }, # duplicate 'slug' here
    { id: 6, .... }
  ]
)

The methods insert, insert! and upsert are wrappers around insert_all, insert_all! and upsert_all, respectively.

Note: 有一篇非常好的文章讨论了针对不同数据库的批量查询. 如果您需要其他信息,请确保 check it out.

在多个数据库之间切换

许多大型应用程序都会喜欢的一个主要特性是:Rails 6最终为您的应用程序增加了对多个数据库的支持, built in and ready to go, out of the box!

数据库切换图

Of course, 设计选择仍然是你的, 是否希望将应用程序分解为多个微服务,每个微服务都有一个单独的数据库, or take a monolithic route, 或者为您的应用程序添加几个读副本.

然而,有能力以如此简单的方式做到这一点有可能节省 a lot 在开发方面的时间.

So, this is how your new database.yml file will look:

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:{写::animals_primary,读::animals_replica}
end

class Dog < AnimalsModel
  同时连接到animals_primary数据库进行写操作,同时连接到animals_replica数据库进行读操作
end

Here is the official GitHub page,这也有很好的记录. Personally, 我期待在未来的Rails更新中也有数据库分片功能(比如 this).

Action Mailbox

另一个有趣的Rails 6特性是添加了 Action Mailbox,它将功能添加到 route incoming emails 到控制器中,就像在Rails中处理邮箱一样.

动作邮箱功能入口Mailgun, Mandrill,邮戳,和SendGrid. 您还可以通过内置的Exim, Postfix和Qmail入口直接处理入站电子邮件. 现在,你可能可以想象潜在的好处,而不需要更多的细节. 它可能是直接处理来自帮助台的邮件以自动化支持票证——rails 6允许客户直接通过电子邮件进行回复, and much, much more. 您可以探索此功能,并提出适合您的应用程序的理想方法.

下面是一个小示例来了解如何使用动作邮箱:

COMMENTS_REGEX = /^comment\+(.+)@example\.com/i

# app /邮箱/ application_mailbox.rb
class ApplicationMailbox < ActionMailbox::Base
  routing COMMENTS_REGEX => :comments
end

# app /邮箱/ 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.创建用户:用户,内容:邮件.body)
  end
end

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

#配置/环境/生产.rb
config.action_mailbox.ingress = :sendgrid

Use rails credentials:edit 将密码添加到应用程序的加密凭据中 action_mailbox.ingress_password,动作邮箱将自动找到它:

action_mailbox:
  ingress_password: …

Configure the SendGrid Inbound Parse to forward inbound emails to /铁路/ action_mailbox / sendgrid / inbound_emails with the username actionmailbox 还有你之前生成的密码. If your application lives at http://example.com,你可以用下面的URL配置SendGrid:

http://actionmailbox:PASSWORD@example.com/rails/action_mailbox/sendgrid/i

如果您想进一步探索这个问题,Rails已经提供了一个指南 here.

Zeitwerk

Zeitwerk是Ruby的新代码加载器. 给定一个常规的文件结构, Zeitwerk根据需要加载项目的类和模块, 这意味着您不需要为自己的文件编写require调用. 要在Rails 6中启用它,你可以这样做:

config.autoloader = :zeitwerk

You can read more about Zeitwerk here.

Optimizer Hints

您担心某些查询执行时间过长? 现在,您也有了一种为查询定义超时的方法.

下面的语句将引发一个 StatementTimeout 如果查询执行时间比正常情况长,则会出现异常:

User.optimizer_hints(“MAX_EXECUTION_TIME(5000)”).all

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

Truncate Database

What about seeding data? 下面的语句将截断所有的数据库表,然后你可以继续播种你的数据:

rails db:truncate_all  

不用再删除你的数据库. 您可能会同意这是一个优雅而快速的解决方案.

Action Text

对于许多使用所见即所得编辑器的应用程序来说,另一个值得注意的特性可能是增加了对 Trix editor 原生到Rails 6应用程序中. 对于许多项目来说,这无疑是一个很好的升级/补充.

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

Installation:

rails action_text:install

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

You can explore Action Text 在官方文件中有更详细的说明, here.

Security

没有一些安全增强,任何重大升级都是不完整的. Rails 6在安全性方面也没有让人失望. 第一个值得注意的安全升级是增加了对 Host Authorization.

主机授权是一种新的中间件,可以防止 DNS rebinding 通过显式允许可以向主机发送请求的攻击. 这意味着您可以定义可以访问您的应用程序的主机.

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

Webpack作为默认的打包器

这是许多用于前端开发的现代JavaScript框架的事实标准, Rails 6通过webpacker gem添加了Webpack作为默认的JavaScript打包器, 替换Rails资产管道. 这是一个相对简单的加法,我们不会深入讨论太多细节. 可以这么说,Webpack将为过度劳累的前端开发人员带来一些缓解.

Preventing Race Conditions

Rails 6有一个新方法,用于防止代码中的SELECT/INSERT竞争条件(我相信许多读者在扩展项目时都不幸遇到过竞争条件)。. Here is the GitHub thread 如果你需要额外的信息.

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

Credentials in Rails 6

Since the days of Rails 5.2, 凭据被命名为一种新的“Rails方式”来处理敏感信息,并承诺摆脱臭名昭著 .env files once and for all. With credentials, 第三方服务的加密密钥可以直接检查到源代码控制中.

Until now, however, Rails对所有环境使用相同的加密文件, 这使得在开发和制作过程中处理不同的关键问题变得有些棘手, 尤其是在处理大型项目和遗留代码时.

在Rails 6中,最终通过支持每个环境的凭据解决了这个问题. 进一步的细节可以在官方网站上找到 GitHub thread.

Is Rails 6 a Good Update?

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

Rails 6中推出的一些特性似乎只是微小的改进, 而另一些则有可能节省大量的开发时间, improve security, robustness, and so on. 总结:Rails是成熟的, 许多开发者仍然对它的未来充满热情随着Rails 6的发布,它变得更好了.

Of course, 这个Rails 6特性列表是不完整的,要查看完整的更改集, 您需要签出变更日志. 此外,还有许多您应该考虑的弃用. 最后,如果你坚持要检查每一个改动并自己更新,请阅读 full release notes.

Understanding the basics

  • Ruby on Rails在2019年仍然相关吗?

    Rails比市场上任何其他解决方案都更重要. Rails和Ruby都在升级(Matz让Ruby快了3倍). Rails 6提供了简单的技术来本地扩展应用程序. 它仍然是Github、Shopify、Basecamp等许多大公司值得信赖的选择.

  • Should I use Rails?

    答案取决于您的用例. 它仍然是常规web中最好的框架, e-commerce, content management, 以及许多其他类型的应用程序. 它还减少了MVP开发时间,当您没有固定的想法或需要快速的应用程序原型时,它是一个很好的选择.

  • What does Upsert mean?

    Upsert是一个数据库操作,这意味着, 基于为操作提供的属性, 如果数据库中已经存在记录, 然后,我们想要更新该记录,或者如果该记录不存在,则创建一个新记录. Upsert发生在单个事务中,因此可以防止应用程序中的任何竞争条件.

  • 并行测试的优点是什么?

    我们已经看到了仅仅运行测试用例就需要花费数小时的项目. 通过在不同的过程中运行一组测试用例,我们可以减少测试用例的运行时间. 使用Rails 6并行测试, 我们不必依赖外部服务来并行化我们的测试用例.

就这一主题咨询作者或专家.
Schedule a call
Avant Mittal's profile image
Avant Mittal

Located in Delhi, India

Member since April 19, 2019

About the author

Working as a consultant, Avant帮助Chipotle等客户开发了应用程序, Sebamed USA, HealthEngine, DealDey, and many more.

Toptal作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.

Expertise

Previously At

Chipotle

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

世界级的文章,每周发一次.

订阅意味着同意我们的 privacy policy

Toptal Developers

Join the Toptal® community.