2012-08-23 65 views
0

我在編寫和測試涉及幾個連接和關聯的範圍時遇到了問題。我會盡量保持我的解釋簡潔,但儘可能徹底。如何在Rails中使用關聯設置此範圍?

我有以下的關聯:

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範圍,我正在努力縮小與專題相關的專家的所有實時文章(通過文章)。

在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包含至少一個帖子不是直播,它不被視爲直播,因此不應通過致電ExpertTopic.live返回。但是,最後的斷言失敗,因爲ExpertTopic.live.uniq.count返回2而不是1

我不知道我的範圍寫錯了,還是我的測試,我真的很感謝有人幫助調試!

謝謝!

回答

2

您寫道:

的邏輯是,既然@non_live_topic包含不是活至少一種後不被認爲是活

這是不正確的。 live範圍不排除與非實時帖子關聯的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構建,也可以手動編寫大型查詢。

+1

這正是我所需要的。我認爲我的邏輯可能存在缺陷,但我需要比我更聰明的人來指出它是什麼。非常感謝!如果我能提供這樣一個有用的答案,我會給你兩張票。 – pthesis