2015-10-04 10 views
6

Rails nội bộ chuyển đổi phạm vi thành các phương thức lớp, vậy tại sao chúng ta không thể sử dụng các phương thức lớp thay vì đi theo phạm vi.Ruby on Rails Phạm vi ActiveRecord vs các phương thức lớp

+1

Bài viết thú vị và thảo luận về chủ đề trong [bài đăng trên blog này] (http://www.justinweiss.com/articles/should-you-use-scopes-or-class-methods/) –

Trả lời

3

Từ fine guide:

14 Scopes
[...]
Để xác định một phạm vi đơn giản, chúng tôi sử dụng phương pháp scope bên trong lớp, đi qua các truy vấn mà chúng tôi muốn để chạy khi phạm vi này được gọi là:

class Article < ActiveRecord::Base 
    scope :published, -> { where(published: true) } 
end 

Điều này cũng giống như xác định phương pháp lớp học và bạn sử dụng phương pháp này là vấn đề của con người al ưu tiên:

class Article < ActiveRecord::Base 
    def self.published 
    where(published: true) 
    end 
end 

Lưu ý đặc biệt:

Đây là chính xác giống như xác định một phương pháp học, và mà bạn sử dụng là một vấn đề sở thích cá nhân

Và a little further (hướng dẫn Rails3 nói cùng một điều ở đây BTW):

14.1 Truyền tham số
[...]
Sử dụng phương thức lớp là cách ưu tiên để chấp nhận đối số cho phạm vi.

Vì vậy, bạn sử dụng nó là vấn đề ưu tiên và thậm chí bạn nên sử dụng phương pháp lớp cho phạm vi lấy đối số.

Sử dụng scope chủ yếu là vấn đề không hợp lý. Nếu bạn nói scope :whatever thì bạn đang nói rõ ràng rằng whatever là một trình tạo truy vấn; nếu bạn nói def self.whatever thì bạn không ngụ ý bất cứ điều gì về mục đích của phương pháp whatever, bạn chỉ định nghĩa một số phương thức lớp có thể hoặc có thể không hoạt động như một phạm vi.

Tất nhiên, 14.1 làm cho một mớ hỗn độn của sự phân biệt này bằng cách đề xuất bạn không sử dụng scope khi phạm vi của bạn có đối số. Cũng nên nhớ rằng trong Rails3 bạn có thể nói:

scope :published, where(published: true) 

vì vậy một phạm vi argumentless là trực quan "sạch" và ngắn gọn nhưng thêm một lambda để xử lý đối số sẽ làm cho nó trông Messier:

scope :pancakes, ->(x) { where(things: x) } 

Nhưng Rails4 muốn lambdas ngay cả đối với phạm vi tranh luận sự khác biệt thậm chí còn ít ý nghĩa hơn bây giờ.

Tôi nghi ngờ rằng sự khác biệt là lịch sử tại thời điểm này. Phạm vi có lẽ là một cái gì đó đặc biệt trở lại trong thời gian trước nhưng trở thành phương pháp cũ lớp đồng bằng trong thời đại Rails3 để cắt giảm trên sao chép và lưới tốt hơn với giao diện truy vấn mới đi kèm với Rails3.


Vì vậy, bạn có thể bỏ qua scope và chuyển thẳng đến các phương thức lớp học nếu muốn. Bạn thậm chí còn được khuyến khích làm như vậy khi phạm vi của bạn có lý lẽ.

-3

Tại sao tôi nên sử dụng phạm vi nếu đó chỉ là đường cú pháp cho phương pháp lớp? ". Đây là một số ví dụ thú vị để bạn suy nghĩ.

Phạm vi luôn có thể chuỗi => •••••••••••••••••••••••••••••••••••••••••••••••• ••••• Cho phép sử dụng kịch bản sau: người dùng sẽ có thể lọc bài đăng theo trạng thái, sắp xếp theo các cập nhật gần đây nhất. Đủ đơn giản, cho phép viết phạm vi cho điều đó:

class Post < ActiveRecord::Base 
     scope :by_status, -> status { where(status: status) } 
     scope :recent, -> { order("posts.updated_at DESC") } 
    end 
    And we can call them freely like this: 

    Post.by_status('published').recent 
    # SELECT "posts".* FROM "posts" WHERE "posts"."status" = 'published' 
    # ORDER BY posts.updated_at DESC 
    Or with a user provided param: 

    Post.by_status(params[:status]).recent 
    # SELECT "posts".* FROM "posts" WHERE "posts"."status" = 'published' 
     # ORDER BY posts.updated_at DESC 
    So far, so good. Now lets move them to class methods, just for the sake of comparing: 

    class Post < ActiveRecord::Base 
      def self.by_status(status) 
       where(status: status) 
      end 

     def self.recent 
      order("posts.updated_at DESC") 
      end 
    end 

Ngoài việc sử dụng thêm một vài dòng, không có cải tiến lớn. Nhưng bây giờ điều gì sẽ xảy ra nếu thông số trạng thái: là nil hoặc trống?

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 
Oooops, I don't think we wanted to allow these queries, did we? With scopes, we can easily fix that by adding a presence condition to our scope: 

      scope :by_status, -> status { where(status: status) if status.present? } 
    There we go: 

    Post.by_status(nil).recent 
      # SELECT "posts".* FROM "posts" ORDER BY   posts.updated_at DESC 

    Post.by_status('').recent 
         # SELECT "posts".* FROM "posts" ORDER BY posts.updated_at DESC 
    Awesome. Now lets try to do the same with our beloved class method: 

    class Post < ActiveRecord::Base 
    def self.by_status(status) 
     where(status: status) if status.present? 
     end 
    end 
    Running this: 

    Post.by_status('').recent 
         NoMethodError: undefined method `recent' for nil:NilClass 
And . The difference is that a scope will always return a relation, whereas our simple class method implementation will not. The class method should look like this instead: 

     def self.by_status(status) 
      if status.present? 
       where(status: status) 
      else 
        all 
      end 
     end 

Lưu ý rằng tôi trả về tất cả cho trường hợp nil/trống, trong đó Rails 4 trả về một mối quan hệ (trước đó nó trả về mảng các mục từ cơ sở dữ liệu). Trong Rails 3.2.x, bạn nên sử dụng scoped ở đó để thay thế. Và đó chúng tôi đi:

 Post.by_status('').recent 
     # SELECT "posts".* FROM "posts" ORDER BY  posts.updated_at DESC 

Vì vậy, lời khuyên ở đây là: không bao giờ quay trở lại con số không từ một phương pháp học mà nên làm việc như một phạm vi, nếu không bạn đang phá vỡ tình trạng chainability ngụ ý bởi phạm vi, mà luôn luôn trả về một mối quan hệ.

Phạm vi có thể mở rộng => •••••••••••••••••••••••••••••••••••••••• Cho phép phân trang như ví dụ tiếp theo của chúng tôi và tôi ' m sẽ sử dụng đá quý kaminari làm cơ sở. Điều quan trọng nhất bạn cần làm khi paginating một bộ sưu tập là để cho các trang web mà bạn muốn lấy:

Post.page(2) 
    After doing that you might want to say how many records per page you want: 

    Post.page(2).per(15) 
    And you may to know the total number of pages, or   whether you are in the first or last page: 

    posts = Post.page(2) 
    posts.total_pages # => 2 
    posts.first_page? # => false 
    posts.last_page? # => true 

bài này có ý nghĩa khi chúng ta gọi là những thứ theo thứ tự này, nhưng nó không thực hiện bất kỳ ý nghĩa để gọi những phương thức này trong một bộ sưu tập không được phân trang, phải không? Khi bạn viết phạm vi, bạn có thể thêm các tiện ích mở rộng cụ thể sẽ chỉ khả dụng trong đối tượng của bạn nếu phạm vi đó được gọi. Trong trường hợp của kaminari, nó chỉ thêm phạm vi trang vào các mô hình Active Record của bạn và dựa vào tính năng mở rộng phạm vi để thêm tất cả các chức năng khác khi trang được gọi. Về mặt khái niệm, mã sẽ trông giống như sau:

 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 

Phạm vi mở rộng là một kỹ thuật mạnh mẽ và linh hoạt trong chuỗi công cụ của chúng tôi. Nhưng tất nhiên, chúng tôi luôn luôn có thể đi hoang dã và nhận được tất cả những gì với các phương pháp lớp quá:

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 

Đó là tiết hơn một chút so với sử dụng một phạm vi, nhưng nó mang lại kết quả tương tự. Và lời khuyên ở đây là: chọn những gì làm việc tốt hơn cho bạn nhưng hãy chắc chắn rằng bạn biết những gì khuôn khổ cung cấp trước khi phát minh lại bánh xe.

+0

Tính đạo văn rõ ràng của http: //blog.plataformatec.com.br/2013/02/active-record-scopes-vs-class-methods/ – aceofbassgreg

Các vấn đề liên quan