**Nguồn:** http://blog.plataformatec.com.br/2013/02/active-record-scopes-vs-class-methods/ ### 1. Bản chất của scope và class method Bản chất thì scope cũng chính là class method, trong Rails thì scope được định nghĩa như là 1 class method động. ```Ruby def self.scope(name, body) singleton_class.send(:define_method, name, &body) end ``` Ví dụ với scope : ```Ruby scope :published, -> {where(status: "published")} ``` scope này sẽ được triển khai thành : ```Ruby def self.published where(status: "published") end ``` Hầu hết mọi người đặt câu hỏi rằng: "Tại sao lại dùng scope nếu nó chỉ là cách viết khác của class method?". Ở đây chúng ta có 2 ví dụ rất thú vị để bạn suy nghĩ về nó. ### 2. Scope luôn đảm bảo sẽ thực hiện method chain Ví dụ chúng ta có 2 scope như sau: ```Ruby class Post < ActiveRecord::Base scope :by_status, -> status {where(status: status)} scope :recent, -> {order("posts.updated_at DESC")} end ``` Ở ví dụ trên, dường như viết dưới dạng class method cũng vẫn đảm bảo khả năng method chain. Nhưng trong trường hợp tham số `status` là `nil` hoặc `blank` thì sẽ thế nào ? ```Ruby Post.by_status(nil).recent # SELECT "posts".* FROM "posts" WHERE "posts"."status" IS NULL # ORDER BY posts.updated_at DESC Post.by_status('').recent # SELECT "posts".* FROM "posts" WHERE "posts"."status" = '' # ORDER BY posts.updated_at DESC ``` Thông thường, nếu ta không muốn phát sinh ra những query như thế thì ta sẽ sửa thành : ```Ruby scope :by_status, -> status {where(status: status) if status.present?} ``` Với cách viết như trên, các scope chắc chắn sẽ thực hiện chain mà không gặp phải vấn đề gì. Vậy với class method thì sao ? Code lúc ấy sẽ như sau : ```Ruby class Post < ActiveRecord::Base def self.by_status(status) where(status: status) if status.present? end end Post.by_status('').recent NoMethodError: undefined method `recent' for nil:NilClass ``` Ngược lại với scope với những query là `nil`, scope sẽ trả về `.all`, vì thế mà quá trình chain vẫn diễn ra bình thường. ### 3. Scope có thể mở rộng được Trong thư viện pagination, cách viết như sau thường được sử dụng : ```Ruby Post.page(2).per(15) posts = Post.page(2) posts.total_pages # => 2 posts.first_page? # => false posts.last_page? # => true ``` Các method như `total_pages`, `first_page?`, `last_pages?`, … ta chỉ muốn gọi lên sau khi đã gọi `.page`. Khi đó ta sẽ viết dưới dạng scope extensions. ```Ruby scope :page, -> num { # some limit + offset logic here for pagination } do def per(num) # more logic here end def total_pages # some more here end def first_page? # and a bit more end def last_page? # and so on end end ``` Nhưng nếu viết dưới dạng class method sẽ như sau : ```Ruby def self.page(num) scope = # some limit + offset logic here for pagination scope.extend PaginationExtensions scope end module PaginationExtensions def per(num) # more logic here end def total_pages # some more here end def first_page? # and a bit more end def last_page? # and so on end end ``` ### 4. Kết luận Như chúng ta thấy rõ ràng viết bằng scope có nhiều ưu điểm hơn cách viết bằng class method. Tuy nhiên chúng ta không nên quá lạm dụng nó. Quan trọng nhất là sử dụng với đúng mục đích và yêu cầu của công việc.