Quem sou eu

Recommend Me

Meu nome é Davis Zanetti Cabral, tenho 23 anos, sou muito bem casado, tenho uma filha linda, sou programador e atuo na área de Desenvolvimento Web há mais de 8 anos. Atualmente trabalho em casa e presto serviços em Ruby on Rails, PHP e xHTML/CSS. Saiba mais

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 STI com associações polimórficas é necessário uma pequena alteração no seu modelo.

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 STI de User.

Para o uso de associações polimórficas com STI eu preciso de um pequeno ajuste no meu modelo 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