5

I am using Ruby on Rails 3.2.2 and I would like to retrieve / scope associated objects by "specifying" / "filtering on" an attribute value on those associated objects. That is, at this time I am using the following code:

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

In the above code the where('articles.title LIKE ?', "%#{search}%") clause is repeated twice and so I thought that it may be improved with the DRY principle: is it possible to use the Article.search_by_title method directly in the ArticleAssociation.search_by_article_title method?


Typical use cases are:

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

2 に答える 2

2

コード構造を完全に変更しない限り、いいえ。

ラムダを使ってハッキングすることもできますが、それは DRY しているコードよりも多くのコードになります。良いリファクタリングと悪いリファクタリングがあります。非常に複雑または長いコードが 2 つ以上の場所で使用されていない限り、リファクタリングについて心配することができます。コード規則は重要ですが、そのような小さな 1 つのメソッド呼び出しの場合は無駄であり、おそらくコードをより不可解なものにするでしょう。

とはいえ、人々があなたの質問に答えないのは迷惑だとわかっているので、ここに:

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

whereこれは、指定されたオブジェクトに対して呼び出しを実行する定数としてラムダを作成するだけです。どちらのメソッドもそのラムダをラップするだけです。

注: これはより洗練されていると考えられるかもしれませんが、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 に答える