2010-09-26 1 views
2

В тестовом коде Rails (3.0) я клонировал объект, чтобы я мог скрыть его для проверки проверки без изменения оригинала. Если я вызвал assert (original.valid?) перед клонированием, то клон передает проверку validates_presence_of даже после того, как я установил значение member_id равным nil.Rails3: клонирование уже проверенного объекта предотвращает недействительность клонирования - это странно или нормально?

Следующие два примера иллюстрируют это. В тестовой версии клон создается до. Оригинал («контакт») проверяется. Клонировать правильно не удалось проверить, когда отсутствует член_и_ид. Утверждение C преуспевает.

В испытании два, клон создан после оригинал валидирован. Несмотря на то, что clone.member_id установлен на ноль, он передает валидацию. Другими словами, утверждение 2C терпит неудачу. только разница между тестами является порядок двух линий:

cloned = contact.clone 
    assert(contact.valid?,"A") 

Что здесь происходит? Является ли это обычным рубиновым поведением re: клонирование, которое я просто не понимаю?

test "clone problem 1" do 
    contact = Contact.new(:member_id => 1) 
    cloned = contact.clone 
    assert(contact.valid?,"A") 
    cloned.member_id = nil 
    assert(!cloned.valid?,"C") 
end 

test "clone problem 2" do 
    contact = Contact.new(:member_id => 1) 
    assert(contact.valid?,"2A") 
    cloned = contact.clone 
    cloned.member_id = nil 
    assert(!cloned.valid?,"2C") 
end 
+0

Я забыл упомянуть, что в тесте 2 я проверил, чтобы cloned.member_id был нулем после назначения, используя assert (! Cloned.member_id). Это _is_ ноль, но все еще проходит проверку. –

ответ

3

Вы будете удивлены - он не может работать!

ОК, причина может быть найдена в коде Rails. Первая проверка будет запускать код:

# Validations module 

# Returns the Errors object that holds all information about 
# attribute error messages. 
def errors 
    @errors ||= Errors.new(self) 
end 

Как это первый запуск, он создаст новый экземпляр класса Errors. Простой, не так ли? Но есть gotcha - параметр сам. В вашем случае это объект «контакт».

Позже, когда вы снова назовете это на клонированном объекте, экземпляр @errors не будет создан снова - поскольку он не является нулевым. И вот оно! Вместо того, чтобы передавать «клонированное» я, используется более старое я.

Позже в коде проверки существует класс Errors, который считывает значение из @base, которое является self из инициализации. Видишь? Значения для теста считываются из оригинальной модели, а не из клона! Таким образом, проверка на «клонированный» объект выполняется по значениям из оригинала.

Хорошо, до сих пор для «почему бы нет» и теперь несколько слов о «как».

Решение простое - просто установите @errors на нуль после клонирования и до проверки. Поскольку это довольно личное, простое назначение не работает. Но это работает:

cloned.instance_eval do 
    @errors = nil 
end 

И некоторые наконечник для интересного чтения: http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/

Это довольно всеобъемлющее объяснение, как валидация в Rails 3 работ.

+0

Спасибо, мне придется подумать об этом, но, несомненно, я узнаю что-то в этом процессе. Я слишком привык к простым процедурным языкам, и иногда тонкости объектов полностью меня пропускают. –

 Смежные вопросы

  • Нет связанных вопросов^_^