В чем разница между [X, Y, Z] .each {| m | включить m} и включить X, Y, Z? - PullRequest
8 голосов
/ 21 апреля 2009

Примечание Первоначально это был вопрос 404 ошибок, но теперь вопрос, почему патч, который я применил, будет иметь значение.

Как получить кэшированное действие, возвращающее 404 для всех запросов, которые вызывают исключение ActiveRecord :: RecordNotFound, а не только для первого запроса?

Например, если вы запускаете пустой проект rails, добавляете модель продукта и контроллер, настраиваете свой database.yml, настраиваете бэкэнд своего кэша в production.rb, rake db: migrate, затем запускаете в производство и заходите на сайт для несуществующий объект, например http://localhost:3000/product/show/1234

class ProductController < ApplicationController

  caches_action :show

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

end

При первом обращении к странице возвращается страница 404, как и ожидалось. Однако каждый последующий переход по этому URL возвращает пустую страницу с 200 OK. Как вы получаете, чтобы он возвращал 404 каждый раз?

Вот запросы CURL, за которыми следуют журналы

~ $ 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

Второй ответ явно неверен.

Вот копия журнала для 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]

Действительно, если вы извлечете кэшированное действие из кэша, там будет какой-то пустой мусор.

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

Что я здесь не так делаю?

Редактировать

Я подтвердил, что в Rails 2.1.2 и 2.2.2 такого поведения нет, а в 2.3.2. (то есть старые версии не хранят пустой ответ в кеше, и они действительно выбрасывают 404 для последующих запросов)

У меня проблемы с тестированием на пограничных Rails, потому что при его загрузке возникает следующая ошибка при запуске сервера: foobar / vendor / rails / activesupport / lib / active_support / dependencies.rb: 440: в `load_missing_constant ': неинициализированная константа ActionController :: Failsafe (NameError)

Я проверил по текущей главе 2-3-стабильной ветви, 375e8976e3, и она тоже демонстрирует такое поведение.

Редактировать # 2 Я попытался отследить, когда произошло изменение в кодовой базе Rails, чтобы определить, было ли оно намеренным. Кажется, что этот, казалось бы, безобидный коммит - это то место, где начинается ошибка.

Вот подробности деления пополам, где 404 обозначает желаемое поведение, а 200 нежелательно.

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

Вот патч, который отменяет изменение, которое при применении к тегу v2.3.2.1, то есть dc88847e5ce392eed210b97525c14fca55852867, устраняет проблему. Я, однако, не достаточно умен, чтобы понять, почему это, казалось бы, маленькое изменение действительно имеет значение! Возможно, кто-то умнее меня мог бы пролить свет на ситуацию?

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

Редактировать # 3 Похоже, что патч также исправляет связанную с этим ошибку, показанную выше, в которой «Завершено в XYms (DB: Z) | 404 Not Found [http://0.0.0.0/product/1234]"» не отображалось в журнале.

Редактировать # 4 Вышеупомянутый патч сломал другие вещи в ActionPack, поэтому я углубился и создал исправление для проблемы, которая не наносит сопутствующий ущерб. Патч и все последующие обновления будут на рельсовом маяке

1 Ответ

16 голосов
/ 21 апреля 2009

Похоже, что include(X, Y, Z) на самом деле работает в другом порядке, чем include X; include Y; include Z.

Ниже я вставил код C, который реализует метод Module # include в 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;
}

Даже если вы не знакомы с внутренними компонентами Ruby C, довольно ясно, что в этой функции есть цикл for, повторяющийся вверх, чтобы проверить, что тип всех аргументов равен T_MODULE, а затем использует цикл while, повторяющийся вниз, чтобы фактически модули - так что модули в include(X, Y, Z) будут фактически включены в порядок Z, Y, X. Я не изучил все рассматриваемые модули Rails, но я думаю, что есть кое-что зависящее от порядка, которое начинало давать сбой после переключения порядка включения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...