Permalink
Browse files

merged and resolved conflicts from akitaonrails/master

  • Loading branch information...
pboling committed Aug 7, 2009
2 parents 7c67b0a + e5717cf commit 51318b68deca504b8b9a055b4f7e4bb5acc0a65f
View
@@ -1,3 +1,4 @@
*.gem
doc
.project
+.idea
View
@@ -1,5 +1,5 @@
# Copyright (c) 2008 Peter Boling
-# Portions Copyright (c) 2005 Jamis Buck
+# Copyright (c) 2005 Jamis Buck
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -18,4 +18,4 @@
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
@@ -277,9 +277,28 @@ in ExceptionNotifiable for an example of how to go about that.
== HTTP Error Codes Used by Exception Notifier by default
- For reference these are the error codes that Exception Notifier can inherently handle. Official w3.org HTTP 1.1 Error Codes: http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
-
- 400 Bad Request * The request could not be understood by the server due to malformed syntax. * The client SHOULD NOT repeat the request without modifications. 403 Forbidden * The server understood the request, but is refusing to fulfill it 404 Not Found * The server has not found anything matching the Request-URI 405 Method Not Allowed * The method specified in the Request-Line is not allowed for the resource identified by the Request-URI * This is not implemented entirely as the response is supposed to contain a list of accepted methods. 410 Gone * The requested resource is no longer available at the server and no forwarding address is known. This condition is expected to be considered permanent 500 Internal Server Error * The server encountered an unexpected condition which prevented it from fulfilling the request. 501 Not Implemented * The server does not support the functionality required to fulfill the request. 503 Service Unavailable * The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.
+ For reference these are the error codes that Exception Notifier can inherently handle.
+ Official w3.org HTTP 1.1 Error Codes:
+ http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
+
+ 400 Bad Request
+ * The request could not be understood by the server due to malformed syntax.
+ * The client SHOULD NOT repeat the request without modifications.
+ 403 Forbidden
+ * The server understood the request, but is refusing to fulfill it
+ 404 Not Found
+ * The server has not found anything matching the Request-URI
+ 405 Method Not Allowed
+ * The method specified in the Request-Line is not allowed for the resource identified by the Request-URI
+ * This is not implemented entirely as the response is supposed to contain a list of accepted methods.
+ 410 Gone
+ * The requested resource is no longer available at the server and no forwarding address is known. This condition is expected to be considered permanent
+ 500 Internal Server Error
+ * The server encountered an unexpected condition which prevented it from fulfilling the request.
+ 501 Not Implemented
+ * The server does not support the functionality required to fulfill the request.
+ 503 Service Unavailable
+ * The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.
Copyright (c) 2008 Peter H. Boling, released under the MIT license
Portions Copyright (c) 2005 Jamis Buck, released under the MIT license
View
@@ -1,15 +1,22 @@
-require 'rubygems'
-require 'rake/testtask'
-require 'rake/rdoctask'
-
-Rake::TestTask.new do |t|
- t.libs << "test"
- t.test_files = FileList['test/*_test.rb']
- t.verbose = true
-end
-
-Rake::RDocTask.new do |rd|
- rd.main = "README.rdoc"
- rd.rdoc_files.include("README.rdoc", "lib/*.rb")
- rd.rdoc_dir = 'doc'
-end
+require 'rake'
+require 'rake/testtask'
+require 'rake/rdoctask'
+
+desc 'Default: run unit tests.'
+task :default => :test
+
+desc 'Test exception_notification plugin.'
+Rake::TestTask.new(:test) do |t|
+ t.libs << 'lib'
+ t.pattern = 'test/**/*_test.rb'
+ t.verbose = true
+end
+
+desc 'Generate documentation for exception_notification plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'exception_notification'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
View
@@ -1,4 +1,4 @@
---
:major: 1
-:minor: 4
+:minor: 5
:patch: 0
@@ -1,6 +1,6 @@
Gem::Specification.new do |s|
s.name = 'exception_notification'
- s.version = '1.4'
+ s.version = '1.5'
s.date = '2008-07-28'
s.summary = "Allows unhandled exceptions to be captured and sent via email"
View
@@ -1,16 +1,15 @@
require 'ipaddr'
module ExceptionNotifiable
- include ExceptionHandler
+ include ExceptionHandler
- # exceptions of these types will not generate notification emails
SILENT_EXCEPTIONS = [
ActiveRecord::RecordNotFound,
ActionController::UnknownController,
ActionController::UnknownAction,
ActionController::RoutingError,
ActionController::MethodNotAllowed
- ]
+ ] unless defined?(SILENT_EXCEPTIONS)
HTTP_ERROR_CODES = {
"400" => "Bad Request",
@@ -21,8 +20,8 @@ module ExceptionNotifiable
"500" => "Internal Server Error",
"501" => "Not Implemented",
"503" => "Service Unavailable"
- }
-
+ } unless defined?(HTTP_ERROR_CODES)
+
def self.codes_for_rails_error_classes
classes = {
NameError => "503",
@@ -46,8 +45,6 @@ def self.included(base)
# can be defined at controller level to the name of the layout,
# or set to true to render the controller's own default layout,
# or set to false to render errors with no layout
- base.cattr_accessor :silent_exceptions
- base.silent_exceptions = SILENT_EXCEPTIONS
base.cattr_accessor :http_error_codes
base.http_error_codes = HTTP_ERROR_CODES
base.cattr_accessor :error_layout
@@ -56,6 +53,8 @@ def self.included(base)
base.rails_error_classes = self.codes_for_rails_error_classes
base.cattr_accessor :exception_notifier_verbose
base.exception_notifier_verbose = false
+ base.cattr_accessor :silent_exceptions
+ base.silent_exceptions = SILENT_EXCEPTIONS
end
module ClassMethods
@@ -92,61 +91,83 @@ def local_request?
!self.class.local_addresses.detect { |addr| addr.include?(remote) }.nil?
end
- def render_error_template(status_cd, request, exception, file_path = nil)
+ def notify_and_render_error_template(status_cd, request, exception, file_path = nil)
status = self.class.http_error_codes[status_cd] ? status_cd + " " + self.class.http_error_codes[status_cd] : status_cd
-
file = file_path ? ExceptionNotifier.get_view_path(file_path) : ExceptionNotifier.get_view_path(status_cd)
- send_email = ExceptionNotifier.should_send_email?(status_cd, exception)
- if self.class.exception_notifier_verbose
- puts "[EXCEPTION] #{exception}"
- puts "[EXCEPTION CLASS] #{exception.class}"
- puts "[EXCEPTION STATUS_CD] #{status_cd}"
- puts "[ERROR LAYOUT] #{self.class.error_layout}"
- puts "[ERROR VIEW PATH] #{ExceptionNotifier.view_path}" if !ExceptionNotifier.nil?
- puts "[ERROR RENDER] #{file}"
- puts "[ERROR EMAIL] #{send_email ? "YES" : "NO"}"
- logger.error("render_error(#{status_cd}, #{self.class.http_error_codes[status_cd]}) invoked for request_uri=#{request.request_uri} and env=#{request.env.inspect}")
- end
-
- #send the email before rendering to avert possible errors on render preventing the email from being sent.
+ send_email = should_notify_on_exception?(status_cd, exception)
+
+ # Debugging output
+ verbose_output(exception, status_cd, file, send_email, request) if self.class.exception_notifier_verbose
+ # Send the email before rendering to avert possible errors on render preventing the email from being sent.
send_exception_email(exception) if send_email
-
+ # Render the error page to the end user
+ render_error_template(file, status)
+ end
+
+ def render_error_template(file, status)
respond_to do |type|
type.html { render :file => file,
- :layout => self.class.error_layout,
+ :layout => self.class.error_layout,
:status => status }
- type.all { render :nothing => true,
+ type.all { render :nothing => true,
:status => status}
end
end
- def send_exception_email(exception)
- unless self.class.silent_exceptions.any? {|klass| klass === exception}
- deliverer = self.class.exception_data
- data = case deliverer
- when nil then {}
- when Symbol then send(deliverer)
- when Proc then deliverer.call(self)
- end
- the_blamed = lay_blame(exception)
+ def verbose_output(exception, status_cd, file, send_email, request = nil)
+ puts "[EXCEPTION] #{exception}"
+ puts "[EXCEPTION CLASS] #{exception.class}"
+ puts "[EXCEPTION STATUS_CD] #{status_cd}"
+ puts "[ERROR LAYOUT] #{self.class.error_layout}"
+ puts "[ERROR VIEW PATH] #{ExceptionNotifier.view_path}" if !ExceptionNotifier.nil?
+ puts "[ERROR RENDER] #{file}"
+ puts "[ERROR EMAIL] #{send_email ? "YES" : "NO"}"
+ req = request ? " for request_uri=#{request.request_uri} and env=#{request.env.inspect}" : ""
+ logger.error("render_error(#{status_cd}, #{self.class.http_error_codes[status_cd]}) invoked#{req}")
+ end
- ExceptionNotifier.deliver_exception_notification(exception, self,
- request, data, the_blamed)
+ def send_exception_email(exception)
+ deliverer = self.class.exception_data
+ data = case deliverer
+ when nil then {}
+ when Symbol then send(deliverer)
+ when Proc then deliverer.call(self)
end
+ the_blamed = lay_blame(exception)
+
+ ExceptionNotifier.deliver_exception_notification(exception, self,
+ request, data, the_blamed)
+ end
+
+ def should_notify_on_exception?(status_cd, exception)
+ #don't mail exceptions raised locally
+ return false if (consider_all_requests_local || local_request?)
+ #don't mail exceptions raised that match ExceptionNotifiable.silent_exceptions
+ return false if self.class.silent_exceptions.any? {|klass| klass === exception}
+ return false unless ExceptionNotifier.should_send_email?(status_cd, exception)
+ return true
end
def rescue_action_in_public(exception)
- status_code = self.class.rails_error_classes[exception.class].nil? ? '500' : self.class.rails_error_classes[exception.class].blank? ? '200' : self.class.rails_error_classes[exception.class]
# If the error class is NOT listed in the rails_errror_class hash then we get a generic 500 error:
# OTW if the error class is listed, but has a blank code or the code is == '200' then we get a custom error layout rendered
# OTW the error class is listed!
+ status_code = status_code_for_exception(exception)
if status_code == '200'
- render_error_template(status_code, request, exception, exception.to_s.delete(':').gsub( /([A-Za-z])([A-Z])/, '\1' << '_' << '\2' ).downcase)
+ notify_and_render_error_template(status_code, request, exception, exception_to_filename(exception))
else
- render_error_template(status_code, request, exception)
+ notify_and_render_error_template(status_code, request, exception)
end
end
-
+
+ def status_code_for_exception(exception)
+ self.class.rails_error_classes[exception.class].nil? ? '500' : self.class.rails_error_classes[exception.class].blank? ? '200' : self.class.rails_error_classes[exception.class]
+ end
+
+ def exception_to_filename(exception)
+ exception.to_s.delete(':').gsub( /([A-Za-z])([A-Z])/, '\1' << '_' << '\2' ).downcase
+ end
+
def lay_blame(exception)
error = {}
unless(ExceptionNotifier.git_repo_path.nil?)
@@ -172,16 +193,16 @@ def lay_blame(exception)
end
error
end
-
+
def blame_output(line_number, path)
app_directory = Dir.pwd
Dir.chdir ExceptionNotifier.git_repo_path
blame = `git blame -p -L #{line_number},#{line_number} #{path}`
Dir.chdir app_directory
-
+
blame
end
-
+
def exception_in_project?(path) # should be a path like /path/to/broken/thingy.rb
dir = File.split(path).first rescue ''
if(File.directory?(dir) and !(path =~ /vendor\/plugins/) and path.include?(RAILS_ROOT))
@@ -56,6 +56,9 @@ def exception_notification(exception, controller = nil, request = nil, data={},
#content_type "text/plain"
recipients exception_recipients
from sender_address
+
+ request.session.inspect # Ensure session data is loaded (Rails 2.3 lazy-loading)
+
subject "#{email_prefix}#{data[:location]} (#{exception.class}) #{exception.message.inspect}"
body data
end
@@ -1,9 +1,9 @@
require 'pp'
module ExceptionNotifierHelper
- VIEW_PATH = "views/exception_notifier"
- APP_PATH = "#{RAILS_ROOT}/app/#{VIEW_PATH}"
- PARAM_FILTER_REPLACEMENT = "[FILTERED]"
+ VIEW_PATH = "views/exception_notifier" unless defined?(VIEW_PATH)
+ APP_PATH = "#{RAILS_ROOT}/app/#{VIEW_PATH}" unless defined?(APP_PATH)
+ PARAM_FILTER_REPLACEMENT = "[FILTERED]" unless defined?(PARAM_FILTER_REPLACEMENT)
COMPAT_MODE = RAILS_GEM_VERSION ? RAILS_GEM_VERSION < '2' : false
def render_section(section)
@@ -44,11 +44,11 @@ def object_to_yaml(object)
def exclude_raw_post_parameters?
@controller && @controller.respond_to?(:filter_parameters)
end
-
+
def filter_sensitive_post_data_parameters(parameters)
exclude_raw_post_parameters? ? COMPAT_MODE ? @controller.filter_parameters(parameters) : @controller.__send__(:filter_parameters, parameters) : parameters
end
-
+
def filter_sensitive_post_data_from_env(env_key, env_value)
return env_value unless exclude_raw_post_parameters?
return PARAM_FILTER_REPLACEMENT if (env_key =~ /RAW_POST_DATA/i)
View
@@ -1,6 +1,8 @@
require "action_mailer"
+$:.unshift "#{File.dirname(__FILE__)}/lib"
require "exception_notifier"
require "exception_notifiable"
require "exception_notifier_helper"
+require "notifiable"
Object.class_eval do include Notifiable end
@@ -1,4 +1,4 @@
-require File.dirname(__FILE__) + '/test_helper'
+require File.expand_path(File.dirname(__FILE__) + '/test_helper')
require 'exception_notifier_helper'
class ExceptionNotifierHelperTest < Test::Unit::TestCase
@@ -38,31 +38,34 @@ def test_should_return_params_if_controller_can_not_filter_parameters
assert_equal :params, @helper.filter_sensitive_post_data_parameters(:params)
end
- # Controller with filtering
+ # Controller with filter paramaters method, no params to filter
class ControllerWithFilterParameters
- def filter_parameters(params); {:some_key => :filtered} end
+ def filter_parameters(params)
+ params.keys.each do |k|
+ params[k] = :filtered
+ end
+ params
+ end
end
def test_should_filter_env_values_for_raw_post_data_keys_if_controller_can_filter_parameters
stub_controller(ControllerWithFilterParameters.new)
- assert_equal "[FILTERED]", @helper.filter_sensitive_post_data_from_env("RAW_POST_DATA", "secret")
- end
- def test_should_filter_env_values_for_all_keys_if_controller_can_filter_parameters
- stub_controller(ControllerWithFilterParameters.new)
- assert_equal :filtered, @helper.filter_sensitive_post_data_from_env("SOME_OTHER_KEY", "secret")
+
+ assert_equal("[FILTERED]", @helper.filter_sensitive_post_data_from_env("RAW_POST_DATA", "secret"))
+ assert_equal(:filtered, @helper.filter_sensitive_post_data_from_env("SOME_OTHER_KEY", "secret"))
end
def test_should_exclude_raw_post_parameters_if_controller_can_filter_parameters
- stub_controller(ControllerWithFilterParameters.new)
+ stub_controller(ControllerWithFilterParametersThatDoesntFilter.new)
assert @helper.exclude_raw_post_parameters?
end
def test_should_delegate_param_filtering_to_controller_if_controller_can_filter_parameters
stub_controller(ControllerWithFilterParameters.new)
- assert_equal :filtered, @helper.filter_sensitive_post_data_parameters(:params).values[0]
+ assert_equal({:param => :filtered}, @helper.filter_sensitive_post_data_parameters({:param => :value}))
end
private
def stub_controller(controller)
@helper.instance_variable_set(:@controller, controller)
end
-end
+end
Oops, something went wrong.

0 comments on commit 51318b6

Please sign in to comment.