Ruby Rails中的FactoryGirl-具有唯一约束的关联
这个问题是这里提出的问题的延伸: 所提供的答案对我非常有效。下面是它的样子:Ruby Rails中的FactoryGirl-具有唯一约束的关联,ruby,rspec,singleton,factory-bot,Ruby,Rspec,Singleton,Factory Bot,这个问题是这里提出的问题的延伸: 所提供的答案对我非常有效。下面是它的样子: # Creates a class variable for factories that should be only created once. module FactoryGirl class Singleton @@singletons = {} def self.execute(factory_key) begin @@singletons[factor
# Creates a class variable for factories that should be only created once.
module FactoryGirl
class Singleton
@@singletons = {}
def self.execute(factory_key)
begin
@@singletons[factory_key] = FactoryGirl.create(factory_key)
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
# already in DB so return nil
end
@@singletons[factory_key]
end
end
end
我遇到的问题是,当我需要手动构建关联以支持钩子中具有唯一性约束的多态关联时。例如:
class Matchup < ActiveRecord::Base
belongs_to :event
belongs_to :matchupable, :polymorphic => true
validates :event_id, :uniqueness => { :scope => [:matchupable_id, :matchupable_type] }
end
class BaseballMatchup < ActiveRecord::Base
has_one :matchup, :as => :matchupable
end
FactoryGirl.define do
factory :matchup do
event { FactoryGirl::Singleton.execute(:event) }
matchupable { FactoryGirl::Singleton.execute(:baseball_matchup) }
home_team_record '10-5'
away_team_record '9-6'
end
factory :baseball_matchup do
home_pitcher 'Joe Bloe'
home_pitcher_record '21-0'
home_pitcher_era 1.92
home_pitcher_arm 'R'
away_pitcher 'Jack John'
away_pitcher_record '0-21'
away_pitcher_era 9.92
away_pitcher_arm 'R'
after_build do |bm|
bm.matchup = Factory.create(:matchup, :matchupable => bm)
end
end
end
类匹配true
验证:事件\u id,:唯一性=>{:范围=>[:可匹配\u id,:可匹配\u类型]}
结束
类baseBallMatchip:matchupable
结束
工厂女孩
工厂:配对吗
事件{FactoryGirl::Singleton.execute(:事件)}
匹配表{FactoryGirl::Singleton.execute(:barball_matchup)}
主场球队创下10胜5负的记录
客场球队创下9胜6负的记录
结束
工厂:棒球比赛吗
本垒投手乔·布勒
本垒投手创下21-0的记录
本垒投手时代1.92
本垒投手手臂
客场投手“杰克·约翰”
客场投手记录“0-21”
客场投手时代9.92
客场投手手手手
建造完成后进行bm|
bm.matchup=Factory.create(:matchup,:matchupable=>bm)
结束
结束
结束
我当前的singleton实现不支持调用FactoryGirl::singleton.execute(:matchup,:matchupable=>bm)
,仅FactoryGirl::singleton.execute(:matchup)
您建议如何修改singleton工厂以支持调用,例如FactoryGirl::singleton.execute(:matchup,:matchupable=>bm)
或FactoryGirl::singleton.execute(:matchup)
因为现在,每当钩子在factory:Basketball\u Matchip上运行时,上面的代码都会抛出唯一性验证错误(“事件已被捕获”)。最终,这就是需要修复的问题,以便数据库中不会有多个匹配或Basketball\u匹配 Ruby方法可以为参数设置默认值,因此请使用空的默认选项散列定义singleton方法:
def self.execute(factory_key, options={})
现在,您可以用两种方式来称呼它:
FactoryGirl::Singleton.execute(:matchup)
FactoryGirl::Singleton.execute(:matchup, :matchupable => bm)
在该方法中,测试options参数散列以查看是否传入了任何内容:
if options.empty?
# no options specified
else
# options were specified
end
正如Zettetic所提到的,您可以在execute函数上定义第二个参数,以发送调用FactoryGirl.create期间要使用的属性,默认值为空哈希,这样在您不使用它的情况下,它不会覆盖任何属性(在这种特殊情况下,您不需要检查属性哈希是否为空) 还请注意,在本例中,您不需要定义begin..end块,因为在解救之后没有任何事情要做,所以您可以通过将解救定义为方法定义的一部分来简化方法。在初始化良好的情况下进行赋值也将返回所分配的值,因此无需再次显式访问散列以返回该值。通过所有这些更改,代码将以如下方式结束:
# Creates a class variable for factories that should be only created once.
module FactoryGirl
class Singleton
@@singletons = {}
def self.execute(factory_key, attrs = {})
@@singletons[factory_key] = FactoryGirl.create(factory_key, attrs)
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
# already in DB so return nil
end
end
end
您需要做两件事来实现这一目标:
execute
方法的参数execute
接受属性,对execute(:matchip,attributes)
的第一次调用将缓存该结果,并在执行(:matchip)
时返回该结果,即使您尝试将不同的属性传递给execute
。这就是为什么您还需要更改用作@@singletons
哈希的哈希键的内容
下面是我测试过的一个实现:
module FactoryGirl
class Singleton
@@singletons = {}
def self.execute(factory_key, attributes = {})
# form a unique key for this factory and set of attributes
key = [factory_key.to_s, '?', attributes.to_query].join
begin
@@singletons[key] = FactoryGirl.create(factory_key, attributes)
rescue ActiveRecord::RecordInvalid, ActiveRecord::RecordNotUnique
# already in DB so return nil
end
@@singletons[key]
end
end
end
键是一个字符串,由工厂名称和属性哈希的查询字符串表示组成(类似于“matchup?event=6&matchupable=2”
)。我能够创建多个具有不同属性的不同匹配,但它尊重事件/匹配组合的唯一性
> e = FactoryGirl.create(:event)
> bm = FactoryGirl.create(:baseball_matchup)
> m = FactoryGirl::Singleton.execute(:matchup, :event => e, :matchupable => bm)
> m.id
2
> m = FactoryGirl::Singleton.execute(:matchup, :event => e, :matchupable => bm)
> m.id
2
> f = FactoryGirl.create(:event)
> m = FactoryGirl::Singleton.execute(:matchup, :event => f, :matchupable => bm)
> m.id
3
如果这对您不起作用,请告诉我。我认为这里的问题是,当您调用FactoryGirl.create并且数据库中有一个现有记录(因此引发异常)时,将不会返回singleton,因为救援后我们没有调用@singleton[factory\u key]。