【问题标题】:How to DRY scope methods used in two different classes?如何干燥两个不同类中使用的范围方法?
【发布时间】:2012-06-05 15:12:53
【问题描述】:

我正在使用 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 个相关模型)中遇到了同样的情况,我创建了一个模块来保存常见的搜索方法。 Ut 并不完全符合您的要求,但它是一个典型的解决方案。
  • 类之间共享的代码通常在一个模块中结束
  • 顺便说一句,squeel 的 sifters 可以用来做这个,但是使用 squeel 代替 AR 是一个很大的变化。github.com/ernie/squeel#sifters
  • @tokland - 你能提供一个在模块中实现的示例吗?
  • @apneadiving:并非总是如此。在这种情况下,我们有两个(似乎是)相关的类,只是碰巧没有继承关系。当它在某些类之间共享并且对于这些类特定时,我会将它放在类中。特定的数据库查询字符串是否值得拥有自己的模块?

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


【解决方案1】:

除非您完全更改代码结构,否则不会。

您可以使用 lambda 进行一些修改,但那将是比您正在干燥的代码更多的代码。有好的重构之类的东西,也有坏的重构之类的东西。除非在 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 这样的动态语言中是昂贵的。但我认为这对你来说不是问题。

【讨论】:

  • @Linux_iOS.rb.cpp.c.lisp.n - 感谢您的回答。但是,我有很多地方需要使用代码 where('articles.title LIKE ?', "%#{search}%"),即使你说“对于像这样的微小的单一方法调用的事情,它也是一种浪费,可能会让你代码更神秘”,在我的课程中到处重复该代码是不是很糟糕?
  • @Backo:如果您经常使用代码,那么 lambda 可能是一个更好的主意。如果您只使用两次,如示例所示,则最好重复使用。
【解决方案2】:

根据 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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-06
    • 1970-01-01
    • 1970-01-01
    • 2018-04-05
    相关资源
    最近更新 更多