aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--app/controllers/application_controller.rb59
-rw-r--r--app/controllers/general_controller.rb6
-rw-r--r--config/application.rb5
-rw-r--r--spec/integration/errors_spec.rb131
4 files changed, 134 insertions, 67 deletions
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 029b536ec..2615b61f2 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -12,6 +12,11 @@ require 'open-uri'
class ApplicationController < ActionController::Base
class PermissionDenied < StandardError
end
+ class RouteNotFound < StandardError
+ end
+ # assign our own handler method for non-local exceptions
+ rescue_from Exception, :with => :render_exception
+
# Standard headers, footers and navigation for whole site
layout "default"
include FastGettext::Translation # make functions like _, n_, N_ etc available)
@@ -111,55 +116,35 @@ class ApplicationController < ActionController::Base
end
end
- # Override default error handler, for production sites.
- def rescue_action_in_public(exception)
- # Looks for before_filters called something like `set_view_paths_{themename}`. These
- # are set by the themes.
- # Normally, this is called by the theme itself in a
- # :before_filter, but when there's an error, this doesn't
- # happen. By calling it here, we can ensure error pages are
- # still styled according to the theme.
- ActionController::Base.before_filters.select{|f| f.to_s =~ /set_view_paths/}.each do |f|
- self.send(f)
- end
- # Make sure expiry time for session is set (before_filters are
- # otherwise missed by this override)
- session_remember_me
+ def render_exception(exception)
- # Make sure the locale is set correctly too
- set_gettext_locale
+ # In development, or the admin interface, or for a local request, let Rails handle the exception
+ # with its stack trace templates. Local requests in testing are a special case so that we can
+ # test this method - there we use consider_all_requests_local to control behaviour.
+ if Rails.application.config.consider_all_requests_local || local_request? ||
+ (request.local? && !Rails.env.test?)
+ raise exception
+ end
+ @exception_backtrace = exception.backtrace.join("\n")
+ @exception_class = exception.class.to_s
+ @exception_message = exception.message
case exception
- when ActiveRecord::RecordNotFound, ActionController::UnknownAction, ActionController::RoutingError
+ when ActiveRecord::RecordNotFound, RouteNotFound
@status = 404
when PermissionDenied
@status = 403
else
+ message = "\n#{@exception_class} (#{@exception_message}):\n"
+ backtrace = Rails.backtrace_cleaner.clean(exception.backtrace, :silent)
+ message << " " << backtrace.join("\n ")
+ Rails.logger.fatal("#{message}\n\n")
+ ExceptionNotifier::Notifier.exception_notification(request.env, exception).deliver
@status = 500
- notify_about_exception exception
end
- # Display user appropriate error message
- @exception_backtrace = exception.backtrace.join("\n")
- @exception_class = exception.class.to_s
- @exception_message = exception.message
render :template => "general/exception_caught", :status => @status
end
- # FIXME: This was disabled during the Rails 3 upgrade as this is now handled by Rack
- # # For development sites.
- # alias original_rescue_action_locally rescue_action_locally
- # def rescue_action_locally(exception)
- # # Make sure expiry time for session is set (before_filters are
- # # otherwise missed by this override)
- # session_remember_me
-
- # # Make sure the locale is set correctly too
- # set_gettext_locale
-
- # # Display default, detailed error for developers
- # original_rescue_action_locally(exception)
- # end
-
def local_request?
false
end
diff --git a/app/controllers/general_controller.rb b/app/controllers/general_controller.rb
index 0df685829..9d0f91dda 100644
--- a/app/controllers/general_controller.rb
+++ b/app/controllers/general_controller.rb
@@ -222,5 +222,11 @@ class GeneralController < ApplicationController
@locale = self.locale_from_params()
render(:layout => false, :content_type => 'text/css')
end
+
+ # Handle requests for non-existent URLs - will be handled by ApplicationController::render_exception
+ def not_found
+ raise RouteNotFound
+ end
+
end
diff --git a/config/application.rb b/config/application.rb
index f5b525a36..92fd30685 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -55,8 +55,11 @@ module Alaveteli
# will be in this time zone
config.time_zone = ::AlaveteliConfiguration::time_zone
- config.after_initialize do
+ config.after_initialize do |app|
require 'routing_filters.rb'
+ # Add a catch-all route to force routing errors to be handled by the application,
+ # rather than by middleware.
+ app.routes.append{ match '*path', :to => 'general#not_found' }
end
config.autoload_paths << "#{Rails.root.to_s}/lib/mail_handler"
diff --git a/spec/integration/errors_spec.rb b/spec/integration/errors_spec.rb
index edf570182..ed0d7bfec 100644
--- a/spec/integration/errors_spec.rb
+++ b/spec/integration/errors_spec.rb
@@ -1,40 +1,113 @@
+# -*- coding: utf-8 -*-
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
-describe "When rendering errors" do
+describe "When errors occur" do
- before(:each) do
- load_raw_emails_data
+ def set_consider_all_requests_local(value)
+ @requests_local = Rails.application.config.consider_all_requests_local
+ Rails.application.config.consider_all_requests_local = value
end
- it "should render a 404 for users that don't exist" do
- get("/user/wobsnasm")
- response.code.should == "404"
+ def restore_consider_all_requests_local
+ Rails.application.config.consider_all_requests_local = @requests_local
end
- it "should render a 404 for bodies that don't exist" do
- get("/body/wobsnasm")
- response.code.should == "404"
+
+ before(:each) do
+ # This should happen automatically before each test but doesn't with these integration
+ # tests for some reason.
+ ActionMailer::Base.deliveries = []
end
- it "should render a 500 for general errors" do
- ir = info_requests(:naughty_chicken_request)
- # Set an invalid state for the request. Note that update_attribute doesn't run the validations
- ir.update_attribute(:described_state, "crotchety")
- get("/request/#{ir.url_title}")
- response.code.should == "500"
+
+ after(:each) do
+ restore_consider_all_requests_local
end
- it "should render a 403 for attempts at directory listing for attachments" do
- # make a fake cache
- foi_cache_path = File.expand_path(File.join(File.dirname(__FILE__), '../../cache'))
- FileUtils.mkdir_p(File.join(foi_cache_path, "views/en/request/101/101/response/1/attach/html/1"))
- get("/request/101/response/1/attach/html/1/" )
- response.body.should include("Directory listing not allowed")
- response.code.should == "403"
- get("/request/101/response/1/attach/html" )
- response.body.should include("Directory listing not allowed")
- response.code.should == "403"
+
+ context 'when considering all requests local (by default all in development)' do
+
+ before(:each) { set_consider_all_requests_local(true) }
+
+ it 'should show a full trace for general errors' do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example")
+ response.body.should have_selector('div[id=traces]')
+ response.body.should match('An example error')
+ end
+
end
- it "should render a 404 for non-existent 'details' pages for requests" do
- get("/details/request/wobble" )
- response.code.should == "404"
+
+ context 'when not considering all requests local' do
+
+ before(:each) { set_consider_all_requests_local(false) }
+
+ it "should render a 404 for unrouteable URLs using the general/exception_caught template" do
+ get("/frobsnasm")
+ response.should render_template('general/exception_caught')
+ response.code.should == "404"
+ end
+
+ it "should render a 404 for users or bodies that don't exist using the general/exception_caught
+ template" do
+ ['/user/wobsnasm', '/body/wobsnasm'].each do |non_existent_url|
+ get(non_existent_url)
+ response.should render_template('general/exception_caught')
+ response.code.should == "404"
+ end
+ end
+
+ it "should render a 500 for general errors using the general/exception_caught template" do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example")
+ response.should render_template('general/exception_caught')
+ response.body.should match('An example error')
+ response.code.should == "500"
+ end
+
+ it 'should notify of a general error' do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example")
+ deliveries = ActionMailer::Base.deliveries
+ deliveries.size.should == 1
+ mail = deliveries[0]
+ mail.body.should =~ /An example error/
+ end
+
+ it 'should log a general error' do
+ Rails.logger.should_receive(:fatal)
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/request/example")
+ end
+
+ it 'should assign the locale for the general/exception_caught template' do
+ InfoRequest.stub!(:find_by_url_title!).and_raise("An example error")
+ get("/es/request/example")
+ response.should render_template('general/exception_caught')
+ response.body.should match('Lo sentimos, hubo un problema procesando esta página')
+ response.body.should match('An example error')
+ end
+
+ it "should render a 403 with text body for attempts at directory listing for attachments" do
+ # make a fake cache
+ foi_cache_path = File.expand_path(File.join(File.dirname(__FILE__), '../../cache'))
+ FileUtils.mkdir_p(File.join(foi_cache_path, "views/en/request/101/101/response/1/attach/html/1"))
+ get("/request/101/response/1/attach/html/1/" )
+ response.body.should include("Directory listing not allowed")
+ response.code.should == "403"
+ get("/request/101/response/1/attach/html" )
+ response.body.should include("Directory listing not allowed")
+ response.code.should == "403"
+ end
+
+ context "in the admin interface" do
+
+ it 'should show a full trace for general errors' do
+ InfoRequest.stub!(:find).and_raise("An example error")
+ get("/admin/request/show/333")
+ response.body.should have_selector('div[id=traces]')
+ response.body.should match('An example error')
+ end
+
+ end
+
end
-end
+end