find_или_create_by_name_and_location создание дубликатов активных записей в Rails

#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 🙂