Syntax errors and other exceptions thrown outside of an action are now gracefully handled by the dispatcher

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@51 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson 2004-12-06 18:25:01 +00:00
parent ed14042fea
commit 0ffb6c1625
12 changed files with 40 additions and 52 deletions

View File

@ -325,7 +325,6 @@ methods:
def create
@post = Post.create(@params["post"])
@post.save
redirect_to :action => "display", :id => @post.id
end
end

View File

@ -32,9 +32,9 @@ require 'action_controller/benchmarking'
require 'action_controller/filters'
require 'action_controller/layout'
require 'action_controller/flash'
require 'action_controller/dependencies'
require 'action_controller/scaffolding'
require 'action_controller/helpers'
require 'action_controller/dependencies'
require 'action_controller/cookies'
require 'action_controller/cgi_process'
@ -44,9 +44,9 @@ ActionController::Base.class_eval do
include ActionController::Flash
include ActionController::Benchmarking
include ActionController::Rescue
include ActionController::Dependencies
include ActionController::Scaffolding
include ActionController::Helpers
include ActionController::Dependencies
include ActionController::Cookies
end

View File

@ -5,10 +5,6 @@ require 'action_controller/support/class_attribute_accessors'
require 'action_controller/support/class_inheritable_attributes'
require 'action_controller/support/inflector'
unless Object.respond_to?(:require_dependency)
Object.send(:define_method, :require_dependency) { |file_name| ActionController::Base.require_dependency(file_name) }
end
module ActionController #:nodoc:
class ActionControllerError < StandardError #:nodoc:
end
@ -197,12 +193,6 @@ module ActionController #:nodoc:
@@consider_all_requests_local = true
cattr_accessor :consider_all_requests_local
# When turned on (which is default), all dependencies are included using "load". This mean that any change is instant in cached
# environments like mod_ruby or FastCGI. When set to false, "require" is used, which is faster but requires server restart to
# be effective.
@@reload_dependencies = true
cattr_accessor :reload_dependencies
# Template root determines the base from which template references will be made. So a call to render("test/template")
# will be converted to "#{template_root}/test/template.rhtml".
cattr_accessor :template_root
@ -248,7 +238,7 @@ module ActionController #:nodoc:
def process(request, response) #:nodoc:
new.process(request, response)
end
# Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
def controller_class_name
Inflector.demodulize(name)
@ -258,22 +248,17 @@ module ActionController #:nodoc:
def controller_name
Inflector.underscore(controller_class_name.sub(/Controller/, ""))
end
# Loads the <tt>file_name</tt> if reload_dependencies is true or requires if it's false.
def require_dependency(file_name)
reload_dependencies ? silence_warnings { load("#{file_name}.rb") } : require(file_name)
end
end
public
# Extracts the action_name from the request parameters and performs that action.
def process(request, response) #:nodoc:
def process(request, response, method = :perform_action, *arguments) #:nodoc:
initialize_template_class(response)
assign_shortcuts(request, response)
initialize_current_url
log_processing unless logger.nil?
perform_action
send(method, *arguments)
close_session
return @response

View File

@ -1,11 +1,29 @@
unless Object.respond_to?(:require_dependency)
Object.send(:define_method, :require_dependency) { |file_name| ActionController::Base.require_dependency(file_name) }
end
module ActionController #:nodoc:
module Dependencies #:nodoc:
def self.append_features(base)
super
base.class_eval do
# When turned on (which is default), all dependencies are included using "load". This mean that any change is instant in cached
# environments like mod_ruby or FastCGI. When set to false, "require" is used, which is faster but requires server restart to
# be effective.
@@reload_dependencies = true
cattr_accessor :reload_dependencies
end
base.extend(ClassMethods)
end
module ClassMethods
# Loads the <tt>file_name</tt> if reload_dependencies is true or requires if it's false.
def require_dependency(file_name)
reload_dependencies ? silence_warnings { load("#{file_name}.rb") } : require(file_name)
end
def model(*models)
require_dependencies(:model, models)
depend_on(:model, models)

View File

@ -8,12 +8,19 @@ module ActionController #:nodoc:
module Rescue
def self.append_features(base) #:nodoc:
super
base.extend(ClassMethods)
base.class_eval do
alias_method :perform_action_without_rescue, :perform_action
alias_method :perform_action, :perform_action_with_rescue
end
end
module ClassMethods
def process_with_exception(request, response, exception)
new.process(request, response, :rescue_action, exception)
end
end
protected
# Exception handler called when the performance of an action raises an exception.
def rescue_action(exception)
@ -87,8 +94,7 @@ module ActionController #:nodoc:
end
def clean_backtrace(exception)
base_dir = File.expand_path(File.dirname(__FILE__) + "/../../../../")
exception.backtrace.collect { |line| line.gsub(base_dir, "").gsub("/public/../config/environments/../../", "").gsub("/public/../", "") }
exception.backtrace.collect { |line| Object.const_defined?(:RAILS_ROOT) ? line.gsub(RAILS_ROOT, "") : line }
end
end
end

View File

@ -1,6 +1,4 @@
<%
base_dir = File.expand_path(File.dirname(__FILE__))
request_parameters_without_action = @request.parameters.clone
request_parameters_without_action.delete("action")
request_parameters_without_action.delete("controller")

View File

@ -1,8 +1,6 @@
<%
base_dir = File.expand_path(File.dirname(__FILE__))
clean_backtrace = @exception.backtrace.collect { |line| line.gsub(base_dir, "").gsub("/../config/environments/../../", "") }
app_trace = clean_backtrace.reject { |line| line[0..6] == "vendor/" || line.include?("dispatch.cgi") }
clean_backtrace = @exception.backtrace.collect { |line| Object.const_defined?(:RAILS_ROOT) ? line.gsub(RAILS_ROOT, "") : line }
app_trace = clean_backtrace.reject { |line| line =~ /(vendor|dispatch|ruby)/ }
framework_trace = clean_backtrace - app_trace
%>
@ -10,9 +8,9 @@
<%=h @exception.class.to_s %> in
<%=h @request.parameters["controller"].capitalize %>#<%=h @request.parameters["action"] %>
</h1>
<p><%=h @exception.message %></p>
<p><%=h Object.const_defined?(:RAILS_ROOT) ? @exception.message.gsub(RAILS_ROOT, "") : @exception.message %></p>
<% unless app_trace.empty? %><pre><code><%=h app_trace.collect { |line| line.gsub("/../", "") }.join("\n") %></code></pre><% end %>
<% unless app_trace.empty? %><pre><code><%=h app_trace.join("\n") %></code></pre><% end %>
<% unless framework_trace.empty? %>
<a href="#" onclick="document.getElementById('framework_trace').style.display='block'; return false;">Show framework trace</a>

View File

@ -1,11 +1,3 @@
<%
base_dir = File.expand_path(File.dirname(__FILE__))
framework_trace = @exception.original_exception.backtrace.collect do |line|
line.gsub(base_dir, "").gsub("/../config/environments/../../", "")
end
%>
<h1>
<%=h @exception.original_exception.class.to_s %> in
<%=h @request.parameters["controller"].capitalize %>#<%=h @request.parameters["action"] %>
@ -21,6 +13,6 @@
<p><%=h @exception.sub_template_message %></p>
<a href="#" onclick="document.getElementById('framework_trace').style.display='block'">Show template trace</a>
<pre id="framework_trace" style="display:none"><code><%=h framework_trace.join("\n") %></code></pre>
<pre id="framework_trace" style="display:none"><code><%=h @exception.original_exception.backtrace.collect { |line| Object.const_defined?(:RAILS_ROOT) ? line.gsub(RAILS_ROOT, "") : line }.join("\n") %></code></pre>
<%= render_file(@rescues_path + "/_request_and_response.rhtml", false) %>

View File

@ -1 +0,0 @@
ruby public/dispatch.servlet

View File

@ -24,7 +24,7 @@
class Dispatcher
DEFAULT_SESSION_OPTIONS = { "database_manager" => CGI::Session::PStore, "prefix" => "ruby_sess.", "session_path" => "/" }
def self.dispatch(cgi = CGI.new, session_options = DEFAULT_SESSION_OPTIONS, error_page = nil)
def self.dispatch(cgi = CGI.new, session_options = DEFAULT_SESSION_OPTIONS)
begin
request = ActionController::CgiRequest.new(cgi, session_options)
response = ActionController::CgiResponse.new(cgi)
@ -35,14 +35,8 @@ class Dispatcher
require_dependency(controller_path(controller_name, module_name))
controller_class(controller_name).process(request, response).out
rescue Object => e
begin
ActionController::Base.logger.info "\n\nException throw during dispatch: #{e.message}\n#{e.backtrace.join("\n")}"
rescue Exception
# Couldn't log error
end
if error_page then cgi.out{ IO.readlines(error_page) } else raise e end
rescue Object => exception
ActionController::Base.process_with_exception(request, response, exception).out
ensure
ActiveRecord::Base.reset_associations_loaded

View File

@ -73,7 +73,7 @@ module Generator
end
end
# Generate model, unit test, and fixtures.
# Generate model, unit test, and fixtures.
class Model < Base
def generate

View File

@ -85,7 +85,6 @@ class DispatchServlet < WEBrick::HTTPServlet::AbstractServlet
res.set_error(err)
return true
rescue => err
p err
return false
end
end