wrangler 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README +247 -0
- data/lib/wrangler/exception_handler.rb +280 -0
- data/lib/wrangler/exception_notifier.rb +142 -0
- data/lib/wrangler/wrangler_exceptions.rb +63 -0
- data/lib/wrangler/wrangler_helper.rb +95 -0
- data/lib/wrangler.rb +284 -0
- data/rails/app/views/wrangler/400.html +6 -0
- data/rails/app/views/wrangler/401.html +6 -0
- data/rails/app/views/wrangler/403.html +6 -0
- data/rails/app/views/wrangler/404.html +6 -0
- data/rails/app/views/wrangler/405.html +6 -0
- data/rails/app/views/wrangler/410.html +7 -0
- data/rails/app/views/wrangler/422.html +6 -0
- data/rails/app/views/wrangler/423.html +7 -0
- data/rails/app/views/wrangler/500.html +6 -0
- data/rails/app/views/wrangler/501.html +8 -0
- data/rails/app/views/wrangler/503.html +6 -0
- data/views/wrangler/exception_notifier/exception_notification.text.plain.erb +44 -0
- data/wrangler.gemspec +61 -0
- metadata +87 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
module Wrangler
|
2
|
+
# handles notifying (sending emails) for the wrangler gem. configuration for
|
3
|
+
# the class is set in an initializer via the configure() method, e.g.
|
4
|
+
#
|
5
|
+
# Wrangler::ExceptionNotifier.configure do |config|
|
6
|
+
# config[:key] = value
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# see README or source code for possible values and default settings
|
10
|
+
#-----------------------------------------------------------------------------
|
11
|
+
class ExceptionNotifier < ActionMailer::Base
|
12
|
+
# the default configuration
|
13
|
+
@@config ||= {
|
14
|
+
# who the emails will be coming from. if nil or missing or empty string,
|
15
|
+
# effectively disables email notification
|
16
|
+
:from_address => '',
|
17
|
+
# array of addresses that the emails will be sent to. if nil or missing
|
18
|
+
# or empty array, effectively disables email notification.
|
19
|
+
:recipient_addresses => [],
|
20
|
+
# what will show up at the beginning of the subject line for each email
|
21
|
+
# sent note: will be preceded by "[<app_name (if any)>...", where app_name
|
22
|
+
# is the :app_name config value from ExceptionHandler (or explicit
|
23
|
+
# proc_name given to notify_on_error() method)
|
24
|
+
:subject_prefix => "#{(defined?(Rails) ? Rails.env : RAILS_ENV).capitalize} ERROR",
|
25
|
+
# can use this to define app-specific mail views using the same data
|
26
|
+
# made available in exception_notification()
|
27
|
+
:mailer_template_root => File.join(WRANGLER_ROOT, 'views')
|
28
|
+
}
|
29
|
+
|
30
|
+
cattr_accessor :config
|
31
|
+
|
32
|
+
def self.configure(&block)
|
33
|
+
yield @@config
|
34
|
+
end
|
35
|
+
|
36
|
+
self.template_root = config[:mailer_template_root]
|
37
|
+
|
38
|
+
# form and send the notification email (note: this gets called indirectly
|
39
|
+
# when you call ExceptionNotifier.deliver_exception_notification())
|
40
|
+
#
|
41
|
+
# arguments:
|
42
|
+
# - exception: the exception that was raised
|
43
|
+
# - proc_name: the name of the process in which the exception arised
|
44
|
+
# - backtrace: the stack trace from the exception (passing in excplicitly
|
45
|
+
# because serializing the exception does not preserve the
|
46
|
+
# backtrace (in case delayed_job is used to async the email)
|
47
|
+
# - status_code: the (string) http status code that the exception has been
|
48
|
+
# mapped to. Optional, but no default is provided, so
|
49
|
+
# no status code info will be contained in the email.
|
50
|
+
# - request_data: hash with relevant data from the request object.
|
51
|
+
# Optional, but if not present, then assumed the exception
|
52
|
+
# was not due to an http request and thus no request
|
53
|
+
# data will be contained in the email.
|
54
|
+
# - request: the original request that resulted in the exception. may
|
55
|
+
# be nil and MUST be nil if calling this method with
|
56
|
+
# delayed_job. Optional.
|
57
|
+
#---------------------------------------------------------------------------
|
58
|
+
def exception_notification(exception, proc_name, backtrace,
|
59
|
+
status_code = nil,
|
60
|
+
request_data = nil,
|
61
|
+
request = nil)
|
62
|
+
|
63
|
+
# don't try to send email if there are no from or recipient addresses
|
64
|
+
if config[:from_address].nil? ||
|
65
|
+
config[:from_address].empty? ||
|
66
|
+
config[:recipient_addresses].nil? ||
|
67
|
+
config[:recipient_addresses].empty?
|
68
|
+
|
69
|
+
return nil
|
70
|
+
end
|
71
|
+
|
72
|
+
ensure_session_loaded(request)
|
73
|
+
|
74
|
+
# NOTE: be very careful pulling data out of request in the view...it is
|
75
|
+
# NOT cleaned, and may contain private data (e.g. passwords), so
|
76
|
+
# scrutinize any use of @request in the views!
|
77
|
+
|
78
|
+
body_hash =
|
79
|
+
{ :exception => exception,
|
80
|
+
:backtrace => backtrace,
|
81
|
+
:status_code => status_code,
|
82
|
+
:request_data => request_data,
|
83
|
+
:request => request
|
84
|
+
}
|
85
|
+
|
86
|
+
body_hash.merge! extract_data_from_request_data(request_data)
|
87
|
+
from config[:from_address]
|
88
|
+
recipients config[:recipient_addresses]
|
89
|
+
subject "[#{proc_name + (proc_name ? ' ' : '')}" +
|
90
|
+
"#{config[:subject_prefix]}] " +
|
91
|
+
"#{exception.class.name}: " +
|
92
|
+
"#{exception.message.inspect}"
|
93
|
+
body body_hash
|
94
|
+
sent_on Time.now
|
95
|
+
content_type 'text/plain'
|
96
|
+
end
|
97
|
+
|
98
|
+
# helper to force loading of session data in case of lazy loading (Rails
|
99
|
+
# 2.3). if the argument isn't a request object, then don't bother, cause
|
100
|
+
# it won't have session.
|
101
|
+
#---------------------------------------------------------------------------
|
102
|
+
def ensure_session_loaded(request)
|
103
|
+
request.session.inspect if !request.nil? && request.respond_to?(:session)
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
# extract relevant (and serializable) data from a request object
|
108
|
+
#---------------------------------------------------------------------------
|
109
|
+
def extract_data_from_request_data(request_data)
|
110
|
+
if request_data
|
111
|
+
{ :host => host_from_request_data(request_data),
|
112
|
+
:protocol => protocol_from_request_data(request_data),
|
113
|
+
:uri => uri_from_request_data(request_data)
|
114
|
+
}
|
115
|
+
else
|
116
|
+
{}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# extract the host from request object
|
121
|
+
#---------------------------------------------------------------------------
|
122
|
+
def host_from_request_data(request_data)
|
123
|
+
request_data['HTTP_X_REAL_IP'] ||
|
124
|
+
request_data['HTTP_X_FORWARDED_HOST'] ||
|
125
|
+
request_data['HTTP_HOST']
|
126
|
+
end
|
127
|
+
|
128
|
+
# extract protocol from request object
|
129
|
+
#---------------------------------------------------------------------------
|
130
|
+
def protocol_from_request_data(request_data)
|
131
|
+
request_data['SERVER_PROTOCOL']
|
132
|
+
end
|
133
|
+
|
134
|
+
# extract URI from request object
|
135
|
+
#---------------------------------------------------------------------------
|
136
|
+
def uri_from_request_data(request_data)
|
137
|
+
request_data['REQUEST_URI']
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# a bunch of handy exceptions. using the Http ones will require rails
|
2
|
+
module Wrangler
|
3
|
+
|
4
|
+
# base class for all the http-related exception classes
|
5
|
+
#-----------------------------------------------------------------------------
|
6
|
+
class HttpStatusError < Exception
|
7
|
+
def initialize(status_code, addl_msg = nil)
|
8
|
+
@status_code = status_code
|
9
|
+
@addl_msg = addl_msg
|
10
|
+
|
11
|
+
full_message = self.class.status_to_msg(status_code)
|
12
|
+
full_message += addl_msg unless addl_msg.nil?
|
13
|
+
|
14
|
+
super(full_message)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :status_code, :addl_msg
|
18
|
+
|
19
|
+
# accepts either a Fixnum status code or a symbol representation of a
|
20
|
+
# status code (e.g. 404 or :not_found)
|
21
|
+
# yes, this is total duplication of code in
|
22
|
+
# action_controller/status_codes.rb, but it's been made a private instance
|
23
|
+
# method in the module.
|
24
|
+
#---------------------------------------------------------------------------
|
25
|
+
def self.status_to_msg(status)
|
26
|
+
case status
|
27
|
+
when Fixnum then
|
28
|
+
"#{status} #{ActionController::StatusCodes::STATUS_CODES[status]}".strip
|
29
|
+
when Symbol then
|
30
|
+
status_to_msg(ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[status] ||
|
31
|
+
"500 Unknown Status #{status.inspect}")
|
32
|
+
else
|
33
|
+
status.to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
class HttpUnauthorized < HttpStatusError
|
40
|
+
def initialize(msg = nil); super(401, msg); end
|
41
|
+
end
|
42
|
+
|
43
|
+
class HttpNotFound < HttpStatusError
|
44
|
+
def initialize(msg = nil); super(404, msg); end
|
45
|
+
end
|
46
|
+
|
47
|
+
class HttpNotAcceptable < HttpStatusError
|
48
|
+
def initialize(msg = nil); super(406, msg); end
|
49
|
+
end
|
50
|
+
|
51
|
+
class HttpInternalServerError < HttpStatusError
|
52
|
+
def initialize(msg = nil); super(500, msg); end
|
53
|
+
end
|
54
|
+
|
55
|
+
class HttpNotImplemented < HttpStatusError
|
56
|
+
def initialize(msg = nil); super(501, msg); end
|
57
|
+
end
|
58
|
+
|
59
|
+
class HttpServiceUnavailable < HttpStatusError
|
60
|
+
def initialize(msg = nil); super(503, msg); end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Wrangler
|
2
|
+
WRANGLER_ROOT = "#{File.dirname(__FILE__)}/../.."
|
3
|
+
|
4
|
+
# make all of these instance methods act as module functions as well
|
5
|
+
# (any instance method below this gets added as a module function as well)
|
6
|
+
module_function
|
7
|
+
|
8
|
+
# utility to determine if a class has another class as its ancestor.
|
9
|
+
#
|
10
|
+
# returns the ancestor class (if any) that is found to be the ancestor
|
11
|
+
# of klass (will return the nearest ancestor in other_klasses).
|
12
|
+
# returns nil or false otherwise.
|
13
|
+
#
|
14
|
+
# arguments:
|
15
|
+
# - klass: the class we're determining whether it has one of the other
|
16
|
+
# classes as an ancestor
|
17
|
+
# - other_klasses: a Class, an Array (or any other container that responds
|
18
|
+
# to include?() ) of Classes
|
19
|
+
#-----------------------------------------------------------------------------
|
20
|
+
def class_has_ancestor?(klass, other_klasses)
|
21
|
+
return nil if !klass.is_a?(Class)
|
22
|
+
|
23
|
+
other_klasses = [other_klasses] if other_klasses.is_a?(Class)
|
24
|
+
|
25
|
+
current_klass = klass
|
26
|
+
while current_klass
|
27
|
+
return current_klass if other_klasses.include?(current_klass)
|
28
|
+
current_klass = current_klass.superclass
|
29
|
+
end
|
30
|
+
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# given an array of search directory strings (or a single directory string),
|
36
|
+
# searches for files matching pattern.
|
37
|
+
#
|
38
|
+
# pattern expressed in cmd line wildcards...like "*.rb" or "foo.?"...
|
39
|
+
# and may contain subdirectories.
|
40
|
+
#---------------------------------------------------------------------------
|
41
|
+
def find_file_matching_pattern(search_dirs, pattern)
|
42
|
+
search_dirs = [search_dirs] unless search_dirs.is_a?(Array)
|
43
|
+
|
44
|
+
search_dirs.each do |d|
|
45
|
+
matches = Dir.glob(File.join(d, pattern))
|
46
|
+
return matches.first if matches.size > 0
|
47
|
+
end
|
48
|
+
return nil
|
49
|
+
end
|
50
|
+
|
51
|
+
# log the exception using logger if available. if object does not have a
|
52
|
+
# logger, will just puts()
|
53
|
+
#-----------------------------------------------------------------------------
|
54
|
+
def log_exception(exception, request_data = nil, status_code = nil)
|
55
|
+
msgs = []
|
56
|
+
|
57
|
+
msgs << "An exception was caught (#{exception.class.name}):"
|
58
|
+
msgs << exception.message
|
59
|
+
unless request_data.blank?
|
60
|
+
msgs << "Request params were:"
|
61
|
+
msgs << request_data.inspect
|
62
|
+
end
|
63
|
+
unless status_code.blank?
|
64
|
+
msgs << "Handling with status code: #{status_code}"
|
65
|
+
end
|
66
|
+
unless exception.backtrace.blank?
|
67
|
+
msgs << exception.backtrace.join("\n ")
|
68
|
+
end
|
69
|
+
|
70
|
+
log_error msgs
|
71
|
+
end
|
72
|
+
|
73
|
+
# handles logging error messages, using logger if available and puts otherwise
|
74
|
+
#-----------------------------------------------------------------------------
|
75
|
+
def log_error(msgs)
|
76
|
+
unless msgs.is_a?(Array)
|
77
|
+
msgs = [msgs]
|
78
|
+
end
|
79
|
+
|
80
|
+
msgs.each do |m|
|
81
|
+
if respond_to?(:logger)
|
82
|
+
logger.error m
|
83
|
+
else
|
84
|
+
puts m
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# shorthand access to the exception handling config
|
90
|
+
#-----------------------------------------------------------------------------
|
91
|
+
def config
|
92
|
+
Wrangler::ExceptionHandler.config
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
data/lib/wrangler.rb
ADDED
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'wrangler/wrangler_helper.rb'
|
2
|
+
require 'wrangler/exception_handler.rb'
|
3
|
+
require 'wrangler/exception_notifier.rb'
|
4
|
+
|
5
|
+
module Wrangler
|
6
|
+
|
7
|
+
def self.included(base)
|
8
|
+
# only add in the controller-specific methods if the including class is one
|
9
|
+
if class_has_ancestor?(base, ActionController::Base)
|
10
|
+
base.send(:include, ControllerMethods)
|
11
|
+
|
12
|
+
# conditionally including these methods (each wrapped in a separate
|
13
|
+
# module) based on the configuration of whether to handle exceptions in
|
14
|
+
# the given environment or not. this allows the default implementation
|
15
|
+
# of the two rescue methods to run when Wrangler-based exception handling
|
16
|
+
# is disabled.
|
17
|
+
|
18
|
+
if Wrangler::ExceptionHandler.config[:handle_public_errors]
|
19
|
+
Rails.logger.info "Configuring #{base.name} with Wrangler's rescue_action_in_public"
|
20
|
+
base.send(:include, PublicControllerMethods)
|
21
|
+
else
|
22
|
+
Rails.logger.info "NOT Configuring #{base.name} with Wrangler's rescue_action_in_public"
|
23
|
+
end
|
24
|
+
|
25
|
+
if Wrangler::ExceptionHandler.config[:handle_local_errors]
|
26
|
+
Rails.logger.info "Configuring #{base.name} with Wrangler's rescue_action_locally"
|
27
|
+
base.send(:include, LocalControllerMethods)
|
28
|
+
else
|
29
|
+
Rails.logger.info "NOT configuring #{base.name} with Wrangler's rescue_action_locally"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
# methods to only be included into a controller if Wrangler is configured to
|
36
|
+
# handle exceptions for public reqeusts. (Conditionally included into
|
37
|
+
# controllers in the Wrangler::included() method).
|
38
|
+
#-----------------------------------------------------------------------------
|
39
|
+
module PublicControllerMethods
|
40
|
+
# override default behavior and let Wrangler handle the exception for
|
41
|
+
# public (non-local) requests.
|
42
|
+
#---------------------------------------------------------------------------
|
43
|
+
def rescue_action_in_public(exception)
|
44
|
+
handle_exception(exception, :request => request,
|
45
|
+
:render_errors => true)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
# methods to only be included into a controller if Wrangler is configured to
|
51
|
+
# handle exceptions for local reqeusts. (Conditionally included into
|
52
|
+
# controllers in the Wrangler::included() method).
|
53
|
+
#-----------------------------------------------------------------------------
|
54
|
+
module LocalControllerMethods
|
55
|
+
# override default behavior and let Wrangler handle the exception for
|
56
|
+
# local requests.
|
57
|
+
#---------------------------------------------------------------------------
|
58
|
+
def rescue_action_locally(exception)
|
59
|
+
handle_exception(exception, :request => request,
|
60
|
+
:render_errors => true)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# module of instance methods to be added to the class including Wrangler
|
66
|
+
# only if the including class is a rails controller class
|
67
|
+
#-----------------------------------------------------------------------------
|
68
|
+
module ControllerMethods
|
69
|
+
|
70
|
+
# called by rails if the exception has already been handled (e.g. by
|
71
|
+
# calling the rescue_from method in a controller and rendering a response)
|
72
|
+
#---------------------------------------------------------------------------
|
73
|
+
def rescue_with_handler(exception)
|
74
|
+
to_return = super
|
75
|
+
if to_return &&
|
76
|
+
(
|
77
|
+
(local_request? && Wrangler::ExceptionHandler.config[:handle_local_errors]) ||
|
78
|
+
(!local_request? && Wrangler::ExceptionHandler.config[:handle_public_errors])
|
79
|
+
)
|
80
|
+
|
81
|
+
handle_exception(exception, :request => request,
|
82
|
+
:render_errors => false)
|
83
|
+
end
|
84
|
+
to_return
|
85
|
+
end
|
86
|
+
|
87
|
+
|
88
|
+
# extract a hash of relevant (and serializable) parameters from a request
|
89
|
+
#---------------------------------------------------------------------------
|
90
|
+
def request_data_from_request(request)
|
91
|
+
return nil if request.nil?
|
92
|
+
|
93
|
+
request_data = {}
|
94
|
+
request.env.each_pair do |k,v|
|
95
|
+
next if skip_request_env?(k)
|
96
|
+
|
97
|
+
if self.respond_to?(:filter_parameters)
|
98
|
+
request_data.merge! self.send(:filter_parameters, k => v)
|
99
|
+
else
|
100
|
+
request_data.merge! k => v
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
params = {}
|
105
|
+
if self.respond_to?(:filter_parameters)
|
106
|
+
params.merge!(
|
107
|
+
filter_parameters(request.env['action_controller.request.query_parameters'])
|
108
|
+
)
|
109
|
+
params.merge!(
|
110
|
+
filter_parameters(request.env['action_controller.request.request_parameters'])
|
111
|
+
)
|
112
|
+
else
|
113
|
+
params.merge! request.env['action_controller.request.query_parameters']
|
114
|
+
params.merge! request.env['action_controller.request.request_parameters']
|
115
|
+
end
|
116
|
+
|
117
|
+
request_data.merge! :params => params unless params.blank?
|
118
|
+
|
119
|
+
return request_data
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
# determine if the request env param should be ommitted from the request
|
124
|
+
# data object, as specified in config (either for aesthetic reasons or
|
125
|
+
# because the param won't serialize well).
|
126
|
+
#---------------------------------------------------------------------------
|
127
|
+
def skip_request_env?(request_param)
|
128
|
+
skip_env = false
|
129
|
+
Wrangler::ExceptionHandler.config[:request_env_to_skip].each do |pattern|
|
130
|
+
if (pattern.is_a?(String) && pattern == request_param) ||
|
131
|
+
(pattern.is_a?(Regexp) && pattern =~ request_param)
|
132
|
+
skip_env = true
|
133
|
+
break
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
return skip_env
|
138
|
+
end
|
139
|
+
|
140
|
+
|
141
|
+
# select the proper file to render and do so. if the usual places don't
|
142
|
+
# turn up an appropriate template (see README), then fall back on
|
143
|
+
# an app-specific default error page or the ultimate back up gem default
|
144
|
+
# page.
|
145
|
+
#---------------------------------------------------------------------------
|
146
|
+
def render_error_template(exception, status_code)
|
147
|
+
file_path = get_view_path_for_exception(exception, status_code)
|
148
|
+
|
149
|
+
# if that didn't work, fall back on configured app-specific default
|
150
|
+
if file_path.blank? || !File.exists?(file_path)
|
151
|
+
file_path = config[:default_error_template]
|
152
|
+
|
153
|
+
log_error(["Could not find an error template in the usual places " +
|
154
|
+
"for exception #{exception.class}, status code " +
|
155
|
+
"#{status_code}.",
|
156
|
+
"Trying to default to app-specific default: '#{file_path}'"])
|
157
|
+
end
|
158
|
+
|
159
|
+
# as a last resort, just render the gem's 500 error
|
160
|
+
if file_path.blank? || !File.exists?(file_path)
|
161
|
+
file_path = config[:absolute_last_resort_default_error_template]
|
162
|
+
|
163
|
+
log_error("Still no template found. Using gem default of " +
|
164
|
+
file_path)
|
165
|
+
end
|
166
|
+
|
167
|
+
log_error("Will render error template: '#{file_path}'")
|
168
|
+
|
169
|
+
render :file => file_path,
|
170
|
+
:status => status_code
|
171
|
+
end
|
172
|
+
|
173
|
+
|
174
|
+
# select the appropriate view path for the exception/status code. see
|
175
|
+
# README or the code for the different attempts that are made to find
|
176
|
+
# a template.
|
177
|
+
#---------------------------------------------------------------------------
|
178
|
+
def get_view_path_for_exception(exception, status_code)
|
179
|
+
|
180
|
+
# maintenance note: this method has lots of RETURN statements, so be
|
181
|
+
# a little careful, but basically, it returns as soon as it finds a
|
182
|
+
# file match, so there shouldn't be any processing to perform after
|
183
|
+
# a match is found. any such logic does not belong in this method
|
184
|
+
|
185
|
+
if exception.is_a?(Class)
|
186
|
+
exception_class = exception
|
187
|
+
else
|
188
|
+
exception_class = exception.class
|
189
|
+
end
|
190
|
+
|
191
|
+
# Note: this converts "::" to "/", so views need to be nested under
|
192
|
+
# exceptions' modules if appropriate
|
193
|
+
exception_filename_root = exception_class.name.underscore
|
194
|
+
|
195
|
+
template_mappings = nil
|
196
|
+
case request.format
|
197
|
+
when /html/
|
198
|
+
response_format = 'html'
|
199
|
+
template_mappings = config[:error_class_html_templates]
|
200
|
+
when /js/
|
201
|
+
response_format = 'js'
|
202
|
+
template_mappings = config[:error_class_js_templates]
|
203
|
+
when /xml/
|
204
|
+
'xml'
|
205
|
+
end
|
206
|
+
format_extension_pattern = ".#{response_format || ''}*"
|
207
|
+
|
208
|
+
if template_mappings
|
209
|
+
|
210
|
+
# search for direct mapping from exception name to error template
|
211
|
+
|
212
|
+
if template_mappings[exception_class]
|
213
|
+
error_file = template_mappings[exception_class]
|
214
|
+
|
215
|
+
return error_file if File.exists?(error_file)
|
216
|
+
|
217
|
+
log_error("Found mapping from exception class " +
|
218
|
+
"#{exception_class.name} to error file '#{error_file}', " +
|
219
|
+
"but error file was not found")
|
220
|
+
end
|
221
|
+
|
222
|
+
# search for mapping from an ancestor class to error template
|
223
|
+
|
224
|
+
ancestor_class =
|
225
|
+
Wrangler::class_has_ancestor?(exception_class.superclass,
|
226
|
+
template_mappings)
|
227
|
+
|
228
|
+
if ancestor_class
|
229
|
+
error_file = template_mappings[ancestor_class]
|
230
|
+
|
231
|
+
return error_file if File.exists?(error_file)
|
232
|
+
|
233
|
+
log_error("Found mapping from ancestor exception class " +
|
234
|
+
"#{ancestor_class.name} to error file '#{error_file}', " +
|
235
|
+
"but error file was not found")
|
236
|
+
end
|
237
|
+
|
238
|
+
end # end if template_mappings
|
239
|
+
|
240
|
+
# search for a file named after the exception in one of the search dirs
|
241
|
+
|
242
|
+
search_paths = [ config[:error_template_dir],
|
243
|
+
File.join(RAILS_ROOT, 'public'),
|
244
|
+
File.join(WRANGLER_ROOT, 'rails', 'app', 'views', 'wrangler')
|
245
|
+
]
|
246
|
+
|
247
|
+
# find files in specified directory like 'exception_class_name.format', e.g.
|
248
|
+
# standard_error.html or standard_error.js.erb
|
249
|
+
exception_pattern = "#{exception_filename_root}#{format_extension_pattern}"
|
250
|
+
file_path = find_file_matching_pattern(search_paths, exception_pattern)
|
251
|
+
|
252
|
+
return file_path if file_path
|
253
|
+
|
254
|
+
# search for a file named after the error status code in search dirs
|
255
|
+
|
256
|
+
status_code_pattern = "#{status_code}#{format_extension_pattern}"
|
257
|
+
file_path = find_file_matching_pattern(search_paths, status_code_pattern)
|
258
|
+
|
259
|
+
return file_path if file_path
|
260
|
+
|
261
|
+
# search for a file named after ancestors of the exception in search dirs
|
262
|
+
|
263
|
+
# look through exception's entire ancenstry to see if there's a matching
|
264
|
+
# template in the search directories
|
265
|
+
curr_ancestor = exception_class.superclass
|
266
|
+
while curr_ancestor
|
267
|
+
# find files in specified directory like 'exception_class_name.format', e.g.
|
268
|
+
# standard_error.html or standard_error.js.erb
|
269
|
+
exception_pattern =
|
270
|
+
"#{curr_ancestor.name.underscore}#{format_extension_pattern}"
|
271
|
+
file_path = find_file_matching_pattern(search_paths, exception_pattern)
|
272
|
+
|
273
|
+
return file_path if file_path
|
274
|
+
|
275
|
+
curr_ancestor = curr_ancestor.superclass
|
276
|
+
end
|
277
|
+
|
278
|
+
# didn't find anything
|
279
|
+
return nil
|
280
|
+
end
|
281
|
+
|
282
|
+
end # end ControllerMethods module
|
283
|
+
|
284
|
+
end # end Wrangler module
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- This file lives in gems/wrangler/rails/app/views/wrangler. See README for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>Not Authorized</h1>
|
4
|
+
|
5
|
+
<p>You are not authorized to access this resource. You may be either not logged in at all, or not logged in as the appropriate user.</p>
|
6
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- This file lives in gems/wrangler/rails/app/views/wrangler. 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/wrangler/rails/app/views/wrangler. 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,7 @@
|
|
1
|
+
<!-- This file lives in gems/wrangler/rails/app/views/wrangler. 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
|
+
|
5
|
+
<p>It might become available again soon, if you try again!</p>
|
6
|
+
<p>Maybe you tried to change something you didn't have access to.</p>
|
7
|
+
</div>
|
@@ -0,0 +1,6 @@
|
|
1
|
+
<!-- This file lives in gems/wrangler/rails/app/views/wrangler. See README for instructions on customizing. -->
|
2
|
+
<div class="dialog">
|
3
|
+
<h1>We're sorry, but something went wrong.</h1>
|
4
|
+
|
5
|
+
<p>We've been notified about this issue and we'll take a look at it shortly.</p>
|
6
|
+
</div>
|
@@ -0,0 +1,8 @@
|
|
1
|
+
<!-- This file lives in gems/wrangler/rails/app/views/wrangler. 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/wrangler/rails/app/views/wrangler. 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>
|