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ủa Ruby on Rails 6.1
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.
1
2
3
4
5
6
7
8
9
10
# 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:
1
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
1
2
3
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
1
2
3
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
1
2
3
class Project < ApplicationRecord
has_many :commits, strict_loading: true
end
1
2
3
4
5
6
7
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:
1
2
3
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.
1
2
3
class Comment < ActiveRecord::Base
belongs_to :article, dependent: :destroy_async
end
Những cái tiến mới của Ruby on Rails 6.1
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:
1
2
3
4
5
6
7
8
9
10
11
12
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_job
và jbuilder
Với Rails 6.1, option --minimal
được thêm vào
1
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
1
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
:
1
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
1
2
3
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
1
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.
1
2
3
4
5
6
7
8
9
10
11
<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:
1
<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.
1
<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ụ:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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.
Rails 6.1 còn có 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_with
là local: 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
1
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)
1
2
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)
1
2
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