# ActiveRecord Polymorphism

## Objectives

* Define polymorphism (in general)
* Describe the purpose of polymorphic associations
* Use the Rails model generator to generate a polymorphic model
* Implement polymorphic associations

## What are Polymorphic Associations?

To understand what polymorphic associations are, let's understand the first word, `polymorphism`.

> In object-oriented programming, polymorphism is the provision of a single interface to entities of different types.

In the context of Rails models, polymorphic associations allow a **single model** to reference **multiple models** via an association.

Sometimes a model needs to be able to reference multiple models. For example, a model to hold a vote could belong to a post and a comment (like on Reddit). While we could organize our models to include an id for each model, like this:

* Vote
  * user\_id
  * value
  * post\_id
  * comment\_id

...this model has a couple key issues.

1. A vote ideally only belongs to a post or comment, not both at once. Therefore, if the vote belonged to a post, the comment attribute would be set to NULL. This is a waste of space!
2. If we wanted a vote to reference another model (such as a message), we'd have to add another column for the model. This results in having to create another migration, and now the problem we had in issue #1 is multiplied.

Polymorphism solves this problem by abstracting the the "multiple models" into two columns.

* Vote
  * user\_id
  * value
  * votable\_type
  * votable\_id

Instead of giving the model attribute a specific name, we store the name of the model and the id as attributes. Now, `votable_type` can be any model we choose, and `votable_id` will be the `id` of the `votable_type`. Here's some examples:

* Vote 1
  * user\_id: 3
  * value: -1
  * votable\_id: 4
  * votable\_type: 'Post'
* Vote 2
  * user\_id: 9
  * value: 1
  * votable\_id: 8
  * votable\_type: 'Comment'

Let's setup a vote model in order to implement this functionality in our Link Board.

### Setup the Vote Model

```
rails g model vote value:integer user:references votable:references{polymorphic}
```

Here, we're creating a model that has a value, a user that created the vote, and a polymorphic association called `votable`. We're going to use `votable` to associate a vote to posts and comments.

**Check the migration**

make sure the vote migration has `polymorphic:true`

```ruby
t.references :votable, polymorphic:true, index: true
```

**run migration**

```
rake db:migrate
```

Now that we're migrated, we'll be adding the associations to posts and users. First thing's first, let's double-check that the vote model has a polymorphic association:

**models/vote.rb**

```ruby
belongs_to :votable, polymorphic: true
```

Now, let's associate votes to posts. We're going to define the attribute name as the plural of vote, which is `votes`. We'll define the association through the polymorphic attributes set up in vote.

**models/post.rb**

* has many votes (votes for/against this post)

```ruby
has_many :votes, as: :votable
```

Now for user, we need to define the association for two different attributes. First, the user will have many `ratings`, which refer to a regular 1:M association. This means that each user has many votes that they cast.

The second association will be polymorphic, and will refer to users voting for other users.

**models/user.rb**

* has many `ratings`
  * votes created by user
  * not polymorphic - regular one to many
* has many `votes`
  * votes for/against this user
  * polymorphic

```ruby
has_many :ratings, class_name: 'Vote'
has_many :votes , as: :votable
```

## Try it out

in terminal

```ruby
#list comments, posts, users
User.all
Vote.all
Post.all

#user up votes a post
User.first.ratings << Post.first.votes.create({value:1})

#user down votes a post
User.first.ratings << Post.last.votes.create({value:-1})

#user up votes a user
User.first.ratings << User.first.votes.create({value:1})

#list user ratings (votes cast by user)
User.first.ratings

#list user votes (votes about a user)
User.first.votes
```

### Resources

* [Rails guide - Polymorphic Models](http://guides.rubyonrails.org/association_basics.html#polymorphic-associations)
* [Rails Advanced Generators](http://railsguides.net/advanced-rails-model-generators/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://romebell.gitbook.io/sei-1019/rails/rails-polymorphism.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
