Fixtures para Polymorphic Associations com o Rails 2
Junho 08, 2008 @ 01:00 PM
Uma dica rápida de como fazer suas fixtures para associações polimórficas. Digamos que a estrutura abaixo faz parte de sua aplicação:
1 2 3 4 5 6 7 8 9 10 11 |
class Address < ActiveRecord::Base belongs_to :addressable, :polymorphic => true end class Person < ActiveRecord::Base has_one :contact, :as => :addressable end class Company < ActiveRecord::Base has_one :contact, :as => :addressable end |
Abaixo as fixtures para esses modelos:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# Person daviscabral: username: daviscabral password: super_secret # Company impact: name = Impact Media # Address daviscabral_address: contactable: daviscabral (Person) address: Rua Meia Mole, 69 impact_address: addressable: impact (Company) address: Av. Brazuca, 999 |
O grande detalhe, é o (class_name) que é a chave que define o campo type do relacionamento.
Livro de Rails 2.1
O Carlos Brando, do blog Nome do Jogo, publicou o primeiro livro que cobre as novas funcionalidades de Rails 2.1. Junto do Marcos Tapajós, que deu uns tapas no livro revisando e escrevendo o código do fazedor de livros, e do Daniel Lopes, que fez a capa. Bem, códigos do fazedor de livros? Isso mesmo, eles escreveram um projeto em ruby que gera o PDF do livro.
O projeto está no Github e a galera está convidada a ajudar a traduzir para o inglês.
Ótima iniciativa de todos os envolvidos. A comunidade só ganha com atitudes como essa.
Sem perder tempo, fica também a dica do projeto para escrita de livros feito pelo Nando Vieira, do blog Simples idéias. O projeto é muito parecido, mas parece que ambos tiveram a mesma idéia.
Fica a minha sugestão para um merge dos projetos! :-)
Nova fase
Não comentei sobre isso aqui no Blog, mas fazem alguns meses que me desliguei da empresa onde trabalhava. Com a saída de lá, estou podendo me dedicar muito ao Rails e a projetos pessoais.
Com essas mudanças, aderi totalmente as melhores práticas, desde o SCRUM até o BDD. Isso acabou me levando a comprar um MacBook, e a testar o famoso textmate. Realmente o Mac OS é incrível. O textmate não fica atrás, nem as várias coisas novas que ando experimentando com essa nova aquisição.
Na quarta-feira passada, tive a oportunidade de presenciar e participar da primeira palestra sobre SCRUM aqui em Cascavel, PR, que foi realizada na Unioeste. Fiz uma gravação, mas o audio não saiu muito bom. Estou providenciando um microfone bluetooth para ver se a coisa fica melhor.
Demos uma introdução ao SCRUM e também já demos sinais que as próximas palestras e/ou mini-cursos serão sobre testes, mão na massa mesmo. Podem surgir bons artigos a respeito disso.
Polymorphic resources, de uma forma DRY
Maio 25, 2008 @ 03:00 PM

Modelo polimórfico
Um modelo polimórfico pode pertencer a mais de uma classe modelo pai. Por exemplo, Comments pode pertencer a Articles e Documents.
Para isso teriamos um campo string chamado type em nossa tabela comments. Esse campo é chave no Rails e é preenchido com a classe do modelo pai num relacionamento polimórfico.
Nossos modelos ficariam como abaixo:
1 2 3 4 5 6 7 8 9 10 11 |
class Article < ActiveRecord::Base has_many :comments, :as => :commentable end class Document < ActiveRecord::Base has_many :comments, :as => :commentable end class Comment < ActiveRecord::Base belongs_to :commentable, :polymorphic => true end |
Dessa maneira, você pode referenciar article.comments e document.comments para seus objetos Article e Document, respectivamente.
Rotas para nosso controlador polimórfico
Agora temos nossos modelos Article e Document possuindo o mesmo modelo Comment. Definindo nossas rotas, veremos que ele será polimórfico também, pois vai estar aninhado aos controladores Articles e Documents .
Abaixo as rotas:
1 2 3 4 |
ActionController::Routing::Routes.draw do |map| map.resources :articles, :has_many => [ :comments ] map.resources :documents, :has_many => [ :comments ] end |
Abaixo exemplo de alguns itens gerados a partir dessas duas rotas:
1 2 3 4 5 |
article_comment_path(@article,@comment) # => /articles/1/comments/22 article_new_comment_path(@article) # => /articles/1/comments/new document_comment_path(@document,@comment) # => /documents/1/comments/22 document_new_comment_path(@document) # => /documents/1/comments/new |
Controlador polimórfico
Através das novas rotas, vimos que um comentário poderia ser criado a partir de /articles/1/comments/new ou /documents/1/comments/new. Podemos fazer com que nosso controlador gerencie isso de uma forma simples, como abaixo:
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 28 29 30 31 32 33 34 35 36 37 |
class CommentsController < ApplicationController def new @parent = parent_object @comment = Comment.new end def create @parent = parent_object @comment = @parent.comments.build(params[:comment]) if @comment.valid? and @comment.save redirect_to parent_url(@parent) else render :action => 'new' end end private def parent_object case when params[:article_id] then Article.find_by_id(params[:article_id]) when params[:document_id] then Document.find_by_id(params[:document_id]) end end def parent_url(parent) case when params[:article_id] then article_url(parent) when params[:document_id] then document_url(parent) end end end |
Faz o que precisa? Faz. Mas isso pode ser mais bonito. Dessa forma, sempre que adicionar-mos um item que pode receber um comentário, ele deverá criar uma nova condição em nossos case. Para isso, vamos passar todo esse código de forma genérica para nosso ApplicationController.
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 28 29 30 31 |
class ApplicationController < ActionController::Base protected class << self attr_reader :parents def parent_resources(*parents) @parents = parents end end def parent_id(parent) request.path_parameters["#{ parent }_id"] end def parent_type self.class.parents.detect { |parent| parent_id(parent) } end def parent_class parent_type && parent_type.to_s.classify.constantize end def parent_object parent_class && parent_class.find_by_id(parent_id(parent_type)) end end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
class CommentsController < ApplicationController parent_resources :article, :document def new @parent = parent_object @comment = Comment.new end def create @parent = parent_object @comment = @parent.comments.build(params[:comment]) if @comment.valid? and @comment.save redirect_to send("#{ parent_type }_url", @parent) else render :action => 'new' end end end |
Essa solução foi publicada inicialmente por Val Aleksenko, em seu blog Revolution On Rails. Através dela, criei um plugin chamado parent_resources.
Isso será adicionado ao Edge e já tem um patch sendo desenvolvido. Então esse plugin tem um tempo bem curto de vida. Mas a solução me salvou de várias repetições de código. Fica ae para o caso de alguem precisar.
STI e Polymorphic associations
Maio 03, 2008 @ 02:00 PM

Essa semana me deparei com um problema: meus modelos que herdavam do modelo User, faziam parte de uma associação polimórfica, onde cada tipo distinto de usuário, poderia ter um vepiculo. Esse problema é documentado no manual, e para utilizar
O meu problema era, que ao chamar current_user.vehicles.find ele pesquisava por veículos com owner_type igual a User.
Quando utilizo associações polimórficas, eu preciso de dois campos para isso, o campo que armazena o código, id, e o campo que armazena o tipo, type. Como minha associação se chama owner, meu modelo possui dois campos owner_id e owner_type.
Mas na hora do meu cadastro, o tipo armazenado no veículo era Manager, que é um modelo User.
Para o uso de associações polimórficas com Vehicle para que ele armazene a classe base do objeto que estiver sendo fornecido como owner, como está no código abaixo:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class Vechile < ActiveRecord::Base belongs_to :owner, :polymorphic => true def owner_type=(sType) super(sType.to_s.classify.constantize.base_class.to_s) end end class User < ActiveRecord::Base has_many :vehicles, :as => :owner, :dependent => :destroy end class Manager < User end class Administrator < User end |


