Ответ 1
Я написал это правильно с наследованием и всем этим. Мои коммиты объединены в здесь и здесь.
Теперь в FactoryGirl 1.2.3, woot!
# 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)
, вы получите пустой массив. Я знаю, что вы можете их построить
вручную или создать вспомогательные методы самостоятельно, чтобы построить граф объектов, но я хотел бы избежать этого.
Итак, мой вопрос в том, как вы можете создать отношения has_many в factory_girl, соблюдая стратегию сборки?
Я написал это правильно с наследованием и всем этим. Мои коммиты объединены в здесь и здесь.
Теперь в FactoryGirl 1.2.3, woot!
Я закончил исправление factory девушки, чтобы разрешить обратные вызовы 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
Надеюсь, это поможет кому-то в будущем. Я, вероятно, попытаюсь представить это ребятам в раздумьях, но там уже есть несколько устаревших билетов на свой трекер ошибок по этому вопросу.
Мне обычно нравится выделять здание и создавать, поэтому я все равно могу построить объект, не переходя в базу данных.
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