Проверка игнорируется при клонировании вновь созданной записи - PullRequest
5 голосов
/ 25 мая 2011

У меня есть модель UserFile, которая belongs_to a 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 Ответ

3 голосов
/ 26 мая 2011

Mischa, клонирование это зверь.

record.errors запоминается, а переменная экземпляра @errors также клонируется.

file1.errors = new_file.errors

это будет не ноль, поскольку create называется проверкой на file1.

что теперь происходит, когда вы клонируете file1 и говорите new_file.save!? Глубоко внутри valid? вызывает error.clear для нового_файла, но он по-прежнему указывает на тот же объект ошибки, что и файл1. Теперь, злобно, валидатор присутствия реализован так:

def validate(record)
   record.errors.add_on_blank(attributes, options)
end

который (очевидно) может получить доступ только к ошибкам. http://apidock.com/rails/ActiveModel/Errors/add_on_blank

так, хотя проверки выполняются для new_file как записи, проверка присутствия проходит с

new_file.errors.instance_eval { @base } == file1

и для file1.folder_id НЕ пусто.

Теперь ваш второй тест пройден, потому что если вы читаете запись файла из базы данных, file2.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

надеюсь, это помогло

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...