Ruby on rails 如何避免在factory中循环创建关联模型?
我有一个应用程序,用户可以使用多种服务登录,例如Google Plus、Facebook、Twitter等 为了方便这一点,我有一个基本的Ruby on rails 如何避免在factory中循环创建关联模型?,ruby-on-rails,ruby,rspec,factory-bot,Ruby On Rails,Ruby,Rspec,Factory Bot,我有一个应用程序,用户可以使用多种服务登录,例如Google Plus、Facebook、Twitter等 为了方便这一点,我有一个基本的用户模型,有许多身份记录 每个标识记录都有一个提供者字段(例如“Google”,“Facebook”,等等),以指示使用哪个提供者登录 ActiveRecord验证只允许用户拥有每种类型的提供者中的一种。因此,用户不能拥有2个“谷歌”身份 我设立工厂的方式如下: FactoryGirl.define do factory :user do se
用户
模型,有许多身份
记录
- 每个
标识
记录都有一个提供者
字段(例如“Google”
,“Facebook”
,等等),以指示使用哪个提供者登录
- ActiveRecord验证只允许用户拥有每种类型的提供者中的一种。因此,用户不能拥有2个
“谷歌”
身份
我设立工厂的方式如下:
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Julio Jones-#{n}"}
sequence(:email) { |n| "julio.jones-#{n}@atl.com" }
after(:create) do |user|
create(:identity, user: user)
end
end
factory :identity do
user
provider "Google"
email { user.email }
password "password"
end
end
用户
模型有一个回调,用于创建一个标识
记录。跑步时效果很好
user = FactoryGirl.create(:user)
但是,如果我创建标识
identity = FactoryGirl.create(:identity)
标识
工厂将首先尝试创建一个父用户
,这将反过来创建另一个标识
。当它最终返回到创建我调用的标识
时,另一个标识
已经存在,并且该用户的提供者
相同,但失败了
本质上,我需要一种方法,使(:create)
之后的回调在用户
由:identity
工厂创建时不触发。有没有一种方法可以告诉我们是什么原因导致创建了一个特定的工厂?我认为对于一个工厂来说,没有一种好方法可以告诉我们它是被另一个没有合作的人调用的。(您始终可以检查调用方位置,但这并不好。)相反,让一个工厂使用瞬态属性告诉另一个工厂不同的行为:
FactoryGirl.define do
factory :user do
transient do
create_identity true
end
after(:create) do |user, evaluator|
if evaluator.create_identity
create(:identity, user: user)
end
end
end
factory :identity do
association :user, factory: :user, create_identity: false
end
end
正如Dave所指出的,使用瞬态属性是一种选择。另一个选项是在构建关联工厂时通过nil
FactoryGirl:避免关联之间的循环/无限循环
让我举例说明:
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Julio Jones-#{n}"}
sequence(:email) { |n| "julio.jones-#{n}@atl.com" }
# we pass user: nil here because it will cause the identity factory
# to just skip the line user { ... }.
identity { build(:identity, user: nil) }
end
factory :identity do
# we pass user: nil here because it will cause the user factory
# to just skip the line idenitity { ... }.
user { build(:user, identity: nil) }
provider "Google"
email "email@example.com"
password "password"
end
end
当我们调用build(:user)
时,代码最终到达以下行:
identity { build(:identity, user: nil) }
这称为身份工厂。当它到达通常构建用户关联的行(user{build(:user,identity:nil)}
)时,它将跳过它,因为用户已经被设置为(nil)。恭喜你,你刚刚避免了循环依赖
当您调用build(:identity)
时,它的工作方式相同
FactoryGirl:从关联工厂中的一个工厂访问属性
还有最后一件事:在您的情况下,您需要访问identity factory中用户的电子邮件属性。在代码示例中,您说:
factory :identity do
...
email { user.email }
end
显然,当我们调用build(:user)
时,这会失败,因为我们在调用标识工厂时将user设置为nil。不要害怕!当我们调用身份工厂时,我们只需通过电子邮件传递一个新的用户对象。因此,这条线变成:
identity { build(:identity, user: User.new(email: email)) }
这既可以防止循环无限关联循环,也可以确保电子邮件属性在标识工厂中可用
最后,您的代码如下所示:
FactoryGirl.define do
factory :user do
sequence(:name) { |n| "Julio Jones-#{n}"}
sequence(:email) { |n| "julio.jones-#{n}@atl.com" }
# we pass user: User.new here because it will...
# a) cause the identity factory to skip the line user { ... } and
# b) allow us to use the email attribute in the identity factory.
identity { build(:identity, user: User.new(email: email)) }
end
factory :identity do
# we pass user: nil here because it will cause the user factory
# to just skip the line idenitity { ... }.
user { build(:user, identity: nil) }
provider "Google"
email { user.email }
password "password"
end
end
希望对你有帮助 谢谢!这可能是最直接、最可靠的解决方案