ActiveRecord :: Base before_validation with conditional not triggered

问题描述:

我使用before_validation来确保我的User模型的状态。ActiveRecord :: Base before_validation with conditional not triggered

before_validation :renter, if: 'resident? && active? && unit.present? && units.empty?' 

当我尝试使用新记录时,条件为真。

user.resident? && user.active? && user.unit.present? && user.units.empty? 
=> true 

而且回调在没有条件的情况下完美运作。

但是,使用具有条件的回调不起作用。

user = User.new(resident_attr_except_resident_type) 
user.save 
=> false 
user.errors.full_messages 
=> ["Resident type can't be blank"] 

user.renter 
=> #<User ..., resident_type: 0> # resident_type 0 because is a enum 
user.save 
=> true 

只是为了澄清,该renter方法执行以下操作:

def renter 
    self.resident_type = :renter 
    self 
end 

def renter! 
    renter.save 
end 

这有什么,我很想念?

我怀疑两件事1)有条件的和2)renter的回报和我误解before_validation如何工作。

,你可以尝试这个...

before_validation :renter, if: :check_renter? 

def check_renter? 
    resident? && active? && unit.present? && units.empty? 
end 
+0

上帝不!我之前在另一个项目中这样做,这是一个完整的噩梦。不要做?将只使用一次或两次的方法! – fbelanger

+0

@fbelanger:我同意Sourabh的看法,你应该给这个复杂的条件一个合适的名称,并将其转化为一种方法。像这样的条件很难阅读,并为其他开发人员了解,这是很难测试。你能否详细说明为什么你认为这是一个*完整的噩梦*? – spickermann

+0

我不同意。这样做会导致庞大的模型,但是出于所有错误的原因。这种方法永远不会被真正重用,并且如果你正在编写并行测试的话,这个方法很容易测试。在我看来,它更加清洁,使每次增量检查分开并在回调中使用组合。否则,我最终会用'building_blank?','unit_blank?','active_resident?','acitve_resident_with_unit?','active_resident_with_unit_and_without_units?'等等。对于其他一切,但是,我为此写了一些方法。 – fbelanger

IMO你的回调和你的renter方法很难阅读和理解。我认为将代码转移到自己的方法是值得的:

before_validation :determine_renter_type 

private 
def detemine_renter_type 
    if resident? && active? && unit.present? && units.empty? 
    self.renter_type = User.renter_types[:renter] 
    end 

    self # avoid aborting the save process by returning a truthy value 
end 
+0

我比其他提议的方法更好地解决了代码清理的问题。谢谢!虽然我会补充说这个回调会因为我的回答中的解释而失败。 – fbelanger

+0

你是对的,我更新了我的答案。我总是把它与自定义验证器混合在一起,它在返回虚假值时不会中断过程。 – spickermann