2012-03-01 8 views
5

Déjeme visualizar eso para usted.¿Cómo detecta ActiveRecord la última llamada al método en la cadena?

class Product < ActiveRecord::Base 
end 

Product.first.title 
#=> "My sample product" 

Nada extraordinario aquí. Solo una simple llamada a un método. Ahora eche un vistazo al siguiente ejemplo.

class Product < ActiveRecord::Base 
    def method_missing 
    end 
end 

Product.first.title 
#=> nil 

Product.first 
Product.first.title 
#=> "My sample product" 

¿Cómo es esto posible? De alguna manera, determinan el final de la cadena de métodos y actúan sobre eso? Al menos esa es mi teoría.

¿Alguien puede explicar este comportamiento?

Respuesta

7

Estás viendo un artefacto de usar irb para investigar cosas.

Cuando se dice esto:

> Product.first.title 
#=> nil 

Su method_missing se llamará a lazy-cargar el método title y se obtiene nil.

Cuando se dice esto:

> Product.first 

Estás haciendo efectivamente esto:

> p = Product.first; puts p.inspect 

La primera instancia del producto se cargará y luego irb llamará inspect en él y AR añadirá el métodos de acceso en el camino. El resultado es que el Producto ahora tendrá un método title. Por lo tanto, hacer esto:

> Product.first 
> Product.first.title 

no llamará a su method_missing en absoluto, ya que habrá un verdadero método para titleProduct.first.title llamar.

si intenta de nuevo como esto:

> Product.first; nil 
> Product.first.title 

verás dos nil s.


En lo que va de encadenamiento, ActiveRecord en realidad no detectar el final, es sólo que algunos método llama, naturalmente, requiere datos reales de la base de datos y otros no.

Si llama where, order, o cualquiera de los otros métodos de consultas, de obtener una instancia ActiveRecord::Relation atrás y se pueden encadenar más métodos de consulta y alcances en ese objeto relación. Por ejemplo, where (que ActiveRecord :: Relation obtiene mediante la inclusión de ActiveRecord::QueryMethods) tiene el siguiente aspecto:

def where(opts, *rest) 
    return self if opts.blank? 

    relation = clone 
    relation.where_values += build_where(opts, rest) 
    relation 
end 

lo que sólo tiene una copia de la consulta actual, añade algunas cosas para la copia, y le da la copia espalda.

Si llama first, last, to_a, all, cualquiera de los métodos Enumerable (es decir, se llama a each), ...luego pregunta por instancias específicas y ActiveRecord tendrá que ejecutar la consulta para realizar las instancias del modelo en cuestión. Por ejemplo, ActiveRecord::Relation#to_a se parece a esto:

def to_a 
    logging_query_plan do 
    exec_queries 
    end 
end 

y all es poco más que una envoltura alrededor de to_a.

ActiveRecord realmente no sabe dónde está el final de la cadena, simplemente no carga nada desde la base de datos hasta que lo tiene que decir para que la cadena termine diciendo vaya y recíbeme algunos datos.

+0

Creo que detecta el final de la cadena: ¡es donde no se obtiene un objeto proxy buscador! – phoet

+0

Esto no es de lo que se trata la pregunta. La pregunta es por qué '' 'Product.first.title''' devuelve' '' nil''' hasta que se invoca '' 'Product.first''', cuando' '' method_missing''' se usa dentro de un modelo. Una vez que haya llamado '' 'Product.first''' title, devuelve' '' My sample blog'''. – user544941

+0

@ user544941: Ah, veo por qué pregunta: qué está reemplazando o eludiendo su 'method_missing'. –

Cuestiones relacionadas