mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
906aebceed
Resolved all the conflicts since 2.3.0 -> HEAD. Following is a list of commits that could not be applied cleanly or are obviated with the abstract_controller refactor. They all need to be revisited to ensure that fixes made in 2.3 do not reappear in 3.0:2259ecf368
AR not available * This will be reimplemented with ActionORM or equivalent06182ea02e
implicitly rendering a js response should not use the default layout [#1844 state:resolved] * This will be handled generically893e9eb995
Improve view rendering performance in development mode and reinstate template recompiling in production [#1909 state:resolved] * We will need to reimplement rails-dev-boost on top of the refactor; the changes here are very implementation specific and cannot be cleanly applied. The following commits are implicated:199e750d46
3942cb406e
f8ea9f85d4
e3b166aab3
ae9f258e03
44423126c6
0cb020b4d6
workaround for picking layouts based on wrong view_paths [#1974 state:resolved] * The specifics of this commit no longer apply. Since it is a two-line commit, we will reimplement this change.8c5cc66a83
make action_controller/layouts pick templates from the current instance's view_paths instead of the class view_paths [#1974 state:resolved] * This does not apply at all. It should be trivial to apply the feature to the reimplemented ActionController::Base.87e8b16246
fix HTML fallback for explicit templates [#2052 state:resolved] * There were a number of patches related to this that simply compounded each other. Basically none of them apply cleanly, and the underlying issue needs to be revisited. After discussing the underlying problem with Koz, we will defer these fixes for further discussion.
539 lines
15 KiB
Ruby
539 lines
15 KiB
Ruby
require 'abstract_unit'
|
|
|
|
class RescueController < ActionController::Base
|
|
class NotAuthorized < StandardError
|
|
end
|
|
class NotAuthorizedToRescueAsString < StandardError
|
|
end
|
|
|
|
class RecordInvalid < StandardError
|
|
end
|
|
class RecordInvalidToRescueAsString < StandardError
|
|
end
|
|
|
|
class NotAllowed < StandardError
|
|
end
|
|
class NotAllowedToRescueAsString < StandardError
|
|
end
|
|
|
|
class InvalidRequest < StandardError
|
|
end
|
|
class InvalidRequestToRescueAsString < StandardError
|
|
end
|
|
|
|
class BadGateway < StandardError
|
|
end
|
|
class BadGatewayToRescueAsString < StandardError
|
|
end
|
|
|
|
class ResourceUnavailable < StandardError
|
|
end
|
|
class ResourceUnavailableToRescueAsString < StandardError
|
|
end
|
|
|
|
# We use a fully-qualified name in some strings, and a relative constant
|
|
# name in some other to test correct handling of both cases.
|
|
|
|
rescue_from NotAuthorized, :with => :deny_access
|
|
rescue_from 'RescueController::NotAuthorizedToRescueAsString', :with => :deny_access
|
|
|
|
rescue_from RecordInvalid, :with => :show_errors
|
|
rescue_from 'RescueController::RecordInvalidToRescueAsString', :with => :show_errors
|
|
|
|
rescue_from NotAllowed, :with => proc { head :forbidden }
|
|
rescue_from 'RescueController::NotAllowedToRescueAsString', :with => proc { head :forbidden }
|
|
|
|
rescue_from InvalidRequest, :with => proc { |exception| render :text => exception.message }
|
|
rescue_from 'InvalidRequestToRescueAsString', :with => proc { |exception| render :text => exception.message }
|
|
|
|
rescue_from BadGateway do
|
|
head :status => 502
|
|
end
|
|
rescue_from 'BadGatewayToRescueAsString' do
|
|
head :status => 502
|
|
end
|
|
|
|
rescue_from ResourceUnavailable do |exception|
|
|
render :text => exception.message
|
|
end
|
|
rescue_from 'ResourceUnavailableToRescueAsString' do |exception|
|
|
render :text => exception.message
|
|
end
|
|
|
|
# This is a Dispatcher exception and should be in ApplicationController.
|
|
rescue_from ActionController::RoutingError do
|
|
render :text => 'no way'
|
|
end
|
|
|
|
before_filter(:only => :before_filter_raises) { raise 'umm nice' }
|
|
|
|
def before_filter_raises
|
|
end
|
|
|
|
def raises
|
|
render :text => 'already rendered'
|
|
raise "don't panic!"
|
|
end
|
|
|
|
def method_not_allowed
|
|
raise ActionController::MethodNotAllowed.new(:get, :head, :put)
|
|
end
|
|
|
|
def not_implemented
|
|
raise ActionController::NotImplemented.new(:get, :put)
|
|
end
|
|
|
|
def not_authorized
|
|
raise NotAuthorized
|
|
end
|
|
def not_authorized_raise_as_string
|
|
raise NotAuthorizedToRescueAsString
|
|
end
|
|
|
|
def not_allowed
|
|
raise NotAllowed
|
|
end
|
|
def not_allowed_raise_as_string
|
|
raise NotAllowedToRescueAsString
|
|
end
|
|
|
|
def invalid_request
|
|
raise InvalidRequest
|
|
end
|
|
def invalid_request_raise_as_string
|
|
raise InvalidRequestToRescueAsString
|
|
end
|
|
|
|
def record_invalid
|
|
raise RecordInvalid
|
|
end
|
|
def record_invalid_raise_as_string
|
|
raise RecordInvalidToRescueAsString
|
|
end
|
|
|
|
def bad_gateway
|
|
raise BadGateway
|
|
end
|
|
def bad_gateway_raise_as_string
|
|
raise BadGatewayToRescueAsString
|
|
end
|
|
|
|
def resource_unavailable
|
|
raise ResourceUnavailable
|
|
end
|
|
def resource_unavailable_raise_as_string
|
|
raise ResourceUnavailableToRescueAsString
|
|
end
|
|
|
|
def missing_template
|
|
end
|
|
|
|
protected
|
|
def deny_access
|
|
head :forbidden
|
|
end
|
|
|
|
def show_errors(exception)
|
|
head :unprocessable_entity
|
|
end
|
|
end
|
|
|
|
class RescueControllerTest < ActionController::TestCase
|
|
FIXTURE_PUBLIC = "#{File.dirname(__FILE__)}/../fixtures".freeze
|
|
|
|
def setup
|
|
super
|
|
set_all_requests_local
|
|
populate_exception_object
|
|
end
|
|
|
|
def set_all_requests_local
|
|
RescueController.consider_all_requests_local = true
|
|
@request.remote_addr = '1.2.3.4'
|
|
@request.host = 'example.com'
|
|
end
|
|
|
|
def populate_exception_object
|
|
begin
|
|
raise 'foo'
|
|
rescue => @exception
|
|
end
|
|
end
|
|
|
|
def test_rescue_exceptions_raised_by_filters
|
|
with_rails_root FIXTURE_PUBLIC do
|
|
with_all_requests_local false do
|
|
get :before_filter_raises
|
|
end
|
|
end
|
|
|
|
assert_response :internal_server_error
|
|
end
|
|
|
|
def test_rescue_action_locally_if_all_requests_local
|
|
@controller.expects(:local_request?).never
|
|
@controller.expects(:rescue_action_locally).with(@exception)
|
|
@controller.expects(:rescue_action_in_public).never
|
|
|
|
with_all_requests_local do
|
|
@controller.send :rescue_action, @exception
|
|
end
|
|
end
|
|
|
|
def test_rescue_action_locally_if_remote_addr_is_localhost
|
|
@controller.expects(:local_request?).returns(true)
|
|
@controller.expects(:rescue_action_locally).with(@exception)
|
|
@controller.expects(:rescue_action_in_public).never
|
|
|
|
with_all_requests_local false do
|
|
@controller.send :rescue_action, @exception
|
|
end
|
|
end
|
|
|
|
def test_rescue_action_in_public_otherwise
|
|
@controller.expects(:local_request?).returns(false)
|
|
@controller.expects(:rescue_action_locally).never
|
|
@controller.expects(:rescue_action_in_public).with(@exception)
|
|
|
|
with_all_requests_local false do
|
|
@controller.send :rescue_action, @exception
|
|
end
|
|
end
|
|
|
|
def test_rescue_action_in_public_with_localized_error_file
|
|
# Change locale
|
|
old_locale = I18n.locale
|
|
I18n.locale = :da
|
|
|
|
with_rails_root FIXTURE_PUBLIC do
|
|
with_all_requests_local false do
|
|
get :raises
|
|
end
|
|
end
|
|
|
|
assert_response :internal_server_error
|
|
body = File.read("#{FIXTURE_PUBLIC}/public/500.da.html")
|
|
assert_equal body, @response.body
|
|
ensure
|
|
I18n.locale = old_locale
|
|
end
|
|
|
|
def test_rescue_action_in_public_with_error_file
|
|
with_rails_root FIXTURE_PUBLIC do
|
|
with_all_requests_local false do
|
|
get :raises
|
|
end
|
|
end
|
|
|
|
assert_response :internal_server_error
|
|
body = File.read("#{FIXTURE_PUBLIC}/public/500.html")
|
|
assert_equal body, @response.body
|
|
end
|
|
|
|
def test_rescue_action_in_public_without_error_file
|
|
with_rails_root '/tmp' do
|
|
with_all_requests_local false do
|
|
get :raises
|
|
end
|
|
end
|
|
|
|
assert_response :internal_server_error
|
|
assert_equal ' ', @response.body
|
|
end
|
|
|
|
def test_rescue_unknown_action_in_public_with_error_file
|
|
with_rails_root FIXTURE_PUBLIC do
|
|
with_all_requests_local false do
|
|
get :foobar_doesnt_exist
|
|
end
|
|
end
|
|
|
|
assert_response :not_found
|
|
body = File.read("#{FIXTURE_PUBLIC}/public/404.html")
|
|
assert_equal body, @response.body
|
|
end
|
|
|
|
def test_rescue_unknown_action_in_public_without_error_file
|
|
with_rails_root '/tmp' do
|
|
with_all_requests_local false do
|
|
get :foobar_doesnt_exist
|
|
end
|
|
end
|
|
|
|
assert_response :not_found
|
|
assert_equal ' ', @response.body
|
|
end
|
|
|
|
def test_rescue_missing_template_in_public
|
|
with_rails_root FIXTURE_PUBLIC do
|
|
with_all_requests_local true do
|
|
get :missing_template
|
|
end
|
|
end
|
|
|
|
assert_response :internal_server_error
|
|
assert @response.body.include?('missing_template'), "Response should include the template name."
|
|
end
|
|
|
|
def test_rescue_action_locally
|
|
get :raises
|
|
assert_response :internal_server_error
|
|
assert_template 'diagnostics.erb'
|
|
assert @response.body.include?('RescueController#raises'), "Response should include controller and action."
|
|
assert @response.body.include?("don't panic"), "Response should include exception message."
|
|
end
|
|
|
|
def test_local_request_when_remote_addr_is_localhost
|
|
@controller.expects(:request).returns(@request).at_least_once
|
|
with_remote_addr '127.0.0.1' do
|
|
assert @controller.send(:local_request?)
|
|
end
|
|
end
|
|
|
|
def test_local_request_when_remote_addr_isnt_locahost
|
|
@controller.expects(:request).returns(@request)
|
|
with_remote_addr '1.2.3.4' do
|
|
assert !@controller.send(:local_request?)
|
|
end
|
|
end
|
|
|
|
def test_rescue_responses
|
|
responses = ActionController::Base.rescue_responses
|
|
|
|
assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses.default
|
|
assert_equal ActionController::Rescue::DEFAULT_RESCUE_RESPONSE, responses[Exception.new]
|
|
|
|
assert_equal :not_found, responses[ActionController::RoutingError.name]
|
|
assert_equal :not_found, responses[ActionController::UnknownAction.name]
|
|
assert_equal :not_found, responses['ActiveRecord::RecordNotFound']
|
|
assert_equal :conflict, responses['ActiveRecord::StaleObjectError']
|
|
assert_equal :unprocessable_entity, responses['ActiveRecord::RecordInvalid']
|
|
assert_equal :unprocessable_entity, responses['ActiveRecord::RecordNotSaved']
|
|
assert_equal :method_not_allowed, responses['ActionController::MethodNotAllowed']
|
|
assert_equal :not_implemented, responses['ActionController::NotImplemented']
|
|
end
|
|
|
|
def test_rescue_templates
|
|
templates = ActionController::Base.rescue_templates
|
|
|
|
assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates.default
|
|
assert_equal ActionController::Rescue::DEFAULT_RESCUE_TEMPLATE, templates[Exception.new]
|
|
|
|
assert_equal 'missing_template', templates[ActionView::MissingTemplate.name]
|
|
assert_equal 'routing_error', templates[ActionController::RoutingError.name]
|
|
assert_equal 'unknown_action', templates[ActionController::UnknownAction.name]
|
|
assert_equal 'template_error', templates[ActionView::TemplateError.name]
|
|
end
|
|
|
|
def test_not_implemented
|
|
with_all_requests_local false do
|
|
with_rails_public_path(".") do
|
|
head :not_implemented
|
|
end
|
|
end
|
|
assert_response :not_implemented
|
|
assert_equal "GET, PUT", @response.headers['Allow']
|
|
end
|
|
|
|
def test_method_not_allowed
|
|
with_all_requests_local false do
|
|
with_rails_public_path(".") do
|
|
get :method_not_allowed
|
|
end
|
|
end
|
|
assert_response :method_not_allowed
|
|
assert_equal "GET, HEAD, PUT", @response.headers['Allow']
|
|
end
|
|
|
|
def test_rescue_handler
|
|
get :not_authorized
|
|
assert_response :forbidden
|
|
end
|
|
def test_rescue_handler_string
|
|
get :not_authorized_raise_as_string
|
|
assert_response :forbidden
|
|
end
|
|
|
|
def test_rescue_handler_with_argument
|
|
@controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) }
|
|
get :record_invalid
|
|
end
|
|
def test_rescue_handler_with_argument_as_string
|
|
@controller.expects(:show_errors).once.with { |e| e.is_a?(Exception) }
|
|
get :record_invalid_raise_as_string
|
|
end
|
|
|
|
def test_proc_rescue_handler
|
|
get :not_allowed
|
|
assert_response :forbidden
|
|
end
|
|
def test_proc_rescue_handler_as_string
|
|
get :not_allowed_raise_as_string
|
|
assert_response :forbidden
|
|
end
|
|
|
|
def test_proc_rescue_handle_with_argument
|
|
get :invalid_request
|
|
assert_equal "RescueController::InvalidRequest", @response.body
|
|
end
|
|
def test_proc_rescue_handle_with_argument_as_string
|
|
get :invalid_request_raise_as_string
|
|
assert_equal "RescueController::InvalidRequestToRescueAsString", @response.body
|
|
end
|
|
|
|
def test_block_rescue_handler
|
|
get :bad_gateway
|
|
assert_response 502
|
|
end
|
|
def test_block_rescue_handler_as_string
|
|
get :bad_gateway_raise_as_string
|
|
assert_response 502
|
|
end
|
|
|
|
def test_block_rescue_handler_with_argument
|
|
get :resource_unavailable
|
|
assert_equal "RescueController::ResourceUnavailable", @response.body
|
|
end
|
|
|
|
def test_block_rescue_handler_with_argument_as_string
|
|
get :resource_unavailable_raise_as_string
|
|
assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body
|
|
end
|
|
|
|
def test_rescue_dispatcher_exceptions
|
|
env = @request.env
|
|
env["action_controller.rescue.request"] = @request
|
|
env["action_controller.rescue.response"] = @response
|
|
|
|
RescueController.call_with_exception(env, ActionController::RoutingError.new("Route not found"))
|
|
assert_equal "no way", @response.body
|
|
end
|
|
|
|
def test_rescue_dispatcher_exceptions_without_request_set
|
|
@request.env['REQUEST_URI'] = '/no_way'
|
|
response = RescueController.call_with_exception(@request.env, ActionController::RoutingError.new("Route not found"))
|
|
assert_kind_of ActionDispatch::Response, response
|
|
assert_equal "no way", response.body
|
|
end
|
|
|
|
protected
|
|
def with_all_requests_local(local = true)
|
|
old_local, ActionController::Base.consider_all_requests_local =
|
|
ActionController::Base.consider_all_requests_local, local
|
|
yield
|
|
ensure
|
|
ActionController::Base.consider_all_requests_local = old_local
|
|
end
|
|
|
|
def with_remote_addr(addr)
|
|
old_remote_addr, @request.remote_addr = @request.remote_addr, addr
|
|
yield
|
|
ensure
|
|
@request.remote_addr = old_remote_addr
|
|
end
|
|
|
|
def with_rails_public_path(rails_root)
|
|
old_rails = Object.const_get(:Rails) rescue nil
|
|
mod = Object.const_set(:Rails, Module.new)
|
|
(class << mod; self; end).instance_eval do
|
|
define_method(:public_path) { "#{rails_root}/public" }
|
|
end
|
|
yield
|
|
ensure
|
|
Object.module_eval { remove_const(:Rails) } if defined?(Rails)
|
|
Object.const_set(:Rails, old_rails) if old_rails
|
|
end
|
|
|
|
def with_rails_root(path = nil,&block)
|
|
old_rails_root = RAILS_ROOT if defined?(RAILS_ROOT)
|
|
if path
|
|
silence_warnings { Object.const_set(:RAILS_ROOT, path) }
|
|
else
|
|
Object.remove_const(:RAILS_ROOT) rescue nil
|
|
end
|
|
|
|
with_rails_public_path(path, &block)
|
|
|
|
ensure
|
|
if old_rails_root
|
|
silence_warnings { Object.const_set(:RAILS_ROOT, old_rails_root) }
|
|
else
|
|
Object.remove_const(:RAILS_ROOT) rescue nil
|
|
end
|
|
end
|
|
end
|
|
|
|
class ExceptionInheritanceRescueController < ActionController::Base
|
|
|
|
class ParentException < StandardError
|
|
end
|
|
|
|
class ChildException < ParentException
|
|
end
|
|
|
|
class GrandchildException < ChildException
|
|
end
|
|
|
|
rescue_from ChildException, :with => lambda { head :ok }
|
|
rescue_from ParentException, :with => lambda { head :created }
|
|
rescue_from GrandchildException, :with => lambda { head :no_content }
|
|
|
|
def raise_parent_exception
|
|
raise ParentException
|
|
end
|
|
|
|
def raise_child_exception
|
|
raise ChildException
|
|
end
|
|
|
|
def raise_grandchild_exception
|
|
raise GrandchildException
|
|
end
|
|
end
|
|
|
|
class ExceptionInheritanceRescueControllerTest < ActionController::TestCase
|
|
def test_bottom_first
|
|
get :raise_grandchild_exception
|
|
assert_response :no_content
|
|
end
|
|
|
|
def test_inheritance_works
|
|
get :raise_child_exception
|
|
assert_response :created
|
|
end
|
|
end
|
|
|
|
class ControllerInheritanceRescueController < ExceptionInheritanceRescueController
|
|
class FirstExceptionInChildController < StandardError
|
|
end
|
|
|
|
class SecondExceptionInChildController < StandardError
|
|
end
|
|
|
|
rescue_from FirstExceptionInChildController, 'SecondExceptionInChildController', :with => lambda { head :gone }
|
|
|
|
def raise_first_exception_in_child_controller
|
|
raise FirstExceptionInChildController
|
|
end
|
|
|
|
def raise_second_exception_in_child_controller
|
|
raise SecondExceptionInChildController
|
|
end
|
|
end
|
|
|
|
class ControllerInheritanceRescueControllerTest < ActionController::TestCase
|
|
def test_first_exception_in_child_controller
|
|
get :raise_first_exception_in_child_controller
|
|
assert_response :gone
|
|
end
|
|
|
|
def test_second_exception_in_child_controller
|
|
get :raise_second_exception_in_child_controller
|
|
assert_response :gone
|
|
end
|
|
|
|
def test_exception_in_parent_controller
|
|
get :raise_parent_exception
|
|
assert_response :created
|
|
end
|
|
end
|