2012-06-05 27 views
5

Tôi đang sử dụng Ruby on Rails 3.2.2 và tôi muốn lấy/phạm vi các đối tượng liên kết bằng cách "xác định"/"lọc trên" một giá trị thuộc tính trên các đối tượng liên quan. Đó là, vào thời điểm này tôi đang sử dụng đoạn mã sau:Làm thế nào để DRY phạm vi phương pháp được sử dụng trong hai lớp khác nhau?

class Article < ActiveRecord::Base 
    def self.search_by_title(search) 
    where('articles.title LIKE ?', "%#{search}%") 
    end 
end 

class ArticleAssociation < ActiveRecord::Base 
    def self.search_by_article_title(search) 
    joins(:article).where('articles.title LIKE ?', "%#{search}%") 
    end 
end 

Trong đoạn mã trên mệnh đề where('articles.title LIKE ?', "%#{search}%") được lặp lại hai lần và vì vậy tôi nghĩ rằng nó có thể được cải thiện với các nguyên tắc DRY: là nó có thể sử dụng phương phápArticle.search_by_titletrực tiếp trong phương thứcArticleAssociation.search_by_article_title?


trường hợp sử dụng điển hình là:

  • ArticleAssociation.search_by_article_title("Sample string")
  • Article.search_by_title("Sample string")
+0

Tôi phải đối mặt với tình huống tương tự này trong một dự án (4 mô hình liên quan) và tôi đã tạo mô-đun để giữ các phương pháp tìm kiếm phổ biến. Ut không chính xác những gì bạn yêu cầu nhưng nó là một giải pháp điển hình. – tokland

+0

Mã được chia sẻ giữa các lớp thường kết thúc trong một mô-đun – apneadiving

+0

theo cách của chúng, số rây của squeel có thể được sử dụng cho điều này, nhưng sử dụng squeel thay vì AR là một thay đổi lớn .. https://github.com/ernie/squeel#sifters – tokland

Trả lời

2

Trừ khi bạn thay đổi cấu trúc mã hoàn toàn, không có.

Bạn có thể thực hiện một số hack với lambdas, nhưng đó sẽ là mã nhiều hơn sau đó mã bạn đang DRYing. Có một thứ như tái cấu trúc tốt, và một thứ như tái cấu trúc xấu. Trừ khi một đoạn mã rất phức tạp hoặc dài được sử dụng ở 2 địa điểm trở lên, bạn có thể lo lắng về việc tái cấu trúc. Quy ước về mã là quan trọng, nhưng đối với những thứ gọi là một phương thức nhỏ như vậy thì đó là một sự lãng phí và có thể làm cho mã của bạn trở nên khó hiểu hơn.

Mặc dù, tôi biết rằng đó là gây phiền nhiễu khi người ta không trả lời câu hỏi của bạn, vì vậy ở đây:

class Article < ActiveRecord::Base 
    SEARCH_BY_TITLE=lambda {|obj, search| obj.where('articles.title LIKE ?', "%#{search}%")} 
    def self.search_by_title(search) 
    SEARCH_BY_TITLE.call(self, search) 
    end 
end 

class ArticleAssociation < ActiveRecord::Base 
    def self.search_by_article_title(search) 
    Article::SEARCH_BY_TITLE.call(joins(:article),search) 
    end 
end 

Đó chỉ làm cho một lambda như một hằng số mà thực hiện cuộc gọi where vào một đối tượng cụ thể. Cả hai phương pháp chỉ quấn lambda đó.

Lưu ý: Mặc dù điều này có thể được coi là thanh lịch hơn, nó sẽ làm giảm hiệu suất rất nhiều, như lambdas, đóng cửa, và cuộc gọi thêm là đắt tiền trong một ngôn ngữ năng động như Ruby. Nhưng tôi không nghĩ đó là vấn đề cho bạn.

+0

@ Linux_iOS.rb.cpp.c.lisp.n - Cảm ơn câu trả lời của bạn. Tuy nhiên, tôi có * rất nhiều nơi mà tôi cần sử dụng mã * 'ở đâu ('articles.title LIKE?',"% # {Search}% ")' và, ngay cả khi bạn nói "cho một phương thức nhỏ những thứ như vậy là một sự lãng phí và có lẽ sẽ làm cho mã của bạn trở nên khó hiểu hơn ", * có xấu khi lặp lại mã đó ở mọi nơi trong lớp học của tôi không? * – Backo

+1

@Backo: Nếu bạn sử dụng mã rất nhiều, lambda có lẽ tốt hơn ý kiến. Nếu bạn sử dụng nó chỉ hai lần, như trong ví dụ, bạn đang lặp lại tốt hơn. – Linuxios

1

Theo yêu cầu của OP, tôi gửi mã mà tôi đã viết cho một tìm kiếm 3-mô-đun sử dụng một mô-đun:

module Listable 
    extend ActiveSupport::Concern 

    module ClassMethods 
    # Search a listable module search in properties (or related) tables 
    def search_from_properties(string) 
     return where({}) if string.blank? 
     associations = self.reflect_on_all_associations.map(&:name) & 
     [:property, :properties, :supplier, :suppliers, :address] 
     s = "%#{string}%" 
     associations.inject(self, :includes).where(
     ((Address[:base] =~ s) | (Address[:city] =~ s)) | 
     ((Property[:owner] =~ s) | (Property[:cif] =~ s)) | 
     ((Supplier[:cups] =~ s) | (Supplier[:contract] =~ s)) 
    ) 
    end 
    end 
end 

Bây giờ chỉ cần bao gồm module này trong các lớp liên quan:

class Property < ActiveRecord::Base 
    include Listable 
end 

Lưu ý: Tất cả các mô hình đều có các liên kết được xác định để tiếp cận các mô hình khác (đó là lý do tại sao công việc joins). Ngoài ra, nó sử dụng this wrapper over AR.

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