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 CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 8c0c0a01fc9e68148d1e6edee85fe7676c0a4378
4
- data.tar.gz: 7d233b3e0bff69be9e33b708b48d8470941c5927
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OWZjZTY5NGM1OGJiYmY4MzliNTgzMWZiZWU1NjMyMDU2Mzg3Y2Q2NA==
5
+ data.tar.gz: !binary |-
6
+ ODQ4NjFmMTMxNzJmMjk3ZjJiMjFlOWUzYmQxOWZmMjkzMTQzNTg2ZQ==
5
7
  SHA512:
6
- metadata.gz: a51f638e3fd3d3defffd71833c798001b2486be1fd8f17090490357065eac39fc1f7542a1ad4100f1179c0e8cd95a14719232521eb7c5eae6950684047ac7c37
7
- data.tar.gz: 688fbe57b528e091c95b8efe66ab59f8a0ea1c12650c818f481b9d3fa387d12d3bc68d7181944d626b445808520bcbeaa3e5d581101cad387cec0e74c1d6ea56
8
+ metadata.gz: !binary |-
9
+ YTAzMWVjZGM0ODJlZGM1NjZkMmMyN2MwNzU1OGUzNTU0N2EyYzVkMWYxNWZm
10
+ MmMyZDFiYzYwNTI4OTU2OTFmZTQ3MmYyOGNiOWNiZjZhM2NiYjYwZWMzZWYx
11
+ MWVkMWU3YTg4NmQ3NjllNjYwMjlkZDdlZWU3Yzc3MTFlZmJlYmE=
12
+ data.tar.gz: !binary |-
13
+ ZDE1ODgyNGUwZDRlNTkyZDY5NWM1M2NhY2VlZjZiMDFmM2M1YTMwZDMxZmJl
14
+ ZGU0YTE1YzY2YjU4ZWU5YmYyMTQxYzY4M2I2YWNhZDZkNWZjN2M0MWM5MmIz
15
+ MTc5YjJjNmM4OWUzOGNiM2M5MmM1ZmVhOGNjNDk2ZTcwYTVkODU=
data/.gitignore CHANGED
@@ -6,8 +6,8 @@
6
6
  /doc/
7
7
  /pkg/
8
8
  /spec/reports/
9
- /tmp/
10
- log/*.log
9
+ tmp/
10
+ *.log
11
11
  .DS_Store
12
12
  *~
13
13
 
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
- - 2.2.3
4
+ - "1.9.3"
5
+ - "2.2.3"
5
6
  cache: bundler
6
7
  bundler_args: --retry=6
7
- before_install: gem install bundler -v 1.10.6
8
- branches:
9
- only:
10
- - master
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
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/rescue_from`. To experiment with that code, run `bin/console` for an interactive prompt.
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
- TODO: Write usage instructions here
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 spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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/[USERNAME]/rescue_from.
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
@@ -4,3 +4,7 @@ require "rspec/core/rake_task"
4
4
  RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  task :default => :spec
7
+
8
+ Bundler::GemHelper.install_tasks
9
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
10
+ load 'rails/tasks/engine.rake'
@@ -0,0 +1,4 @@
1
+ <h3><%= @message %></h3>
2
+
3
+ <p>We're sorry this <%= @code %> error occurred. If you need help, please contact <%= openstax_rescue_from_contact_info %><%= " and reference error #{@error_id}" if @error_id %>.</p>
4
+
@@ -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
- # attr_accessor, attr_reader, attr_writers for variables
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
- # set default values for variables
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
@@ -1,5 +1,5 @@
1
1
  module OpenStax
2
2
  module RescueFrom
3
- VERSION = "0.0.1"
3
+ VERSION = "1.1.0"
4
4
  end
5
5
  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
@@ -1,37 +1,11 @@
1
- ActiveSupport::Inflector.inflections do |inflect|
2
- inflect.acronym 'OpenStax'
3
- end
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
- class << self
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
- def configure
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: 0.0.1
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-09-17 00:00:00.000000000 Z
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: '0'
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: '0'
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
- - ".gitignore"
63
- - ".rspec"
64
- - ".travis.yml"
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
- - rescue_from.gemspec
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.8
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.