如何在两个不同的类中使用DRY范围方法?

时间:2012-06-05 15:12:53

标签: ruby-on-rails ruby ruby-on-rails-3 refactoring dry

我正在使用Ruby on Rails 3.2.2,我希望通过“指定”/“过滤”这些关联对象上的属性值来检索/范围关联对象。也就是说,此时我正在使用以下代码:

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

在上面的代码中,where('articles.title LIKE ?', "%#{search}%")子句重复两次,因此我认为可以使用DRY原则对其进行改进:是否可以使用 Article.search_by_title < em>方法直接在 ArticleAssociation.search_by_article_title 方法?


典型用例是:

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

2 个答案:

答案 0 :(得分:2)

除非你完全改变代码结构,否则不行。

你可以用lambdas做一些黑客攻击,但那会比你正在干的代码更多的代码。有一个好的重构,以及糟糕的重构。除非在2个或更多位置使用非常复杂或长代码,否则您可以担心重构。代码约定很重要,但是对于很小的单方法调用这样的东西是浪费,可能会使你的代码更加神秘。

虽然,我知道当人们不回答你的问题时很烦人,所以在这里:

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

这只是将lambda作为一个常量,对指定对象执行where调用。两种方法都只包装lambda。

注意:虽然这可能被认为更优雅,但它会大大降低性能,因为lambda,闭包和额外的调用在像Ruby这样的动态语言中很昂贵。但我不认为这对你来说是一个问题。

答案 1 :(得分:1)

根据OP请求,我使用模块发布了我为3模块搜索编写的代码:

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

现在只需将此模块包含在相关类中:

class Property < ActiveRecord::Base
  include Listable
end

注意:所有模型都定义了与其他模型相关联的关联(这就是joins工作的原因)。此外,它使用this wrapper over AR