5

我正在使用 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 直接在方法中使用该 ArticleAssociation.search_by_article_title 方法?


典型的用例是:

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

2 回答 2

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 这样的动态语言中是昂贵的。但我认为这对你来说不是问题。

于 2012-06-05T15:27:03.620 回答
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工作的原因)。此外,它在 AR 上使用此包装器

于 2012-06-05T15:53:09.230 回答