2012-08-29 18 views
18

Me interesa saber cómo los rieles implementan filtros como before_filter.¿Cómo implementa los rieles before_filter?

Pero después de leer el código fuente, todavía estoy confundido.

Me di cuenta de que el marco de los raíles mantiene un filter_chain, y ejecuta los filtros antes del método de destino.

Pero, no entiendo un proceso importante: ¿cómo captura un raíl las llamadas a un método? Por ejemplo, tengo una clase Dog, y establecí un before_filter en la corteza del método.

Cuando llamo al dog.bark, los rieles deben capturar esta llamada de alguna manera, y ejecutar su método modificado en su lugar.

Sin embargo, no encuentro dicho código en el código fuente.

¿Alguien puede decirme la idea o indicar dónde está el código?

Respuesta

20

Cuando se establece un before_filter, o cualquier filtro similar (piensan after_filter, around_filter), por lo que estás haciendo, ya sea con un Symbol o una Proc, lambda o bloque.

before_filter :bark 
before_filter Proc.new { |k| k.bark } 

Lo anterior añade los símbolos o bloques para una pila here, llamando set_callback. Esto construye la 'cadena' a la que se refiere.

Cada elemento de esta 'cadena' es una instancia de la clase ActiveSupport::Callbacks::Callback. Esta clase sabe

  • El método (símbolo) o bloque debe ejecutar (es decir. La clase :bark método)
  • El contexto que debería ejecutarse dentro (es decir. La clase Dog)

Los Callback casos se añaden a un ActiveSupport::Callbacks::CallbackChain en __update_callbacks.

Cuando se inicializa cada clase Callback, _compile_filter se ejecuta para normalizar el filtro de la Symbol, Proc, lambda, o bloquear en un formato común, invocable.

Por último, cuando se ejecuta el CallbackChain, se llamará start en cada Callback ejemplo, y su en este punto que el filtro se ejecuta realmente en el contexto adecuado.


Es importante señalar que a usted le no crear un filtro como

before_filter dog.bark 

esto va a ejecutar dog.bark y pasar su valor de retorno a before_filter que se anexará a la CallbackChain. La intención es pasar algún tipo de instrucción al before_filter para que los rieles se ejecuten más tarde. Se podría hacer algo como lugar

before_filter Proc.new { d = Dog.new; d.bark } 

El código dentro del Proc no se ejecuta. cuando la línea de arriba es ejecutada por Rails. En cambio, se le dice a Rails que pase el Proc al CallbackChain. El Proc es la 'instrucción' que le está pasando a Rails para que se ejecute en el momento apropiado.


cómo en el primer lugar no raíles saben que he llamado: Corteza

En cuanto a esto, digamos que su clase Dog se define simplemente como

class Dog 
    def bark 

    end 

    def eat 

    end 
end 

(Aunque este es un terrible ejemplo), es posible que desee tener algo como

before_bark :eat 

Para ello, debe definir la devolución de llamada bark, y luego a su método de bark para ejecutar los relacionados bark devoluciones de llamada.

class Dog 
    extend ActiveModel::Callbacks 

    define_callbacks :bark 

    before_bark :eat 

    def bark 
    run_callbacks(:bark) { YOUR BARK CODE HERE } 
    end 

    def eat 

    end 
end 

Puedes ver cómo ActiveRecord::Callbacks hace esto.

Esto realmente es un mal ejemplo porque puedes (y deberías) simplemente llamar al eat directamente desde bark, pero esto debería aclarar el punto.

+0

¡Gracias al principio! Pero aún confusión. Usted dijo 'Finalmente, cuando se ejecuta el CallbackChain ...', y mi pregunta es ¿cómo sabe exactamente que los rieles deben ejecutarse CallbackChain? Diga, si configuro before_filter: eat to class Dog. Luego, cuando llame a dog.bark,: eat debería ejecutarse primero. Pero, ¿cómo sabe en primer lugar que he llamado: ladrar? – HanXu

+0

Actualicé mi respuesta. – deefour

+0

Mucho más claro, pero todavía un poco confundido. En su ejemplo, redefinimos ladrar manualmente, pero los rieles lo hacen automáticamente por algún código. No encuentro dónde están estos códigos. Según @harald, los rieles han modificado cada acción por adelantado. Noté 'attr_internal' en AbstractController :: Base, ¿hay magia? – HanXu

1

Rails no captura las llamadas de método de la manera que usted describe. Si nos fijamos en AbstractController::Base.process buscará el método para ser llamado para la acción despachada, ejecutará los filtros y luego llamará al método real. En otras palabras, su método de controlador no se llama directamente, sino a través de este método process.

+0

Lo tengo, carriles parece utilizar 'attr_internal' para cambiar cada método a un nuevo nombre interno y hacer todo con el nuevo nombre? – HanXu