2015-07-24 12 views
6

Ho un controller di rail con un blocco rescue_from in cui chiamo render.request.format in rescue_from block

class SomeController < ApplicationController 
    rescue_from Some::Error, :some_error 

    private 

    def some_error error 
    @error = error 
    render 'error' 
    end 
end 

La cosa strana è che, anche se ho una vista error.js.erb, rotaie utilizzeranno sempre error.html.erb, anche se la richiesta è un JS:

Started GET /some/1 
Processing by SomeController#show as JS 
... 
Rendered some/error.html.erb 

Non come nel registro accorciato sopra c'è scritto viene visualizzato come JS, ma utilizza ancora il file HTML. Il numero .js.erb si trova nella posizione corretta e il rendering delle viste JS quando non è presente il numero rescue_from funziona correttamente.

Cosa sta succedendo qui?

Update 1: Ho creato un test repository per dimostrare il problema

Update 2ho trovato una soluzione (vedi sotto). Qualcuno può venire con una soluzione più generica come quella qui sotto, o puoi dire il mio perché questa sarebbe impossibile o una pessima idea? La taglia è ancora aperta.

  • Avrebbe senso per creare una richiesta di pull per impostare self.formats in ActionController::Rescue.process_actiono
  • diventare matto e cercare di riprendere la pila di un livello più profondo in cui è stata sollevata l'errore
+0

Hai provato l'errore 'render ', format: request.format'? – eirikir

+0

Ho aggiornato la domanda con un collegamento a un repository di test. Se aggiungo 'formats: request.format' verrà generato un errore che dice che non è possibile trovare un modello da renderizzare. – amiuhle

risposta

5

Soluzione

Ho fatto un po 'di debugging e scavando nelle fonti di rail e trovato una soluzione io stesso:

def error 
    @error = error 
    self.formats = request.formats.map(&:ref).compact 
    render 'error' 
end 

Spiegazione

Richiamo del blocco rescue_from avviene in ActionController::Rescue.process_action. Se c'è un errore, verrà chiamato il blocco. Se non ci sono errori, alla fine ActionController::Rendering.process_action verrà chiamato che ha appena imposta self.formats:

def process_action(*) #:nodoc: 
    self.formats = request.formats.map(&:ref).compact 
    super 
end 

Ecco uno stacktrace completa tra ActionController::Rescue.process_action e l'azione di controllo vero e proprio.

#0 TestController.index at /tmp/rescue_from/app/controllers/test_controller.rb:11 
#1 ActionController::ImplicitRender.send_action(method#String, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/implicit_render.rb:4 
#2 AbstractController::Base.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/abstract_controller/base.rb:198 
#3 ActionController::Rendering.process_action(action, *args) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rendering.rb:10 
#4 block in AbstractController::Callbacks.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/abstract_controller/callbacks.rb:20 
ͱ-- #5 Proc.call(*args) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:115 
#6 ActiveSupport::Callbacks::Filters::End.call(env#ActiveSupport::Callbacks::Filters::Environment) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:115 
#7 block (2 levels) in ActiveSupport::Callbacks::CallbackChain.compile at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:553 
ͱ-- #8 Proc.call(*args) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:503 
#9 ActiveSupport::Callbacks::CallbackSequence.call(*args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:503 
#10 ActiveSupport::Callbacks.run_callbacks(kind#Symbol, &block#Proc) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/callbacks.rb:88 
#11 AbstractController::Callbacks.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/abstract_controller/callbacks.rb:19 
#12 ActionController::Rescue.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rescue.rb:29 

Ecco uno stacktrace pieno quando c'è un errore (gettato in un before_action):

--> #0 TestController.standard_error(error#RuntimeError) at /Users/timou/tmp/rescue_from/app/controllers/test_controller.rb:20 
ͱ-- #1 Method.call(*args) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/rescuable.rb:80 
#2 ActiveSupport::Rescuable.rescue_with_handler(exception#RuntimeError) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/activesupport-4.2.3/lib/active_support/rescuable.rb:80 
#3 ActionController::Rescue.rescue_with_handler(exception#RuntimeError) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rescue.rb:15 
#4 rescue in ActionController::Rescue.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rescue.rb:32 
#5 ActionController::Rescue.process_action(action#NilClass, *args#Array) at /usr/local/Cellar/rbenv/0.4.0/versions/2.2.2/lib/ruby/gems/2.2.0/gems/actionpack-4.2.3/lib/action_controller/metal/rescue.rb:29 

Quindi il mio problema era in realtà che l'errore è sollevata in un before_action, che è gestito da AbstractController::Callbacks.process_action e succede prima che ActionController::Rendering.process_action può impostare self.formats.

Se l'errore viene generato nell'azione stessa, self.formats sarà già impostato e la vista corretta verrà visualizzata senza le impostazioni self.formats nel blocco rescue_from.