Дизайнът
Имам потребителски модел, който принадлежи към профил чрез полиморфна асоциация. Причината, поради която избрах този дизайн, може да бъде намерена тук, За да обобщим, има много потребители на приложението, които имат наистина различни профили.
class User < ActiveRecord::Base
belongs_to :profile, :dependent => :destroy, :polymorphic => true
end
class Artist < ActiveRecord::Base
has_one :user, :as => :profile
end
class Musician < ActiveRecord::Base
has_one :user, :as => :profile
end
След избора на този дизайн, трудно ми идва с добри тестове. С помощта на FactoryGirl и RSpec не съм сигурен как да обявим асоциацията за най-ефективен начин.
Първи опит
factories.rb
Factory.define :user do |f|
# ... attributes on the user
# this creates a dependency on the artist factory
f.association :profile, :factory => :artist
end
Factory.define :artist do |a|
# ... attributes for the artist profile
end
user_spec.rb
it "should destroy a users profile when the user is destroyed" do
# using the class Artist seems wrong to me, what if I change my factories?
user = Factory(:user)
profile = user.profile
lambda {
user.destroy
}.should change(Artist, :count).by(-1)
end
Коментари / други мисли
Както бе споменато в коментарите в спецификациите на потребителя, използването на Artist изглежда крехко. Ами ако моите фабрики се променят в бъдеще?
Може би трябва да използвам обратните извиквания на фабриката и дефиниране на "потребител на изпълнител" и "потребител на музикант"? Всички данни са оценени.
Отговори:
10 за отговор № 1Обратните обаждания на Factory_Girl ще направят живота много по-лесен. Какво ще кажете за нещо подобно?
Factory.define :user do |user|
#attributes for user
end
Factory.define :artist do |artist|
#attributes for artist
artist.after_create {|a| Factory(:user, :profile => a)}
end
Factory.define :musician do |musician|
#attributes for musician
musician.after_create {|m| Factory(:user, :profile => m)}
end
141 за отговор № 2
Въпреки че има приет отговор, ето един код, използващ новия синтаксис, който работи за мен и може да бъде полезен на някой друг.
спец / factories.rb
FactoryGirl.define do
factory :musical_user, class: "User" do
association :profile, factory: :musician
#attributes for user
end
factory :artist_user, class: "User" do
association :profile, factory: :artist
#attributes for user
end
factory :artist do
#attributes for artist
end
factory :musician do
#attributes for musician
end
end
спец / модели / artist_spec.rb
before(:each) do
@artist = FactoryGirl.create(:artist_user)
end
Което ще създаде потребителската инстанция, както и потребителския потребителски модел. Така че можете да се обадите:
@artist.profile
за да получите копие на изпълнителя.
32 за отговор № 3
Използвайте такива черти;
FactoryGirl.define do
factory :user do
# attributes_for user
trait :artist do
association :profile, factory: :artist
end
trait :musician do
association :profile, factory: :musician
end
end
end
сега можете да получите потребителски потребителски екземпляр от FactoryGirl.create(:user, :artist)
4 за отговор № 4
Можете също така да решите това с помощта на вложени фабрики (наследяване), по този начин ще създадете основен завод за всеки клас гнездо фабрики, които наследяват от този основен родител.
FactoryGirl.define do
factory :user do
# attributes_for user
factory :artist_profile do
association :profile, factory: :artist
end
factory :musician_profile do
association :profile, factory: :musician
end
end
end
Вече имате достъп до вложените фабрики както следва:
artist_profile = create(:artist_profile)
musician_profile = create(:musician_profile)
Надявам се, че това помага на някого.
2 за отговор № 5
Изглежда, че полиморфните асоциации във фабриките се държат по същия начин, както редовните асоциации на Rails.
Така че има и друг по-малко подробен начин, ако не се интересувате от атрибутите на модела от страна на асоциацията "anywhere_to" (потребител в този пример):
# Factories
FactoryGirl.define do
sequence(:email) { Faker::Internet.email }
factory :user do
# you can predefine some user attributes with sequence
email { generate :email }
end
factory :artist do
# define association according to documentation
user
end
end
# Using in specs
describe Artist do
it "created from factory" do
# its more naturally to starts from "main" Artist model
artist = FactoryGirl.create :artist
artist.user.should be_an(User)
end
end
Асоциации на FactoryGirl: https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md#associations
1 за отговор № 6
Понастоящем използвам това приложение за справяне с полиморфни асоциации в FactoryGirl
:
В /spec/factories/users.rb:
FactoryGirl.define do
factory :user do
# attributes for user
end
# define your Artist factory elsewhere
factory :artist_user, parent: :user do
profile { create(:artist) }
profile_type "Artist"
# optionally add attributes specific to Artists
end
# define your Musician factory elsewhere
factory :musician_user, parent: :user do
profile { create(:musician) }
profile_type "Musician"
# optionally add attributes specific to Musicians
end
end
След това създайте записи както обикновено: FactoryGirl.create(:artist_user)