完全に機能する例を次に示します。
移行ファイル:
class CreateUserEntities < ActiveRecord::Migration
def change
create_table :user_entities do |t|
t.integer :user_id
t.references :entity, polymorphic: true
t.timestamps
end
add_index :user_entities, [:user_id, :entity_id, :entity_type]
end
end
モデル:
class User < ActiveRecord::Base
has_one :user_entity
has_one :client, through: :user_entity, source: :entity, source_type: 'Client'
has_one :agency, through: :user_entity, source: :entity, source_type: 'Agency'
def entity
self.user_entity.try(:entity)
end
def entity=(newEntity)
self.build_user_entity(entity: newEntity)
end
end
class UserEntity < ActiveRecord::Base
belongs_to :user
belongs_to :entity, polymorphic: true
validates_uniqueness_of :user
end
class Client < ActiveRecord::Base
has_many :user_entities, as: :entity
has_many :users, through: :user_entities
end
class Agency < ActiveRecord::Base
has_many :user_entities, as: :entity
has_many :users, through: :user_entities
end
ご覧のとおり、「エンティティ」という名前のゲッターとセッターを追加しました。これhas_one :entity, through: :user_entity
は、次のエラーが発生するためです。
ActiveRecord::HasManyThroughAssociationPolymorphicSourceError: Cannot have a has_many :through association 'User#entity' on the polymorphic object 'Entity#entity' without 'source_type'. Try adding 'source_type: "Entity"' to 'has_many :through' definition.
最後に、これが私が設定したテストです。これらのオブジェクト間でデータを設定してアクセスできる方法を誰もが理解できるように、それらを提供します。FactoryGirlモデルの詳細は説明しませんが、かなり明白です
require 'test_helper'
class UserEntityTest < ActiveSupport::TestCase
test "access entity from user" do
usr = FactoryGirl.create(:user_with_client)
assert_instance_of client, usr.user_entity.entity
assert_instance_of client, usr.entity
assert_instance_of client, usr.client
end
test "only right entity is set" do
usr = FactoryGirl.create(:user_with_client)
assert_instance_of client, usr.client
assert_nil usr.agency
end
test "add entity to user using the blind rails method" do
usr = FactoryGirl.create(:user)
client = FactoryGirl.create(:client)
usr.build_user_entity(entity: client)
usr.save!
result = UserEntity.where(user_id: usr.id)
assert_equal 1, result.size
assert_equal client.id, result.first.entity_id
end
test "add entity to user using setter" do
usr = FactoryGirl.create(:user)
client = FactoryGirl.create(:client)
usr.client = client
usr.save!
result = UserEntity.where(user_id: usr.id)
assert_equal 1, result.size
assert_equal client.id, result.first.entity_id
end
test "add entity to user using blind setter" do
usr = FactoryGirl.create(:user)
client = FactoryGirl.create(:client)
usr.entity = client
usr.save!
result = UserEntity.where(user_id: usr.id)
assert_equal 1, result.size
assert_equal client.id, result.first.entity_id
end
test "add user to entity" do
usr = FactoryGirl.create(:user)
client = FactoryGirl.create(:client)
client.users << usr
result = UserEntity.where(entity_id: client.id, entity_type: 'client')
assert_equal 1, result.size
assert_equal usr.id, result.first.user_id
end
test "only one entity by user" do
usr = FactoryGirl.create(:user)
client = FactoryGirl.create(:client)
agency = FactoryGirl.create(:agency)
usr.agency = agency
usr.client = client
usr.save!
result = UserEntity.where(user_id: usr.id)
assert_equal 1, result.size
assert_equal client.id, result.first.entity_id
end
test "user uniqueness" do
usr = FactoryGirl.create(:user)
client = FactoryGirl.create(:client)
agency = FactoryGirl.create(:agency)
UserEntity.create!(user: usr, entity: client)
assert_raise(ActiveRecord::RecordInvalid) {
UserEntity.create!(user: usr, entity: agency)
}
end
end
これが誰かの助けになることを願っています。私はここにソリューション全体を置くことにしました。MTIと比較して良いソリューションのように思えるので、そのようなものを設定するのにそれほど時間はかからないはずです。