0

いくつかの結合と関連付けを含むスコープの作成とテストに問題があります。説明は簡潔に、しかしできるだけ徹底的にするように努めます。

私には次の関連があります。

ExpertTopic > Topic > Articles > Posts

および次のコード:

class Topic < ActiveRecord::Base
  has_many :articles, :order => "position", :dependent => :destroy
  has_many :posts, :through => :articles

  has_many :expert_topic, :dependent => :delete_all
  has_many :experts, :through => :expert_topic
end

と:

class ExpertTopic < ActiveRecord::Base
  belongs_to :topic, :inverse_of => :expert_topic
  belongs_to :expert, :inverse_of => :expert_topic

  scope :live, joins(:topic => {:articles => :post})
    .where("topics.article_count > ? AND posts.live = ?", 0, true)
end

live範囲でExpertTopic、すべてのライブ投稿を含むトピックに関連する専門家に(記事を通じて)絞り込むようにしています。

Railsコンソールには次のようなExpertTopic.live.to_sqlものがあります。

"SELECT `experts_topics`.* FROM `experts_topics` INNER JOIN 
`topics` ON `topics`.`id` = `experts_topics`.`topic_id` INNER JOIN
`articles` ON `articles`.`topic_id` = `topics`.`id` INNER JOIN
`posts` ON `posts`.`id` = `articles`.`post_id` WHERE
(topics.article_count > 0 AND posts.live = 1)"

私は次のコードでスコープをテストしていますexpert_topic_spec.rb

describe ExpertTopic do
  before do
    @post1 = FactoryGirl.create(:pending_post)
    @post2 = FactoryGirl.create(:live_post)
    @post3 = FactoryGirl.create(:pending_post)
    @post4 = FactoryGirl.create(:live_post)
    @non_live_topic = FactoryGirl.create(:topic_with_posts, :posts => [@post1, @post2, @post3])
    @live_topic = FactoryGirl.create(:topic_with_posts, :posts => [@post2, @post4])
    FactoryGirl.create(:expert_topic, topic_id: @non_live_topic.id)
    FactoryGirl.create(:expert_topic, topic_id: @live_topic.id)
  end

  it 'finds and returns only expert with live topic' do
    ExpertTopic.all.count.should == 2
    ExpertTopic.live.uniq.count.should == 1
  end
end

ロジックは、@non_live_topicライブではない投稿が少なくとも1つ含まれているため、ライブとは見なされないため、への呼び出しによって返されるべきではないということExpertTopic.liveです。ただし、の代わりにがExpertTopic.live.uniq.count返されるため、最後のアサーションは失敗します。21

スコープが間違って書かれているのか、それとも私のテストなのかはわかりません。デバッグに誰かの助けを借りていただければ幸いです。

ありがとう!

4

1 に答える 1

2

あなたが書いた:

ロジックは、@non_live_topic にはライブではない投稿が少なくとも 1 つ含まれているため、ライブとは見なされないということです。

これは正しくありません。スコープは、非ライブ投稿に関連付けられている をlive除外しません。1 つ以上のライブ投稿に関連付けられている がExpertTopic含まれているだけです。ExpertTopicこれは、ライブ投稿と非ライブ投稿の両方が関連付けられている場合、それが含まれることを意味します。

スコープを予想されるロジックに変更するには、除外句を使用する必要があります。次に例を示します。

scope :live, lambda {
    non_live_sql = joins(:topic => {:articles => :post})
      .where("topics.article_count > ? AND posts.live = ?", 0, false)
      .select('expert_topics.id').to_sql
    joins(:topic).where("topics.article_count > ? AND expert_topics.id NOT IN (#{non_live_sql})", 0)
}

SQL で項目を除外する方法は他にもありますが、Squeel などの DSL を使用したり、大規模なクエリを手動で記述したりせずに、Rails で構築するのはおそらくこれが最も簡単です。

于 2012-08-23T21:40:39.173 に答える