2012-03-28 13 views
16

Mam modelu ActiveRecord z polimorficznych związku jak ten:Delegat wszystkim metoda wzywa modelu do stowarzyszenia

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

Model ten działa jak serwer proxy. Muszę tylko przekazać wszystkie wywołania metod tego obiektu do powiązanego obiektu :reachable. Myślę, że delegate nie pomoże tutaj, ponieważ muszę wyraźnie wymienić wszystkie metody, które muszę przekazać. Potrzebuję czegoś takiego jak delegate :all do delegowania wszystkich metod (nie metody all).

Odpowiedz

16

Są dwie rzeczy, które można zrobić tutaj:

  1. Wolniejszy (performance-wise), ale łatwiej metodą jest użycie 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. Metoda szybsza jego efektywności byłoby Zdefiniuj każdą metodę dynamicznie, którą chcesz przekazać:

    class Reach < ActiveRecord::Base 
    
        [:all, :my, :methods, :here].each do |m| 
        define_method(m) do |*args| 
         reachable.send(m, *args) 
        end 
        end 
    end 
    

Możesz nawet użyć tej metody w bardziej dynamiczny sposób, jeśli chcesz, biorąc klasę zasięgu, znajdując metody, które są na niej zdefiniowane i samodzielnie, i definiując tylko te, które są dostępne na Reachable. Zrobiłbym to ręcznie, ale ponieważ niektóre z nich prawdopodobnie nie będą ci się podobały.

+0

odnośnie Twojego pierwszego podejścia. Czy to nie wywoła metody w obiekcie 'Reach', jeśli istnieje? – badawym

+0

Tak, przykro mi, myślę, że wpisałem 'reach', kiedy miałem na myśli' osiągalny'. – Veraticus

+6

Jeśli masz listę metod, które chcesz delegować, możesz po prostu ... przekazać je jak zwykle. – Andrew

4

do szyn, zrobiłem co następuje:

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

- User.new... jest nie zastępują istniejących atrybutów User (np created_at)

Nie jestem pewien, jak to podejście będzie działać z polimorfizmem , jednak.

1

Znalazłem schludny sposób podejścia do problemu za pomocą udoskonaleń. Istnieje już klasa w standardowej bibliotece, która pozwala na delegowanie każdego wywołania metody do obiektu docelowego. Delegator and by extension SimpleDelegator Teraz istnieje sposób, aby wstawić SimpleDelegator do łańcucha dziedziczenia bez dziedziczenie z nim bezpośrednio z poprawkami:

def self.include_delegator 
    mod = Module.new do 
    include refine(SimpleDelegator) { yield if block_given? } 
    end 
    self.send :include, mod 
end 
include_delegator 

Teraz w celu skorzystania z SimpleDelegator docelową delegacji w zwrotnego po zainicjowanie tak:

after_initialize do |instance| 
    __setobj__(instance.reachable) 
end 

Jest to równoznaczne z odziedziczeniem bezpośrednio z SimpleDelegator i ustawieniem delegacji w konstrukcji, nie ma ręcznego porządkowania metod do delegowania i można uniknąć stosowania brakującej metody.