Делегировать все вызовы методов в модели для ассоциации - PullRequest
17 голосов
/ 28 марта 2012

У меня есть модель ActiveRecord с полиморфной ассоциацией, подобной этой:

class Reach < ActiveRecord::Base
  belongs_to :reachable, :polymorphic => true
end

Эта модель действует как прокси.Что мне нужно сделать, так это перенаправить все вызовы методов этого объекта на связанный объект :reachable.Я думаю, delegate здесь не поможет, потому что я должен явно назвать все методы, которые мне нужно делегировать.Мне нужно что-то вроде delegate :all для делегирования всех методов (не all method).

Ответы [ 4 ]

17 голосов
/ 28 марта 2012

Здесь можно сделать две вещи:

  1. Более медленным (с точки зрения производительности), но более простым методом является использование method_missing:

    class Reach < ActiveRecord::Base
    
      def method_missing(method, *args)
        return reachable.send(method, *args) if reachable.respond_to?(method)
        super
      end
    end
    
  2. Более быстрым методом будет динамическое определение каждого метода, который вы хотите делегировать:

    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. Я бы сделал это вручную, потому что есть некоторые, которые вы, вероятно, не захотите включать.

5 голосов
/ 11 мая 2018

Начиная с Rails 5.1 + вы можете делегировать все, что не реализовано, с помощью delegate_missing_to :reachable

По сути, делайте то, что ожидаете.Вы можете прочитать больше на Api Doc

Если вы застряли в предыдущей версии, просто порекомендуйте использовать method_missing из @ Veraticus answer , это меньше производительности- как уже упоминалось, но я думаю, что это более гибкий подход.

5 голосов
/ 29 января 2017

Для Rails я сделал следующее:

class User < ApplicationRecord
  has_one :member
  delegate (Member.new.attributes.keys - User.new.attributes.keys), to: :member
end

- User.new... не должен переопределять существующие атрибуты на User (например, created_at)

I'mоднако я не уверен, как этот подход будет работать с полиморфизмом.

1 голос
/ 30 января 2017

Я нашел изящный способ решения проблемы с помощью доработок. В стандартной библиотеке уже есть класс, который позволяет делегировать каждый вызов метода целевому объекту. 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 и установке делегирования в конструкции, отсутствует ручное ведение методов делегирования, и вы можете избежать использования отсутствующего метода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...