2009-08-27 58 views
10

我編寫了自己的中間件,爲我們的應用程序提供API端點。中間件加載提供API方法的類,並將請求路由到適當的類/方法。這些類通過String#constantize動態加載。類在自定義中間件中未捕獲的異常後停止加載

在開發模式下運行時,類會自動重新加載。但是,如果存在未捕獲的異常(隨後由故障安全中間件處理),則自動重新加載停止工作。 constantize仍然被稱爲,但它似乎返回舊類。

看來還有其他東西可以卸載類,而未捕獲的異常會破壞它。這可能是什麼?

運行Ruby 1.8.7,Rails 2.3.3和Thin 1.2.2。

+0

我也有這個問題。 – 2011-04-14 18:52:26

回答

0

Rails會緩存很多類並在開發模式下或在config.cache_classes設置爲true時將其卸載並重新加載。這裏有關於這個主題的一些想法也解釋了它是如何工作的。 http://www.spacevatican.org/2008/9/28/required-or-not/

不告訴你,你這樣做是不對的,但超載串#constantize似乎是一個哈克的方式來重新加載你的代碼。你有沒有考慮使用像watchr這樣的東西在開發中運行你的應用服務器,並在你的API子樹中保存文件時重啓它? https://github.com/mynyml/watchr/

此外,對於如何進一步調試,看看這個回答一些隨機的想法:https://stackoverflow.com/a/7907289/632022

1

我認爲這種影響來自ActionController::Reloader寫入的方式。下面是從ActionController::Reloader#call 2.3.3,請注意評論:

def call(env) 
    Dispatcher.reload_application 
    status, headers, body = @app.call(env) 
    # We do not want to call 'cleanup_application' in an ensure block 
    # because the returned Rack response body may lazily generate its data. This 
    # is for example the case if one calls 
    # 
    # render :text => lambda { ... code here which refers to application models ... } 
    # 
    # in an ActionController. 
    # 
    # Instead, we will want to cleanup the application code after the request is 
    # completely finished. So we wrap the body in a BodyWrapper class so that 
    # when the Rack handler calls #close during the end of the request, we get to 
    # run our cleanup code. 
    [status, headers, BodyWrapper.new(body)] 
end 

Dispatcher.reload_application不會刪除自動加載的常量,Dispatcher.cleanup_application一樣。 BodyWrapper#close與可能出現的異常寫在心中:

def close 
    @body.close if @body.respond_to?(:close) 
ensure 
    Dispatcher.cleanup_application 
end 

然而,這並沒有幫助,因爲如果@app.callActionController::Reloader#call拋出一個異常,BodyWrapper不會被實例化,Dispatcher.cleanup_application不會被調用。

試想以下情形:

  • 我使我的文件一個影響API調用
  • 我打API調用,看看錯誤,在這一點上,包括一個有缺陷的所有文件的變化AREN 「T卸載
  • 我做一個codefix和命中相同 API調用來檢查它是否工作
  • 呼叫會像以前一樣擊潰了同樣的方式,以老班/對象/模塊。這會拋出相同的錯誤並再次在內存中留下加載的常量

傳統控制器引發錯誤時不會發生這種情況,因爲這些錯誤由ActionController::Rescue處理。這種例外情況未達到ActionController::Reloader

簡單的解決辦法是把後備救援條款列入API路由中間件,這樣一些變化:

def call(env) 
    # route API call 
resuce Exception 
    Dispatcher.cleanup_application 
    raise 
end 

注意,這就是我的回答對3歲的問題,我也跟着2.3.3調用堆棧。較新版本的rails可能會有不同的處理方式。