2009-04-20 12 views
8

Nota Esto originalmente comenzó como una pregunta sobre errores 404, pero ahora es una pregunta por qué el parche que apliqué marcaría la diferencia.Cuál es la diferencia entre [X, Y, Z] .each {| m | incluye m} e incluye X, Y, Z?

¿Cómo se obtiene una acción en caché para devolver un 404 en todas las solicitudes que generan una excepción ActiveRecord :: RecordNotFound, no solo la primera solicitud?

Por ejemplo, si inicia un proyecto de rieles vacíos, agregue un modelo de Producto y controlador, configure su database.yml, configure su backend de caché en production.rb, rake db: migrate, luego inicie en producción y acceda al sitio para un objeto inexistente, por ejemplo http://localhost:3000/product/show/1234

class ProductController < ApplicationController 

    caches_action :show 

    def show 
    @product = Product.find(params[:id]) 
    render :text => "asdf" 
    end 

end 

La primera vez que la página es golpeado, devuelve la página 404 como se esperaba. Sin embargo, cada hit posterior a esa URL devuelve una página en blanco con 200 OK. ¿Cómo consigues que regrese 404 todo el tiempo?

Éstos son las solicitudes CURL, seguido por la logs

~ $ curl -I http://0.0.0.0:3000/product/show/1234 
HTTP/1.1 404 Not Found 
Connection: close 
Date: Mon, 20 Apr 2009 22:49:18 GMT 
Content-Type: text/html; charset=utf-8 
Cache-Control: no-cache 
Content-Length: 14097 

~ $ curl -I http://0.0.0.0:3000/product/show/1234 
HTTP/1.1 200 OK 
Connection: close 
Date: Mon, 20 Apr 2009 22:49:19 GMT 
X-Runtime: 6 
Content-Type: text/html; charset=utf-8 
Cache-Control: no-cache 
Content-Length: 0 

La segunda respuesta es claramente erróneo.

Aquí está una copia del registro de las solicitudes 2:

Processing ProductController#show (for 127.0.0.1 at 2009-04-20 17:35:24) [GET] 
    Parameters: {"id"=>"1234"} 

ActiveRecord::RecordNotFound (Couldn't find Product with ID=1234): 
    app/controllers/product_controller.rb:6:in `show' 

Rendering rescues/layout (not_found) 


Processing ProductController#show (for 127.0.0.1 at 2009-04-20 17:35:30) [GET] 
    Parameters: {"id"=>"1234"} 
Filter chain halted as [#<ActionController::Caching::Actions::ActionCacheFilter:0x23e36d4 @options={:cache_path=>nil, :store_options=>{}, :layout=>nil}>] rendered_or_redirected. 
Filter chain halted as [#<ActionController::Filters::AroundFilter:0x23e3580 @kind=:filter, @options={:unless=>nil, :if=>nil, :only=>#<Set: {"show"}>}, @method=#<ActionController::Caching::Actions::ActionCacheFilter:0x23e36d4 @options={:cache_path=>nil, :store_options=>{}, :layout=>nil}>, @identifier=nil>] did_not_yield. 
Completed in 12ms (View: 0, DB: 0) | 200 OK [http://0.0.0.0/product/show/1234] 

De hecho, si se tira de la acción en caché de la memoria caché, tiene algún tipo de basura vacío en ese país.

cache.fetch("views/0.0.0.0:3000/product/show/1234") 
=> ["", nil, [], []] 

¿Qué estoy haciendo mal aquí?

Editar

He confirmado que Rails 2.1.2 y 2.2.2 no presentan este comportamiento, pero lo hace 2.3.2. (es decir, las versiones anteriores no almacenan una respuesta vacía en la memoria caché y de hecho arrojan un 404 para las solicitudes posteriores)

Tengo problemas para realizar pruebas contra bordes de borde, ya que al cargarlo se produce el siguiente error al iniciar el servidor: foobar/vendedor/carriles/ActiveSupport/lib/active_support/dependencies.rb: 440: en `load_missing_constant ': ActionController constante sin inicializar :: Failsafe (NameError)

he probado contra la cabeza actual del 2 -3-stable branch, 375e8976e3, y también presenta este comportamiento.

Edit # 2 Intenté rastrear cuando ocurrió el cambio en la base de código de Rails para determinar si fue intencional. Parece que this seemingly innocuous commit es donde comienza el error.

Aquí están los detalles de la bisección, donde 404 indica el comportamiento deseado, 200 no deseado.

 
2-3-stable branch 
    375e8976e3 - 200 
    b1c989f28d - 200 
    beca1f2e15 - 200 
    f1fff0a48 - 200 
    f1e20ce9a7 - 200 
    a5004573d8 - 200 
    2e1132fad8 - 200 - the difference seems to start at this commit 
    c69d8c043f - 404 
    d961592886 - 404 
    276ec16007 - 404 
    0efec6452 - 404 
    13c6c3cfc5 - 404 
    fb2325e35 - 404 

2-2 stable 
    3cb89257b4 - 404 

Aquí es un parche que invierte el cambio, que cuando se aplica a la etiqueta v2.3.2.1, es decir dc88847e5ce392eed210b97525c14fca55852867, soluciona el problema. Yo, sin embargo, no soy lo suficientemente inteligente como para comprender por qué este cambio aparentemente pequeño realmente marcaría la diferencia. Quizás alguien más inteligente que yo podría arrojar algo de luz sobre la situación?

diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb 
index 0facf70..0790807 100644 
--- a/actionpack/lib/action_controller/base.rb 
+++ b/actionpack/lib/action_controller/base.rb 
@@ -1403,12 +1403,9 @@ module ActionController #:nodoc: 
    end 

    Base.class_eval do 
- [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers, 
-  Cookies, Caching, Verification, Streaming, SessionManagement, 
-  HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, 
-  RecordIdentifier, RequestForgeryProtection, Translation 
- ].each do |mod| 
-  include mod 
- end 
+ include Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers 
+ include Cookies, Caching, Verification, Streaming, SessionManagement 
+ include HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods 
+ include RecordIdentifier, RequestForgeryProtection, Translation 
    end 
end 

Edición # 3 El parche parece solucionar también el error relacionado, mostrado anteriormente, donde el "completó en XYms (DB: Z) | 404 No se ha encontrado [http://0.0.0.0/product/1234]" no se presentaron en el registro.

Edición # 4 El parche anterior se rompió otras cosas en ActionPack, por lo que profundizó y generó una solución para el problema que no causa daños colaterales. El parche y las actualizaciones posteriores se encontrarán en the rails lighthouse

Respuesta

16

Parece que include(X, Y, Z) en realidad funciona en un orden diferente que include X; include Y; include Z.

A continuación he pegado el código C que implementa el método Module # include en Ruby 1.8.6.

static VALUE 
rb_mod_include(argc, argv, module) 
    int argc; 
    VALUE *argv; 
    VALUE module; 
{ 
    int i; 

    for (i=0; i<argc; i++) Check_Type(argv[i], T_MODULE); 
    while (argc--) { 
     rb_funcall(argv[argc], rb_intern("append_features"), 1, module); 
     rb_funcall(argv[argc], rb_intern("included"), 1, module); 
    } 
    return module; 
} 

Incluso si usted no está familiarizado con las partes internas C de Ruby, que está bastante claro que esta función tiene un bucle iterando hacia arriba para comprobar que el tipo de todos los argumentos es T_MODULE, y luego utiliza un iteración bucle while hacia abajo para incluir realmente los módulos, por lo que los módulos en include(X, Y, Z) se incluirán realmente en el pedido Z, Y, X. No he revisado todos los módulos de Rails en cuestión, pero imagino que hay algo dependiente del pedido que comenzó a fallar una vez que se cambió el orden de inclusión.

+0

de hecho, eso parece razonable. Gracias por su investigación y visión! –

Cuestiones relacionadas