带工厂机器人的委托类型,相互依赖的外键问题
Delegated type with factory bot, mutually dependent foreign key issue
我想弄清楚如何以正确的顺序创建 Factory 对象,以便委托 class 可以访问它的父模型。这是我的模型:
class Alert < ApplicationRecord
delegated_type :alertable, types: %w[QuotaAlert]
delegate :trigger_percent, :trigger_percent=, :quota, :quota_public_id, to: :quota_alert
accepts_nested_attributes_for :alertable, update_only: true
module Alertable
extend ActiveSupport::Concern
included do
has_one :alert, as: :alertable, touch: true
delegate :company, :public_id, :channel, to: :alert
class QuotaAlert < ApplicationRecord
validates_presence_of :trigger_percent
validates_inclusion_of :trigger_percent, in: 0..1
belongs_to :quota
include Alertable
end
还有我的工厂:
FactoryBot.define do
factory :alert do
company
channel { 'email' }
trait :quota_alert_always_trigger do
before(:create) do |alert|
alert.alertable = create :quota_alert, trigger_percent: 0
end
end
然而,alert.company公司的代表团却在抱怨,因为它说quota_alert.alert是零。我想我需要更改工厂创建对象的顺序,但我不太清楚正确的顺序。
QuotaAlert
需要已经创建 alert
才能访问 alert.company
,但是,如果没有 QuotaAlert
.[=22,则无法创建 alert
=]
实际错误为:Module::DelegationError: QuotaAlert#company delegated to alert.company, but alert is nil
正确的做法是什么?
更新:
# frozen_string_literal: true
FactoryBot.define do
factory :quota_alert do
quota { create :quota, :with_random_current_count }
trigger_percent { rand }
end
end
更新 2:
这是唯一一家被触及的工厂:
FactoryBot.define do
factory :quota do
limit_amount { rand(5000..10_000) }
name { Faker::Coffee.blend_name }
feature
company
product
customer
subscription
trait :with_random_current_count do
after(:create) do |quota|
quota.set_current_count! rand(1..1000)
end
end
end
end
# app/models/*.rb
class Company < ApplicationRecord; end
class Quota < ApplicationRecord; end
class Alert < ApplicationRecord
belongs_to :company
delegated_type :alertable, types: %w[QuotaAlert]
# should delegate to `alertable`; otherwise delegated type setup seems unnecessary
delegate :trigger_percent, :trigger_percent=, :quota, :quota_public_id, to: :alertable
accepts_nested_attributes_for :alertable, update_only: true
end
class QuotaAlert < ApplicationRecord
# include Alertable
has_one :alert, as: :alertable, touch: true
delegate :company, :public_id, :channel, to: :alert
belongs_to :quota
validates :trigger_percent, presence: true, inclusion: { in: 0..1 }
end
# spec/factories/alerts.rb
FactoryBot.define do
factory :quota
factory :company
factory :quota_alert do
quota
trigger_percent { 0.2 }
end
factory :alert do
company
channel { 'email' }
association :alertable, factory: :quota_alert
end
end
# spec/fatories_spec.rb
require 'rails_helper'
RSpec.describe "Factories" do
it { p create(:alert) }
end
在创建记录之前,您正在工厂或模型中的某处调用 company
。创建提醒本身不会触发公司委派。
$ rspec spec/factories_spec.rb
Factories
#<Alert id: 1, channel: "email", public_id: nil, company_id: 1, alertable_type: "QuotaAlert", alertable_id: 1>
example at ./spec/factories_spec.rb:4
Finished in 0.07686 seconds (files took 0.88 seconds to load)
1 example, 0 failures
在控制台中创建警报以确保一切正常,然后在工厂中分解它可能更简单。
>> Alert.create!(alertable: QuotaAlert.create!(trigger_percent: 0.1, quota: Quota.create!), company: Company.create!)
# ...
=> #<Alert:0x00007ff608b5acd8 id: 1, channel: nil, public_id: nil, company_id: 1, alertable_type: "QuotaAlert", alertable_id: 1>
让我知道缺少什么。我会更新答案。
我想弄清楚如何以正确的顺序创建 Factory 对象,以便委托 class 可以访问它的父模型。这是我的模型:
class Alert < ApplicationRecord
delegated_type :alertable, types: %w[QuotaAlert]
delegate :trigger_percent, :trigger_percent=, :quota, :quota_public_id, to: :quota_alert
accepts_nested_attributes_for :alertable, update_only: true
module Alertable
extend ActiveSupport::Concern
included do
has_one :alert, as: :alertable, touch: true
delegate :company, :public_id, :channel, to: :alert
class QuotaAlert < ApplicationRecord
validates_presence_of :trigger_percent
validates_inclusion_of :trigger_percent, in: 0..1
belongs_to :quota
include Alertable
end
还有我的工厂:
FactoryBot.define do
factory :alert do
company
channel { 'email' }
trait :quota_alert_always_trigger do
before(:create) do |alert|
alert.alertable = create :quota_alert, trigger_percent: 0
end
end
然而,alert.company公司的代表团却在抱怨,因为它说quota_alert.alert是零。我想我需要更改工厂创建对象的顺序,但我不太清楚正确的顺序。
QuotaAlert
需要已经创建 alert
才能访问 alert.company
,但是,如果没有 QuotaAlert
.[=22,则无法创建 alert
=]
实际错误为:Module::DelegationError: QuotaAlert#company delegated to alert.company, but alert is nil
正确的做法是什么?
更新:
# frozen_string_literal: true
FactoryBot.define do
factory :quota_alert do
quota { create :quota, :with_random_current_count }
trigger_percent { rand }
end
end
更新 2: 这是唯一一家被触及的工厂:
FactoryBot.define do
factory :quota do
limit_amount { rand(5000..10_000) }
name { Faker::Coffee.blend_name }
feature
company
product
customer
subscription
trait :with_random_current_count do
after(:create) do |quota|
quota.set_current_count! rand(1..1000)
end
end
end
end
# app/models/*.rb
class Company < ApplicationRecord; end
class Quota < ApplicationRecord; end
class Alert < ApplicationRecord
belongs_to :company
delegated_type :alertable, types: %w[QuotaAlert]
# should delegate to `alertable`; otherwise delegated type setup seems unnecessary
delegate :trigger_percent, :trigger_percent=, :quota, :quota_public_id, to: :alertable
accepts_nested_attributes_for :alertable, update_only: true
end
class QuotaAlert < ApplicationRecord
# include Alertable
has_one :alert, as: :alertable, touch: true
delegate :company, :public_id, :channel, to: :alert
belongs_to :quota
validates :trigger_percent, presence: true, inclusion: { in: 0..1 }
end
# spec/factories/alerts.rb
FactoryBot.define do
factory :quota
factory :company
factory :quota_alert do
quota
trigger_percent { 0.2 }
end
factory :alert do
company
channel { 'email' }
association :alertable, factory: :quota_alert
end
end
# spec/fatories_spec.rb
require 'rails_helper'
RSpec.describe "Factories" do
it { p create(:alert) }
end
在创建记录之前,您正在工厂或模型中的某处调用 company
。创建提醒本身不会触发公司委派。
$ rspec spec/factories_spec.rb
Factories
#<Alert id: 1, channel: "email", public_id: nil, company_id: 1, alertable_type: "QuotaAlert", alertable_id: 1>
example at ./spec/factories_spec.rb:4
Finished in 0.07686 seconds (files took 0.88 seconds to load)
1 example, 0 failures
在控制台中创建警报以确保一切正常,然后在工厂中分解它可能更简单。
>> Alert.create!(alertable: QuotaAlert.create!(trigger_percent: 0.1, quota: Quota.create!), company: Company.create!)
# ...
=> #<Alert:0x00007ff608b5acd8 id: 1, channel: nil, public_id: nil, company_id: 1, alertable_type: "QuotaAlert", alertable_id: 1>
让我知道缺少什么。我会更新答案。