我的模型UserFile
belongs_to
一个Folder
:
class UserFile < ActiveRecord::Base
has_attached_file :attachment
belongs_to :folder
validates_attachment_presence :attachment
validates_presence_of :folder_id
def copy(target_folder)
new_file = self.clone
new_file.folder = target_folder
new_file.save!
end
end
以下测试意外失败:
test 'cannot copy a file to anything other than a folder' do
folder = Factory(:folder)
file1 = UserFile.create(:attachment => File.open("#{Rails.root}/test/fixtures/textfile.txt"), :folder => Folder.root)
file2 = UserFile.find(file1)
# Should pass, but fails
assert_raise(ActiveRecord::RecordInvalid) { file1.copy(nil) }
# Same record, but this DOES pass
assert_raise(ActiveRecord::RecordInvalid) { file2.copy(nil) }
assert file1.copy(folder)
end
使用新创建的对象时会忽略validates_presence_of :folder_id
,但当我执行ActiveRecord#find
时,它会起作用。我认为这与在clone
方法中调用copy
有关,但我无法弄明白。有谁知道发生了什么或如何让测试通过?
答案 0 :(得分:3)
米沙, 克隆是一种野兽。
记录了record.errors,并且也克隆了@errors实例变量。
file1.errors = new_file.errors
自create
file1
调用验证后,这将是非零的。
现在当您克隆file1并说出new_file.save!
时会发生什么?
内部valid?
深入调用new_file上的errors.clear但它仍然指向与file1相同的错误对象。
现在恶毒,存在验证器实现如下:
def validate(record)
record.errors.add_on_blank(attributes, options)
end
其中(显然)只能访问errors.base http://apidock.com/rails/ActiveModel/Errors/add_on_blank
所以,虽然验证确实在new_file上作为记录运行,但是存在验证从
开始new_file.errors.instance_eval { @base } == file1
并且file1.folder_id
不是空白。
现在,您的第二个测试通过,因为如果您从数据库中读取文件条目,file2.errors
为零,那么当您克隆它并在克隆上调用验证时,将使用正确的基础重新创建errors对象(由于行folder_id
而new_file.folder = target_folder
为空的克隆)。
只需添加
即可解决您的问题def copy(target_folder)
new_file = self.clone
new_file.instance_eval { @errors = nil } # forces new error object on clone
new_file.folder = target_folder
new_file.save!
end
希望这有帮助