提交 f650e032 编写于 作者: J Jeff Kreeftmeijer

Use `#performed?` to terminate controller callbacks

Since 69009f, `ActionController::Metal::DataStreaming#send_file` doesn't
set `@_response_body` anymore.

`AbstractController::Callbacks` used `@_response_body` in its callback
terminator, so it failed to halt the callback cycle when using `#send_file`
from a `before_action`.

Instead, it now uses `#performed?` on `AbstractController::Base` and
`ActionController::Metal`, which checks `response.committed?`, besides
 checking if `@_response_body` is set, if possible.

Example application: https://gist.github.com/jeffkreeftmeijer/78ae4572f36b198e729724b0cf79ef8e
上级 4e85538d
......@@ -150,6 +150,13 @@ def available_action?(action_name)
_find_action_name(action_name)
end
# Tests if a response body is set. Used to determine if the
# +process_action+ callback needs to be terminated in
# +AbstractController::Callbacks+.
def performed?
response_body
end
# Returns true if the given controller is capable of rendering
# a path. A subclass of +AbstractController::Base+
# may return false. An Email controller for example does not
......
......@@ -9,7 +9,7 @@ module Callbacks
included do
define_callbacks :process_action,
terminator: ->(controller, result_lambda) { result_lambda.call if result_lambda.is_a?(Proc); controller.response_body },
terminator: ->(controller, result_lambda) { result_lambda.call if result_lambda.is_a?(Proc); controller.performed? },
skip_after_callbacks_if_terminated: true
end
......
......@@ -11,6 +11,8 @@ class SendFileController < ActionController::Base
include ActionController::Testing
layout "layouts/standard" # to make sure layouts don't interfere
before_action :file, only: :file_from_before_action
attr_writer :options
def options
@options ||= {}
......@@ -20,6 +22,10 @@ def file
send_file(file_path, options)
end
def file_from_before_action
raise 'No file sent from before action.'
end
def test_send_file_headers_bang
options = {
:type => Mime[:png],
......@@ -192,6 +198,15 @@ def test_send_file_without_content_disposition_header
assert_nil @controller.headers['Content-Disposition']
end
def test_send_file_from_before_action
response = nil
assert_nothing_raised { response = process('file_from_before_action') }
assert_not_nil response
assert_kind_of String, response.body
assert_equal file_data, response.body
end
%w(file data).each do |method|
define_method "test_send_#{method}_status" do
@controller.options = { :stream => false, :status => 500 }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册