首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Rails中的FactoryGirl -关联w/唯一约束

Rails中的FactoryGirl -关联w/唯一约束
EN

Stack Overflow用户
提问于 2012-03-13 04:26:52
回答 3查看 2.2K关注 0票数 9

这个问题是这里提出的问题的延伸:

Using factory_girl in Rails with associations that have unique constraints. Getting duplicate errors

所提供的答案对我来说是非常有效的。看上去是这样的:

代码语言:javascript
复制
# 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

我遇到的问题是,当我需要手动构建一个关联以支持带有钩子中的唯一性约束的多态关联时。例如:

代码语言:javascript
复制
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。

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2012-04-06 14:09:20

你需要做两件事才能做到这一点:

  1. 接受属性作为参数-- execute方法。在创建单例工厂时,
  2. 键从工厂名称和属性中删除。

注意,步骤1不足以解决您的问题。即使您允许execute接受属性,对execute(:matchup, attributes)的第一次调用也会缓存该结果并在任何时候返回它,即使您试图将不同的属性传递给execute。这就是为什么您还需要更改作为@@singletons哈希的哈希键的内容。

下面是我测试过的一个实现:

代码语言:javascript
复制
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")的查询字符串表示的字符串。我能够用不同的属性创建多个不同的比赛,但它尊重事件/可匹配组合的唯一性。

代码语言:javascript
复制
> 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

如果这对你不起作用,请告诉我。

票数 1
EN

Stack Overflow用户

发布于 2012-03-30 05:50:17

正如zetetic所提到的,您可以在execute函数上定义第二个参数,以发送在调用FactoryGirl.create期间使用的属性,其默认值为空散列,因此在不使用它的情况下,它不会覆盖任何属性(如果属性散列是空的,则不需要检查该属性)。

还请注意,在这种情况下,您不需要定义begin..end块,因为在救援之后没有任何事情要做,所以可以通过将援救定义为方法定义的一部分来简化方法。初始化良好的情况下的分配也将返回指定的值,因此不需要再次显式访问哈希来返回它。通过所有这些更改,代码的结尾将如下:

代码语言:javascript
复制
# 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
票数 3
EN

Stack Overflow用户

发布于 2012-03-15 00:48:20

Ruby方法可以有参数的默认值,所以使用空的默认选项哈希来定义您的单例方法:

代码语言:javascript
复制
  def self.execute(factory_key, options={})

现在,您可以将其称为两种方式:

代码语言:javascript
复制
  FactoryGirl::Singleton.execute(:matchup)
  FactoryGirl::Singleton.execute(:matchup, :matchupable => bm)

在该方法中,测试options参数哈希,以查看是否传入了任何内容:

代码语言:javascript
复制
if options.empty?
  # no options specified
else
  # options were specified
end
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9678365

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档