openstax_rescue_from 0.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.
|