Có gì mới trong Rails 6.0?

- Trong phiên bản mới này, rails đã cải tiến nhiều về tốc độ và khả năng mở rộng.
- Chúng ta sẽ tìm hiểu qua xem chúng có những thay đổi gì mới nhé!

Active Record

- Thêm #delete_by và #destroy_by cho ActiveRecord::Relation methods
  • Trước Rails 6, việc deleting/destroying bản ghi phù hợp với điều kiện đã cho được thực hiện như sau:
 # Ví dụ để destroy all users phù hợp với điều kiện đã cho
  User.find_by(email: "[email protected]").destroy
  User.where(email: "[email protected]", status: 1).destroy_all

  # Ví dụ để delete  all users phù hợp với điều kiện đã cho
  User.find_by(email: "[email protected]").delete
  User.where(email: "[email protected]", status: 1).delete_all
  • Trong Rails 6, các phương thức delete_bydestroy_by mới đã được thêm vào ActiveRecord::Relation methods. ActiveRecord::Relation#delete_by là cách viết gọn cho câu lệnh relation.where(conditions).delete_all. Tương tự như vậy, ActiveRecord::Relation#destroy_by là cách viết gọn cho câu lệnh relation.where(conditions).destroy_all. Ví dụ:
 # Ví dụ để destroy all users phù hợp với điều kiện đã cho bằng cách sử dụng destroy_by
  User.destroy_by(email: "[email protected]")
  User.destroy_by(email: "[email protected]", status: 1)

  # Ví dụ để delete all users phù hợp với điều kiện đã cho bằng cách sử dụng delete_by
  User.delete_by(email: "[email protected]")
  User.delete_by(email: "[email protected]", status: 1)
- Thêm #touch_all cho ActiveRecord::Relation methods
  • Trước khi tìm hiểu về nó, chúng ta sẽ xem touch method là cái gì đã.
    #touch được sử dụng để cập nhật trường update_at mặc định là cập nhật nó đến thời gian hiện tại. Nó cũng có thể dùng thời gian tùy chỉnh hoặc các cột khác nhau làm tham số.
  • Rails 6 đã thêm#touch_all trên ActiveRecord :: Relation để touch vào nhiều bản ghi trong một lần (trước Rails 6, chúng ta cần lặp lại tất cả các bản ghi bằng cách sử dụng vòng lặp để đạt được điều này.)
  • Ví dụ trong Rails 5.2:
>> User.count
SELECT COUNT(\*) FROM "users"
=> 3

>> User.all.touch_all
=> Traceback (most recent call last):1: from (irb):2
NoMethodError (undefined method 'touch_all' for 
#<User::ActiveRecord_Relation:0x00007fe6261f9c58>)

>> User.all.each(&:touch)
SELECT "users".* FROM "users"
begin transaction
  UPDATE "users" SET "updated_at" = ? WHERE "users"."id" = ?  [["updated_at", "2019-03-16 17:45:51.495203"], ["id", 1]]
  commit transaction
begin transaction
  UPDATE "users" SET "updated_at" = ? WHERE "users"."id" = ?  [["updated_at", "2019-03-16 17:45:51.503415"], ["id", 2]]
commit transaction
begin transaction
  UPDATE "users" SET "updated_at" = ? WHERE "users"."id" = ?  [["updated_at", "2019-03-16 17:45:51.509058"], ["id", 3]]
commit transaction
=> [#<User id: 1, name: "ABC", created_at: "2019-03-16 16:09:29", 
    updated_at: "2019-03-16 17:45:51">, #<User id: 2, name: "IGH", 
    created_at: "2019-03-16 16:09:43", updated_at: "2019-03-16 17:45:51">, 
    #<User id: 3, name: "KOU", created_at: "2019-03-16 16:09:45", 
    updated_at: "2019-03-16 17:45:51">]
  • Còn Rails 6.0.0.beta2:
>> User.count
SELECT COUNT(*) FROM "users"

=> 3

>> User.all.touch_all
UPDATE "users" SET "updated_at" = ?  [["updated_at", 
"2019-03-16 16:08:47.490507"]]

=> 3

#touch_all trả về số lượng các bản ghi mà nó được gọi.
#touch_all cũng nhận thời gian tùy chỉnh hoặc các cột khác nhau làm tham số.

  • Trong Rails 6.0.0.beta2:
>> User.count
SELECT COUNT(*) FROM "users"

=> 3

>> User.all.touch_all(time: Time.new(2019, 3, 2, 1, 0, 0))
UPDATE "users" SET "updated_at" = ?  [["updated_at", "2019-03-16 00:00:00"]]

=> 3

>> User.all.touch_all(:created_at)
UPDATE "users" SET "updated_at" = ?, "created_at" = ?  [["updated_at", 
"2019-03-16 17:55:41.828347"], ["created_at", "2019-03-16 17:55:41.828347"]]

=> 3
- Thêm ActiveRecord::Relation#pick
  • Trước Rails 6, việc chọn giá trị đầu tiên từ một cột trong tập hợp các bản ghi khá là phức tạp. Ví dụ ta muốn lấy name đầu tiên trong tất cả các bài viết có thể loại là 1:
>> Post.where(category: 1).limit(1).pluck(:name).first
   SELECT "posts"."name"
   FROM "posts"
   WHERE "posts"."category" = ?
   LIMIT ?  [["category", 1], ["LIMIT", 1]]
=> "Hello!"
  • Trong Rails 6, phương thức ActiveRecord::Relation#pickmới đã được thêm vào làm tối giản đi cách viết để chọn ra giá trị đầu tiên.
>> Post.where(category: 1).pick(:name, :author)
   SELECT "posts"."name", "posts"."author"
   FROM "posts"
   WHERE "posts"."category" = ?
   LIMIT ?  [["category", 1], ["LIMIT", 1]]
=> ["Hello", "VN"]
- Thêm negative scopes cho enum
  • Khi một thuộc tính enum được định nghĩa trên model, Rails thêm một số default scope để lọc các bản ghi dựa trên các giá trị của enum trên trường enum.
  • Ví dụ:
class Post < ActiveRecord::Base
  enum status: %i[drafted active trashed]
end

Post.drafted # => where(status: :drafted)
Post.active  # => where(status: :active)
  • Trong Rails 6, negative scope được thêm vào các giá trị enum.
  • Ví dụ:
class Post < ActiveRecord::Base
  enum status: %i[drafted active trashed]
end

Post.not_drafted # => where.not(status: :drafted)
Post.not_active  # => where.not(status: :active)
- Ngoài ra, rails 6 còn thêm basic API dùng cho việc chuyển đổi kết nối để hỗ trợ nhiều cơ sở dữ liệu khác nhau

Action Mailbox

  • Action Mailbox được giới thiệu trong ver 6 của Rails, Action Mailbox cung cấp một bộ công cụ cho phép các ứng dụng tích hợp tốt hơn với các luồng email gửi đến (những điều cơ bản về cách setup nó được trình bày trong Action Mailbox Basics Rails Guide )
  • Các bạn có thể tìm hiểu thêm về nó tại đây set up Action Mailbox.

Action View

  • Rails 6 sẽ báo cáo về các phân bổ được thực hiện trong các khung nhìn, điều này sẽ cho phép ta nhìn thấy được việc dành bao nhiêu thời gian để phân bổ và thu gom các đối tượng trong bộ nhớ xử lý.
  Rendered posts/_form.html.erb (Duration: 7.1ms | Allocations: 6004)
  Rendered posts/new.html.erb within layouts/application 
  (Duration: 8.3ms | Allocations: 6654)
  Completed 200 OK in 858ms (Views: 848.4ms | ActiveRecord: 0.4ms
  | Allocations: 1539564)

Active Model

  • Cho phép tên thuộc tính có thể định cấu hình cho #has_secure_password.
  • Điều này vẫn mặc định cho thuộc tính có tên là 'password'. Trong ver mới này, có một phương thức mới mang tên #authenticate_XXX, trong đó XXX là tên thuộc tính được xác định để cấu hình, có thể coi #authenticate hiện tại thành bí danh khi thuộc tính là 'password' mặc định.
class User < ActiveRecord::Base
  has_secure_password :recovery_password, validations: false
end
user = User.new()
user.recovery_password = "42password"
user.recovery_password_digest # => "$2a$04$iOfhwahFymCs5weB3BNH/uX..."
user.authenticate_recovery_password('42password') # => user
Tài liệu tham khảo

Medium Ruby Inside