rails_exception_handler 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. data/.travis.yml +11 -0
  2. data/Gemfile +9 -0
  3. data/Gemfile.lock +91 -0
  4. data/LICENCE +20 -0
  5. data/README.markdown +192 -0
  6. data/Rakefile +31 -0
  7. data/VERSION +1 -0
  8. data/app/controllers/error_response_controller.rb +6 -0
  9. data/app/models/error_message.rb +3 -0
  10. data/lib/patch/show_exceptions.rb +14 -0
  11. data/lib/rails_exception_handler/configuration.rb +13 -0
  12. data/lib/rails_exception_handler/handler.rb +69 -0
  13. data/lib/rails_exception_handler/parser.rb +73 -0
  14. data/lib/rails_exception_handler.rb +37 -0
  15. data/rails_exception_handler.gemspec +87 -0
  16. data/spec/integration/configuration_spec.rb +161 -0
  17. data/spec/integration/rails_exception_handler_spec.rb +67 -0
  18. data/spec/spec_helper.rb +25 -0
  19. data/spec/test_macros.rb +61 -0
  20. data/spec/testapp_30/.gitignore +4 -0
  21. data/spec/testapp_30/Gemfile +31 -0
  22. data/spec/testapp_30/Gemfile.lock +75 -0
  23. data/spec/testapp_30/Rakefile +7 -0
  24. data/spec/testapp_30/app/controllers/application_controller.rb +12 -0
  25. data/spec/testapp_30/app/controllers/home_controller.rb +22 -0
  26. data/spec/testapp_30/app/helpers/application_helper.rb +2 -0
  27. data/spec/testapp_30/app/models/stored_exception.rb +9 -0
  28. data/spec/testapp_30/app/views/home/view_error.html.erb +2 -0
  29. data/spec/testapp_30/app/views/layouts/fallback.html.erb +15 -0
  30. data/spec/testapp_30/app/views/layouts/home.html.erb +15 -0
  31. data/spec/testapp_30/config/application.rb +44 -0
  32. data/spec/testapp_30/config/boot.rb +6 -0
  33. data/spec/testapp_30/config/database.yml +12 -0
  34. data/spec/testapp_30/config/environment.rb +5 -0
  35. data/spec/testapp_30/config/environments/development.rb +26 -0
  36. data/spec/testapp_30/config/environments/production.rb +49 -0
  37. data/spec/testapp_30/config/environments/test.rb +35 -0
  38. data/spec/testapp_30/config/examples/database.yml +13 -0
  39. data/spec/testapp_30/config/locales/en.yml +5 -0
  40. data/spec/testapp_30/config/routes.rb +6 -0
  41. data/spec/testapp_30/config.ru +4 -0
  42. data/spec/testapp_30/db/migrate/20110630174538_create_error_messages.rb +22 -0
  43. data/spec/testapp_30/db/migrate/20110702131654_add_sessions_table.rb +16 -0
  44. data/spec/testapp_30/db/schema.rb +39 -0
  45. data/spec/testapp_30/db/seeds.rb +7 -0
  46. data/spec/testapp_30/lib/tasks/.gitkeep +0 -0
  47. data/spec/testapp_30/script/rails +6 -0
  48. data/spec/testapp_30/script/setup +19 -0
  49. data/spec/testapp_30/vendor/plugins/.gitkeep +0 -0
  50. data/spec/unit/configuration_spec.rb +37 -0
  51. data/spec/unit/handler_spec.rb +136 -0
  52. data/spec/unit/parser_spec.rb +112 -0
  53. metadata +107 -0
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ before_script:
2
+ - "spec/exception_handler_test_app/script/setup"
3
+
4
+ notifications:
5
+ disable: true
6
+
7
+ rvm:
8
+ - 1.9.2
9
+ - 1.8.7
10
+ - ree
11
+ - ruby-head
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # A sample Gemfile
2
+ source "http://rubygems.org"
3
+
4
+ group :test do
5
+ gem "rails", '3.0.9'
6
+ gem "rspec-rails"
7
+ gem "rack-test", '0.5.7'
8
+ gem "mysql2", '0.2.6'
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,91 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.9)
6
+ actionpack (= 3.0.9)
7
+ mail (~> 2.2.19)
8
+ actionpack (3.0.9)
9
+ activemodel (= 3.0.9)
10
+ activesupport (= 3.0.9)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.5.0)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.14)
16
+ rack-test (~> 0.5.7)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.9)
19
+ activesupport (= 3.0.9)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.5.0)
22
+ activerecord (3.0.9)
23
+ activemodel (= 3.0.9)
24
+ activesupport (= 3.0.9)
25
+ arel (~> 2.0.10)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.9)
28
+ activemodel (= 3.0.9)
29
+ activesupport (= 3.0.9)
30
+ activesupport (3.0.9)
31
+ arel (2.0.10)
32
+ builder (2.1.2)
33
+ diff-lcs (1.1.2)
34
+ erubis (2.6.6)
35
+ abstract (>= 1.0.0)
36
+ i18n (0.5.0)
37
+ mail (2.2.19)
38
+ activesupport (>= 2.3.6)
39
+ i18n (>= 0.4.0)
40
+ mime-types (~> 1.16)
41
+ treetop (~> 1.4.8)
42
+ mime-types (1.16)
43
+ mysql2 (0.2.6)
44
+ polyglot (0.3.1)
45
+ rack (1.2.3)
46
+ rack-mount (0.6.14)
47
+ rack (>= 1.0.0)
48
+ rack-test (0.5.7)
49
+ rack (>= 1.0)
50
+ rails (3.0.9)
51
+ actionmailer (= 3.0.9)
52
+ actionpack (= 3.0.9)
53
+ activerecord (= 3.0.9)
54
+ activeresource (= 3.0.9)
55
+ activesupport (= 3.0.9)
56
+ bundler (~> 1.0)
57
+ railties (= 3.0.9)
58
+ railties (3.0.9)
59
+ actionpack (= 3.0.9)
60
+ activesupport (= 3.0.9)
61
+ rake (>= 0.8.7)
62
+ rdoc (~> 3.4)
63
+ thor (~> 0.14.4)
64
+ rake (0.9.2)
65
+ rdoc (3.8)
66
+ rspec (2.6.0)
67
+ rspec-core (~> 2.6.0)
68
+ rspec-expectations (~> 2.6.0)
69
+ rspec-mocks (~> 2.6.0)
70
+ rspec-core (2.6.4)
71
+ rspec-expectations (2.6.0)
72
+ diff-lcs (~> 1.1.2)
73
+ rspec-mocks (2.6.0)
74
+ rspec-rails (2.6.1)
75
+ actionpack (~> 3.0)
76
+ activesupport (~> 3.0)
77
+ railties (~> 3.0)
78
+ rspec (~> 2.6.0)
79
+ thor (0.14.6)
80
+ treetop (1.4.9)
81
+ polyglot (>= 0.3.1)
82
+ tzinfo (0.3.29)
83
+
84
+ PLATFORMS
85
+ ruby
86
+
87
+ DEPENDENCIES
88
+ mysql2 (= 0.2.6)
89
+ rack-test (= 0.5.7)
90
+ rails (= 3.0.9)
91
+ rspec-rails
data/LICENCE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Bjørn Trondsen
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,192 @@
1
+ # Rails Exception Handler [![Build Status](http://travis-ci.org/Sharagoz/rails_execption_handler.png)](http://travis-ci.org/#!/Sharagoz/rails_exception_handler)
2
+
3
+ This is an exception handler for Rails 3 built as Rack middleware. It enables you to save the key information from the error message in a database somewhere and display a customized error message to the user within the applications layout file. You can hook this gem into all your rails apps and gather the exception reports in one place. If you make yourself a simple web front on top of that you have a user friendly way of keeping track of exceptions raised by your rails apps.
4
+
5
+ ## Installation
6
+ Add this line to your Gemfile:
7
+
8
+ ```
9
+ config.gem 'rails_exception_handler', '~> 1.0'
10
+ ```
11
+
12
+ Create an initializer in **config/initializers** called **rails_exception_handler.rb** and uncomment the options where you want something other than the default:
13
+
14
+ ```ruby
15
+ RailsExceptionHandler.configure do |config|
16
+ # config.environments = [:development, :test, :production] # Defaults to [:production]
17
+ # config.storage_strategies = [:active_record, :rails_log, :remote_url => {:target => 'http://example.com'}] # Defaults to []
18
+ # config.fallback_layout = 'home' # Defaults to 'application'
19
+ # config.store_user_info = {:method => :current_user, :field => :login} # Defaults to false
20
+ # config.responses['404'] = "<h1>404</h1><p>Page not found</p>"
21
+ # config.responses['500'] = "<h1>500</h1><p>Internal server error</p>"
22
+ # config.filters = [ # No filters are enabled by default
23
+ # :all_404s,
24
+ # :no_referer_404s,
25
+ # {:user_agent_regxp => /\b(ApptusBot|TurnitinBot|DotBot|SiteBot)\b/i},
26
+ # {:target_url_regxp => /\b(myphpadmin)\b/i}
27
+ #]
28
+ end
29
+ ```
30
+
31
+ ## Configuration options explained
32
+
33
+ ### environments
34
+ An array of symbols that says which Rails environments you want the exception handler to run in.
35
+
36
+ ```ruby
37
+ config.environments = [:production, :test, :development]
38
+ ```
39
+
40
+ Default value: [:production]
41
+
42
+ ### storage_strategies
43
+ An array of zero or more symbols that says which storage strategies you want to use. Each are explained in detail in separate sections at the end of this document.
44
+
45
+ ```ruby
46
+ config.storage_strategies = [:active_record, :rails_log, :remote_url => {:target => 'http://example.com'}]
47
+ ```
48
+
49
+ Default value: []
50
+
51
+
52
+ ### fallback_layout
53
+
54
+ ```ruby
55
+ config.fallback_layout = 'home'
56
+ ```
57
+
58
+ Default value: 'application'
59
+
60
+ The exception handler will always use the layout file of the controller action that was accessed when the error occured. However, when routing errors occures there are no controller action to get this layout from, so it falls back to the default 'application' layout that most apps have. If your application does not have a layout file called 'application', then you need to override this, otherwise a "missing layout" exception will occur.
61
+
62
+ ### store_user_info
63
+
64
+ Having some way of identifying the user can be very useful at times, so I always store information on who generated the exception in applications that have a log in feature.
65
+ To enable this you need to specify the name of the controller method that provides the user object and the name of the field which contains the info you want to save.
66
+
67
+ ```ruby
68
+ config.store_user_info = {:method => :current_user, :field => :login}
69
+ ```
70
+
71
+ Default value: false (no info will be stored)
72
+ If you turn this on and the error is generated by a client that is not logged in, then "Anonymous" will be used.
73
+
74
+ ### filters
75
+
76
+ All filters are disabled by default. I recommend you deploy your application this way, and then add filters as they become necessary.
77
+ The only reason I've ever wanted filtering have been due to what seem like poorly programmed web crawlers and black bots probing for security holes.
78
+ Every once in a while I'll get dozens of errors within a few minutes caused by a bot looking for things like Joomla/Wordpress libraries with known security holes, or a web crawler that follows the target of forms.
79
+
80
+ **:all_404s**
81
+
82
+ ```ruby
83
+ config.filters = [:all_404s]
84
+ ```
85
+
86
+ When turned on the following exceptions will no longer be stored: ActionController::RoutingError, AbstractController::ActionNotFound, ActiveRecord::RecordNotFound
87
+ Consider this a last resort. You will miss all "real" 404s when this is turned on, like broken redirections.
88
+
89
+ **:no_referer_404s**
90
+
91
+ ```ruby
92
+ config.filters = [:no_referer_404s]
93
+ ```
94
+
95
+ ActionController::RoutingError, AbstractController::ActionNotFound, ActiveRecord::RecordNotFound will be ignored if it was caused by a request without a referer.
96
+ This is very effective against bots. 99.9% of the time a routing error with no referer will be caused by a bot, and then once in a while it will be caused by a real user that happened to generate an error on the first page he opened. You will get a lot less false positives with this filter than :all_404s.
97
+
98
+ **:user_agent_regxp**
99
+
100
+ The legit bots usually adds something to the user agent string that lets you identify them. You can use this to filter out the errors they genereate, and be pretty sure you are not going to get any false positives.
101
+ The regular expression I use looks something like this:
102
+
103
+ ```ruby
104
+ config.filters = [:user_agent_regxp => /\b(ZyBorg|Yandex|Jyxobot|Huaweisymantecspider|ApptusBot|TurnitinBot|DotBot)\b/i]
105
+ ```
106
+
107
+ If you (like me) dont know regular expressions by heart, then http://www.rubular.com/ is great tool to use when creating a regxp.
108
+
109
+ **:target_url_regxp**
110
+
111
+ Sometimes the bad bots add a common user agent string and a referer to their requests, which makes it hard to filter them without filtering all routing errors. I guess they do this to make it look less suspicious.
112
+ What you can often do is to filter on what they target, which is usually security holes in some well known library like phpMyAdmin.
113
+
114
+ ```ruby
115
+ config.filters = [:target_url_regxp => /\b(phpMyAdmin|joomla|wordpress)\b/i]
116
+ ```
117
+
118
+ ## Storage strategy - active record
119
+ ```ruby
120
+ config.storage_strategies = [:active_record]
121
+ ```
122
+ This means that the error reports will be stored through active record directly to a database, which is pretty much the whole reason why I created this library in the first place. A new entry called **exception_database** is needed in **database.yml**:
123
+
124
+ ```
125
+ # for mysql the entry would look something like this:
126
+ exception_database:
127
+ adapter: mysql2
128
+ encoding: utf8
129
+ reconnect: false
130
+ database: your_database
131
+ pool: 5
132
+ username: user
133
+ password: secret
134
+ host: 127.0.0.1
135
+ ```
136
+
137
+ You could of course store the error messages in the same database as the application uses, but one of the main purposes of this library is to enable you to easily store error reports from many applications in the same database, so I recommend you set up a separate dedicated database for this.
138
+
139
+ The exception database needs a table called **error_messages**. Here's a migration script that you can use to create the table with the necessary fields:
140
+
141
+ ```ruby
142
+ class CreateErrorMessages < ActiveRecord::Migration
143
+ def self.up
144
+ create_table :error_messages do |t|
145
+ t.text :class_name
146
+ t.text :message
147
+ t.text :trace
148
+ t.text :params
149
+ t.text :target_url
150
+ t.text :referer_url
151
+ t.text :user_agent
152
+ t.string :user_info
153
+ t.string :app_name
154
+
155
+ t.timestamps
156
+ end
157
+ end
158
+
159
+ def self.down
160
+ drop_table :error_messages
161
+ end
162
+ end
163
+ ```
164
+
165
+ ## Storage strategy - rails log
166
+
167
+ ```ruby
168
+ config.storage_strategies = [:rails_log]
169
+ ```
170
+
171
+ An error will be logged in the standard rails log. The log i located in the RAILS_ROOT/log directory and is named after the Rails environment.
172
+ Example of what a report might look like:
173
+
174
+ ```
175
+ TARGET: http://example.com/users
176
+ REFERER: http://example.com/
177
+ PARAMS: {"controller"=>"users", "action"=>"index"}
178
+ USER_AGENT: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.112 Safari/534.30
179
+ USER_INFO: matz
180
+ ActionView::Template::Error (ActionView::Template::Error):
181
+ activesupport (3.0.7) lib/active_support/whiny_nil.rb:48:in `method_missing'
182
+ actionpack (3.0.7) lib/action_view/template.rb:135:in `block in render'
183
+ activesupport (3.0.7) lib/active_support/notifications.rb:54:in `instrument'
184
+ (the rest of the stack trace has been omitted from the example)
185
+ ```
186
+
187
+ ## Storage strategy - remote url
188
+ ```ruby
189
+ config.storage_strategies = [:remote_url => {:target => 'http://example.com/error_messages'}]
190
+ ```
191
+ This option is meant for those who want to store the exception in a database table, but does not have direct access to the database itself, making active_record store unsuitable. You need a web app on a server that has access to the database. An HTTP POST request will be sent to the specified URL with the error message as data.
192
+ If you use a Rails app at the other end you should simply be able to do _ErrorMessage.create(params[:error_message])_ to save the report.
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ # encoding: utf-8
2
+ require 'rubygems'
3
+ require 'rake'
4
+
5
+ task :default => :test
6
+
7
+ desc 'Run tests'
8
+ task :test do
9
+ files = ["spec/unit/handler_spec.rb",
10
+ "spec/unit/parser_spec.rb",
11
+ "spec/unit/configuration_spec.rb",
12
+ "spec/integration/rails_exception_handler_spec.rb",
13
+ "spec/integration/configuration_spec.rb"
14
+ ]
15
+ system "bundle exec rspec #{files.join(' ')}"
16
+ end
17
+
18
+ require 'jeweler'
19
+ Jeweler::Tasks.new do |gem|
20
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
21
+ gem.name = "rails_exception_handler"
22
+ gem.homepage = "http://github.com/Sharagoz/rails_exception_handler"
23
+ gem.license = "MIT"
24
+ gem.summary = %Q{Exception Handling for Rails 3}
25
+ gem.description = %Q{}
26
+ gem.email = "contact@sharagoz.com"
27
+ gem.authors = ["Sharagoz"]
28
+ gem.extra_rdoc_files = ['README.markdown']
29
+ # dependencies defined in Gemfile
30
+ end
31
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,6 @@
1
+ class ErrorResponseController < ApplicationController
2
+ def index
3
+ render(:text => RailsExceptionHandler.configuration.responses[@_env['exception_handler.response_code']],
4
+ :layout => @_env['exception_handler.layout'])
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ class ErrorMessage < ActiveRecord::Base
2
+ establish_connection(:exception_database)
3
+ end
@@ -0,0 +1,14 @@
1
+ # This patch enables the exception handler to catch routing errors
2
+ module ActionDispatch
3
+ class ShowExceptions
4
+ private
5
+ def render_exception_with_template(env, exception)
6
+ if(RailsExceptionHandler.configuration.environments.include?(Rails.env.to_sym))
7
+ RailsExceptionHandler::Handler.new(env, exception).handle_exception
8
+ else
9
+ raise "RailsExceptionHandler: This patch should not have been loaded"
10
+ end
11
+ end
12
+ alias_method_chain :render_exception, :template
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ class RailsExceptionHandler::Configuration
2
+ attr_accessor :storage_strategies, :environments, :filters, :responses, :fallback_layout, :store_user_info
3
+
4
+ def initialize
5
+ @environments = [:production]
6
+ @storage_strategies = []
7
+ @filters = []
8
+ @store_user_info = false
9
+ @fallback_layout = 'application'
10
+ @responses = { '404' => '<h1>Page not found</h1><p>The page you were looking for could not be found.</p>',
11
+ '500' => '<h1>Internal server error</h1><p>The application has encountered an unexpected issue.</p>' }
12
+ end
13
+ end
@@ -0,0 +1,69 @@
1
+ class RailsExceptionHandler::Handler
2
+ def initialize(env, exception)
3
+ @exception = exception
4
+ @env = env
5
+ @request = ActionDispatch::Request.new(@env)
6
+ @parsed_error = nil
7
+ if(@env['action_controller.instance'])
8
+ @controller = @env['action_controller.instance']
9
+ else
10
+ # During routing errors a controller object must be created manually and the before filters run,
11
+ # in case they are used to set up the authentication mechanism that provides the current_user object
12
+ @controller = ApplicationController.new
13
+ @controller.request = @request
14
+ filters = @controller._process_action_callbacks.collect {|cb| cb.filter if(cb.kind == :before)}
15
+ filters.each { |filter| @controller.send(filter) }
16
+ end
17
+ end
18
+
19
+ def handle_exception
20
+ @parsed_error = RailsExceptionHandler::Parser.new(@exception, @request, @controller)
21
+ store_error unless(@parsed_error.ignore?)
22
+ return response
23
+ end
24
+
25
+ private
26
+
27
+ def store_error
28
+ RailsExceptionHandler.configuration.storage_strategies.each do |strategy|
29
+ if(strategy.class == Symbol)
30
+ send("store_in_#{strategy}")
31
+ elsif(strategy.class == Hash && strategy[:remote_url])
32
+ store_in_remote_url(strategy[:remote_url])
33
+ else
34
+ raise "RailsExceptionHandler: Unknown storage strategy #{strategy.inspect}"
35
+ end
36
+ end
37
+ end
38
+
39
+ def store_in_active_record
40
+ ErrorMessage.create(@parsed_error.relevant_info)
41
+ end
42
+
43
+ def store_in_rails_log
44
+ info = @parsed_error.relevant_info
45
+ message = "TARGET: #{info[:target_url]}\n"
46
+ message += "REFERER: #{info[:referer_url]}\n"
47
+ message += "PARAMS: #{info[:params]}\n"
48
+ message += "USER_AGENT: #{info[:user_agent]}\n"
49
+ message += "USER_INFO: #{info[:user_info]}\n"
50
+ message += "#{info[:class_name]} (#{info[:message]}):\n"
51
+ message += Rails.backtrace_cleaner.clean(info[:trace].split("\n"), :noise).join("\n")
52
+ Rails.logger.fatal(message)
53
+ end
54
+
55
+ def store_in_remote_url(args)
56
+ uri = URI.parse(args[:target])
57
+ params = {}
58
+ @parsed_error.relevant_info.each do |key,value|
59
+ params["error_message[#{key}]"] = value
60
+ end
61
+ Net::HTTP::post_form(uri, params)
62
+ end
63
+
64
+ def response
65
+ @env['exception_handler.layout'] = @controller.send(:_default_layout) || RailsExceptionHandler.configuration.fallback_layout
66
+ @env['exception_handler.response_code'] = @parsed_error.routing_error? ? '404' : '500'
67
+ ErrorResponseController.action(:index).call(@env)
68
+ end
69
+ end
@@ -0,0 +1,73 @@
1
+ class RailsExceptionHandler::Parser
2
+ def initialize(exception, request, controller)
3
+ @exception = exception
4
+ @request = request
5
+ @controller = controller
6
+ end
7
+
8
+ def relevant_info
9
+ info = {}
10
+ info[:app_name] = Rails.application.class.parent_name
11
+ info[:class_name] = @exception.class.to_s
12
+ info[:message] = @exception.to_s
13
+ info[:trace] = @exception.backtrace.join("\n")
14
+ info[:target_url] = @request.url
15
+ info[:referer_url] = @request.referer
16
+ info[:params] = @request.params.inspect
17
+ info[:user_agent] = @request.user_agent
18
+ info[:user_info] = user_info
19
+ info[:created_at] = Time.now
20
+ return info
21
+ end
22
+
23
+ def ignore?
24
+ filters = RailsExceptionHandler.configuration.filters
25
+ filters.each do |filter|
26
+ if(filter.class == Symbol)
27
+ result = send("filter_#{filter}")
28
+ elsif(filter.class == Hash)
29
+ result = send("filter_#{filter.flatten[0]}", filter.flatten[1])
30
+ else
31
+ raise "RailsExceptionHandler: Unknown filter #{filter.inspect}"
32
+ end
33
+ return true if(result)
34
+ end
35
+ return false
36
+ end
37
+
38
+ def routing_error?
39
+ routing_errors = [ActionController::RoutingError, AbstractController::ActionNotFound, ActiveRecord::RecordNotFound]
40
+ routing_errors.include?(@exception.class)
41
+ end
42
+
43
+ private
44
+
45
+ def blank_referer?
46
+ relevant_info[:referer_url] == "/" || relevant_info[:referer_url].blank?
47
+ end
48
+
49
+ def user_info
50
+ config = RailsExceptionHandler.configuration.store_user_info
51
+ return nil unless(config)
52
+ user_object = @controller.send(config[:method])
53
+ user_object ? user_object.send(config[:field]) : 'Anonymous'
54
+ end
55
+
56
+ def filter_all_404s
57
+ routing_error?
58
+ end
59
+
60
+ def filter_no_referer_404s
61
+ routing_error? && blank_referer?
62
+ end
63
+
64
+ def filter_user_agent_regxp(regxp)
65
+ result = relevant_info[:user_agent].match(regxp)
66
+ result != nil
67
+ end
68
+
69
+ def filter_target_url_regxp(regxp)
70
+ result = relevant_info[:target_url].match(regxp)
71
+ result != nil
72
+ end
73
+ end
@@ -0,0 +1,37 @@
1
+ class RailsExceptionHandler
2
+ def initialize(app)
3
+ @app = app
4
+ end
5
+
6
+ def call(env)
7
+ @app.call(env)
8
+ rescue Exception => e
9
+ Handler.new(env, e).handle_exception
10
+ end
11
+
12
+ def self.configuration
13
+ @configuration ||= Configuration.new
14
+ end
15
+
16
+ def self.configure
17
+ yield configuration
18
+ return unless configuration.environments.include?(Rails.env.to_sym)
19
+
20
+ Rails.configuration.middleware.use(RailsExceptionHandler)
21
+
22
+ Rails.configuration.action_dispatch.show_exceptions = true
23
+ require File.expand_path(File.dirname(__FILE__)) + '/patch/show_exceptions.rb'
24
+
25
+ %w{ models controllers }.each do |dir|
26
+ path = File.join(File.dirname(__FILE__), '../app', dir)
27
+ $LOAD_PATH << path
28
+ ActiveSupport::Dependencies.autoload_paths << path
29
+ ActiveSupport::Dependencies.autoload_once_paths.delete(path)
30
+ end
31
+ end
32
+ end
33
+
34
+ require 'rails_exception_handler/configuration.rb'
35
+ require 'rails_exception_handler/handler.rb'
36
+ require 'rails_exception_handler/parser.rb'
37
+ require 'net/http'
@@ -0,0 +1,87 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rails_exception_handler}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Sharagoz"]
12
+ s.date = %q{2011-07-17}
13
+ s.description = %q{}
14
+ s.email = %q{contact@sharagoz.com}
15
+ s.extra_rdoc_files = [
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".travis.yml",
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "LICENCE",
23
+ "README.markdown",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "app/controllers/error_response_controller.rb",
27
+ "app/models/error_message.rb",
28
+ "lib/patch/show_exceptions.rb",
29
+ "lib/rails_exception_handler.rb",
30
+ "lib/rails_exception_handler/configuration.rb",
31
+ "lib/rails_exception_handler/handler.rb",
32
+ "lib/rails_exception_handler/parser.rb",
33
+ "rails_exception_handler.gemspec",
34
+ "spec/integration/configuration_spec.rb",
35
+ "spec/integration/rails_exception_handler_spec.rb",
36
+ "spec/spec_helper.rb",
37
+ "spec/test_macros.rb",
38
+ "spec/testapp_30/.gitignore",
39
+ "spec/testapp_30/Gemfile",
40
+ "spec/testapp_30/Gemfile.lock",
41
+ "spec/testapp_30/Rakefile",
42
+ "spec/testapp_30/app/controllers/application_controller.rb",
43
+ "spec/testapp_30/app/controllers/home_controller.rb",
44
+ "spec/testapp_30/app/helpers/application_helper.rb",
45
+ "spec/testapp_30/app/models/stored_exception.rb",
46
+ "spec/testapp_30/app/views/home/view_error.html.erb",
47
+ "spec/testapp_30/app/views/layouts/fallback.html.erb",
48
+ "spec/testapp_30/app/views/layouts/home.html.erb",
49
+ "spec/testapp_30/config.ru",
50
+ "spec/testapp_30/config/application.rb",
51
+ "spec/testapp_30/config/boot.rb",
52
+ "spec/testapp_30/config/database.yml",
53
+ "spec/testapp_30/config/environment.rb",
54
+ "spec/testapp_30/config/environments/development.rb",
55
+ "spec/testapp_30/config/environments/production.rb",
56
+ "spec/testapp_30/config/environments/test.rb",
57
+ "spec/testapp_30/config/examples/database.yml",
58
+ "spec/testapp_30/config/locales/en.yml",
59
+ "spec/testapp_30/config/routes.rb",
60
+ "spec/testapp_30/db/migrate/20110630174538_create_error_messages.rb",
61
+ "spec/testapp_30/db/migrate/20110702131654_add_sessions_table.rb",
62
+ "spec/testapp_30/db/schema.rb",
63
+ "spec/testapp_30/db/seeds.rb",
64
+ "spec/testapp_30/lib/tasks/.gitkeep",
65
+ "spec/testapp_30/script/rails",
66
+ "spec/testapp_30/script/setup",
67
+ "spec/testapp_30/vendor/plugins/.gitkeep",
68
+ "spec/unit/configuration_spec.rb",
69
+ "spec/unit/handler_spec.rb",
70
+ "spec/unit/parser_spec.rb"
71
+ ]
72
+ s.homepage = %q{http://github.com/Sharagoz/rails_exception_handler}
73
+ s.licenses = ["MIT"]
74
+ s.require_paths = ["lib"]
75
+ s.rubygems_version = %q{1.6.2}
76
+ s.summary = %q{Exception Handling for Rails 3}
77
+
78
+ if s.respond_to? :specification_version then
79
+ s.specification_version = 3
80
+
81
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
82
+ else
83
+ end
84
+ else
85
+ end
86
+ end
87
+