pboling-super_exception_notifier 1.6.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +21 -0
- data/README.rdoc +419 -0
- data/VERSION.yml +4 -0
- data/exception_notification.gemspec +71 -0
- data/init.rb +1 -0
- data/lib/exception_notifiable.rb +278 -0
- data/lib/exception_notifier.rb +108 -0
- data/lib/exception_notifier_helper.rb +58 -0
- data/lib/hooks_notifier.rb +53 -0
- data/lib/notifiable.rb +8 -0
- data/lib/super_exception_notifier/custom_exception_classes.rb +16 -0
- data/lib/super_exception_notifier/custom_exception_methods.rb +50 -0
- data/rails/app/views/exception_notifiable/400.html +5 -0
- data/rails/app/views/exception_notifiable/403.html +6 -0
- data/rails/app/views/exception_notifiable/404.html +6 -0
- data/rails/app/views/exception_notifiable/405.html +6 -0
- data/rails/app/views/exception_notifiable/410.html +7 -0
- data/rails/app/views/exception_notifiable/418.html +6 -0
- data/rails/app/views/exception_notifiable/422.html +5 -0
- data/rails/app/views/exception_notifiable/423.html +6 -0
- data/rails/app/views/exception_notifiable/501.html +8 -0
- data/rails/app/views/exception_notifiable/503.html +6 -0
- data/rails/init.rb +18 -0
- data/test/exception_notifier_helper_test.rb +76 -0
- data/test/exception_notify_functional_test.rb +102 -0
- data/test/mocks/404.html +1 -0
- data/test/mocks/500.html +1 -0
- data/test/mocks/controllers.rb +46 -0
- data/test/test_helper.rb +28 -0
- data/views/exception_notifier/_backtrace.html.erb +1 -0
- data/views/exception_notifier/_environment.html.erb +14 -0
- data/views/exception_notifier/_inspect_model.html.erb +16 -0
- data/views/exception_notifier/_request.html.erb +8 -0
- data/views/exception_notifier/_session.html.erb +7 -0
- data/views/exception_notifier/_title.html.erb +3 -0
- data/views/exception_notifier/background_exception_notification.text.plain.erb +6 -0
- data/views/exception_notifier/exception_notification.text.plain.erb +10 -0
- metadata +100 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module HooksNotifier
|
5
|
+
# Deliver exception data hash to web hooks, if any
|
6
|
+
#
|
7
|
+
def self.deliver_exception_to_web_hooks(config, exception, controller, request, data={}, the_blamed = nil)
|
8
|
+
params = build_web_hook_params(config, exception, controller, request, data, the_blamed)
|
9
|
+
# TODO: use threads here
|
10
|
+
config[:web_hooks].each do |address|
|
11
|
+
post_hook(params, address)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
# Parameters hash based on Merb Exceptions example
|
17
|
+
#
|
18
|
+
def self.build_web_hook_params(config, exception, controller, request, data={}, the_blamed = nil)
|
19
|
+
host = (request.env["HTTP_X_FORWARDED_HOST"] || request.env["HTTP_HOST"])
|
20
|
+
p = {
|
21
|
+
'environment' => (defined?(Rails) ? Rails.env : RAILS_ENV),
|
22
|
+
'exceptions' => [{
|
23
|
+
:class => exception.class.to_s,
|
24
|
+
:backtrace => exception.backtrace,
|
25
|
+
:message => exception.message
|
26
|
+
}],
|
27
|
+
'app_name' => config[:app_name],
|
28
|
+
'version' => config[:version],
|
29
|
+
'blame' => "#{the_blamed}"
|
30
|
+
}
|
31
|
+
if !request.nil?
|
32
|
+
p.merge!({'request_url' => "#{request.protocol}#{host}#{request.request_uri}"})
|
33
|
+
p.merge!({'request_action' => request.parameters['action']})
|
34
|
+
p.merge!({'request_params' => request.parameters.inspect})
|
35
|
+
end
|
36
|
+
p.merge!({'request_controller' => controller.class.name}) if !controller.nil?
|
37
|
+
p.merge!({'status' => exception.status}) if exception.respond_to?(:status)
|
38
|
+
return p
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.post_hook(params, address)
|
42
|
+
uri = URI.parse(address)
|
43
|
+
uri.path = '/' if uri.path=='' # set a path if one isn't provided to keep Net::HTTP happy
|
44
|
+
|
45
|
+
headers = { 'Content-Type' => 'text/x-json' }
|
46
|
+
data = params.to_json
|
47
|
+
Net::HTTP.start(uri.host, uri.port) do |http|
|
48
|
+
http.request_post(uri.path, data, headers)
|
49
|
+
end
|
50
|
+
data
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
data/lib/notifiable.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#Copyright (c) 2008-2009 Peter H. Boling of 9thBit LLC
|
2
|
+
#Released under the MIT license
|
3
|
+
|
4
|
+
module SuperExceptionNotifier
|
5
|
+
module CustomExceptionClasses
|
6
|
+
|
7
|
+
class AccessDenied < StandardError; end
|
8
|
+
class ResourceGone < StandardError; end
|
9
|
+
class NotImplemented < StandardError; end
|
10
|
+
class PageNotFound < StandardError; end
|
11
|
+
class InvalidMethod < StandardError; end
|
12
|
+
class CorruptData < StandardError; end
|
13
|
+
class MethodDisabled < StandardError; end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#Copyright (c) 2008-2009 Peter H. Boling of 9thBit LLC
|
2
|
+
#Released under the MIT license
|
3
|
+
|
4
|
+
module SuperExceptionNotifier
|
5
|
+
module CustomExceptionMethods
|
6
|
+
|
7
|
+
#For a while after disabling a route/URL that had been functional we should set it to resource gone to inform people to remove bookmarks.
|
8
|
+
def resource_gone
|
9
|
+
raise ResourceGone
|
10
|
+
end
|
11
|
+
#Then for things that have never existed or have not for a long time we call not_implemented
|
12
|
+
def not_implemented
|
13
|
+
raise NotImplemented
|
14
|
+
end
|
15
|
+
#Resources that must be requested with a specific HTTP Meethod (GET, PUT, POST, DELETE, AJAX, etc) but are requested otherwise should:
|
16
|
+
def invalid_method
|
17
|
+
raise InvalidMethod
|
18
|
+
end
|
19
|
+
#If your ever at a spot in the code that should never get reached, but corrupt data might get you there anyways then this is for you:
|
20
|
+
def corrupt_data
|
21
|
+
raise CorruptData
|
22
|
+
end
|
23
|
+
def page_not_found
|
24
|
+
raise PageNotFound
|
25
|
+
end
|
26
|
+
def record_not_found
|
27
|
+
raise ActiveRecord::RecordNotFound
|
28
|
+
end
|
29
|
+
def method_disabled
|
30
|
+
raise MethodDisabled
|
31
|
+
end
|
32
|
+
#The current user does not have enough privileges to access the requested resource
|
33
|
+
def access_denied
|
34
|
+
raise AccessDenied
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
|
39
|
+
def generic_error
|
40
|
+
error_stickie("Sorry, an error has occurred.")
|
41
|
+
corrupt_data
|
42
|
+
end
|
43
|
+
|
44
|
+
def invalid_page
|
45
|
+
error_stickie("Sorry, the page number you requested was not valid.")
|
46
|
+
page_not_found
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/400.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>Unable to Process Requested Data</h1>
|
4
|
+
<p>Reloading the page will probably not help.</p>
|
5
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/403.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>Access Restricted</h1>
|
4
|
+
|
5
|
+
<p>If you need access to this resource please contact support or an administrator.</p>
|
6
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/404.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>The page you were looking for doesn't exist.</h1>
|
4
|
+
|
5
|
+
<p>You may have mistyped the address or the page may have moved.</p>
|
6
|
+
</div>
|
@@ -0,0 +1,7 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/410.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>Requested resource is no longer available</h1>
|
4
|
+
|
5
|
+
<p>Please remove bookmarks to this resource.</p>
|
6
|
+
<p>If you arrived here via a link from somewhere else please inform them of the broken link.</p>
|
7
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/418.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>418 I�m a teapot</h1>
|
4
|
+
|
5
|
+
<p>I MAY be short and stout. Defined by the April Fools specification RFC 2324. See Hyper Text Coffee Pot Control Protocol for more information.</p>
|
6
|
+
</div>
|
@@ -0,0 +1,5 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/422.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>Unprocessable Request</h1>
|
4
|
+
<p>The request was well-formed but was unable to be followed due to semantic errors.</p>
|
5
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/423.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>The change you wanted was rejected because the resource is locked</h1>
|
4
|
+
<p>It might become available again soon, if you try again!</p>
|
5
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
6
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/501.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>Not Implemented</h1>
|
4
|
+
|
5
|
+
<p>Please remove bookmarks to this resource.</p>
|
6
|
+
<p>You have tried to access a feature that has not been implemented.</p>
|
7
|
+
<p>If you arrived here via a link from somewhere else please inform them of the broken link.</p>
|
8
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- This file lives in gems/super_exception_notifier/rails/app/views/exception_notifiable/503.html. See Readme for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>The server is temporarily unavailable.</h1>
|
4
|
+
|
5
|
+
<p>The resource you requested is temporarilly unavailable due to server overload, maintenance, or other downtime. Generally, this is a temporary state.</p>
|
6
|
+
</div>
|
data/rails/init.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "action_mailer"
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', "super_exception_notifier", "custom_exception_classes")
|
4
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', "super_exception_notifier", "custom_exception_methods")
|
5
|
+
|
6
|
+
$:.unshift "#{File.dirname(__FILE__)}/lib"
|
7
|
+
|
8
|
+
require "hooks_notifier"
|
9
|
+
require "exception_notifier"
|
10
|
+
require "exception_notifiable"
|
11
|
+
require "exception_notifier_helper"
|
12
|
+
require "notifiable"
|
13
|
+
|
14
|
+
Object.class_eval do include Notifiable end
|
15
|
+
|
16
|
+
if ActionController::Base.respond_to?(:append_view_path)
|
17
|
+
ActionController::Base.append_view_path(File.join(File.dirname(__FILE__), 'app', 'views'))
|
18
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
require 'exception_notifier_helper'
|
3
|
+
|
4
|
+
class ExceptionNotifierHelperTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
class ExceptionNotifierHelperIncludeTarget
|
7
|
+
include ExceptionNotifierHelper
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@helper = ExceptionNotifierHelperIncludeTarget.new
|
12
|
+
end
|
13
|
+
|
14
|
+
# No controller
|
15
|
+
|
16
|
+
def test_should_not_exclude_raw_post_parameters_if_no_controller
|
17
|
+
assert !@helper.exclude_raw_post_parameters?
|
18
|
+
end
|
19
|
+
|
20
|
+
# Controller, no filtering
|
21
|
+
|
22
|
+
class ControllerWithoutFilterParameters; end
|
23
|
+
|
24
|
+
def test_should_not_filter_env_values_for_raw_post_data_keys_if_controller_can_not_filter_parameters
|
25
|
+
stub_controller(ControllerWithoutFilterParameters.new)
|
26
|
+
assert @helper.filter_sensitive_post_data_from_env("RAW_POST_DATA", "secret").include?("secret")
|
27
|
+
end
|
28
|
+
def test_should_not_exclude_raw_post_parameters_if_controller_can_not_filter_parameters
|
29
|
+
stub_controller(ControllerWithoutFilterParameters.new)
|
30
|
+
assert !@helper.exclude_raw_post_parameters?
|
31
|
+
end
|
32
|
+
def test_should_return_params_if_controller_can_not_filter_parameters
|
33
|
+
stub_controller(ControllerWithoutFilterParameters.new)
|
34
|
+
assert_equal :params, @helper.filter_sensitive_post_data_parameters(:params)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Controller with filter paramaters method, no params to filter
|
38
|
+
|
39
|
+
class ControllerWithFilterParametersThatDoesntFilter
|
40
|
+
def filter_parameters(params); params end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_should_filter_env_values_for_raw_post_data_keys_if_controller_can_filter_parameters
|
44
|
+
stub_controller(ControllerWithFilterParametersThatDoesntFilter.new)
|
45
|
+
assert !@helper.filter_sensitive_post_data_from_env("RAW_POST_DATA", "secret").include?("secret")
|
46
|
+
assert @helper.filter_sensitive_post_data_from_env("SOME_OTHER_KEY", "secret").include?("secret")
|
47
|
+
end
|
48
|
+
def test_should_exclude_raw_post_parameters_if_controller_can_filter_parameters
|
49
|
+
stub_controller(ControllerWithFilterParametersThatDoesntFilter.new)
|
50
|
+
assert @helper.exclude_raw_post_parameters?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Controller with filter paramaters method, filtering a secret param
|
54
|
+
|
55
|
+
class ControllerWithFilterParametersThatDoesFilter
|
56
|
+
def filter_parameters(params); :filtered end
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_should_delegate_param_filtering_to_controller_if_controller_can_filter_parameters
|
60
|
+
stub_controller(ControllerWithFilterParametersThatDoesFilter.new)
|
61
|
+
assert_equal :filtered, @helper.filter_sensitive_post_data_parameters(:secret)
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_compat_mode_constant
|
65
|
+
if defined?(RAILS_GEM_VERSION)
|
66
|
+
assert_equal(ExceptionNotifierHelper::COMPAT_MODE, RAILS_GEM_VERSION >= 2)
|
67
|
+
else
|
68
|
+
assert_equal(ExceptionNotifierHelper::COMPAT_MODE, false)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def stub_controller(controller)
|
74
|
+
@helper.instance_variable_set(:@controller, controller)
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/test_helper')
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
RAILS_DEFAULT_LOGGER = Logger.new(nil)
|
5
|
+
|
6
|
+
require File.join(File.dirname(__FILE__), 'mocks/controllers')
|
7
|
+
|
8
|
+
ActionController::Routing::Routes.clear!
|
9
|
+
ActionController::Routing::Routes.draw {|m| m.connect ':controller/:action/:id' }
|
10
|
+
|
11
|
+
class ExceptionNotifyFunctionalTest < ActionController::TestCase
|
12
|
+
|
13
|
+
def setup
|
14
|
+
@request = ActionController::TestRequest.new
|
15
|
+
@response = ActionController::TestResponse.new
|
16
|
+
ActionController::Base.consider_all_requests_local = false
|
17
|
+
@@delivered_mail = []
|
18
|
+
ActionMailer::Base.class_eval do
|
19
|
+
def deliver!(mail = @mail)
|
20
|
+
@@delivered_mail << mail
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_view_path_200; assert_view_path_string("200"); end
|
26
|
+
def test_view_path_400; assert_view_path_string("400"); end
|
27
|
+
def test_view_path_403; assert_view_path_string("403"); end
|
28
|
+
def test_view_path_404; assert_view_path_string("404"); end
|
29
|
+
def test_view_path_405; assert_view_path_string("405"); end
|
30
|
+
def test_view_path_410; assert_view_path_string("410"); end
|
31
|
+
def test_view_path_418; assert_view_path_string("422"); end
|
32
|
+
def test_view_path_422; assert_view_path_string("422"); end
|
33
|
+
def test_view_path_423; assert_view_path_string("423"); end
|
34
|
+
def test_view_path_500; assert_view_path_string("500"); end
|
35
|
+
def test_view_path_501; assert_view_path_string("501"); end
|
36
|
+
def test_view_path_503; assert_view_path_string("503"); end
|
37
|
+
def test_view_path_nil; assert_view_path_string(nil); end
|
38
|
+
def test_view_path_empty; assert_view_path_string(""); end
|
39
|
+
def test_view_path_nonsense; assert_view_path_string("slartibartfarst"); end
|
40
|
+
|
41
|
+
def test_old_style_where_requests_are_local
|
42
|
+
ActionController::Base.consider_all_requests_local = true
|
43
|
+
@controller = OldStyle.new
|
44
|
+
get "runtime_error"
|
45
|
+
|
46
|
+
assert_nothing_mailed
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_new_style_where_requests_are_local
|
50
|
+
ActionController::Base.consider_all_requests_local = true
|
51
|
+
@controller = NewStyle.new
|
52
|
+
get "runtime_error"
|
53
|
+
|
54
|
+
# puts @response.body
|
55
|
+
assert_nothing_mailed
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_old_style_runtime_error_sends_mail
|
59
|
+
@controller = OldStyle.new
|
60
|
+
get "runtime_error"
|
61
|
+
assert_error_mail_contains("This is a runtime error that we should be emailed about")
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_old_style_record_not_found_does_not_send_mail
|
65
|
+
@controller = OldStyle.new
|
66
|
+
get "record_not_found"
|
67
|
+
assert_nothing_mailed
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_new_style_runtime_error_sends_mail
|
71
|
+
@controller = NewStyle.new
|
72
|
+
get "runtime_error"
|
73
|
+
assert_error_mail_contains("This is a runtime error that we should be emailed about")
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_new_style_record_not_found_does_not_send_mail
|
77
|
+
@controller = NewStyle.new
|
78
|
+
get "record_not_found"
|
79
|
+
assert_nothing_mailed
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def assert_view_path_string(status)
|
85
|
+
assert(ExceptionNotifier.get_view_path(status).is_a?(String), "View Path is not a string for status code '#{status}'")
|
86
|
+
end
|
87
|
+
|
88
|
+
def assert_error_mail_contains(text)
|
89
|
+
assert(mailed_error.index(text),
|
90
|
+
"Expected mailed error body to contain '#{text}', but not found. \n actual contents: \n#{mailed_error}")
|
91
|
+
end
|
92
|
+
|
93
|
+
def assert_nothing_mailed
|
94
|
+
assert @@delivered_mail.empty?, "Expected to have NOT mailed out a notification about an error occuring, but mailed: \n#{@@delivered_mail}"
|
95
|
+
end
|
96
|
+
|
97
|
+
def mailed_error
|
98
|
+
assert @@delivered_mail.last, "Expected to have mailed out a notification about an error occuring, but none mailed"
|
99
|
+
@@delivered_mail.last.encoded
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
data/test/mocks/404.html
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
simulate 404 error file in public
|
data/test/mocks/500.html
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
simulate 500 error file in public
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Rails
|
2
|
+
def self.public_path
|
3
|
+
File.dirname(__FILE__)
|
4
|
+
end
|
5
|
+
|
6
|
+
def self.env
|
7
|
+
'test'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
class Application < ActionController::Base
|
12
|
+
|
13
|
+
def runtime_error
|
14
|
+
raise "This is a runtime error that we should be emailed about"
|
15
|
+
end
|
16
|
+
|
17
|
+
def record_not_found
|
18
|
+
raise ActiveRecord::RecordNotFound
|
19
|
+
end
|
20
|
+
|
21
|
+
def local_request?
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class OldStyle < Application
|
28
|
+
include ExceptionNotifiable
|
29
|
+
self.exception_notifier_verbose = false
|
30
|
+
end
|
31
|
+
|
32
|
+
class SpecialErrorThing < RuntimeError
|
33
|
+
end
|
34
|
+
|
35
|
+
class NewStyle < Application
|
36
|
+
include ExceptionNotifiable
|
37
|
+
self.exception_notifier_verbose = false
|
38
|
+
|
39
|
+
rescue_from ActiveRecord::RecordNotFound do |exception|
|
40
|
+
render :text => "404", :status => 404
|
41
|
+
end
|
42
|
+
|
43
|
+
rescue_from RuntimeError do |exception|
|
44
|
+
render :text => "500", :status => 500
|
45
|
+
end
|
46
|
+
end
|