Skip to content

Instantly share code, notes, and snippets.

@oojikoo-gist
Created April 20, 2015 13:34
Show Gist options
  • Save oojikoo-gist/5ef6b82bc0a3fc85d420 to your computer and use it in GitHub Desktop.
Save oojikoo-gist/5ef6b82bc0a3fc85d420 to your computer and use it in GitHub Desktop.
rails: polymorphic model alternative

Alternative polymorphic model

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
# 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!

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment