2011-09-01 19 views
7

我試圖做到這一點默默跳過添加before_add關聯回調,而不是引發異常?

has_many :roles, :before_add => :enforce_unique 

def enforce_unique(assoc) 
    false if exists? assoc 
end 

從文檔:「如果一個before_add回調拋出一個異常,對象不會被添加到集合」。在使用上述虛假不阻止補充,所以我不得不這樣做:

def enforce_unique(assoc) 
    raise if exists? assoc 
end 

這樣一來,這是真的,它不會增加,但同時也提出了一個必須處理的異常。這裏對我不太有用。我更喜歡這種行爲更像普通的AR回調before_save,其中返回FALSE也阻止保存(或添加),但不會引發異常。

在上面的例子中,我寧願這樣做只是不會默默地添加關聯。有沒有辦法做到這一點?我錯過了什麼?或者在這裏提出一個例外唯一的選擇?

回答

1

如果該關聯不是多態的,你可以這樣做:

validates_uniqueness_of :name_of_model 

角色裏面,其中name_of_model我們你與

1

這個問題是有點老了關聯,但我碰到最近同樣的問題。這裏是我如何解決它:

def enforce_unique |obj, x| 
    v = obj.roles 
    if i = v.index(x) 
    v.slice! i 
    end 
end 
2

的方式來解決這個問題,我認爲是用throwcatch,這在紅寶石都是爲了流量控制。提出例外並不適合,因爲這不是一個特殊的情況。

我落得這樣做:

catch(:duplicate) do 
    association.create({}) 
end 

然後在before_add回調,我所做的:

if(Class.where({}).first) 
    throw :duplicate 
end 

更多關於擲/趕上這裏:

http://rubylearning.com/blog/2011/07/12/throw-catch-raise-rescue-im-so-confused/

0

由於這個問題是關於保存而不是阻止它被包含列表中的d暫時(例如,通過控制器沒有興趣控制其型號),你可以嘗試重寫保存在相關模型,如果角色存在只是沒有保存:

class Role < ActiveRecord::Base 
    belongs_to :user, inverse_of: :roles 

    def save 
    super unless self.new_record? && user.has_existing_role?(self) 
    end 
end 

旁註:我不當與Active Record模式一起使用時,不要購買skinny控制器參數,因爲業務邏輯必須放在某處。對於像Active Record這樣的業務領域不良模式(不是專指Ruby AR gem),它實際上需要在上面存在一層(即在控制器層中),您可以使用服務對象或裝飾器模式作爲實現此目的的一種手段。

另一種方法是覆蓋關聯的更新方法,如關聯<<,並在與現有角色匹配時自動放棄角色。重寫關聯方法的詳細信息在ActiveRecord Association Class Methods Documentation