has_many while respecting build strategy in factory_girl

后端 未结 3 2067
名媛妹妹
名媛妹妹 2020-12-15 06:22

Situation

# Models
class User < ActiveRecord::Base
  has_many :items 
end 

class Items < ActiveRecord::Base
  belongs_to :user 
  validates_presence         


        
相关标签:
3条回答
  • 2020-12-15 06:27

    I wrote it properly with inheritance and all that. My commits are merged in here and here.

    It's now in FactoryGirl 1.2.3, woot!

    0 讨论(0)
  • 2020-12-15 06:34

    I ended up patching factory girl to allow after_build and after_create callbacks.

    Implementation

    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
    

    This could be an evil twin or just an extension you require.

    Example Usage

    # 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
    

    Hope this helps someone in the future. I'll probably attempt to submit this to the guys at thoughtbot, but there's a couple stale tickets in their bug tracker on the subject already.

    0 讨论(0)
  • 2020-12-15 06:51

    I usually like to separate building and creating so i can still build the object without going to the database.

    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
    
    0 讨论(0)
提交回复
热议问题