openstax_rescue_from 0.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +13 -5
- data/.gitignore +2 -2
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +15 -5
- data/README.md +157 -9
- data/Rakefile +4 -0
- data/app/views/errors/any.html.erb +4 -0
- data/lib/generators/open_stax/rescue_from/install/install_generator.rb +13 -0
- data/lib/generators/open_stax/rescue_from/install/templates/rescue_from.rb +51 -0
- data/lib/generators/open_stax/rescue_from/views/views_generator.rb +13 -0
- data/lib/openstax/rescue_from/configuration.rb +27 -2
- data/lib/openstax/rescue_from/controller.rb +38 -0
- data/lib/openstax/rescue_from/default_exceptions.rb +42 -0
- data/lib/openstax/rescue_from/engine.rb +33 -0
- data/lib/openstax/rescue_from/exception_options.rb +22 -0
- data/lib/openstax/rescue_from/exception_proxy.rb +55 -0
- data/lib/openstax/rescue_from/logger.rb +28 -0
- data/lib/openstax/rescue_from/mute_listener.rb +14 -0
- data/lib/openstax/rescue_from/version.rb +1 -1
- data/lib/openstax/rescue_from/view_helpers.rb +18 -0
- data/lib/openstax/rescue_from.rb +124 -0
- data/lib/openstax_rescue_from.rb +7 -33
- data/{rescue_from.gemspec → openstax_rescue_from.gemspec} +10 -3
- metadata +131 -18
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
OWZjZTY5NGM1OGJiYmY4MzliNTgzMWZiZWU1NjMyMDU2Mzg3Y2Q2NA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ODQ4NjFmMTMxNzJmMjk3ZjJiMjFlOWUzYmQxOWZmMjkzMTQzNTg2ZQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YTAzMWVjZGM0ODJlZGM1NjZkMmMyN2MwNzU1OGUzNTU0N2EyYzVkMWYxNWZm
|
10
|
+
MmMyZDFiYzYwNTI4OTU2OTFmZTQ3MmYyOGNiOWNiZjZhM2NiYjYwZWMzZWYx
|
11
|
+
MWVkMWU3YTg4NmQ3NjllNjYwMjlkZDdlZWU3Yzc3MTFlZmJlYmE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZDE1ODgyNGUwZDRlNTkyZDY5NWM1M2NhY2VlZjZiMDFmM2M1YTMwZDMxZmJl
|
14
|
+
ZGU0YTE1YzY2YjU4ZWU5YmYyMTQxYzY4M2I2YWNhZDZkNWZjN2M0MWM5MmIz
|
15
|
+
MTc5YjJjNmM4OWUzOGNiM2M5MmM1ZmVhOGNjNDk2ZTcwYTVkODU=
|
data/.gitignore
CHANGED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
openstax-rescue-from
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3
|
data/.travis.yml
CHANGED
@@ -1,10 +1,20 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
-
|
4
|
+
- "1.9.3"
|
5
|
+
- "2.2.3"
|
5
6
|
cache: bundler
|
6
7
|
bundler_args: --retry=6
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
script:
|
9
|
+
- bundle exec rake
|
10
|
+
notifications:
|
11
|
+
email: false
|
12
|
+
addons:
|
13
|
+
postgresql: "9.3"
|
14
|
+
before_install:
|
15
|
+
- gem install bundler
|
16
|
+
before_script:
|
17
|
+
- export OXT_DB_USER=postgres
|
18
|
+
- export OXT_DB_PASS=
|
19
|
+
- export OXT_TEST_DB=travis_ci_test
|
20
|
+
- bundle exec rake db:create:all db:migrate
|
data/README.md
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
# RescueFrom
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
This is the gem that brings together disparate systems within OpenStax and abstracts consistent exception rescuing with html and json responses, and email notifying
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
9
7
|
Add this line to your application's Gemfile:
|
10
8
|
|
11
9
|
```ruby
|
12
|
-
gem 'rescue_from'
|
10
|
+
gem 'rescue_from', '~> 1.0.0'
|
13
11
|
```
|
14
12
|
|
15
13
|
And then execute:
|
@@ -22,20 +20,170 @@ Or install it yourself as:
|
|
22
20
|
|
23
21
|
## Usage
|
24
22
|
|
25
|
-
|
23
|
+
Run the install generator to get the config initializer
|
24
|
+
|
25
|
+
```
|
26
|
+
$ rails g open_stax:rescue_from:install
|
27
|
+
```
|
28
|
+
|
29
|
+
Declare that you want to use the openstax exception rescuer in your controller, preferably your `ApplicationController`
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
class ApplicationController < ActionController::Base
|
33
|
+
|
34
|
+
# ...
|
35
|
+
|
36
|
+
use_openstax_exception_rescue
|
37
|
+
|
38
|
+
# ...
|
39
|
+
|
40
|
+
# Override the rescued hook which is called when configuration.raise_exceptions is false
|
41
|
+
# (See 'Controller hook')
|
42
|
+
#
|
43
|
+
# def openstax_exception_rescued(exception_proxy)
|
44
|
+
# app_name = openstax_rescue_config.app_name
|
45
|
+
# # RescueFrom.configuration private method available to you
|
46
|
+
#
|
47
|
+
# respond_to do |f|
|
48
|
+
# f.xml { render text: "I respond strangely to the XML format!",
|
49
|
+
# status: exception_proxy.status }
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
end
|
53
|
+
```
|
54
|
+
|
55
|
+
## Configuration
|
56
|
+
|
57
|
+
This configuration, which is placed in `./config/initializers/rescue_from.rb` by the install generator, shows the defaults:
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
OpenStax::RescueFrom.configure do |config|
|
61
|
+
config.raise_exceptions = ![false, 'false'].include?(ENV['RAISE_EXCEPTIONS']) ||
|
62
|
+
Rails.application.config.consider_all_requests_local
|
63
|
+
|
64
|
+
config.app_name = ENV['APP_NAME']
|
65
|
+
config.app_env = ENV['APP_ENV']
|
66
|
+
config.contact_name = ENV['EXCEPTION_CONTACT_NAME']
|
67
|
+
# can be a name, or a web/email address. See 'View helper' below
|
68
|
+
|
69
|
+
config.notifier = ExceptionNotifier
|
70
|
+
|
71
|
+
config.html_error_template_path = 'errors/any'
|
72
|
+
config.html_error_template_layout_name = 'application'
|
73
|
+
|
74
|
+
config.email_prefix = "[#{app_name}] (#{app_env}) "
|
75
|
+
config.sender_address = ENV['EXCEPTION_SENDER']
|
76
|
+
config.exception_recipients = ENV['EXCEPTION_RECIPIENTS']
|
77
|
+
end
|
78
|
+
```
|
79
|
+
|
80
|
+
## Registering exceptions
|
81
|
+
|
82
|
+
In `./config/initializers/rescue_from.rb` it is recommended you register your exceptions
|
83
|
+
|
84
|
+
Note that any unregistered exceptions rescued during run-time will be registered with the default options. See below.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
require 'openstax_rescue_from'
|
88
|
+
|
89
|
+
OpenStax::RescueFrom.configure do |c|
|
90
|
+
# c.app_name ...
|
91
|
+
end
|
92
|
+
|
93
|
+
# OpenStax::RescueFrom#register_exception default options:
|
94
|
+
#
|
95
|
+
# { notify: true,
|
96
|
+
# status: :internal_server_error,
|
97
|
+
# extras: ->(exception) { {} } }
|
98
|
+
#
|
99
|
+
# Any unregistered exceptions rescued during run-time
|
100
|
+
# will be registered during rescue with the options above
|
101
|
+
|
102
|
+
OpenStax::RescueFrom.register_exception('SecurityTransgression',
|
103
|
+
notify: false,
|
104
|
+
status: :forbidden)
|
105
|
+
|
106
|
+
OpenStax::RescueFrom.register_exception(ActiveRecord::NotFound,
|
107
|
+
notify: false,
|
108
|
+
status: :not_found)
|
109
|
+
|
110
|
+
OpenStax::RescueFrom.register_exception('OAuth2::Error',
|
111
|
+
extras: ->(exception) {
|
112
|
+
{ headers: exception.response.headers,
|
113
|
+
status: exception.response.status,
|
114
|
+
body: exception.response.body }
|
115
|
+
})
|
116
|
+
|
117
|
+
OpenStax::RescueFrom.translate_status_codes({
|
118
|
+
forbidden: 'You are not allowed to access this.',
|
119
|
+
not_found: 'We couldn't find what you asked for.',
|
120
|
+
})
|
121
|
+
#
|
122
|
+
# Default:
|
123
|
+
# internal_server_error: "Sorry, #{OpenStax::RescueFrom.configuration.app_name} had some unexpected trouble with your request."
|
124
|
+
```
|
125
|
+
|
126
|
+
## Controller hook
|
127
|
+
```ruby
|
128
|
+
#
|
129
|
+
# -- Mixed in Controller module instance method --
|
130
|
+
#
|
131
|
+
# -- this method is ONLY called when Exceptions are not raised --
|
132
|
+
#
|
133
|
+
# -- check your OpenStax::RescueFrom.configuration.raise_exceptions setting --
|
134
|
+
#
|
135
|
+
|
136
|
+
def openstax_exception_rescued(exception_proxy)
|
137
|
+
@message = exception_proxy.friendly_message
|
138
|
+
@status = exception_proxy.status
|
139
|
+
@error_id = exception_proxy.error_id
|
140
|
+
|
141
|
+
respond_to do |f|
|
142
|
+
f.html { render template: openstax_rescue_config.html_error_template_path,
|
143
|
+
layout: openstax_rescue_config.html_error_template_layout_name,
|
144
|
+
status: exception_proxy.status }
|
145
|
+
f.json { render json: { error_id: exception_proxy.error_id }
|
146
|
+
status: exception_proxy.status }
|
147
|
+
f.all { render nothing: true, status: exception_proxy.status }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Just override this method in your own controller if you wish
|
152
|
+
```
|
153
|
+
|
154
|
+
You will readily note that for HTML response, there is an error template rendered from within the gem. See below for overriding these default views.
|
155
|
+
|
156
|
+
## Override the views
|
157
|
+
|
158
|
+
You can either declare your own template path variables:
|
159
|
+
|
160
|
+
```ruby
|
161
|
+
OpenStax::RescueFrom.configure do |config|
|
162
|
+
config.html_error_template_path = 'my/path'
|
163
|
+
config.html_error_template_layout_name = 'my_layout'
|
164
|
+
end
|
165
|
+
```
|
166
|
+
|
167
|
+
or, you can generate the views into the default path:
|
168
|
+
|
169
|
+
```
|
170
|
+
$ rails g open_stax:rescue_from:views
|
171
|
+
```
|
172
|
+
|
173
|
+
## View helper
|
174
|
+
|
175
|
+
The gem provides an `openstax_rescue_from_contact_info` view helper that uses `OpenStax::RescueFrom.configuration.contact_name` to provide either just the name, or to link web and email addresses automatically for you.
|
26
176
|
|
27
177
|
## Development
|
28
178
|
|
29
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake
|
179
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
180
|
|
31
181
|
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
182
|
|
33
183
|
## Contributing
|
34
184
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
36
|
-
|
185
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/openstax/rescue_from.
|
37
186
|
|
38
187
|
## License
|
39
188
|
|
40
189
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
41
|
-
|
data/Rakefile
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module OpenStax
|
4
|
+
module RescueFrom
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path("../templates", __FILE__)
|
7
|
+
|
8
|
+
def copy_initializer
|
9
|
+
copy_file "rescue_from.rb", "config/initializers/rescue_from.rb"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'openstax_rescue_from'
|
2
|
+
|
3
|
+
OpenStax::RescueFrom.configure do |config|
|
4
|
+
config.raise_exceptions = ![false, 'false'].include?(ENV['RAISE_EXCEPTIONS']) ||
|
5
|
+
Rails.application.config.consider_all_requests_local
|
6
|
+
|
7
|
+
# config.app_name = ENV['APP_NAME']
|
8
|
+
# config.app_env = ENV['APP_ENV']
|
9
|
+
# config.contact_name = ENV['EXCEPTION_CONTACT_NAME']
|
10
|
+
|
11
|
+
# config.notifier = ExceptionNotifier
|
12
|
+
|
13
|
+
# config.html_error_template_path = 'errors/any'
|
14
|
+
# config.html_error_template_layout_name = 'application'
|
15
|
+
|
16
|
+
# config.email_prefix = "[#{app_name}] (#{app_env}) "
|
17
|
+
# config.sender_address = ENV['EXCEPTION_SENDER']
|
18
|
+
# config.exception_recipients = ENV['EXCEPTION_RECIPIENTS']
|
19
|
+
end
|
20
|
+
|
21
|
+
# OpenStax::RescueFrom#register_exception default options:
|
22
|
+
#
|
23
|
+
# { notify: true,
|
24
|
+
# status: :internal_server_error,
|
25
|
+
# extras: ->(exception) { {} } }
|
26
|
+
#
|
27
|
+
# NOTE: Any unregistered exceptions rescued during run-time
|
28
|
+
# will be registered with RescueFrom with the above options
|
29
|
+
|
30
|
+
# OpenStax::RescueFrom.register_exception('SecurityTransgression',
|
31
|
+
# notify: false,
|
32
|
+
# status: :forbidden)
|
33
|
+
#
|
34
|
+
# OpenStax::RescueFrom.register_exception(ActiveRecord::NotFound,
|
35
|
+
# notify: false,
|
36
|
+
# status: :not_found)
|
37
|
+
#
|
38
|
+
# OpenStax::RescueFrom.register_exception('OAuth2::Error',
|
39
|
+
# notify: true,
|
40
|
+
# extras: ->(exception) {
|
41
|
+
# { headers: exception.response.headers,
|
42
|
+
# status: exception.response.status,
|
43
|
+
# body: exception.response.body }
|
44
|
+
#
|
45
|
+
# OpenStax::RescueFrom.translate_status_codes({
|
46
|
+
# forbidden: "You are not allowed to access this.",
|
47
|
+
# :not_found => "We couldn't find what you asked for.",
|
48
|
+
# })
|
49
|
+
#
|
50
|
+
# Default:
|
51
|
+
# - internal_server_error: "Sorry, #{OpenStax::RescueFrom.configuration.app_name} had some unexpected trouble with your request."
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
module OpenStax
|
4
|
+
module RescueFrom
|
5
|
+
class ViewsGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path("../../../../../../app/views", __FILE__)
|
7
|
+
|
8
|
+
def copy_views
|
9
|
+
copy_file "errors/any.html.erb", "app/views/errors/any.html.erb"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,11 +1,36 @@
|
|
1
|
+
require 'exception_notification'
|
2
|
+
|
1
3
|
module OpenStax
|
2
4
|
module RescueFrom
|
3
5
|
class Configuration
|
4
6
|
|
5
|
-
|
7
|
+
attr_accessor :raise_exceptions, :notifier, :html_error_template_path,
|
8
|
+
:html_error_template_layout_name, :app_name, :app_env, :contact_name,
|
9
|
+
:email_prefix, :sender_address, :exception_recipients
|
6
10
|
|
7
11
|
def initialize
|
8
|
-
|
12
|
+
@raise_exceptions = ![false, 'false'].include?(ENV['RAISE_EXCEPTIONS'])
|
13
|
+
|
14
|
+
@app_name = ENV['APP_NAME']
|
15
|
+
@app_env = ENV['APP_ENV']
|
16
|
+
@contact_name = ENV['EXCEPTION_CONTACT_NAME']
|
17
|
+
|
18
|
+
@notifier = ExceptionNotifier
|
19
|
+
|
20
|
+
@html_error_template_path = 'errors/any'
|
21
|
+
@html_error_template_layout_name = 'application'
|
22
|
+
|
23
|
+
@sender_address = ENV['EXCEPTION_SENDER']
|
24
|
+
@exception_recipients = ENV['EXCEPTION_RECIPIENTS']
|
25
|
+
end
|
26
|
+
|
27
|
+
def email_prefix
|
28
|
+
return(@email_prefix) if defined?(@email_prefix) && !@email_prefix.blank?
|
29
|
+
|
30
|
+
name = app_name.blank? ? nil : "[#{app_name}]"
|
31
|
+
env = app_env.blank? ? nil : "(#{app_env}) "
|
32
|
+
|
33
|
+
@email_prefix = [name, env].compact.join(' ')
|
9
34
|
end
|
10
35
|
end
|
11
36
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module RescueFrom
|
3
|
+
module Controller
|
4
|
+
def self.included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def use_openstax_exception_rescue
|
10
|
+
rescue_from Exception do |exception|
|
11
|
+
RescueFrom.perform_rescue(exception, self)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def openstax_exception_rescued(proxy)
|
17
|
+
@message = proxy.friendly_message
|
18
|
+
@code = proxy.status_code
|
19
|
+
@error_id = proxy.error_id
|
20
|
+
|
21
|
+
respond_to do |f|
|
22
|
+
f.html { render template: openstax_rescue_config.html_error_template_path,
|
23
|
+
layout: openstax_rescue_config.html_error_template_layout_name,
|
24
|
+
status: proxy.status }
|
25
|
+
|
26
|
+
f.json { render json: { error_id: proxy.error_id }, status: proxy.status }
|
27
|
+
|
28
|
+
f.all { render nothing: true, status: proxy.status }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def openstax_rescue_config
|
34
|
+
RescueFrom.configuration
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module RescueFrom
|
3
|
+
class DefaultExceptions
|
4
|
+
def self.pre_register!
|
5
|
+
RescueFrom.register_exception(ActiveRecord::RecordNotFound,
|
6
|
+
notify: false,
|
7
|
+
status: :not_found)
|
8
|
+
|
9
|
+
RescueFrom.register_exception(ActionController::RoutingError,
|
10
|
+
notify: false,
|
11
|
+
status: :not_found)
|
12
|
+
|
13
|
+
RescueFrom.register_exception(ActionController::UnknownController,
|
14
|
+
notify: false,
|
15
|
+
status: :not_found)
|
16
|
+
|
17
|
+
RescueFrom.register_exception(ActionController::InvalidAuthenticityToken,
|
18
|
+
notify: false,
|
19
|
+
status: :unprocessable_entity)
|
20
|
+
|
21
|
+
RescueFrom.register_exception(AbstractController::ActionNotFound,
|
22
|
+
notify: false,
|
23
|
+
status: :not_found)
|
24
|
+
|
25
|
+
RescueFrom.register_exception(ActionView::MissingTemplate,
|
26
|
+
notify: false,
|
27
|
+
status: :bad_request)
|
28
|
+
|
29
|
+
RescueFrom.register_exception('SecurityTransgression',
|
30
|
+
notify: false,
|
31
|
+
status: :forbidden)
|
32
|
+
|
33
|
+
RescueFrom.register_exception('OAuth2::Error',
|
34
|
+
extras: ->(ex) {
|
35
|
+
{ headers: ex.response.headers,
|
36
|
+
status: ex.response.status,
|
37
|
+
body: ex.response.body }
|
38
|
+
})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'openstax/rescue_from/default_exceptions'
|
2
|
+
|
3
|
+
module OpenStax
|
4
|
+
module RescueFrom
|
5
|
+
class Engine < ::Rails::Engine
|
6
|
+
initializer 'openstax.rescue_from.inflection' do
|
7
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
8
|
+
inflect.acronym 'OpenStax'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
initializer "openstax.rescue_from.action_controller" do
|
13
|
+
ActionController::Base.send :include, Controller
|
14
|
+
end
|
15
|
+
|
16
|
+
initializer "openstax.rescue_from.view_helpers" do
|
17
|
+
ActionView::Base.send :include, ViewHelpers
|
18
|
+
end
|
19
|
+
|
20
|
+
initializer "openstax.rescue_from.use_exception_notification_middleware" do
|
21
|
+
Rails.application.config.middleware.use ExceptionNotification::Rack, email: {
|
22
|
+
email_prefix: RescueFrom.configuration.email_prefix,
|
23
|
+
sender_address: RescueFrom.configuration.sender_address,
|
24
|
+
exception_recipients: RescueFrom.configuration.exception_recipients
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
initializer "openstax.rescue_from.pre_register_exceptions" do
|
29
|
+
OpenStax::RescueFrom::DefaultExceptions.pre_register!
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module RescueFrom
|
3
|
+
class ExceptionOptions
|
4
|
+
attr_accessor :notify, :status_code, :extras
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
options.stringify_keys!
|
8
|
+
options = { 'notify' => true,
|
9
|
+
'status' => :internal_server_error,
|
10
|
+
'extras' => ->(exception) { {} } }.merge(options)
|
11
|
+
|
12
|
+
@notify = options['notify']
|
13
|
+
@status_code = options['status']
|
14
|
+
@extras = options['extras']
|
15
|
+
end
|
16
|
+
|
17
|
+
def notify?
|
18
|
+
@notify
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module RescueFrom
|
3
|
+
class ExceptionProxy
|
4
|
+
attr_reader :exception
|
5
|
+
|
6
|
+
def initialize(exception)
|
7
|
+
@exception = exception
|
8
|
+
end
|
9
|
+
|
10
|
+
def name
|
11
|
+
@name ||= exception.class.name
|
12
|
+
end
|
13
|
+
|
14
|
+
def error_id
|
15
|
+
@error_id ||= RescueFrom.generate_id
|
16
|
+
end
|
17
|
+
|
18
|
+
def message
|
19
|
+
@message ||= exception.message
|
20
|
+
end
|
21
|
+
|
22
|
+
def friendly_message
|
23
|
+
RescueFrom.friendly_message(status)
|
24
|
+
end
|
25
|
+
|
26
|
+
def extras
|
27
|
+
@extras ||= RescueFrom.extras_proc(name).call(exception)
|
28
|
+
end
|
29
|
+
|
30
|
+
def cause
|
31
|
+
@cause ||= exception.respond_to?(:cause) ? exception.cause : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def backtrace
|
35
|
+
@backtrace ||= cause.blank? ? first_backtrace_line : all_backtrace_lines
|
36
|
+
end
|
37
|
+
|
38
|
+
def all_backtrace_lines
|
39
|
+
@all_backtrace_lines ||= exception.backtrace.join("\n")
|
40
|
+
end
|
41
|
+
|
42
|
+
def first_backtrace_line
|
43
|
+
@first_backtrace_line ||= exception.backtrace.first
|
44
|
+
end
|
45
|
+
|
46
|
+
def status
|
47
|
+
@status ||= RescueFrom.status(name)
|
48
|
+
end
|
49
|
+
|
50
|
+
def status_code
|
51
|
+
@status_code ||= RescueFrom.http_code(status)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module RescueFrom
|
3
|
+
class Logger
|
4
|
+
attr_reader :proxy
|
5
|
+
|
6
|
+
def initialize(proxy)
|
7
|
+
@proxy = proxy
|
8
|
+
end
|
9
|
+
|
10
|
+
def record_system_error!(prefix = "An exception occurred")
|
11
|
+
Rails.logger.error("#{prefix}: #{proxy.name} [#{proxy.error_id}] " +
|
12
|
+
"<#{proxy.message}> #{proxy.extras}\n\n" +
|
13
|
+
"#{proxy.backtrace}")
|
14
|
+
|
15
|
+
record_system_error_recursively!
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def record_system_error_recursively!
|
20
|
+
if proxy.cause
|
21
|
+
RescueFrom.register_exception(proxy.cause.class)
|
22
|
+
@proxy = ExceptionProxy.new(proxy.cause)
|
23
|
+
record_system_error!("Exception cause")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module RescueFrom
|
3
|
+
class MuteListener
|
4
|
+
def openstax_exception_rescued(proxy)
|
5
|
+
Rails.logger.warn("MuteListener does nothing after rescuing " +
|
6
|
+
"ExceptionProxy#error_id #=> #{proxy.error_id}")
|
7
|
+
end
|
8
|
+
|
9
|
+
def request
|
10
|
+
OpenStruct.new(remote_ip: '0.0.0.0')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module OpenStax
|
2
|
+
module RescueFrom
|
3
|
+
module ViewHelpers
|
4
|
+
def openstax_rescue_from_contact_info
|
5
|
+
info = OpenStax::RescueFrom.configuration.contact_name
|
6
|
+
|
7
|
+
if info.match(/\S+@\S+\.\w{2,4}/)
|
8
|
+
mail_to info
|
9
|
+
elsif info.match(/\S+\.\w{2,4}\z/)
|
10
|
+
info = info.match(/\Ahttps?:\/\//) ? info : "http://#{info}"
|
11
|
+
link_to info, info
|
12
|
+
else
|
13
|
+
info
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'openstax/rescue_from/exception_options'
|
2
|
+
require 'openstax/rescue_from/controller'
|
3
|
+
require 'openstax/rescue_from/view_helpers'
|
4
|
+
require 'openstax/rescue_from/configuration'
|
5
|
+
|
6
|
+
module OpenStax
|
7
|
+
module RescueFrom
|
8
|
+
class << self
|
9
|
+
def perform_rescue(exception, listener = MuteListener.new)
|
10
|
+
unless registered_exceptions.keys.include?(exception.class.name)
|
11
|
+
register_exception(exception.class)
|
12
|
+
end
|
13
|
+
|
14
|
+
proxy = ExceptionProxy.new(exception)
|
15
|
+
log_system_error(proxy)
|
16
|
+
send_notifying_exceptions(proxy, listener)
|
17
|
+
finish_exception_rescue(proxy, listener)
|
18
|
+
end
|
19
|
+
|
20
|
+
def register_exception(exception, options = {})
|
21
|
+
name = exception.is_a?(String) ? exception : exception.name
|
22
|
+
@@registered_exceptions ||= {}
|
23
|
+
@@registered_exceptions[name] = ExceptionOptions.new(options)
|
24
|
+
end
|
25
|
+
|
26
|
+
def translate_status_codes(map = {})
|
27
|
+
map.each do |k, v|
|
28
|
+
friendly_status_messages[k] = v
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def registered_exceptions
|
33
|
+
@@registered_exceptions.dup
|
34
|
+
end
|
35
|
+
|
36
|
+
def non_notifying_exceptions
|
37
|
+
@@registered_exceptions.reject { |_, v| v.notify? }.keys
|
38
|
+
end
|
39
|
+
|
40
|
+
def notifying_exceptions
|
41
|
+
@@registered_exceptions.select { |_, v| v.notify? }.keys
|
42
|
+
end
|
43
|
+
|
44
|
+
def friendly_message(status)
|
45
|
+
friendly_status_messages[status] || default_friendly_message
|
46
|
+
end
|
47
|
+
|
48
|
+
def notifies_for?(exception_name)
|
49
|
+
notifying_exceptions.include?(exception_name)
|
50
|
+
end
|
51
|
+
|
52
|
+
def status(exception_name)
|
53
|
+
@@registered_exceptions[exception_name].status_code
|
54
|
+
end
|
55
|
+
|
56
|
+
def http_code(status)
|
57
|
+
Rack::Utils.status_code(status)
|
58
|
+
end
|
59
|
+
|
60
|
+
def extras_proc(exception_name)
|
61
|
+
@@registered_exceptions[exception_name].extras
|
62
|
+
end
|
63
|
+
|
64
|
+
def generate_id
|
65
|
+
"%06d#{SecureRandom.random_number(10**6)}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def configure
|
69
|
+
yield configuration
|
70
|
+
end
|
71
|
+
|
72
|
+
def configuration
|
73
|
+
@configuration ||= Configuration.new
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
def friendly_status_messages
|
78
|
+
@@friendly_status_messages ||= {
|
79
|
+
internal_server_error: default_friendly_message
|
80
|
+
}
|
81
|
+
end
|
82
|
+
|
83
|
+
def default_friendly_message
|
84
|
+
"Sorry, #{configuration.app_name} had some unexpected trouble with your request."
|
85
|
+
end
|
86
|
+
|
87
|
+
def resolve_ip(ip)
|
88
|
+
Resolv.getname(ip) rescue 'unknown'
|
89
|
+
end
|
90
|
+
|
91
|
+
def log_system_error(proxy)
|
92
|
+
logger = Logger.new(proxy)
|
93
|
+
logger.record_system_error!
|
94
|
+
end
|
95
|
+
|
96
|
+
def send_notifying_exceptions(proxy, listener)
|
97
|
+
if notifies_for?(proxy.name)
|
98
|
+
configuration.notifier.notify_exception(
|
99
|
+
proxy.exception,
|
100
|
+
env: listener.request.env,
|
101
|
+
data: {
|
102
|
+
error_id: proxy.error_id,
|
103
|
+
:class => proxy.name,
|
104
|
+
message: proxy.message,
|
105
|
+
first_line_of_backtrace: proxy.first_backtrace_line,
|
106
|
+
cause: proxy.cause,
|
107
|
+
dns_name: resolve_ip(listener.request.remote_ip),
|
108
|
+
extras: proxy.extras
|
109
|
+
},
|
110
|
+
sections: %w(data request session environment backtrace)
|
111
|
+
)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def finish_exception_rescue(proxy, listener)
|
116
|
+
if configuration.raise_exceptions
|
117
|
+
raise proxy.exception
|
118
|
+
else
|
119
|
+
listener.openstax_exception_rescued(proxy)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
data/lib/openstax_rescue_from.rb
CHANGED
@@ -1,37 +1,11 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
require "openstax/rescue_from/configuration"
|
6
|
-
require "openstax/rescue_from/version"
|
7
|
-
|
8
|
-
module OpenStax
|
9
|
-
module RescueFrom
|
1
|
+
require 'openstax/rescue_from'
|
2
|
+
require 'openstax/rescue_from/version'
|
3
|
+
require 'openstax/rescue_from/engine'
|
10
4
|
|
11
|
-
|
5
|
+
require 'openstax/rescue_from/mute_listener'
|
6
|
+
require 'openstax/rescue_from/logger'
|
12
7
|
|
13
|
-
|
14
|
-
#
|
15
|
-
# Configuration machinery.
|
16
|
-
#
|
17
|
-
# To configure OpenStax RescueFrom, put the following code in your
|
18
|
-
# application's initialization logic
|
19
|
-
# (eg. in the config/initializers in a Rails app)
|
20
|
-
#
|
21
|
-
# OpenStax::RescueFrom.configure do |config|
|
22
|
-
# config.<parameter name> = <parameter value>
|
23
|
-
# ...
|
24
|
-
# end
|
25
|
-
#
|
8
|
+
require 'openstax/rescue_from/exception_proxy'
|
26
9
|
|
27
|
-
|
28
|
-
yield configuration
|
29
|
-
end
|
30
|
-
|
31
|
-
def configuration
|
32
|
-
@configuration ||= Configuration.new
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
36
|
-
end
|
10
|
+
module OpenStax
|
37
11
|
end
|
@@ -6,8 +6,8 @@ require 'openstax/rescue_from/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "openstax_rescue_from"
|
8
8
|
spec.version = OpenStax::RescueFrom::VERSION
|
9
|
-
spec.authors = ["JP Slavinsky"]
|
10
|
-
spec.email = ["jps@kindlinglabs.com"]
|
9
|
+
spec.authors = ["JP Slavinsky", "Joe Sak"]
|
10
|
+
spec.email = ["jps@kindlinglabs.com", "joe@avant-gardelabs.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Common exception `rescue_from` handling for OpenStax sites.}
|
13
13
|
spec.homepage = "https://github.com/openstax/rescue_from"
|
@@ -18,7 +18,14 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency "rails", '>= 3.1', '< 5.0'
|
22
|
+
spec.add_dependency "exception_notification", '>= 4.1', '< 5.0'
|
23
|
+
|
24
|
+
spec.add_development_dependency "pg", '~> 0.18.3'
|
21
25
|
spec.add_development_dependency "bundler", "~> 1.10"
|
22
26
|
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
-
spec.add_development_dependency "rspec"
|
27
|
+
spec.add_development_dependency "rspec-rails", '~> 3.3.3'
|
28
|
+
spec.add_development_dependency "pry-nav", '~> 0.2.4'
|
29
|
+
spec.add_development_dependency "pry-rails", '~> 0.3.4'
|
30
|
+
spec.add_development_dependency "database_cleaner", '~> 1.5.0'
|
24
31
|
end
|
metadata
CHANGED
@@ -1,77 +1,190 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: openstax_rescue_from
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- JP Slavinsky
|
8
|
+
- Joe Sak
|
8
9
|
autorequire:
|
9
10
|
bindir: exe
|
10
11
|
cert_chain: []
|
11
|
-
date: 2015-
|
12
|
+
date: 2015-10-02 00:00:00.000000000 Z
|
12
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '3.1'
|
21
|
+
- - <
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: '5.0'
|
24
|
+
type: :runtime
|
25
|
+
prerelease: false
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '3.1'
|
31
|
+
- - <
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.0'
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: exception_notification
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.1'
|
41
|
+
- - <
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '5.0'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ! '>='
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: '4.1'
|
51
|
+
- - <
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '5.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: pg
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: 0.18.3
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ~>
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 0.18.3
|
13
68
|
- !ruby/object:Gem::Dependency
|
14
69
|
name: bundler
|
15
70
|
requirement: !ruby/object:Gem::Requirement
|
16
71
|
requirements:
|
17
|
-
- -
|
72
|
+
- - ~>
|
18
73
|
- !ruby/object:Gem::Version
|
19
74
|
version: '1.10'
|
20
75
|
type: :development
|
21
76
|
prerelease: false
|
22
77
|
version_requirements: !ruby/object:Gem::Requirement
|
23
78
|
requirements:
|
24
|
-
- -
|
79
|
+
- - ~>
|
25
80
|
- !ruby/object:Gem::Version
|
26
81
|
version: '1.10'
|
27
82
|
- !ruby/object:Gem::Dependency
|
28
83
|
name: rake
|
29
84
|
requirement: !ruby/object:Gem::Requirement
|
30
85
|
requirements:
|
31
|
-
- -
|
86
|
+
- - ~>
|
32
87
|
- !ruby/object:Gem::Version
|
33
88
|
version: '10.0'
|
34
89
|
type: :development
|
35
90
|
prerelease: false
|
36
91
|
version_requirements: !ruby/object:Gem::Requirement
|
37
92
|
requirements:
|
38
|
-
- -
|
93
|
+
- - ~>
|
39
94
|
- !ruby/object:Gem::Version
|
40
95
|
version: '10.0'
|
41
96
|
- !ruby/object:Gem::Dependency
|
42
|
-
name: rspec
|
97
|
+
name: rspec-rails
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - ~>
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: 3.3.3
|
103
|
+
type: :development
|
104
|
+
prerelease: false
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 3.3.3
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: pry-nav
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - ~>
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: 0.2.4
|
117
|
+
type: :development
|
118
|
+
prerelease: false
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ~>
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 0.2.4
|
124
|
+
- !ruby/object:Gem::Dependency
|
125
|
+
name: pry-rails
|
126
|
+
requirement: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ~>
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 0.3.4
|
131
|
+
type: :development
|
132
|
+
prerelease: false
|
133
|
+
version_requirements: !ruby/object:Gem::Requirement
|
134
|
+
requirements:
|
135
|
+
- - ~>
|
136
|
+
- !ruby/object:Gem::Version
|
137
|
+
version: 0.3.4
|
138
|
+
- !ruby/object:Gem::Dependency
|
139
|
+
name: database_cleaner
|
43
140
|
requirement: !ruby/object:Gem::Requirement
|
44
141
|
requirements:
|
45
|
-
- -
|
142
|
+
- - ~>
|
46
143
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
144
|
+
version: 1.5.0
|
48
145
|
type: :development
|
49
146
|
prerelease: false
|
50
147
|
version_requirements: !ruby/object:Gem::Requirement
|
51
148
|
requirements:
|
52
|
-
- -
|
149
|
+
- - ~>
|
53
150
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
151
|
+
version: 1.5.0
|
55
152
|
description:
|
56
153
|
email:
|
57
154
|
- jps@kindlinglabs.com
|
155
|
+
- joe@avant-gardelabs.com
|
58
156
|
executables: []
|
59
157
|
extensions: []
|
60
158
|
extra_rdoc_files: []
|
61
159
|
files:
|
62
|
-
-
|
63
|
-
-
|
64
|
-
-
|
160
|
+
- .gitignore
|
161
|
+
- .rspec
|
162
|
+
- .ruby-gemset
|
163
|
+
- .ruby-version
|
164
|
+
- .travis.yml
|
65
165
|
- Gemfile
|
66
166
|
- LICENSE.txt
|
67
167
|
- README.md
|
68
168
|
- Rakefile
|
169
|
+
- app/views/errors/any.html.erb
|
69
170
|
- bin/console
|
70
171
|
- bin/setup
|
172
|
+
- lib/generators/open_stax/rescue_from/install/install_generator.rb
|
173
|
+
- lib/generators/open_stax/rescue_from/install/templates/rescue_from.rb
|
174
|
+
- lib/generators/open_stax/rescue_from/views/views_generator.rb
|
175
|
+
- lib/openstax/rescue_from.rb
|
71
176
|
- lib/openstax/rescue_from/configuration.rb
|
177
|
+
- lib/openstax/rescue_from/controller.rb
|
178
|
+
- lib/openstax/rescue_from/default_exceptions.rb
|
179
|
+
- lib/openstax/rescue_from/engine.rb
|
180
|
+
- lib/openstax/rescue_from/exception_options.rb
|
181
|
+
- lib/openstax/rescue_from/exception_proxy.rb
|
182
|
+
- lib/openstax/rescue_from/logger.rb
|
183
|
+
- lib/openstax/rescue_from/mute_listener.rb
|
72
184
|
- lib/openstax/rescue_from/version.rb
|
185
|
+
- lib/openstax/rescue_from/view_helpers.rb
|
73
186
|
- lib/openstax_rescue_from.rb
|
74
|
-
-
|
187
|
+
- openstax_rescue_from.gemspec
|
75
188
|
homepage: https://github.com/openstax/rescue_from
|
76
189
|
licenses:
|
77
190
|
- MIT
|
@@ -82,17 +195,17 @@ require_paths:
|
|
82
195
|
- lib
|
83
196
|
required_ruby_version: !ruby/object:Gem::Requirement
|
84
197
|
requirements:
|
85
|
-
- -
|
198
|
+
- - ! '>='
|
86
199
|
- !ruby/object:Gem::Version
|
87
200
|
version: '0'
|
88
201
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
202
|
requirements:
|
90
|
-
- -
|
203
|
+
- - ! '>='
|
91
204
|
- !ruby/object:Gem::Version
|
92
205
|
version: '0'
|
93
206
|
requirements: []
|
94
207
|
rubyforge_project:
|
95
|
-
rubygems_version: 2.4.
|
208
|
+
rubygems_version: 2.4.6
|
96
209
|
signing_key:
|
97
210
|
specification_version: 4
|
98
211
|
summary: Common exception `rescue_from` handling for OpenStax sites.
|