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 特性列表是不完整的,要查看完整的更改集,您需要查看更改日誌。 此外,您應該考慮很多棄用。 最後,如果您堅持檢查每一個更改並更新自己,請閱讀完整的發行說明。