auto_error 0.0.1
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 +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +20 -0
- data/app/assets/images/auto_error/spinner.gif +0 -0
- data/app/assets/javascripts/auto_error/app_errors/configure.coffee +21 -0
- data/app/assets/javascripts/auto_error/app_errors/index.coffee +13 -0
- data/app/assets/javascripts/auto_error/app_errors/model.coffee +6 -0
- data/app/assets/javascripts/auto_error/app_errors/views/app_errors.coffee +38 -0
- data/app/assets/javascripts/auto_error/app_errors/views/base.coffee +57 -0
- data/app/assets/javascripts/auto_error/application.js +8 -0
- data/app/assets/stylesheets/auto_error/app_errors.css.erb +45 -0
- data/app/assets/stylesheets/auto_error/application.css +5 -0
- data/app/assets/templates/auto_error/app_error.hbs +11 -0
- data/app/controllers/auto_error/app_errors_controller.rb +17 -0
- data/app/controllers/auto_error/application_controller.rb +13 -0
- data/app/controllers/auto_error/errors_controller.rb +32 -0
- data/app/controllers/auto_error/main_controller.rb +9 -0
- data/app/decorators/auto_error/app_error_decorator.rb +13 -0
- data/app/helpers/auto_error/application_helper.rb +12 -0
- data/app/models/auto_error/app_error.rb +64 -0
- data/app/views/auto_error/main/index.html.haml +32 -0
- data/app/views/errors/404.html.erb +1 -0
- data/app/views/errors/500.html.erb +1 -0
- data/app/views/layouts/auto_error/application.html.erb +11 -0
- data/app/views/layouts/errors.html.erb +1 -0
- data/config/initializers/fabrication.rb +6 -0
- data/config/routes.rb +4 -0
- data/db/migrate/20130128004546_create_auto_error_app_errors.rb +19 -0
- data/lib/auto_error.rb +43 -0
- data/lib/auto_error/auth_context.rb +18 -0
- data/lib/auto_error/config.rb +39 -0
- data/lib/auto_error/context_shorthand.rb +15 -0
- data/lib/auto_error/engine.rb +13 -0
- data/lib/auto_error/errors.rb +6 -0
- data/lib/auto_error/version.rb +3 -0
- data/lib/auto_error/view_context.rb +10 -0
- data/lib/core_ext/proc_ext.rb +23 -0
- data/lib/tasks/auto_error_tasks.rake +4 -0
- data/lib/templates/auto_error.rb +11 -0
- metadata +311 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2013 Ryan Funduk
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'AutoError'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.rdoc')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
Bundler::GemHelper.install_tasks
|
Binary file
|
@@ -0,0 +1,21 @@
|
|
1
|
+
namespacedTemplates = ->
|
2
|
+
h = {}
|
3
|
+
for name, func of window.HandlebarsTemplates
|
4
|
+
if name.match( /\// )
|
5
|
+
nesting = h
|
6
|
+
parts = name.split('/')
|
7
|
+
while (part = parts.shift())
|
8
|
+
if parts.length == 0
|
9
|
+
nesting[part] = func
|
10
|
+
else
|
11
|
+
nesting[part] ||= {}
|
12
|
+
nesting = nesting[part]
|
13
|
+
else
|
14
|
+
h[name] = func
|
15
|
+
h
|
16
|
+
|
17
|
+
_.extend( window.App, {
|
18
|
+
mobile: navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/iPod/i)
|
19
|
+
Templates: namespacedTemplates().auto_error
|
20
|
+
Views: {}
|
21
|
+
} )
|
@@ -0,0 +1,13 @@
|
|
1
|
+
#= require ./configure
|
2
|
+
|
3
|
+
#= require ./model
|
4
|
+
#= require ./views/base
|
5
|
+
#= require ./views/app_errors
|
6
|
+
|
7
|
+
$(document).ready ->
|
8
|
+
$.ajaxSetup
|
9
|
+
headers:
|
10
|
+
'X-CSRF-Token': $("meta[name='csrf-token']").attr( 'content' )
|
11
|
+
|
12
|
+
App.rootAppErrorsPath = $('#app_errors').data('url')
|
13
|
+
new App.Views.AppErrors()
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class AppError extends App.Views.PolledItem
|
2
|
+
template: App.Templates.app_error
|
3
|
+
events:
|
4
|
+
'click a.show_backtrace': 'toggleBacktrace'
|
5
|
+
'click a.remove': 'destroy'
|
6
|
+
render: ->
|
7
|
+
json = @model.toJSON()
|
8
|
+
json.prettyData = []
|
9
|
+
for k, v of json.data
|
10
|
+
json.prettyData.push "#{k}: #{v}"
|
11
|
+
json.prettyData = json.prettyData.join("\n")
|
12
|
+
json.timestamp = moment(json.created_at).format( "YYYY-MM-DD[<br/>at] h:mma" )
|
13
|
+
@$el.html( @template( json ) )
|
14
|
+
return @
|
15
|
+
toggleBacktrace: ->
|
16
|
+
link = @$('a.show_backtrace')
|
17
|
+
bt = @$('.backtrace')
|
18
|
+
if bt.is(':visible')
|
19
|
+
bt.hide()
|
20
|
+
link.text('show backtrace...')
|
21
|
+
else
|
22
|
+
bt.html( @model.get('backtrace').replace( /\n/g, "<br/>" ) ).show()
|
23
|
+
link.text('hide backtrace...')
|
24
|
+
destroy: ->
|
25
|
+
@model.destroy()
|
26
|
+
@remove()
|
27
|
+
|
28
|
+
class AppErrorsList extends App.Views.PollingList
|
29
|
+
|
30
|
+
class App.Views.AppErrors extends Backbone.View
|
31
|
+
el: 'body'
|
32
|
+
initialize: ->
|
33
|
+
@errors = new AppErrorsList(
|
34
|
+
el: @$('#app_errors table')
|
35
|
+
reset: false
|
36
|
+
viewClass: AppError
|
37
|
+
collectionClass: App.AppErrors
|
38
|
+
)
|
@@ -0,0 +1,57 @@
|
|
1
|
+
class App.Views.PolledItem extends Backbone.View
|
2
|
+
tagName: 'tr'
|
3
|
+
initialize: ->
|
4
|
+
@model.on 'change', @render, @
|
5
|
+
|
6
|
+
class App.Views.PollingList extends Backbone.View
|
7
|
+
events:
|
8
|
+
'click .poller a': 'togglePolling'
|
9
|
+
initialize: ->
|
10
|
+
@collection = new @options.collectionClass( [] )
|
11
|
+
@spinner = @$('tfoot .poller .spinner')
|
12
|
+
@polling = false
|
13
|
+
@body = @$('tbody')
|
14
|
+
@emptyMessage = @body.html().replace(/[\r\n]\s+/g, '')
|
15
|
+
@collection.on 'remove', @remove, @
|
16
|
+
@collection.on 'add', @render, @
|
17
|
+
@collection.fetch( update: true )
|
18
|
+
@views = {}
|
19
|
+
remove: ( m ) ->
|
20
|
+
@views[m.id] = null
|
21
|
+
@render()
|
22
|
+
render: ->
|
23
|
+
if @collection.isEmpty()
|
24
|
+
message = $(@emptyMessage)
|
25
|
+
message.find('p.loading_message').text( "No #{@collection.name}!" )
|
26
|
+
@body.html( message )
|
27
|
+
else
|
28
|
+
@body.find('p.loading_message').parents('tr').remove()
|
29
|
+
@collection.each (m) =>
|
30
|
+
return if @views[m.id]
|
31
|
+
v = new @options.viewClass( model: m )
|
32
|
+
@views[m.id] = v
|
33
|
+
@body.prepend v.render().el
|
34
|
+
@$('tfoot p.total strong').text( @collection.length )
|
35
|
+
togglePolling: (e) ->
|
36
|
+
link = $(e.target)
|
37
|
+
if @polling == false
|
38
|
+
@polling = true
|
39
|
+
@poll()
|
40
|
+
link.text( 'stop polling' )
|
41
|
+
else
|
42
|
+
@polling = false
|
43
|
+
link.text( 'start polling' )
|
44
|
+
@spinner.hide()
|
45
|
+
poll: ->
|
46
|
+
if @polling
|
47
|
+
startTime = new Date().getTime()
|
48
|
+
@spinner.fadeIn()
|
49
|
+
args =
|
50
|
+
update: true
|
51
|
+
success: =>
|
52
|
+
setTimeout( _.bind(@poll, @), 2000 ) if @polling
|
53
|
+
setTimeout(
|
54
|
+
_.bind( @spinner.fadeOut, @spinner )
|
55
|
+
Math.max( (new Date().getTime() - startTime), 500 )
|
56
|
+
)
|
57
|
+
@collection.fetch args
|
@@ -0,0 +1,45 @@
|
|
1
|
+
table.polling tr td p.loading_message {
|
2
|
+
font-size: 20px;
|
3
|
+
margin: 25px 0;
|
4
|
+
text-align: center;
|
5
|
+
}
|
6
|
+
|
7
|
+
table.polling tbody pre {
|
8
|
+
font-size: 13px;
|
9
|
+
line-height: 15px;
|
10
|
+
}
|
11
|
+
table.polling tbody a.button {
|
12
|
+
display: block;
|
13
|
+
margin-bottom: 4px;
|
14
|
+
}
|
15
|
+
table.polling tfoot p.total {
|
16
|
+
padding: 0;
|
17
|
+
margin: 0;
|
18
|
+
line-height: 24px;
|
19
|
+
margin-right: 5px;
|
20
|
+
font-weight: normal;
|
21
|
+
text-align: right;
|
22
|
+
}
|
23
|
+
table.polling tfoot .poller a {
|
24
|
+
float: left;
|
25
|
+
margin: 0;
|
26
|
+
padding-top: 6px;
|
27
|
+
}
|
28
|
+
table.polling tfoot .poller .spinner {
|
29
|
+
display: none;
|
30
|
+
float: left;
|
31
|
+
width: 24px;
|
32
|
+
height: 24px;
|
33
|
+
background: transparent url(<%= image_path('auto_error/spinner.gif') %>) no-repeat center center;
|
34
|
+
margin-left: 12px;
|
35
|
+
}
|
36
|
+
|
37
|
+
#app_errors table tbody .backtrace {
|
38
|
+
width: 530px;
|
39
|
+
height: 250px;
|
40
|
+
overflow: scroll;
|
41
|
+
font-family: monospace;
|
42
|
+
font-size: 13px;
|
43
|
+
line-height: 15px;
|
44
|
+
white-space: nowrap;
|
45
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<td>
|
2
|
+
<strong>{{message}}</strong>
|
3
|
+
<br/>
|
4
|
+
<a href="javascript:void(0);" class='show_backtrace'>show backtrace...</a>
|
5
|
+
<div class='backtrace' style='display: none;'></div>
|
6
|
+
</td>
|
7
|
+
<td><pre>{{{prettyData}}}</pre></td>
|
8
|
+
<td>{{{timestamp}}}</td>
|
9
|
+
<td>
|
10
|
+
<a href="javascript:void(0);" class='remove button radius tiny secondary'>resolved</a>
|
11
|
+
</td>
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module AutoError
|
2
|
+
class AppErrorsController < ApplicationController
|
3
|
+
before_filter :ensure_authenticated
|
4
|
+
|
5
|
+
def index
|
6
|
+
@errors = AppError.unresolved
|
7
|
+
render json: @errors.map { |ae| AppErrorDecorator.new(ae).as_json }
|
8
|
+
end
|
9
|
+
|
10
|
+
def destroy
|
11
|
+
@error = AppError.find( params[:id] )
|
12
|
+
@error.resolved_at = Time.now
|
13
|
+
@error.save
|
14
|
+
render json: { ok: true }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module AutoError
|
2
|
+
class ApplicationController < ActionController::Base
|
3
|
+
include AutoError::ApplicationHelper
|
4
|
+
helper :all
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
def ensure_authenticated
|
9
|
+
context = AutoError::AuthContext.new(env)
|
10
|
+
raise AutoError::Errors::Denied unless AutoError::Config.auth_with.call(context)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module AutoError
|
2
|
+
class ErrorsController < ApplicationController
|
3
|
+
|
4
|
+
def show
|
5
|
+
# reset action_controller.instance to the original controller
|
6
|
+
# that caused the exception
|
7
|
+
env['action_controller.instance'] = env.delete('auto_error.original_controller.instance')
|
8
|
+
|
9
|
+
@exception = env['action_dispatch.exception']
|
10
|
+
@status_code = ActionDispatch::ExceptionWrapper.new(env, @exception).status_code
|
11
|
+
@rescue_response = ActionDispatch::ExceptionWrapper.rescue_responses[@exception.class.name]
|
12
|
+
@params = env['action_dispatch.request.parameters'].symbolize_keys
|
13
|
+
|
14
|
+
@status_code = 500 unless [403, 404].include?(@status_code)
|
15
|
+
|
16
|
+
if @status_code == 500
|
17
|
+
controller = "#{@params[:controller].camelize}Controller" rescue 'N/A'
|
18
|
+
action = @params[:action] || 'N/A'
|
19
|
+
where = { controller: controller, action: action }
|
20
|
+
data = {
|
21
|
+
path: env['REQUEST_URI'],
|
22
|
+
method: env['REQUEST_METHOD'],
|
23
|
+
ip: env['REMOTE_ADDR']
|
24
|
+
}
|
25
|
+
AppError.log!( env, @exception, where, data )
|
26
|
+
end
|
27
|
+
|
28
|
+
AutoError::Config.error_template_renderer.bind(self).( @status_code )
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module AutoError
|
2
|
+
class AppErrorDecorator < Draper::Decorator
|
3
|
+
def as_json
|
4
|
+
r = source.attributes
|
5
|
+
(r['data']||{}).entries.each do |k, v|
|
6
|
+
next if v.nil?
|
7
|
+
handler = AutoError::Config.data_handlers[k.to_sym].bind(ViewContext.new)
|
8
|
+
r['data'][k.to_s] = handler.(v).html_safe
|
9
|
+
end
|
10
|
+
r
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module AutoError
|
2
|
+
class AppError < ActiveRecord::Base
|
3
|
+
before_create :generate_group
|
4
|
+
|
5
|
+
serialize :data
|
6
|
+
|
7
|
+
scope :unresolved, -> { where( resolved_at: nil ).order( 'created_at ASC' ) }
|
8
|
+
|
9
|
+
def self.log!( env, exception, opts, data={} )
|
10
|
+
opts[:data] = data
|
11
|
+
opts[:data].merge! self.context(env)
|
12
|
+
opts.merge!( {
|
13
|
+
klass: exception.class.name,
|
14
|
+
message: exception.message,
|
15
|
+
backtrace: (exception.backtrace || []).join("\n"),
|
16
|
+
} )
|
17
|
+
app_error = create!( opts )
|
18
|
+
|
19
|
+
if AutoError::Config.email_on_error.any?
|
20
|
+
begin
|
21
|
+
send_email!( env, exception )
|
22
|
+
rescue
|
23
|
+
$stderr.puts "AutoError failed to send exception notification email to #{AutoError::Config.email_on_error.inspect} -- #{$!.inspect}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
app_error
|
28
|
+
end
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def add_context( env, opts )
|
32
|
+
context(env).merge!( opts )
|
33
|
+
end
|
34
|
+
def context( env )
|
35
|
+
env['auto_error.app_error.context'] ||= {}
|
36
|
+
env['auto_error.app_error.context']
|
37
|
+
end
|
38
|
+
def clear_context!( env )
|
39
|
+
env['auto_error.app_error.context'] = {}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def self.send_email!( env, exception )
|
46
|
+
options = env['exception_notifier.options'] || {}
|
47
|
+
options[:ignore_exceptions] ||= ExceptionNotifier.default_ignore_exceptions
|
48
|
+
options[:email_prefix] ||= "[#{Rails.application.class.name.split('::').first} ERROR] "
|
49
|
+
options[:exception_recipients] = AutoError::Config.email_on_error
|
50
|
+
|
51
|
+
unless Array.wrap(options[:ignore_exceptions]).include?( exception.class )
|
52
|
+
ExceptionNotifier::Notifier.exception_notification( env, exception, options ).deliver
|
53
|
+
env['exception_notifier.delivered'] = true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def generate_group
|
58
|
+
self.group = Digest::SHA1.hexdigest( [
|
59
|
+
self.klass.to_s, self.message.to_s,
|
60
|
+
self.controller.to_s, self.action.to_s
|
61
|
+
].join('-') ).to_s
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
- content_for :auto_error_assets do
|
2
|
+
= stylesheet_link_tag 'auto_error/application', media: 'all'
|
3
|
+
= js_namespace
|
4
|
+
= javascript_include_tag 'auto_error/application'
|
5
|
+
|
6
|
+
.row#app_errors{ data: { url: app_errors_path } }
|
7
|
+
.twelve.columns.centered
|
8
|
+
%h2 App Errors
|
9
|
+
|
10
|
+
%table.polling{ width: '100%' }
|
11
|
+
%thead
|
12
|
+
%tr
|
13
|
+
%th{ width: '550px' } Message
|
14
|
+
%th Data
|
15
|
+
%th Time
|
16
|
+
%th
|
17
|
+
%tbody
|
18
|
+
%tr
|
19
|
+
%td{ colspan: 4 }
|
20
|
+
%p.loading_message Loading...
|
21
|
+
%tfoot
|
22
|
+
%tr
|
23
|
+
%td{ colspan: 4 }
|
24
|
+
.row
|
25
|
+
.six.columns
|
26
|
+
.poller
|
27
|
+
%a.tiny.button.radius.primary{ href: void_path } start polling
|
28
|
+
.spinner
|
29
|
+
.six.columns
|
30
|
+
%p.total
|
31
|
+
Total App Errors:
|
32
|
+
%strong …
|
@@ -0,0 +1 @@
|
|
1
|
+
Engine 404
|
@@ -0,0 +1 @@
|
|
1
|
+
Engine 500
|
@@ -0,0 +1 @@
|
|
1
|
+
<%= yield %>
|
data/config/routes.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class CreateAutoErrorAppErrors < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :auto_error_app_errors do |t|
|
4
|
+
t.string :group
|
5
|
+
|
6
|
+
t.string :klass
|
7
|
+
t.string :controller
|
8
|
+
t.string :action
|
9
|
+
|
10
|
+
t.string :message
|
11
|
+
t.text :backtrace
|
12
|
+
t.text :data
|
13
|
+
|
14
|
+
t.datetime :resolved_at, default: nil
|
15
|
+
|
16
|
+
t.timestamps
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/auto_error.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# core ext
|
2
|
+
require 'core_ext/proc_ext'
|
3
|
+
|
4
|
+
# asset pipeline deps
|
5
|
+
require 'handlebars_assets'
|
6
|
+
require 'coffee_script'
|
7
|
+
|
8
|
+
# other deps
|
9
|
+
require 'jquery-rails'
|
10
|
+
require 'haml'
|
11
|
+
require 'draper'
|
12
|
+
require 'exception_notification'
|
13
|
+
|
14
|
+
# lib
|
15
|
+
require 'auto_error/version'
|
16
|
+
require 'auto_error/config'
|
17
|
+
require 'auto_error/errors'
|
18
|
+
require 'auto_error/context_shorthand'
|
19
|
+
require 'auto_error/view_context'
|
20
|
+
require 'auto_error/auth_context'
|
21
|
+
require 'auto_error/engine'
|
22
|
+
|
23
|
+
module AutoError
|
24
|
+
def self.setup!( app_config )
|
25
|
+
Config.send(:set_defaults)
|
26
|
+
app_config.action_dispatch.rescue_responses["AutoError::Errors::Denied"] = :forbidden
|
27
|
+
app_config.action_dispatch.rescue_responses["AutoError::Errors::NotFound"] = :not_found
|
28
|
+
|
29
|
+
original_exceptions_app = app_config.exceptions_app || ActionDispatch::PublicExceptions.new(Rails.public_path)
|
30
|
+
app_config.exceptions_app = ->(env) do
|
31
|
+
# puts "Handling exception for #{env['action_controller.instance'].class}"
|
32
|
+
controller_class = env['action_controller.instance'].class
|
33
|
+
if controller_class.nil? || AutoError.constants.any? { |c| AutoError.const_get(c) == controller_class }
|
34
|
+
# do not log/handle exception at all if the error is actually
|
35
|
+
# IN auto_error :)
|
36
|
+
original_exceptions_app.call(env)
|
37
|
+
else
|
38
|
+
env['auto_error.original_controller.instance'] = env['action_controller.instance']
|
39
|
+
AutoError::ErrorsController.action(:show).call(env)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AutoError
|
2
|
+
class AuthContext
|
3
|
+
def initialize( env )
|
4
|
+
AutoError::Config.auth_helpers.each do |mod_name|
|
5
|
+
mod = Rails.application.class.qualified_const_get(mod_name)
|
6
|
+
class_eval do
|
7
|
+
send( :include, mod )
|
8
|
+
mod.instance_methods.each do |imeth|
|
9
|
+
alias :"#{imeth}_without_env" :"#{imeth}"
|
10
|
+
send( :define_method, imeth ) do
|
11
|
+
method( :"#{imeth}_without_env" ).to_proc.bind( env )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module AutoError
|
2
|
+
module Config
|
3
|
+
def self.setup( &blk )
|
4
|
+
yield self
|
5
|
+
end
|
6
|
+
|
7
|
+
mattr_accessor :auth_with
|
8
|
+
@@auth_with = nil
|
9
|
+
|
10
|
+
mattr_accessor :auth_helpers
|
11
|
+
@@auth_helpers = nil
|
12
|
+
|
13
|
+
mattr_accessor :email_on_error
|
14
|
+
@@email_on_error = nil
|
15
|
+
|
16
|
+
mattr_accessor :error_template_renderer
|
17
|
+
@@error_template_renderer = nil
|
18
|
+
|
19
|
+
mattr_accessor :data_handlers
|
20
|
+
@@data_handlers = nil
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def self.set_defaults
|
25
|
+
self.setup do |config|
|
26
|
+
config.error_template_renderer = ->( status ) do
|
27
|
+
render template: "/errors/#{status}", layout: 'errors', status: status
|
28
|
+
end
|
29
|
+
|
30
|
+
config.email_on_error = []
|
31
|
+
|
32
|
+
config.auth_with = ->( c ) { true }
|
33
|
+
config.auth_helpers = [ 'ApplicationHelper' ]
|
34
|
+
|
35
|
+
config.data_handlers = Hash.new { |h, k| h[k] = ->( value ) { "<strong>#{value}</strong>" } }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module AutoError
|
2
|
+
|
3
|
+
# Convenience methods added to ApplicationController.
|
4
|
+
module ContextShorthand
|
5
|
+
def add_error_context( context )
|
6
|
+
AutoError::AppError.add_context( env, context )
|
7
|
+
end
|
8
|
+
def clear_error_context
|
9
|
+
AutoError::AppError.clear_context!( env )
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
ActionController::Base.send :include, AutoError::ContextShorthand
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module AutoError
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
engine_name 'AutoError'
|
4
|
+
isolate_namespace AutoError
|
5
|
+
|
6
|
+
config.exceptions_app = ->(env) { }
|
7
|
+
|
8
|
+
config.generators do |g|
|
9
|
+
g.test_framework :rspec, fixture: true
|
10
|
+
g.fixture_replacement :fabrication
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class Proc
|
2
|
+
def bind(object)
|
3
|
+
block = self
|
4
|
+
object.class_eval do
|
5
|
+
method_name = :__proc_rebound_method__
|
6
|
+
method = nil
|
7
|
+
Thread.exclusive do
|
8
|
+
method_already_exists =
|
9
|
+
object.respond_to?(method_name) &&
|
10
|
+
instance_method(method_name).owner == self
|
11
|
+
|
12
|
+
old_method = instance_method(method_name) if method_already_exists
|
13
|
+
|
14
|
+
define_method(method_name, &block)
|
15
|
+
method = instance_method(method_name)
|
16
|
+
remove_method(method_name)
|
17
|
+
|
18
|
+
define_method(method_name, old_method) if method_already_exists
|
19
|
+
end
|
20
|
+
method
|
21
|
+
end.bind(object)
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
AutoError::Config.setup do |config|
|
2
|
+
# == Authenticated User Method
|
3
|
+
# Specify a Proc or :warden or :devise, depending on
|
4
|
+
# your authentication scheme.
|
5
|
+
|
6
|
+
# Examples:
|
7
|
+
#
|
8
|
+
# config.auth_with = Proc.new { |h| h.current_user }
|
9
|
+
# config.auth_with = :warden
|
10
|
+
config.auth_with = :warden
|
11
|
+
end
|
metadata
ADDED
@@ -0,0 +1,311 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: auto_error
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ryan Funduk
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-02-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 3.2.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: jquery-rails
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: haml
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: draper
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: exception_notification
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: sprockets-rails
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: coffee-rails
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: handlebars_assets
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: sqlite3
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ! '>='
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ! '>='
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: bcrypt-ruby
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ! '>='
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '0'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ! '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: rspec-rails
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ! '>='
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '0'
|
182
|
+
type: :development
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ! '>='
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '0'
|
190
|
+
- !ruby/object:Gem::Dependency
|
191
|
+
name: fabrication
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ! '>='
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
type: :development
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
206
|
+
- !ruby/object:Gem::Dependency
|
207
|
+
name: faker
|
208
|
+
requirement: !ruby/object:Gem::Requirement
|
209
|
+
none: false
|
210
|
+
requirements:
|
211
|
+
- - ! '>='
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
version: '0'
|
214
|
+
type: :development
|
215
|
+
prerelease: false
|
216
|
+
version_requirements: !ruby/object:Gem::Requirement
|
217
|
+
none: false
|
218
|
+
requirements:
|
219
|
+
- - ! '>='
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0'
|
222
|
+
- !ruby/object:Gem::Dependency
|
223
|
+
name: do_not_want
|
224
|
+
requirement: !ruby/object:Gem::Requirement
|
225
|
+
none: false
|
226
|
+
requirements:
|
227
|
+
- - ! '>='
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0'
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
none: false
|
234
|
+
requirements:
|
235
|
+
- - ! '>='
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '0'
|
238
|
+
description: ! "\n AutoError is a mountable engine for Rails 3.2+ which provides\n
|
239
|
+
\ an 'exceptions_app' which helps you catch exceptions (showing\n the appropriate
|
240
|
+
page to users) and an interface you can mount\n in your admin panel to display
|
241
|
+
those errors.\n "
|
242
|
+
email: ryan.funduk@gmail.com
|
243
|
+
executables: []
|
244
|
+
extensions: []
|
245
|
+
extra_rdoc_files: []
|
246
|
+
files:
|
247
|
+
- app/assets/images/auto_error/spinner.gif
|
248
|
+
- app/assets/javascripts/auto_error/app_errors/configure.coffee
|
249
|
+
- app/assets/javascripts/auto_error/app_errors/index.coffee
|
250
|
+
- app/assets/javascripts/auto_error/app_errors/model.coffee
|
251
|
+
- app/assets/javascripts/auto_error/app_errors/views/app_errors.coffee
|
252
|
+
- app/assets/javascripts/auto_error/app_errors/views/base.coffee
|
253
|
+
- app/assets/javascripts/auto_error/application.js
|
254
|
+
- app/assets/stylesheets/auto_error/app_errors.css.erb
|
255
|
+
- app/assets/stylesheets/auto_error/application.css
|
256
|
+
- app/assets/templates/auto_error/app_error.hbs
|
257
|
+
- app/controllers/auto_error/app_errors_controller.rb
|
258
|
+
- app/controllers/auto_error/application_controller.rb
|
259
|
+
- app/controllers/auto_error/errors_controller.rb
|
260
|
+
- app/controllers/auto_error/main_controller.rb
|
261
|
+
- app/decorators/auto_error/app_error_decorator.rb
|
262
|
+
- app/helpers/auto_error/application_helper.rb
|
263
|
+
- app/models/auto_error/app_error.rb
|
264
|
+
- app/views/auto_error/main/index.html.haml
|
265
|
+
- app/views/errors/404.html.erb
|
266
|
+
- app/views/errors/500.html.erb
|
267
|
+
- app/views/layouts/auto_error/application.html.erb
|
268
|
+
- app/views/layouts/errors.html.erb
|
269
|
+
- config/initializers/fabrication.rb
|
270
|
+
- config/routes.rb
|
271
|
+
- db/migrate/20130128004546_create_auto_error_app_errors.rb
|
272
|
+
- lib/auto_error/auth_context.rb
|
273
|
+
- lib/auto_error/config.rb
|
274
|
+
- lib/auto_error/context_shorthand.rb
|
275
|
+
- lib/auto_error/engine.rb
|
276
|
+
- lib/auto_error/errors.rb
|
277
|
+
- lib/auto_error/version.rb
|
278
|
+
- lib/auto_error/view_context.rb
|
279
|
+
- lib/auto_error.rb
|
280
|
+
- lib/core_ext/proc_ext.rb
|
281
|
+
- lib/tasks/auto_error_tasks.rake
|
282
|
+
- lib/templates/auto_error.rb
|
283
|
+
- MIT-LICENSE
|
284
|
+
- Rakefile
|
285
|
+
- README.rdoc
|
286
|
+
homepage: http://ryanfunduk.com
|
287
|
+
licenses:
|
288
|
+
- MIT
|
289
|
+
post_install_message:
|
290
|
+
rdoc_options: []
|
291
|
+
require_paths:
|
292
|
+
- lib
|
293
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
294
|
+
none: false
|
295
|
+
requirements:
|
296
|
+
- - ! '>='
|
297
|
+
- !ruby/object:Gem::Version
|
298
|
+
version: 1.9.3
|
299
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
300
|
+
none: false
|
301
|
+
requirements:
|
302
|
+
- - ! '>='
|
303
|
+
- !ruby/object:Gem::Version
|
304
|
+
version: '0'
|
305
|
+
requirements: []
|
306
|
+
rubyforge_project:
|
307
|
+
rubygems_version: 1.8.23
|
308
|
+
signing_key:
|
309
|
+
specification_version: 3
|
310
|
+
summary: A rails engine for in-app exception handling.
|
311
|
+
test_files: []
|