
Você sabe o que são associações polimórficas e como elas podem facilitar a sua vida de desenvolvedor Ruby On Rails?
Neste artigo rápido e prático nós vamos explicar através de um exemplo passo a passo como implementar essas associações nos seus APPs.
O que é Polimorfismo?
A palavra polimorfismo vem do grego e significa aquilo que pode tomar várias formas. Esta característica é um dos conceitos essenciais da programação orientada a objeto (POO). Enquanto a herança se refere às classes e a sua hierarquia, o polimorfismo diz respeito aos métodos dos objetos.
Uma característica das linguagens de programação orientadas a objeto é o polimorfismo. O polimorfismo permite “programar no geral” em vez de “programar no específico”. Em particular, o polimorfismo permite escrever programas que processam objetos que compartilham a mesma superclasse em um hierarquia de classes como se todas fossem objetos da superclasse.
Ao escrever aplicativos rails, você encontrará situações em que você tem associações de modelo que parecem semelhantes, por exemplo, suponha que você tenha os modelos Curso e Laboratório. Agora, cada Curso e Laboratório precisa, ao menos, de um Professor, portanto, você precisa associar Cursos e Laboratórios aos seus respectivos Professores. Se você usar has_many / belongs_to, você terá dois modelos similares para Professores do cursos e Professores do Lab. Em vez de ter dois modelos diferentes, você pode ter um modelo único e é possível associar esse modelo aos modelos Curso e Laboratório usando associação polimórfica.
O que vamos criar?
Para que o conceito fique claro, vamos criar o App polimórfico, cujo o objetivo é demostrar como funciona as associações polimórficas no Ruby On Rails. O App permitirá que o usuário tenha um ou mais posts e que esses posts possam ser comentados por outros usuários e os comentários podem ser comentados também. Conjuntamente utilizaremos a gem do devise para realizar a autenticação do usuário.
As tabelas comments e posts (Imagem 1) tem o mesmo conceito de ser comentada, por isso precisamos associar a tabela comentários a dois tipos de diferentes modelos. Esse é o cenário perfeito para um relacionamento polimórfico. Na modelagem do banco de dados adicionamos na tabela comment os seguintes atributes commentable_type que será responsável por informar qual model o objeto pertence e commentable_id que é a chave estrangeira.

Ferramentas
- • Ruby 2.5
- • Ruby on Rails 5.2
- • Devise
Passo a Passo da criação do APP
1. Primeiro vamos criar um novo projeto com o nome polymorphic:
1 |
rails new polymorphic |
2. Entre na pasta do projeto que foi criado:
1 |
cd polymorphic |
3. Nessa etapa iremos fazer a instalação do Devise, abra seu Gemfile e acrescente a seguinte linha de código:
1 |
gem 'devise' |
4. execute:
1 |
bundle install |
5. Para configurar o devise no seu app execute o seguinte comando no terminal:
1 |
rails generate devise:install |
6. Vamos gerar os modelos baseado na modelagem do banco de dados:
1 |
rails generate devise user name:string |
1 |
rails generate model post user:references post:text |
1 |
rails generate model comment user:references comment:text commentable:references{polymorphic} |
7. Para criar o banco de dados e as tabelas rode seguinte comando:
1 |
rake db:create db:migrate |
8. Adicione ao modelo post em app/models/post.rb:
1 |
has_many :comments, as: :commentable |
9. Em app/models/comment.rb acrescente:
1 |
has_many :comments, as: :commentable |
10. Atualize o modelo user em app/models/user.rb:
1 2 |
has_many :comments has_many :posts |
11. Substitua o conteúdo em db/seeds.rb por esse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
@pedro = User.create(email: 'pedro@email.com', name:'Pedro', password: '12345678', password_confirmation: '12345678') @jon = User.create(email: 'jon@email.com', name:'jon', password: '12345678', password_confirmation: '12345678') @post_movie = Post.create(user_id: @pedro.id, post: "Hoje assisti o filme A start is Born") @comment = Comment.create(user_id: @jon.id, comment: "Esse filme é muito bom!", commentable: @post_movie) Comment.create(user_id: @pedro.id, comment: "Achei o filme muito bom mas o do freddie é bem melhor", commentable: @comment) Comment.create(user_id: @jon.id, comment: "Não assisti o filme do freddie", commentable: @comment) Comment.create(user_id: @pedro.id, comment: "Pois deveria assistir é um filme sensacional", commentable: @comment) Comment.create(user_id: @jon.id, comment: "Irei assistir", commentable: @comment) |
Observe como criamos as relações polimórficas:
No diretório db/schema.rb podemos observar como foi criado nossa tabela comments:
123456 create_table "comments", force: :cascade do |t|...t.string "commentable_type"t.bigint "commentable_id"...endNo arquivo seeds.rb podemos ver a criação do @post_study pelo usuário pedro.
1 ex: @post_movie = Post.create(user_id: @pedro.id, post: "Hoje assisti o filme A start is Born")Ainda observando o que foi criado, podemos perceber que o usuário jon fez um comentário no post do pedro:
1 @comment = Comment.create(user_id: @jon.id, comment: "Esse filme é muito bom!", commentable: @post_movie)Nesse caso temos o commentable_id recebendo o valor do @post_movie.id e o commentable_type recebendo o valor do tipo do post_movie
O usuário pedro, faz um comentário ao comentário do jon, em seu post.
1 ex: Comment.create(user_id: @pedro.id, comment: "Achei o filme muito bom mas o do freddie é bem melhor", commentable: @comment)Aqui temos nosso modelo de comentário recebendo um objeto do tipo Comment em sua associação polimórfica. commentable_id recebendo o valor do @comment.id e o commentable_type recebendo o valor de “Comment”
É assim que o ruby funciona com relações polimórficas.
12. Rode no terminal o seguinte comando para executar o arquivo seeds.rb e popular o banco de dados:
1 |
rake db:seed |
13. Crie um controller com o nome posts e um método chamado index usando o seguinte comando:
1 |
rails generate controller posts index |
14. Para exigir que o usuário se autentique antes de acessar alguma rota da aplicação, substitua o código de app/controllers/applications_controller.rb por:
1 2 3 |
class ApplicationController < ActionController::Base before_action :authenticate_user! end |
15. Troque o código do arquivo app/controllers/posts_controller.rb por esse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
class PostsController < ApplicationController def index @posts = Array.new current_user.posts.each do |post| comments = Array.new Comment.where(commentable: post).each do |comment| cmmnts = Comment.where(commentable:comment).order("created_at ASC") comments.push({user:comment.user,comment:comment, comments: cmmnts}) end @posts.push({post: post, comments: comments}) end end end |
Aqui estamos criando um controller simples com apenas um método que vai unir todos os objetos e suas relações em um hash.
16. Substitua o código do arquivo app/views/posts/index.html.erb por esse:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<h1>Posts</h1> <% @posts.each do |post| %> <h3> <p> <%= current_user.name %> says: <%= post[:post][:post] %> </p> </h3> <div style="background-color:#eeeeee"> <% post[:comments].each do |comment| %> <p><b> <%= comment[:user][:name] %> says: </b><%= comment[:comment][:comment] %></p> <div style="background-color:#bdbdbd"> <% comment[:comments].each do |cmmnt| %> <p><b>  <%=cmmnt.user.name %> says: </b><%= cmmnt.comment %> </p> <% end %> </div> <% end %> </div> <br> <% end %> <% if user_signed_in? %> <li> <%= link_to('Logout', destroy_user_session_path, method: :delete) %> </li> <% end %> |
Esse código permite a visualização dos posts do usuário logado e seus comentários associados.
17. Para definir o root do projeto substitua o código do arquivo config/routes.rb por esse:
1 2 3 4 |
Rails.application.routes.draw do devise_for :users root 'posts#index' end |
18. Execute o seguinte commando e abre o browser em localhost:3000:
1 |
rails s |
19 – Faça o login com o usuário cadastrado: email: pedro@email.com e password: 12345678
20. Pronto, agora você pode observar que temos um post com um comentário e outros comentários associados a ele através da relação polimórfica o/
Conclusão
Conseguimos observar neste exemplo que realizando um bom uso das associações polimórficas é possível melhorar muito a qualidade do APP evitando tabelas desnecessárias no banco de dados.
Além disso, podemos observar neste exemplo também que o Ruby On Rails nos ajuda muito na hora de usar esse tipo de associação permitindo a geração dos models e migrations já com as relações presentes.
Se você curtiu aprender mais sobre polimorfismo compartilha esse artigo com seus amigos e deixa um comentário ai na página.
Obrigado o/

Não perca nenhum conteúdo
Receba nosso resumo semanal com os novos posts, cursos, talks e vagas o/
Primeira vez no OneBitCode? Curtiu esse conteúdo?
O OneBitCode tem muito mais para você!
O OneBitCode traz conteúdos de qualidade, e em português, sobre programação com foco em Ruby on Rails e também JavaScript.
Além disso, aqui sempre levamos à você conteúdos valiosos sobre a carreira de programação, dicas sobre currículos, portfólios, perfil profissional, soft skills, enfim, tudo o que você precisa saber para continuar evoluindo como Programador(a)!
Fique por dentro de todos os conteúdos o/
Nossas redes sociais:
📹 • https://youtube.com/Onebitcode [Live todas as terças-feiras às 19h)
💻 • https://linkedin.com/company/onebitcode
🙂 • https://facebook.com/onebitcode
📱 • https://instagram.com/one_bit_code
🐦 • https://twitter.com/onebitcode
Nossos cursos:
🥇 • Programador Full Stack Javascript em 8 Semanas
💎 • Curso Completo de Ruby
⚙ • what do you get a guy for his birthday if you just started dating
🐞 • online dating first skype chat
Espero que curta nossos conteúdos e sempre que precisar de ajuda, fala com a gente!
Estamos aqui para você 🙂
Bem-vindo à família OneBitCode o/
Muito bom, estava com dúvidas no assunto, obrigado por esclarecer!
o/
Olá, tudo bem? Show a publicação, quero dar continuidade, mas travei em uma parte, procurei na internet mudando as partes do erro, mas não achei a solução, você sabe como resolver? Primeiro, quando fui instalar o devise, ele dava um erro, informando que no adapter, o sqlite3 não carregava, eu mudei para o postgres, e deu certo. Gerei os modelos, tudo ok. Quando chegou na parte de criar o banco, ele fica dando esse erro. rake db:create db:migrate Database 'db/development/postgresql' already exists PG::SyntaxError: ERROR: syntax error at or near "." LINE 1: CREATE DATABASE "db/test"."postgresql" ENCODING = 'utf8' ^ :… Read more »
E ai Jonatas, beleza?
Parece que esse database já existe no seu postgreSQL.
Tenta rodar (isso vai apagar e recriar): rails db:drop db:create db:migrate
Abraço
Opa, tudo bem, graças a Deus! E ai, tudo ok? Leonardo, muito grato pela rápida resposta! Eu tentei o comando que você passou, ele mudou o nome do erro, mas ele aponta o mesmo erro! Quando copia o erro no terminal, não mostra aqui, mas ele fica apontando para o ponto . LINE 1: DROP DATABASE IF EXISTS “db/test“.”postgresql” Dropped database 'db/development/postgresql' PG::SyntaxError: ERROR: syntax error at or near "." LINE 1: DROP DATABASE IF EXISTS "db/test"<strong>.</strong>"postgresql" ^ : DROP DATABASE IF EXISTS "db/test"."postgresql" Couldn't drop database 'db/test.postgresql' rails aborted! ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR: syntax error at or near "." LINE… Read more »
Que estranho, como está o seu database.yml?
Então! o database.yml está assim # SQLite version 3.x # gem install sqlite3 # # Ensure the SQLite 3 gem is defined in your Gemfile # gem ‘sqlite3’ # default: &default adapter: postgresql pool: timeout: 5000 host: localhost development: <<: *default database: db/development.postgresql # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default database: db/test.postgresql production: <<: *default database: db/production.postgresql E o GemFile assim source 'https://rubygems.org' git_source(:github) do |repo_name| repo_name = "#{repo_name}/#{repo_name}" unless… Read more »
Muito boa a explicação. vou tentar executar esse tutorial.
Alguem sabe como fazer algo semelhante utilizando banco não relacional? MongoDB, MongoID por exemplo? Ou seja polimorfismo não relacional kkk. O banco do projeto a qual estou trabalhando ja é Mongo, porem estou em uma situação onde o polimorfismo cairia como uma luva.