Делегировать все вызовы методов на модели на объединение
У меня есть модель ActiveRecord с полиморфной ассоциацией вроде этого:
class Reach < ActiveRecord::Base
belongs_to :reachable, :polymorphic => true
end
Эта модель действует как прокси. Что мне нужно сделать, это переслать все вызовы методов на этот объект на связанный объект :reachable
. Я думаю, что delegate
здесь не поможет, потому что я должен явно указать все методы, которые мне нужно делегировать. Мне нужно что-то вроде delegate :all
делегировать все методы (не all
).
Ответы
Ответ 1
Здесь вы можете сделать две вещи:
-
Более медленный (с точки зрения производительности), но более простой способ заключается в использовании метода_поиск:
class Reach < ActiveRecord::Base
def method_missing(method, *args)
return reachable.send(method, *args) if reachable.respond_to?(method)
super
end
end
-
Более быстрый способ заключается в том, чтобы определить каждый метод динамически, который вы хотите делегировать:
class Reach < ActiveRecord::Base
[:all, :my, :methods, :here].each do |m|
define_method(m) do |*args|
reachable.send(m, *args)
end
end
end
Вы могли бы даже использовать этот метод более динамично, если хотите, взяв класс Reach, найдя методы, которые определены на нем, и он один, и определяя только те, которые находятся на Reachable. Я сделал бы это вручную, потому что есть некоторые, которые вы, вероятно, не захотите включить.
Ответ 2
Для Rails я сделал следующее:
class User < ApplicationRecord
has_one :member
delegate (Member.new.attributes.keys - User.new.attributes.keys), to: :member
end
- User.new...
должен не переопределять существующие атрибуты на User
(например, created_at
)
Я не уверен, как этот подход будет работать с полиморфизмом.
Ответ 3
Я нашел аккуратный способ подойти к проблеме, используя уточнения. В стандартной библиотеке уже есть класс, который позволяет делегировать вызов каждого метода целевому объекту. Delegator
и по расширению SimpleDelegator
Теперь есть способ вставить SimpleDelegator
в цепочку наследования, не наследуя его напрямую, используя уточнения:
def self.include_delegator
mod = Module.new do
include refine(SimpleDelegator) { yield if block_given? }
end
self.send :include, mod
end
include_delegator
Теперь, чтобы воспользоваться SimpleDelegator
, установите цель делегирования в после инициализации обратного вызова следующим образом:
after_initialize do |instance|
__setobj__(instance.reachable)
end
Это эквивалентно наследованию непосредственно от SimpleDelegator и настройке делегирования в конструкции, нет ручного ведения методов для делегирования, и вы можете избежать использования метода.