克隆新创建的记录时忽略验证

时间:2011-05-25 15:01:42

标签: ruby-on-rails ruby-on-rails-3 unit-testing

我的模型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有关,但我无法弄明白。有谁知道发生了什么或如何让测试通过?

1 个答案:

答案 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_idnew_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

希望这有帮助