Qualquer um que comece a desenvolver com Active Record (AR), minha primeira recomendação é, para tudo e leia: A Guide to Active Record Associations ou O Guia de Associações do Active Record. O guia é bem completo, e descreve muito bem os tipos de associações que estão disponíveis no AR.
Association Proxy, #wtf !
As associações:
belongs_to
has_one
has_many
has_and_belongs_to_many
Quando usadas, adicionam alguns métodos (veja Detailed Association Reference). Por exemplo, ao declarar uma associação belongs_to, o model “ganhará” os seguintes métodos:
association(force_reload = false)
association=(associate)
build_association(attributes = {})
create_association(attributes = {})
Onde association, será substituído pelo nome da associação. Exemplo retirado do guides:
class Order < ActiveRecord::Base
belongs_to :customer
end
Cada instância de Order, conterá os métodos:
customer
customer=
build_customer
create_customer
O association proxy, é o objeto que faz a ligação do objeto que contém a associação, conhecido como owner, e o objeto associado, conhecido como target.
Legal e daí !?!
Graças ao association proxy, ao declarar uma associação, podemos extendê-la e adicionar comportamentos “customizados”. No guia, é citado como Association Extensions. O código de exemplo abaixo, está no github em random-samples.
Para exemplificar, vamos criar um modulo que adiciona o comportamento de uma galeria a qualquer coleção.
module GalleryColletion
def current=(curr = nil)
@current, @index = nil
if curr.nil?
@current = collection.first
@index = 0
else
collection.each_with_index do |item, index|
if item.id.to_i == curr.to_i
@current = item
@index = index
end
end
end
@current
end
def current
@current
end
def position
@index + 1
end
def previous?
return false if @index.nil?
!!(@index - 1 >= 0)
end
def previous
collection[@index - 1] if previous?
end
def next?
return false if @index.nil?
!!(@index + 1 < collection.size)
end
def next
collection[@index + 1] if next?
end
private
def collection
proxy_owner.send(proxy_reflection.name)
end
end
Note que o modulo está na pasta lib, logo, a pasta tem que ser adicionada no path via config/environment.rb.
Para extender a associação, declare:
class Article < ActiveRecord::Base
has_and_belongs_to_many :images, :extend => GalleryColletion
end
Article model, Image model aqui.
Agora para navegar entre as imagens, você pode usar:
a = Article.first
a.images.current = 1 #1 e o Image.id que deseja selecionar
a.images.current
a.images.position
a.images.next?
a.images.next
a.images.previous?
a.images.previous
Caso esteja com coragem, baixe o projeto e veja rodando.
Dúvidas, sugestões, algum “case” de sucesso, comente!