Active Record Association CollectionProxy не работает: почему @owner.@ Target.count дает результат 1, когда @owner.@ Target возвращает пустой массив?

У меня есть модели в моем приложении Rails:

Sales_Opportunity, которая имеет множество мужчин.

Я настраиваю их с помощью FactoryGirl и запускаю тест, чтобы показать, что при удалении моей Sales_Opportunity я также вызываю удаление связанных Swots. По какой-то причине при отладке с помощью Byebug я получаю странные результаты - записи Sales_Opportunity и Swot создаются правильно, но когда я запускаю sales_opportunity.swots, он возвращает [ ], тогда как sales_opportunity.swots.count возвращает 1. Более странным является точное тот же код прекрасно работает с другой ассоциацией объекта (timeline_events точно так же, как swots, но отлично работает с тем же кодом).

Может кто-нибудь сказать мне, что я делаю не так, пожалуйста?

Sales_Opportunity.rb:

class SalesOpportunity < ActiveRecord::Base
 default_scope { order('close_date ASC') }
 belongs_to :user
 belongs_to :company
 has_many :key_contacts
 has_many :timeline_events, dependent: :destroy
 has_many :swots, dependent: :destroy
end

Swot.rb:

class Swot < ActiveRecord::Base
 belongs_to :sales_opportunity
 validates :swot_details, presence: true
 validates :sales_opportunity_id, presence: true
 enum swot_type: [:strength, :weakness, :opportunity, :threat]
 enum swot_importance: { minimal: 1, marginal: 2, noteworthy: 3, significant: 4, critical: 5 }
 validates :swot_importance, presence: true
end

Swot FactoryGirl spec:

FactoryGirl.define do
factory :swot do
    swot_importance             "minimal"
    swot_details                "Some boring details"
    swot_type                   "threat"

    trait :attached do
        association             :sales_opportunity, :with_user_id
    end
 end
end

Sales_Opportunity FactoryGirl spec:

FactoryGirl.define do
sequence(:opportunity_name) { |n| "Sales Oppotunity - #{n}" }

factory :sales_opportunity do
    user
    opportunity_name {generate(:opportunity_name)}
    close_date                  "2014/12/12"
    sale_value                  10000
    company_id                  7

    trait :with_user_id do
        user_id                     6
    end
end
end

Неудачные тесты Rspec:

describe "when swot's parent sales opportunity is destroyed" do
    let(:swot) { FactoryGirl.create(:swot, :attached) }
    let(:sales_opportunity) { swot.sales_opportunity }

it "should destroy associated swots" do
    dswots = sales_opportunity.swots.to_a
    byebug
    sales_opportunity.destroy
    expect(dswots).not_to be_empty
    dswots.each do |dswot|
    expect(Swot.where(id: dswot.id)).to be_empty
  end
end
  end

Вывод из консоли (byebug) при входе в swot:

#<Swot id: 13, swot_type: 3, swot_importance: 1, sales_opportunity_id: 564, swot_details: "Some boring details", created_at: "2015-07-27 10:57:23", updated_at: "2015-07-27 10:57:23">

Вывод из консоли при ведении журнала sales_opportunity:

#<SalesOpportunity id: 564, close_date: "2014-12-12 00:00:00", user_id: 6, created_at: "2015-07-27 10:57:23", updated_at: "2015-07-27 10:57:23", pipeline_status: 0, opportunity_name: "Sales Oppotunity - 4", company_id: 7, sale_value: #<BigDecimal:7fe9ffd25078,'0.1E5',9(27)>, swot_score: 0>

Вывод для sales_opportunity.swots.count:

(byebug) sales_opportunity.swots.count
 1

Выходные данные для sales_opportunity.swots:

(byebug) sales_opportunity.swots
#<ActiveRecord::Associations::CollectionProxy []>

Я думаю, что я включил всю известную информацию. Тесты Rspec, фабрики FactoryGirl и настройки между sales_opportunities и Swots/Timeline_Events абсолютно одинаковы - но тесты Rspec проходят для Timeline_Events, и collection_proxy работает для них (насколько я могу судить, код идентичен):

Timeline_Event Factory:

FactoryGirl.define do
factory :timeline_event do
    activity                    "Some activity"
    due_date                    "2014/11/11"

    trait :attached do
        association             :sales_opportunity, :with_user_id
    end
end
end

Рабочие тесты Rspec:

describe "when sales opportunity is destroyed for timeline event" do
    let(:timeline_event) { FactoryGirl.create(:timeline_event, :attached) }
    let(:sales_opportunity) { timeline_event.sales_opportunity }

it "should destroy associated timeline events" do
    timeline_events = sales_opportunity.timeline_events.to_a
    sales_opportunity.destroy
    expect(timeline_events).not_to be_empty
    timeline_events.each do |event|
    expect(TimelineEvent.where(id: event.id)).to be_empty
  end
end
end

Timeline_Event.rb:

class TimelineEvent < ActiveRecord::Base
 belongs_to :sales_opportunity
 validates :activity, presence: true 
 validates :due_date, presence: true
 validates :sales_opportunity_id, presence: true
end

При запуске byebug в том же месте, я получаю массив, включая Timeline_Event.

Может ли кто-нибудь помочь мне понять, что происходит в моем коде?

Благодарю.

2 ответа

Решение

Я решил проблему с этим - кажется, что sales_opportunity необходимо перезагрузить, чтобы активные ассоциации записей были сохранены. Этот ответ является ключом к решению.

Вот рабочий код:

    describe "when swot's parent sales opportunity is destroyed" do
     let!(:swot) { FactoryGirl.create(:swot, sales_opportunity: sales_opportunity) }

it "should destroy associated swots" do
    sales_opportunity.reload
    expect { sales_opportunity.destroy }.to change(sales_opportunity.swots, :count).by(-1)
 end
end

Использование некоторых элементов ответа Макса, приведенного выше, также помогло мне улучшить внешний вид кода.

RSpec.describe SalesOpportunity, type: :model do

  let(:sales_opportunity) { create(:sales_opportunity) }

  describe "swots" do
    let!(:swot) { create(:swot, sales_opportunity: sales_opportunity) }

    it "destroys nested swots" do
      sales_opportunity.destroy
      swot.reload
      expect(swot.destroyed?).to be_truthy
    end
  end
end

Обратите внимание, что я добавляю это в спецификацию SalesOpportunity, потому что зависимое поведение уничтожения принадлежит SalesOpportunity, а не дочерней ассоциации.

Редактировать.

Другой способ написать эту спецификацию:

it "destroys nested swots" do
  expect {
    sales_opportunity.destroy
  }.to change(Swot, :count).by(-1)
end
Другие вопросы по тегам