Em linguagens de programação orientadas a objetos, como o Ruby, polimorfismo descreve um conceito que provê uma única interface compartilhada entre objetos diferentes, dependendo de sua classe ou tipo de dado. Isso pode ser obtido de algumas formas, tirando proveito das propriedades das linguagens de programação como, por exemplo, Herança e duck typing. Neste texto, vamos falar sobre uma forma específica de usarmos polimorfismo em Ruby on Rails: a Associação Polimórfica.
Associação Polimórfica
O framework Rails oferece uma forma muito elegante de usarmos polimorfismo por meio da Associação Polimórfica. Essa estratégia se baseia no conceito de Polimorfismo Paramétrico. De acordo com este artigo:
Polimorfismo Paramétrico: código que é escrito sem menção a um tipo específico e, portanto, pode ser usado de forma transparente com qualquer número de tipos.
Para falar mais sobre Associações Polimórficas, vamos utilizar um exemplo importado das redes sociais. É muito comum que publicações nas redes possam ser "curtidas" por outros usuários. Além disso, as fotos e até os comentários nessa publicação também podem receber curtidas. Nesse cenário, nossa aplicação teria as classes Post
, Comment
e Image
, cada um deles podendo receber curtidas, representadas pela classe Like
. Se cada instância de Like
estiver atrelada a um tipo específico, seria necessário criar mais um model para cada associação: post_like
, comment_like
e image_like
, com as amarrações necessárias entre eles.
Utilizando as Associações Polimórficas, um Model pode pertencer a mais de um Model com uma única associação. Vamos fazer o processo passo a passo começando pela criação dos Models com os comandos do Rails.
$ rails generate model post
$ rails generate model comment
$ rails generate model image
Agora falta gerar o Model Like
e é aqui que as coisas ficam interessantes. Com Rails, podemos gerá-lo usando o seguinte comando:
$ rails generate model like likeable:references{polymorphic}
Lembre de rodar
rails db:migrate
em algum momento depois de gerar os Models.
No arquivo like.rb
, a declaração do belongs_to
polimórfico é responsável pela configuração da interface que os outros Models poderão usar:
class Like < ApplicationRecord
belongs_to :likeable, polymorphic: true
end
E a sua migration ficou assim:
class CreateLikes < ActiveRecord::Migration[5.0]
def change
create_table :likes do |t|
t.references :likeable, polymorphic: true, index: true
t.timestamps
end
end
end
Note que likeable
será usado como referência nas amarrações polimórficas. Assim, para finalizar as associações, precisamos declarar o has_many
em cada um dos outros Models que podem receber "curtidas":
class Post < ApplicationRecord
has_many :likes, as: :likeable
end
class Comment < ApplicationRecord
has_many :likes, as: :likeable
end
class Image < ApplicationRecord
has_many :likes, as: :likeable
end
Agora que está tudo pronto, podemos observar como isso vai funcionar na prática, fazendo alguns testes no console do Rails.
$ rails console
Running via Spring preloader in process 10918
Loading development environment (Rails 5.2.3)
2.6.0 :001 > article = Post.create
=> #<Post id: 1, created_at: "2019-07-31 15:21:59", updated_at: "2019-07-31 15:21:59">
2.6.0 :002 > article.likes.create
=> #<Like id: 1, likeable_type: "Post", likeable_id: 1, created_at: "2019-07-31 15:22:18", updated_at: "2019-07-31 15:22:18">
Note que likeable_id
se refere ao objeto article
ao qual a instância de Like
está associada. Nesse caso é 1
, pois é o primeiro objeto da classe Post
. Agora, é possível resgatar as curtidas da publicação, por exemplo, com article.likes
, que nos retorna um Array com todos os likes
associados ao objeto.
2.6.0 :003 > article.likes
=> #<ActiveRecord::Associations::CollectionProxy [#<Like id: 1, likeable_type: "Post", likeable_id: 1, created_at: "2019-07-31 15:22:18", updated_at: "2019-07-31 15:22:18">]>
Quando criamos mais um like
para article
:
2.6.0 :004 > article.likes.create
=> #<Like id: 2, likeable_type: "Post", likeable_id: 1, created_at: "2019-07-31 15:23:20", updated_at: "2019-07-31 15:23:20">
A instância criada tem id
2, mas likeable_id
permanece o mesmo, pois ele está associado ao mesmo objeto article
.
Graças à associação polimórfica, objetos da classe 'Like' podem ser atrelados a qualquer outro objeto ao qual atribuímos a característica ''likeable', independente do seu tipo. :)
Você pode ler mais sobre a associação polimórfica e outras maneiras de utilizar o polimorfismo nos artigos listados abaixo.