Q:

has_one through and polymorphic associations over multi-table inheritance

In the project i'm currently developing under rails 4.0.0beta1, i had the need for a user based authentication in which each user could be linked to an entity. I'm kinda new to rails and had some troubles doing so.
The model is as following:
class User < ActiveRecord::Base
end

class Agency < ActiveRecord::Base
end

class Client < ActiveRecord::Base
  belongs_to :agency
end

What i need is for a user to be able to link to either an agency or a client but not both (those two are what i'll be calling entities). It can have no link at all and at most one link.
First thing i looked for was how to do Mutli-Table inheritance (MTI) in rails. But some things blocked me:

it was not available out of the box
MTI looked kinda hard to implement for a newbie such as me
the gems implementing the solutions seemed old and either too complexe or not complete
the gems would have probably broke under rails4 as they had not been updated for a while

So i looked for another solution and i found polymorphic associations.
I've be on this since yesterday and took some time to make it work even with the help of Rails polymorphic has_many :through and ActiveRecord, has_many :through, and Polymorphic Associations
I managed to make the examples from the question above work but it took a while and i finally have two problems:

How to transform the relations in user into a has_one association and be able to access "blindly" the linked entity ?
How to set a constraint so that no user can have more than one entity ?
Is there a better way to do what i want ?

A:

Here's a fully working example:
The migration file:
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

The models:
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

As you can see i added a getter and a setter that i named "entity". That's because has_one :entity, through: :user_entity raises the following error:
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.

Finally, here are the tests i set up. I give them so that everyone understands know ho you can set and access data between those objects. i won't be detailing my FactoryGirl models but they're pretty obvious
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

I Hope this can be of some help to someone. I decided to put the whole solution here cause it seems to me like a good one compared to MTI and i think it shouldn't take someone that much time to set something like that up.

