wrangler 0.1.0
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/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>
|