Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Scope so với phương thức lớp trong Rails: Cách tiếp cận nào thắng?

Bài viết này cũng có sẵn bằng tiếng Hàn, nhờ Soonsang Hong!

Scope là một cách tuyệt vời để lấy đúng đối tượng từ cơ sở dữ liệu:

app/models/review.rb
class Review < ActiveRecord::Base scope :most_recent, -> (limit) { order("created_at desc").limit(limit) } end

Bạn sẽ dùng scope như thế này:

app/models/homepage_controller.rb
@recent_reviews = Review.most_recent(5)

Gọi scope đó, tuy nhiên, trông chính xác như gọi một phương thức lớp trên Review. Và dễ dàng xây dựng nó như một phương thức lớp, thay vào đó:

app/models/review.rb
def self.most_recent(limit) order("created_at desc").limit(limit) end
app/controllers/homepage_controller.rb
@recent_reviews = Review.most_recent(5)

Vậy tại sao bạn lại dùng scope khi bạn có thể dùng phương thức lớp Ruby thông thường? Có đáng để giữ hai khái niệm hoàn toàn riêng biệt, nhưng tương đương, trong đầu bạn không? Nếu bạn gặp bug kỳ lạ thì sao? Những thứ phụ thêm này có phải là thứ làm Rails khó học hơn không?

Khi sẽ có ý nghĩa để dùng scope thay vì phương thức lớp?

Tại sao dùng scope khi đã có phương thức lớp?

Điều gì xảy ra nếu bạn muốn lấy tất cả review được viết sau một ngày cụ thể? Nhưng nếu không có ngày được chỉ định, bạn muốn tất cả review được trả về?

Là một scope, trông như thế này:

app/models/review.rb
scope :created_since, ->(time) { where("reviews.created_at > ?", time) if time.present? }

Đủ dễ dàng, phải không? Còn phương thức lớp?

app/models/review.rb
def self.created_since(time) if time.present? where("reviews.created_at > ?", time) else all end end

Nó tốn một chút công sức thêm. Scope thích trả về scope, vì vậy chúng dễ dàng chuỗi lại với nhau:

Review.positive.created_since(5.days.ago)

Nhưng để phương thức lớp hoạt động giống nhau, bạn phải cụ thể xử lý trường hợp time là nil. Nếu không, người gọi sẽ phải tự tìm ra liệu nó có một scope hợp lệ, có thể chuỗi được hay không.

Phương thức luôn trả về cùng loại đối tượng rất hữu ích. Bạn không phải lo lắng nhiều về trường hợp biên hay lỗi. Bạn có thể giả định bạn sẽ luôn nhận lại một đối tượng bạn có thể dùng.

Ở đây, nó nghĩa là bạn có thể chuỗi scope lại với nhau, mà không phải lo về giá trị nil trả về.

Vẫn còn cách bạn có thể phá vỡ giả định bạn luôn nhận lại scope:

app/models/review.rb
scope :broken, -> { "Hello!!!" }
irb(main):001:0> Review.broken.most_recent(5) NoMethodError: undefined method `most_recent' for "Hello!!!":String

Nhưng tôi chưa bao giờ gặp điều đó trong mã thực tế.

Điều tôi yêu thích nhất về scope là chúng biểu đạt ý định. Bạn đang nói cho người tiếp theo đọc mã của bạn, "Phương thức này có thể chuỗi, cuối cùng sẽ biến thành danh sách đối tượng, và sẽ giúp bạn chọn đúng tập hợp đối tượng." Đó là nhiều hơn hẳn so với một phương thức lớp chung chung.

Khi nào nên dùng phương thức lớp thay vì scope?

Bởi vì scope biểu đạt ý định, tôi dùng chúng bất cứ khi nào tôi đang chuỗi các scope built-in đơn giản (như where và limit) thành scope lớn hơn. Tìm đúng đám đối tượng là những gì scope được thiết kế cho.

Có hai ngoại lệ:

  1. Khi tôi cần preload scope, tôi biến chúng thành association thay vào đó.
  2. Khi tôi làm nhiều hơn chuỗi built-in scope thành scope lớn hơn, tôi dùng phương thức lớp.

Khi logic scope của bạn phức tạp, phương thức lớp cảm thấy như nơi đúng để đặt nó.

Bên trong phương thức lớp, bạn có thể dễ dàng trộn mã Ruby với mã cơ sở dữ liệu. Nếu bạn có mã sắp xếp dễ viết hơn trong Ruby, bạn có thể lấy đối tượng theo thứ tự mặc định, và dùng sort_by để đặt chúng đúng thứ tự.

Hoặc, nếu bạn cảm thấy đặc biệt khéo léo, một phương thức lớp có thể lấy dữ liệu từ vài nơi khác nhau: cơ sở dữ liệu của bạn, Redis, hoặc một API hoặc dịch vụ bên ngoài. Sau đó, nó có thể lắp ráp tất cả vào một tập hợp đối tượng cảm giác như một scope đã được biến thành mảng.

Ngay cả lúc đó, vẫn tốt để đặt mã chọn lọc, sắp xếp, join, lọc của bạn bên trong scope. Sau đó, dùng scope của bạn bên trong phương thức lớp. Bạn sẽ kết thúc với một phương thức lớp rõ ràng hơn, và scope bạn có thể dùng khắp ứng dụng.

Scope là một trong những tính năng Rails yêu thích của tôi. Bạn có thể làm những thứ mạnh mẽ – đọc bài viết của tôi về sắp xếp và lọc model Rails để xem một ví dụ scope đặc biệt hữu ích.

Và có một cách thực sự đơn giản để thành thạo việc dùng scope: chơi với chúng bên trong những ứng dụng nhỏ, tập trung. Chương mẫu miễn phí của Practicing Rails sẽ chỉ cho bạn cách. Hãy xem!