这个问题是这里提出的问题的延伸:
Using factory_girl in Rails with associations that have unique constraints. Getting duplicate errors
所提供的答案对我来说是非常有效的。看上去是这样的:
# 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我当前的单例实现不支持调用FactoryGirl::Singleton.execute(:matchup, :matchupable => bm),只支持调用FactoryGirl::Singleton.execute(:matchup)。
如何建议修改单例工厂以支持调用(如FactoryGirl::Singleton.execute(:matchup, :matchupable => bm)或FactoryGirl::Singleton.execute(:matchup) )
,因为现在,每当钩子在工厂:baseball_matchup上运行时,上面的代码都会抛出唯一性验证错误(“事件已经被接受”)。最终,这是需要修复的东西,这样DB中就不会有多个匹配或baseball_matchup。
发布于 2012-04-06 14:09:20
你需要做两件事才能做到这一点:
execute方法。在创建单例工厂时,注意,步骤1不足以解决您的问题。即使您允许execute接受属性,对execute(:matchup, attributes)的第一次调用也会缓存该结果并在任何时候返回它,即使您试图将不同的属性传递给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如果这对你不起作用,请告诉我。
发布于 2012-03-30 05:50:17
正如zetetic所提到的,您可以在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发布于 2012-03-15 00:48:20
Ruby方法可以有参数的默认值,所以使用空的默认选项哈希来定义您的单例方法:
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
endhttps://stackoverflow.com/questions/9678365
复制相似问题