Es gibt unterschiedliche Gründe, eine has_many oder has_and_belongs_to_many Assoziation zu erweitern.
Die Proxy Logik kann auch wiederverwendet und über verschiedene Assoziationen geteilt werden. Dafür notwendig ist lediglich die Verlagerung der Proxy Erweiterung in ein Module.
Assoziation mit Proxy Erweiterung
Das Beispiel ist ein sehr vereinfachter Bestellprozess, bestehend aus einem Model für die Bestellung und den bestellbaren Produkten, wobei die m:n Beziehung über ein has_many through abgebildet ist:
class Order < ApplicationRecord
has_many :product_orders
has_many :products, through: :product_orders
end
class ProductOrder < ApplicationRecord
belongs_to :product
belongs_to :order
def to_s
id.to_s.rjust(5, '0')
end
end
class Product < ApplicationRecord
delegate :to_s, to: :name
end
Die beiden Models Product und ProductOrder haben jeweils eine Serializer Methode (#to_s).
Assoziation mit Proxy Erweiterung
Eine sehr einfache Proxy Erweiterung der .products Assoziation könnte ebenfalls eine to_s Methode sein:
class Order < ApplicationRecord
has_many :product_orders
has_many :products, through: :product_orders do
def to_s
map(&:to_s).join(', ')
end
end
end
Die Liste der Produkte einer Bestellung:
order = Order.first
order.products
# => #<ActiveRecord::Associations::CollectionProxy [#<Product id: 1, name: "TV">, #<Product id: 4, name: "Chair">]>
und mithilfe der Proxy Extension:
order.products.to_s # => "TV, Chair"
Modularisierung einer has_many Proxy Erweiterung
Im Falle, dass die has_many Proxy Erweiterung auch für andere Assoziationen angewendet werden soll, macht es Sinn die Logik in ein Module auszulagern und die entsprechenden Assoziationen mit dem Module zu erweitern.
Das Module:
# app/models/concerns/association_proxy/humanizable.rb
module AssociationProxy
module Humanizable
def to_s
map(&:to_s).join(', ')
end
end
end
und zwei Assoziationen, die mit dem selben Module erweitert wurden:
class Order < ApplicationRecord
has_many :product_orders,
extend: AssociationProxy::Humanizable
has_many :products, through: :product_orders,
extend: AssociationProxy::Humanizable
end
Die Verwendung der modularisierten Proxy Erweiterung:
order.products.to_s # => "TV, Chair"
order.product_orders.to_s # => "00001, 00002"
Proxy Erweiterungen kombinieren
Eine has_many Assoziation kann auch mit einer Module Collection erweitert werden:
class Order < ApplicationRecord
has_many :products, through: :product_orders,
extend: [AssociationProxy::Humanizable, AssociationProxy::OtherModule]
end