auto_error 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|