#ruby-on-rails #activerecord
#ruby-on-rails #activerecord
Вопрос:
Я новичок в rails (и кодировании в целом) и пишу небольшой проект, используя RSpec для тестирования. В настоящее время я застрял на модельном тестировании, которое должно быть довольно простым, и я рву на себе волосы.
У меня есть файл журнала для анализа на наличие ошибок. Каждая ошибка имеет имя (например. NoMethodError
) и местоположение (например. "app/controllers/public/profiles_controller.rb:46:in 'index'
) . Я успешно проанализировал их из журнала и теперь пытаюсь записать их в свою Errors
базу данных, используя ActiveRecord.
Я хочу, чтобы каждая комбинация ошибок / местоположений записывалась только один раз. В настоящее время я использую этот блок в своей модели для этого:
#app/models/error.rb
Error.find_or_create_by_name_and_location(@name, @location) do |error|
error.first_seen = (Time.now - 1.day)
error.last_seen = Time.now # These time objects are just filler for now
end
Насколько я могу понять, это должно создать новую запись в моей Errors
базе данных, только если ранее не существовало записи с тем же именем и местоположением. Однако это не так.
Я создал образец журнала для запуска моих тестов, в котором есть две идентичные и одна уникальная пары ошибка / местоположение. Я хочу, чтобы они были записаны как 2 записи Errors
. Однако, когда я запускаю свой тест, дубликаты не отфильтровываются, поэтому я получаю 3 записи.
Я написал тест для проверки метода find_ или_create_by_name_and_location, и он работает нормально:
#RSpec
#spec/models/error_spec.rb
Error.find_or_create_by_name_and_location( "foo", "bar")
Error.find_or_create_by_name_and_location( "foo", "bar")
Error.should have(1).record
#Test passes
Я перепробовал все, что мог придумать, но у меня ничего не получается быстро, поэтому любая помощь будет очень признательна!
Ответ №1:
Вы должны добавить проверку в свою модель для обеспечения уникальности, независимо от find_or_create_by
того, работает ли метод так, как вы ожидаете.
class Error << ActiveRecord::Base
validates_uniqueness_of :name, :scope => :location
end
Таким образом, может быть только одна запись с одинаковым именем и местоположением.
Комментарии:
1. Отлично, это действительно полезно, спасибо — я должен быть в состоянии использовать это как обходной путь. Все же хотелось бы знать, почему
find_or_create_by
работает не так, как я ожидаю… даже мой босс не смог с этим справиться, поэтому я не думаю, что я просто новичок!2. Хорошо, теперь я в полном тупике — это все еще не работает. Даже с моделью, обеспечивающей уникальность, как указано выше, я получаю две повторяющиеся записи. Код find_or_create_by находится в
if
цикле, и я никак не могу представить, что условия гонки вступают в игру (т.е.. код сначала проверяет уникальность обеих ошибок, а затем записывает). Какие-нибудь идеи вообще?3. Некоторое время назад я столкнулся с аналогичной проблемой. Решение состояло в том, чтобы записать его в виде двух запросов
unless instance = Model.find(); instance = Model.create(); end
. Не так красиво, но работает.4. Еще раз спасибо, еще одно хорошее предложение … все еще не могу заставить его работать! Я настроил цикл unless, как вы предложили, но он все равно дает мне дубликаты :
unless instance =Error.find_by_error_name_and_location(@name, @location); instance = Error.create(:error_name => @name, :location => @location ); end
. Абсолютно не представляю, что я сделал не так, самое неприятное! Вместо этого я попробую использовать уникальный индекс, что повлияет на производительность, но, надеюсь, по крайней мере, будет функционировать. Большое спасибо за вашу помощь! Не могу сделать пометку, пока не получу больше репутации, но я вернусь и сделаю это позже, если я когда-нибудь дойду до 15 🙂