代码之家  ›  专栏  ›  技术社区  ›  fowlduck

在尊重工厂建设战略的同时有很多女孩

  •  19
  • fowlduck  · 技术社区  · 17 年前

    情况

    # Models
    class User < ActiveRecord::Base
      has_many :items 
    end 
    
    class Items < ActiveRecord::Base
      belongs_to :user 
      validates_presence_of :user_id 
    end 
    
    # Factories
    Factory.define(:user) do |u| 
      u.name "foo" 
    end 
    
    Factory.define(:user_with_items, :parent => :user) do |u| 
      u.items {|items| [items.association(:item), items.association(:item)]} 
    end
    
    Factory.define(:item) do |i| 
      i.color "red" 
    end 
    
    Factory.define(:item_with_user, :parent => :user) do |i| 
      i.association(:user) 
    end
    

    问题

    如果你跑步 @user = Factory(:user_with_items) 然后 @user.items 包含这两项。问题是这些项与数据库中的用户没有关联。如果重新加载关联 @user.items(true) 然后你得到一个空数组。我知道你可以建造它们 手动或自己创建helper方法来构建对象图,但我想避免这种情况。

    问题

    所以,我的问题是,你如何在一个女孩的基础上建立一个有多个关系,同时尊重建立策略?

    3 回复  |  直到 15 年前
        1
  •  18
  •   fowlduck    16 年前

    我用遗产和其他东西写得很好。我的承诺被合并到 here 和 here 。

    它现在在工厂女孩1.2.3,呜!

        2
  •  11
  •   fowlduck    17 年前

    最后我给工厂女工打了补丁 after_build 和 after_create 回调。

    实施

    Factory.class_eval do
      def run (proxy_class, overrides) #:nodoc:
        proxy = proxy_class.new(build_class)
        proxy.callbacks = @callbacks
        overrides = symbolize_keys(overrides)
        overrides.each {|attr, val| proxy.set(attr, val) }
        passed_keys = overrides.keys.collect {|k| Factory.aliases_for(k) }.flatten
        @attributes.each do |attribute|
          unless passed_keys.include?(attribute.name)
            attribute.add_to(proxy)
          end
        end
        proxy.result
      end
    
      def after_create(&block)
        @callbacks ||= {}
        @callbacks[:after_create] = block
      end
    
      def after_build(&block)
        @callbacks ||= {}
        @callbacks[:after_build] = block
      end
    end
    
    Factory::Proxy.class_eval do
      attr_accessor :callbacks
    
      def run_callback(name)
        callbacks && callbacks[name] && callbacks[name].call(@instance)
      end
    end
    
    Factory::Proxy::Build.class_eval do
      def result
        run_callback(:after_build)
        @instance
      end
    end
    
    Factory::Proxy::Create.class_eval do
      def result
        run_callback(:after_build)
        @instance.save!
        run_callback(:after_create)
        @instance
      end
    end
    

    这可能是一个邪恶的双胞胎或只是一个扩展你需要。

    示例用法

    # Models
    class User < ActiveRecord::Base
      has_many :items
    end
    
    class Items < ActiveRecord::Base
      belongs_to :user
      validates_presence_of :user_id
    end
    
    # Factories
    Factory.define(:user) do |u|
      u.name "foo"
    end
    
    Factory.define(:user_with_items, :parent => :user) do |u|
      u.after_build do |o|
        o.items = [Factory.build(:item, :user => o), Factory.build(:item, :user => o)]
      end
    end
    
    Factory.define(:item) do |i|
      i.color "red"
    end
    
    Factory.define(:item_with_user, :parent => :user) do |i|
      i.association(:user)
    end
    
    # Run
    user = Factory(:user_with_items)
    user.items(true) # Shows the two saved items
    

    希望这对将来有帮助。我可能会尝试把这个提交给thoughtbot的人,但是他们的bug追踪器里已经有一些关于这个主题的过期的罚单了。

        3
  •  4
  •   mgp    15 年前

    我通常喜欢分开构建和创建,这样我仍然可以构建对象而不用去数据库。

    Factory.define(:user_with_items, :parent => :user) do |u|
      u.after_build do |u|
        u.items = (1..2).map {Factory.build(:item, :user => u)}
      end
      u.after_create do |u|
        u.items.each {|i| i.save!}
      end
    end