Yuto Blog

Yuto Blog

Japan, Code and Photo
Một số tính năng mới và sự thay đổi của phiên bản Ruby on Rails 6.1
Một số tính năng mới và sự thay đổi của phiên bản Ruby on Rails 6.1
Updated by Yuto Yasunaga at 2020/12/14 10:09

Phiên bản Ruby on Rails 6.0 chính thức được ra mắt vào tháng 8 năm 2019, và ngày 9/12/2020 phiên bản 6.1 đã được release.

Trong phiên bản 6.1 mới này, có những cải tiến tập trung cho những tính năng mới của Active Record và database, ví dụ như có thể kết nối nhiều database, horizontal sharding, strict loading, thực hiện xóa bất dồng bộ ở background job...v.v...

Điểm lưu ý

Để sử dụng được version Ruby on Rails 6.1 thì cần version Ruby 2.5 trở lên.

Những tính năng mới

Cải tiến về thay đổi kết nối qua lại giữa nhiều database

Từ version 6.0 thì đã có hỗ trợ multiple database, ở version 6.1 này thì có thêm cải tiến, method connected_to không chỉ được gọi từ ActiveRecord::Base mà còn có thể được gọi từ các Abstract Class. Có thể thay đổi kết nối ở mỗi model.

Ở ví dụ dưới đây minh họa về sử dụng method connected_to của AnimalsRecord bên trong block để lấy dữ liệu từ 2 database reading và writing.

# Class User là class con của ApplicationRecord
# Class Dog là class con của AnimalsRecord
ActiveRecord::Base.connected_to(role: :reading) do
  User.first # lấy dữ liệu từ reading
  Dog.first # lấy dữ liệu từ reading
  AnimalsRecord.connected_to(role: :writing) do
    User.first # lấy dữ liệu từ reading, bởi vì class User không phải là class con của AnimalsRecord
    Dog.first # lấy dữ liệu từwriting
  end
end

Để có thể chuyển đổi linh hoạt qua lại giữa các database như ở trên thì cần phải tắt legacy_connection_handling như dưới đây:

config/application.rb
config.active_record.legacy_connection_handling = false

strict_loading

Rails 6.1 thêm strict_loading mode để ngăn ngừa lazy loading associations. Khi sử dụng mode strict_loading, những records liên quan sẽ được eager loaded bởi includes hoặc gây ra ngoại lệ ActiveRecord::StrictLoadingViolationError

p = Project.strict_loading.find_by(id: 'abe99c60-56c4-461e-bd59-63418a719e0d')
p.commits.to_a
=> ActiveRecord::StrictLoadingViolationError: Project is marked as strict_loading and Commit cannot be lazily loaded.

strict_loading giúp chúng ta tránh được N+1 query

p = Project.includes(:commits).strict_loading.find_by(id: 'abe99c60-56c4-461e-bd59-63418a719e0d')
p.commits.first.post
=> ActiveRecord::StrictLoadingViolationError: Commit is marked as strict_loading and Post cannot be lazily loaded.

strict_loading có thể được khai báo trong file model, khi này thì mặc định association của model đó sẽ được strict_loading

app/models/project.rb
class Project < ApplicationRecord
  has_many :commits, strict_loading: true
end
p = Project.first
p.commits.first
=> ActiveRecord::StrictLoadingViolationError: The commits association is marked as strict_loading and cannot be lazily loaded.

p = Project.includes(:commits).first
p.commits.first
=> ...

Hoặc có thể setting mặc định strict_loading cho model bằng cách thêm dòng code sau vào bên trong model:

app/models/project.rb
class Commit < ApplicationRecord
  self.strict_loading_by_default = true
end

strict_loading của phiên bản Rails 6.1 giúp phòng tránh N+1 query giống như gem bullet

Thực hiện xóa bất đồng bộ những record có liên quan

Từ version 6.1 chúng ta có thể thực hiện việc xóa bất đồng bộ những record liên quan bằng khai báo dependent: :destroy_later. Khi một record của model được xóa thì những record liên quan đến nó sẽ được xóa bất đồng bộ bằng Active Job.

Ví dụ dưới đây minh họa về các comment liên quan sẽ được xóa ở background job mỗi khi một article được xóa.

app/models/comment.rb
class Comment < ActiveRecord::Base
  belongs_to :article, dependent: :destroy_async
end

Những cái tiến mới

Option --minimal khi tạo app mới

Với câu lệnh rails new my_app sẽ tạo ra một application mới với đầy đủ features.

Ở các phiên bản trước, nếu như muốn skip một vài features thì sẽ làm như thế này:

rails new tiny_app
    --skip-action-cable
    --skip-action-mailer
    --skip-action-mailbox
    --skip-action-text
    --skip-active-storage
    --skip-bootsnap
    --skip-javascript
    --skip-spring
    --skip-system-test
    --skip-webpack-install
    --skip-turbolinks

Trước khi Rails 6.1 được release thì chúng ta không thể skip active_jobjbuilder

Với Rails 6.1, option --minimal được thêm vào

rails new tiny_app --minimal

Khi thực hiện lệnh trên thì một app Ruby on Rails mới sẽ được tạo ra và không bao gồm những features dưới đây:

action_cable, action_mailbox, action_mailer, action_text, active_job, active_storage, bootsnap, jbuilder, spring, system_tests, turbolinks, webpack

Với option --minimal vẫn có thể dùng kèm với option khác như webpack hoặc database

rails new tiny_app --minimal --database mysql webpack=vue

Có thể import file bên ngoài vào config/routes

File config/routes có thể đọc dữ liệu từ các file định nghĩa routes khác.

Giả sử ta có file sau với tên admin.rb, lưu trong folder config/routes:

config/routes/admin.rb
get :top, to: 'top#index'

Khi đó chỉ cần dùng draw(:admin) ta có thể import code từ file config/routes/admin.rb vào file config/routes.rb

config/routes.rb
Rails.application.routes.draw do
  draw(:admin)
end

Có thể xuất ra tên file template ERB bên trong HTML source

Khi setting theo bên dưới thì tên của template ERB sẽ được xuất ra với comment bên trong HTML source

config/environments/development.rb
config.action_view.annotate_template_file_names = true

Ví dụ dưới đây mô tả việc tên của file template ERB được tự động xuất ra bên trong HTML source. Các template dạng khác (haml, slim) thì hiện tại chưa được support.

<html>
   <head>
     <title>Hello, world!</title>
     ...
   </head>
   <body>
     <!-- BEGIN app/views/hello/index.html.erb -->
     <h1>Hello, world!</h1>
     <!-- END app/views/hello/index.html.erb -->
   </body>
</html>

Thêm helper class_name để tự động gán cho CSS

Từ trước đến nay thì chúng ta thường dùng toán tử ba ngôi để gán tên của class CSS dựa theo một điều kiện nào đó như dưới đây:

<div class="<%= item.for_sale? ? 'active' : '' %>">

Từ bây giờ thì có cách nhanh hơn để gán tên cho class CSS dựa theo điều kiện, đó là dùng helper class_name, key của đối số truyền vào là tên class muốn truyền, và value là điều kiện, khi điều kiện true thì tên class sẽ được gán.

<div class="<%= class_names(active: item.for_sale?) %>">

Thêm method *_previously_was

Sau khi lưu instance của model thì vẫn có thể truy vấn được giá trị trước đó của attribute thông qua method *_previously_was, * là tên attribute.

Ví dụ:

user = User.find_by(name: "Sam")
=> #<User id: 1, name: "Sam", email: nil, created_at: "2019-10-14 17:53:06", updated_at: "2019-10-14 17:53:06">

user.name = "Nick"
user.name_was
=> "Sam"

user.save!

user.previous_changes[:name]
=> ["Sam", "Nick"]

# *_previously_was trả về giá trị trước đó.
user.name_previously_was
=> "Sam"

# Sau khi reload, tất cả dirty tracking sẽ reset lại
user.reload
user.name_previously_was
=> nil

Active Storage đã có thể sử dụng URL cố định cho file

Ở file setting của Active Storage config/storage.yml, nếu set public: true cho các service thì Blob#url sẽ sử dụng được URL cố định. Ở các version trước thì Blob#service_url chỉ trả về URL tạm thời.

Một vài điểm thay đổi khác

Mặc định form_with sẽ là local: true

Từ trước đến nay thì mặc định của form helper form_withlocal: false, khi đó chúng ta viết code để controller thực hiện xử lý request Ajax. Nhưng từ version 6.1 thì mặc định của form_with trở thành local: true

Có thể setting như sau để chuyển default về như các version trước đây

config/application.rb
config.action_view.form_with_generates_remote_forms = true

SQL của truy vấn where.not không còn là NOR mà là NAND

Ở các version trước, khi bỏ nhiều điều kiện vào where.not của ActiveRecord thì câu lệnh SQL NOR như dưới đây sẽ được tạo ra (NOT A AND NOT B)

User.where.not(name: "Jon", role: "admin")
# SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'

Từ version 6.1 trở đi thì câu lệnh SQL NAND như dưới đây sẽ được tạo ra (NOT A AND B)

User.where.not(name: "Jon", role: "admin")
# SELECT * FROM users WHERE NOT (name == 'Jon' AND role == 'admin')

Có một điểm cần chú ý là ở Rails 6.0 khi tạo câu truy vấn NOR thì sẽ có cảnh báo xuất hiện, tuy nhiên ở Rails 6.1 thì sẽ chuyển thành truy vấn NAND


Back