2012-03-28 9 views
16

Estoy teniendo un modelo ActiveRecord con una asociación polimórfica así:Delegado todas las llamadas a métodos en un modelo a una asociación

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

Este modelo actúa como un proxy. Lo que necesito hacer es reenviar todas las llamadas a métodos sobre ese objeto al objeto asociado :reachable. Creo que delegate no ayudará aquí porque tengo que nombrar explícitamente todos los métodos que necesito para delegar. Necesito algo como delegate :all para delegar todos los métodos (no el método all).

Respuesta

16

Hay dos cosas que se pueden hacer aquí:

  1. Cuanto más lenta (en cuanto al rendimiento), pero el método más fácil es utilizar 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. El método más rápido rendimiento sería defina cada método dinámicamente que desea delegar:

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

Incluso podría usar ese método de una manera más dinámica, si lo desea, tomando la clase Reach, encontrando los métodos que están definidos en ella y solo, y definiendo solo aquellos en Accesible. Sin embargo, lo haría a mano porque hay algunos que probablemente no quieras incluir.

+0

con respecto a su primer enfoque. ¿Esto no llamará al método en el objeto 'Reach' si existe allí? – badawym

+0

Sí, lo siento, creo que escribí 'reach' cuando quise decir' alcanzable'. – Veraticus

+6

Si tiene una lista de métodos que desea delegar, puede ... delegarlos como de costumbre. – Andrew

4

para los carriles, hice lo siguiente:

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

la - User.new... es no anular los atributos existentes en User (por ejemplo, created_at)

No estoy seguro de cómo este enfoque funcionaría con el polimorfismo , sin embargo.

1

He encontrado una forma ordenada de abordar el problema usando refinamientos. Ya hay una clase en la biblioteca estándar que permite la delegación de cada llamada de método a un objeto de destino. Delegator and by extension SimpleDelegator Ahora hay una manera de insertar SimpleDelegator en su cadena de herencia sin heredar de ella directamente a través de refinamientos:

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

Ahora con el fin de tomar ventaja de SimpleDelegator fijó el objetivo delegación en una devolución de llamada después de inicializar este modo:

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

Esto es equivalente a heredar directamente de SimpleDelegator y configurar la delegación en la construcción, no hay una limpieza manual de los métodos para delegar y puede evitar el uso del método que falta.

Cuestiones relacionadas