exception_engine 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/GPLv3.txt +674 -0
  2. data/Gemfile +12 -0
  3. data/Gemfile.lock +114 -0
  4. data/LICENSE.txt +17 -0
  5. data/README.rdoc +41 -0
  6. data/Rakefile +29 -0
  7. data/lib/exception_engine.rb +49 -0
  8. data/lib/exception_engine/backtrace.rb +100 -0
  9. data/lib/exception_engine/engine.rb +5 -0
  10. data/lib/exception_engine/exception_middleware.rb +21 -0
  11. data/lib/exception_engine/notice.rb +336 -0
  12. data/lib/exception_engine/version.rb +3 -0
  13. data/test/dummy/Rakefile +7 -0
  14. data/test/dummy/app/controllers/application_controller.rb +3 -0
  15. data/test/dummy/app/controllers/posts_controller.rb +5 -0
  16. data/test/dummy/app/helpers/application_helper.rb +2 -0
  17. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  18. data/test/dummy/config.ru +4 -0
  19. data/test/dummy/config/application.rb +45 -0
  20. data/test/dummy/config/boot.rb +10 -0
  21. data/test/dummy/config/database.yml +22 -0
  22. data/test/dummy/config/environment.rb +5 -0
  23. data/test/dummy/config/environments/development.rb +26 -0
  24. data/test/dummy/config/environments/production.rb +49 -0
  25. data/test/dummy/config/environments/test.rb +35 -0
  26. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  27. data/test/dummy/config/initializers/inflections.rb +10 -0
  28. data/test/dummy/config/initializers/mime_types.rb +5 -0
  29. data/test/dummy/config/initializers/secret_token.rb +7 -0
  30. data/test/dummy/config/initializers/session_store.rb +8 -0
  31. data/test/dummy/config/locales/en.yml +5 -0
  32. data/test/dummy/config/routes.rb +60 -0
  33. data/test/dummy/db/development.sqlite3 +0 -0
  34. data/test/dummy/db/test.sqlite3 +0 -0
  35. data/test/dummy/log/development.log +462 -0
  36. data/test/dummy/log/production.log +0 -0
  37. data/test/dummy/log/server.log +0 -0
  38. data/test/dummy/log/test.log +547 -0
  39. data/test/dummy/public/404.html +26 -0
  40. data/test/dummy/public/422.html +26 -0
  41. data/test/dummy/public/500.html +26 -0
  42. data/test/dummy/public/favicon.ico +0 -0
  43. data/test/dummy/public/javascripts/application.js +2 -0
  44. data/test/dummy/public/javascripts/controls.js +965 -0
  45. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  46. data/test/dummy/public/javascripts/effects.js +1123 -0
  47. data/test/dummy/public/javascripts/prototype.js +6001 -0
  48. data/test/dummy/public/javascripts/rails.js +175 -0
  49. data/test/dummy/script/rails +6 -0
  50. data/test/exception_engine_test.rb +52 -0
  51. data/test/integration/navigation_test.rb +15 -0
  52. data/test/support/integration_case.rb +5 -0
  53. data/test/test_helper.rb +35 -0
  54. metadata +180 -0
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rails", "3.0.3"
4
+ gem "capybara", ">= 0.4.0"
5
+ gem "sqlite3-ruby", :require => "sqlite3"
6
+ gem "mongoid", "2.0.0.rc.5"
7
+ gem 'mongo', '1.1.5'
8
+ gem 'bson_ext', '1.2.0'
9
+
10
+ # To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)
11
+ # gem 'ruby-debug'
12
+ # gem 'ruby-debug19'
data/Gemfile.lock ADDED
@@ -0,0 +1,114 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.3)
6
+ actionpack (= 3.0.3)
7
+ mail (~> 2.2.9)
8
+ actionpack (3.0.3)
9
+ activemodel (= 3.0.3)
10
+ activesupport (= 3.0.3)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.4)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.13)
16
+ rack-test (~> 0.5.6)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.3)
19
+ activesupport (= 3.0.3)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.4)
22
+ activerecord (3.0.3)
23
+ activemodel (= 3.0.3)
24
+ activesupport (= 3.0.3)
25
+ arel (~> 2.0.2)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.3)
28
+ activemodel (= 3.0.3)
29
+ activesupport (= 3.0.3)
30
+ activesupport (3.0.3)
31
+ arel (2.0.7)
32
+ bson (1.2.0)
33
+ bson_ext (1.2.0)
34
+ builder (2.1.2)
35
+ capybara (0.4.1.1)
36
+ celerity (>= 0.7.9)
37
+ culerity (>= 0.2.4)
38
+ mime-types (>= 1.16)
39
+ nokogiri (>= 1.3.3)
40
+ rack (>= 1.0.0)
41
+ rack-test (>= 0.5.4)
42
+ selenium-webdriver (>= 0.0.27)
43
+ xpath (~> 0.1.3)
44
+ celerity (0.8.7)
45
+ childprocess (0.1.6)
46
+ ffi (~> 0.6.3)
47
+ culerity (0.2.15)
48
+ erubis (2.6.6)
49
+ abstract (>= 1.0.0)
50
+ ffi (0.6.3)
51
+ rake (>= 0.8.7)
52
+ i18n (0.5.0)
53
+ json_pure (1.5.0)
54
+ mail (2.2.14)
55
+ activesupport (>= 2.3.6)
56
+ i18n (>= 0.4.0)
57
+ mime-types (~> 1.16)
58
+ treetop (~> 1.4.8)
59
+ mime-types (1.16)
60
+ mongo (1.1.5)
61
+ bson (>= 1.1.5)
62
+ mongoid (2.0.0.rc.5)
63
+ activemodel (~> 3.0)
64
+ mongo (~> 1.1.5)
65
+ tzinfo (~> 0.3.22)
66
+ will_paginate (~> 3.0.pre)
67
+ nokogiri (1.4.4)
68
+ polyglot (0.3.1)
69
+ rack (1.2.1)
70
+ rack-mount (0.6.13)
71
+ rack (>= 1.0.0)
72
+ rack-test (0.5.7)
73
+ rack (>= 1.0)
74
+ rails (3.0.3)
75
+ actionmailer (= 3.0.3)
76
+ actionpack (= 3.0.3)
77
+ activerecord (= 3.0.3)
78
+ activeresource (= 3.0.3)
79
+ activesupport (= 3.0.3)
80
+ bundler (~> 1.0)
81
+ railties (= 3.0.3)
82
+ railties (3.0.3)
83
+ actionpack (= 3.0.3)
84
+ activesupport (= 3.0.3)
85
+ rake (>= 0.8.7)
86
+ thor (~> 0.14.4)
87
+ rake (0.8.7)
88
+ rubyzip (0.9.4)
89
+ selenium-webdriver (0.1.2)
90
+ childprocess (~> 0.1.5)
91
+ ffi (~> 0.6.3)
92
+ json_pure
93
+ rubyzip
94
+ sqlite3 (1.3.3)
95
+ sqlite3-ruby (1.3.3)
96
+ sqlite3 (>= 1.3.3)
97
+ thor (0.14.6)
98
+ treetop (1.4.9)
99
+ polyglot (>= 0.3.1)
100
+ tzinfo (0.3.24)
101
+ will_paginate (3.0.pre2)
102
+ xpath (0.1.3)
103
+ nokogiri (~> 1.3)
104
+
105
+ PLATFORMS
106
+ ruby
107
+
108
+ DEPENDENCIES
109
+ bson_ext (= 1.2.0)
110
+ capybara (>= 0.4.0)
111
+ mongo (= 1.1.5)
112
+ mongoid (= 2.0.0.rc.5)
113
+ rails (= 3.0.3)
114
+ sqlite3-ruby
data/LICENSE.txt ADDED
@@ -0,0 +1,17 @@
1
+ Engineworks
2
+ Copyright (C) 2011 Consoci8 Sdn Bhd
3
+
4
+ This file is part of Engineworks.
5
+
6
+ Engineworks is free software: you can redistribute it and/or modify
7
+ it under the terms of the GNU General Public License as published by
8
+ the Free Software Foundation, either version 3 of the License, or
9
+ (at your option) any later version.
10
+
11
+ Engineworks is distributed in the hope that it will be useful,
12
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ GNU General Public License for more details.
15
+
16
+ You should have received a copy of the GNU General Public License
17
+ along with Engineworks. If not, see <http://www.gnu.org/licenses/>.
data/README.rdoc ADDED
@@ -0,0 +1,41 @@
1
+ = ExceptionEngine
2
+ Copyright (C) 2011 Consoci8 Sdn Bhd
3
+
4
+ The Exception Engine logs your Rails exceptions in your mongodb database and provides a web interface to manage them
5
+
6
+ It assumes that the main rails application it's mounted to uses devise for authentication and you have mongodb
7
+ installed and running in your machine
8
+
9
+
10
+ To use this in your rails engine, add the following line to your gemfile:
11
+
12
+ gem "exception_engine", :git => 'git://github.com/Consoci8/exception_engine'
13
+
14
+ And then run
15
+
16
+ bundle install
17
+
18
+
19
+ Next, run your rails server and you can view all your application exceptions at http://localhost:3000/exceptions
20
+
21
+
22
+
23
+
24
+ For mongo installation:
25
+
26
+ http://www.mongodb.org/display/DOCS/Quickstart
27
+
28
+ After you installed mongodb, make sure you run the mongod server
29
+
30
+ $ mongod
31
+
32
+
33
+
34
+
35
+ This project uses GPLv3 License.
36
+ Check Gemfile for other dependencies.
37
+
38
+ Credits:
39
+
40
+ 1. José Valim for enginex
41
+ 2. Thoughtbot's HoptoadNotifier gem
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rake/rdoctask'
11
+
12
+ require 'rake/testtask'
13
+
14
+ Rake::TestTask.new(:test) do |t|
15
+ t.libs << 'lib'
16
+ t.libs << 'test'
17
+ t.pattern = 'test/**/*_test.rb'
18
+ t.verbose = false
19
+ end
20
+
21
+ task :default => :test
22
+
23
+ Rake::RDocTask.new(:rdoc) do |rdoc|
24
+ rdoc.rdoc_dir = 'rdoc'
25
+ rdoc.title = 'ExceptionEngine'
26
+ rdoc.options << '--line-numbers' << '--inline-source'
27
+ rdoc.rdoc_files.include('README.rdoc')
28
+ rdoc.rdoc_files.include('lib/**/*.rb')
29
+ end
@@ -0,0 +1,49 @@
1
+ require "exception_engine/engine"
2
+ require "exception_engine/exception_middleware"
3
+ require "exception_engine/backtrace"
4
+ require "exception_engine/notice"
5
+ require "mongoid"
6
+
7
+ # We are required to choose a database name
8
+ Mongoid.configure do |config|
9
+ name = "exception_engine-#{Rails.env}"
10
+ host = "localhost"
11
+ config.master = Mongo::Connection.new.db(name)
12
+ config.persist_in_safe_mode = false
13
+ end
14
+
15
+ module ExceptionEngine
16
+ class << self
17
+
18
+ # Stores the notice exception
19
+ # @see ExceptionEngine.exceptionize
20
+ # @params exception
21
+ def exceptionize(exception, opts = {})
22
+ notice = build_notice_for(exception, opts)
23
+ ExceptionEngine::Data.store!(notice)
24
+ end
25
+
26
+ private
27
+
28
+ def build_notice_for(exception, opts = {})
29
+ exception = unwrap_exception(exception)
30
+ if exception.respond_to?(:to_hash)
31
+ opts = opts.merge(exception.to_hash)
32
+ else
33
+ opts = opts.merge(:exception => exception)
34
+ end
35
+ Notice.new(opts)
36
+ end
37
+
38
+ def unwrap_exception(exception)
39
+ if exception.respond_to?(:original_exception)
40
+ exception.original_exception
41
+ elsif exception.respond_to?(:continued_exception)
42
+ exception.continued_exception
43
+ else
44
+ exception
45
+ end
46
+ end
47
+
48
+ end
49
+ end
@@ -0,0 +1,100 @@
1
+ # Credits to Thoughtbot's Hoptoad::Backtrace
2
+ module ExceptionEngine
3
+ # Front end to parsing the backtrace for each notice
4
+ class Backtrace
5
+
6
+ # Handles backtrace parsing line by line
7
+ class Line
8
+
9
+ INPUT_FORMAT = %r{^([^:]+):(\d+)(?::in `([^']+)')?$}.freeze
10
+
11
+ # The file portion of the line (such as app/models/user.rb)
12
+ attr_reader :file
13
+
14
+ # The line number portion of the line
15
+ attr_reader :number
16
+
17
+ # The method of the line (such as index)
18
+ attr_reader :method
19
+
20
+ # Parses a single line of a given backtrace
21
+ # @param [String] unparsed_line The raw line from +caller+ or some backtrace
22
+ # @return [Line] The parsed backtrace line
23
+ def self.parse(unparsed_line)
24
+ _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
25
+ new(file, number, method)
26
+ end
27
+
28
+ def initialize(file, number, method)
29
+ self.file = file
30
+ self.number = number
31
+ self.method = method
32
+ end
33
+
34
+ # Reconstructs the line in a readable fashion
35
+ def to_s
36
+ "#{file}:#{number}:in `#{method}'"
37
+ end
38
+
39
+ def ==(other)
40
+ to_s == other.to_s
41
+ end
42
+
43
+ def inspect
44
+ "<Line:#{to_s}>"
45
+ end
46
+
47
+ private
48
+
49
+ attr_writer :file, :number, :method
50
+ end
51
+
52
+ # holder for an Array of Backtrace::Line instances
53
+ attr_reader :lines
54
+
55
+ def self.parse(ruby_backtrace, opts = {})
56
+ ruby_lines = split_multiline_backtrace(ruby_backtrace)
57
+
58
+ filters = opts[:filters] || []
59
+ filtered_lines = ruby_lines.to_a.map do |line|
60
+ filters.inject(line) do |line, proc|
61
+ proc.call(line)
62
+ end
63
+ end.compact
64
+
65
+ lines = filtered_lines.collect do |unparsed_line|
66
+ Line.parse(unparsed_line)
67
+ end
68
+
69
+ instance = new(lines)
70
+ end
71
+
72
+ def initialize(lines)
73
+ self.lines = lines
74
+ end
75
+
76
+ def inspect
77
+ "<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
78
+ end
79
+
80
+ def ==(other)
81
+ if other.respond_to?(:lines)
82
+ lines == other.lines
83
+ else
84
+ false
85
+ end
86
+ end
87
+
88
+ private
89
+
90
+ attr_writer :lines
91
+
92
+ def self.split_multiline_backtrace(backtrace)
93
+ if backtrace.to_a.size == 1
94
+ backtrace.to_a.first.split(/\n\s*/)
95
+ else
96
+ backtrace
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,5 @@
1
+ module ExceptionEngine
2
+ class Engine < Rails::Engine
3
+ config.app_middleware.use "ExceptionEngine::ExceptionMiddleware"
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ module ExceptionEngine
2
+ class ExceptionMiddleware
3
+ def initialize(app)
4
+ @app = app
5
+ end
6
+
7
+ def call(env)
8
+ begin
9
+ response = @app.call(env)
10
+ rescue Exception => raised
11
+ ExceptionEngine.exceptionize(raised)
12
+ raise
13
+ end
14
+
15
+ if env['rack.exception']
16
+ ExceptionEngine.exceptionize(raised)
17
+ end
18
+ response
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,336 @@
1
+ require 'builder'
2
+
3
+ # Credits to Thoughtbot's Hoptoad::Notice
4
+ module ExceptionEngine
5
+ class Notice
6
+
7
+ # The exception that caused this notice, if any
8
+ attr_reader :exception
9
+
10
+ # The API key for the project to which this notice should be sent
11
+ attr_reader :api_key
12
+
13
+ # The backtrace from the given exception or hash.
14
+ attr_reader :backtrace
15
+
16
+ # The name of the class of error (such as RuntimeError)
17
+ attr_reader :error_class
18
+
19
+ # The name of the server environment (such as "production")
20
+ attr_reader :environment_name
21
+
22
+ # CGI variables such as HTTP_METHOD
23
+ attr_reader :cgi_data
24
+
25
+ # The message from the exception, or a general description of the error
26
+ attr_reader :error_message
27
+
28
+ # See Configuration#backtrace_filters
29
+ attr_reader :backtrace_filters
30
+
31
+ # See Configuration#params_filters
32
+ attr_reader :params_filters
33
+
34
+ # A hash of parameters from the query string or post body.
35
+ attr_reader :parameters
36
+ alias_method :params, :parameters
37
+
38
+ # The component (if any) which was used in this request (usually the controller)
39
+ attr_reader :component
40
+ alias_method :controller, :component
41
+
42
+ # The action (if any) that was called in this request
43
+ attr_reader :action
44
+
45
+ # A hash of session data from the request
46
+ attr_reader :session_data
47
+
48
+ # The path to the project that caused the error (usually RAILS_ROOT)
49
+ attr_reader :project_root
50
+
51
+ # The URL at which the error occurred (if any)
52
+ attr_reader :url
53
+
54
+ # See Configuration#ignore
55
+ attr_reader :ignore
56
+
57
+ # See Configuration#ignore_by_filters
58
+ attr_reader :ignore_by_filters
59
+
60
+ # The name of the notifier library sending this notice, such as "Hoptoad Notifier"
61
+ attr_reader :notifier_name
62
+
63
+ # The version number of the notifier library sending this notice, such as "2.1.3"
64
+ attr_reader :notifier_version
65
+
66
+ # A URL for more information about the notifier library sending this notice
67
+ attr_reader :notifier_url
68
+
69
+ def initialize(args)
70
+ self.args = args
71
+ self.exception = args[:exception]
72
+ self.api_key = args[:api_key]
73
+ self.project_root = args[:project_root]
74
+ self.url = args[:url] || rack_env(:url)
75
+
76
+ self.notifier_name = args[:notifier_name]
77
+ self.notifier_version = args[:notifier_version]
78
+ self.notifier_url = args[:notifier_url]
79
+
80
+ self.ignore = args[:ignore] || []
81
+ self.ignore_by_filters = args[:ignore_by_filters] || []
82
+ self.backtrace_filters = args[:backtrace_filters] || []
83
+ self.params_filters = args[:params_filters] || []
84
+ self.parameters = args[:parameters] ||
85
+ action_dispatch_params ||
86
+ rack_env(:params) ||
87
+ {}
88
+ self.component = args[:component] || args[:controller] || parameters['controller']
89
+ self.action = args[:action] || parameters['action']
90
+
91
+ self.environment_name = args[:environment_name]
92
+ self.cgi_data = args[:cgi_data] || args[:rack_env]
93
+ self.backtrace = Backtrace.parse(exception_attribute(:backtrace, caller), :filters => self.backtrace_filters)
94
+ self.error_class = exception_attribute(:error_class) {|exception| exception.class.name }
95
+ self.error_message = exception_attribute(:error_message, 'Notification') do |exception|
96
+ "#{exception.class.name}: #{exception.message}"
97
+ end
98
+
99
+ also_use_rack_params_filters
100
+ find_session_data
101
+ clean_params
102
+ clean_rack_request_data
103
+ end
104
+
105
+ # Converts the given notice to XML
106
+ def to_xml
107
+ builder = Builder::XmlMarkup.new
108
+ builder.instruct!
109
+ xml = builder.notice(:version => "1.0") do |notice|
110
+ notice.tag!("api-key", api_key)
111
+ notice.notifier do |notifier|
112
+ notifier.name(notifier_name)
113
+ notifier.version(notifier_version)
114
+ notifier.url(notifier_url)
115
+ end
116
+ notice.error do |error|
117
+ error.tag!('class', error_class)
118
+ error.message(error_message)
119
+ error.backtrace do |backtrace|
120
+ self.backtrace.lines.each do |line|
121
+ backtrace.line(:number => line.number,
122
+ :file => line.file,
123
+ :method => line.method)
124
+ end
125
+ end
126
+ end
127
+ if url ||
128
+ controller ||
129
+ action ||
130
+ !parameters.blank? ||
131
+ !cgi_data.blank? ||
132
+ !session_data.blank?
133
+ notice.request do |request|
134
+ request.url(url)
135
+ request.component(controller)
136
+ request.action(action)
137
+ unless parameters.nil? || parameters.empty?
138
+ request.params do |params|
139
+ xml_vars_for(params, parameters)
140
+ end
141
+ end
142
+ unless session_data.nil? || session_data.empty?
143
+ request.session do |session|
144
+ xml_vars_for(session, session_data)
145
+ end
146
+ end
147
+ unless cgi_data.nil? || cgi_data.empty?
148
+ request.tag!("cgi-data") do |cgi_datum|
149
+ xml_vars_for(cgi_datum, cgi_data)
150
+ end
151
+ end
152
+ end
153
+ end
154
+ notice.tag!("server-environment") do |env|
155
+ env.tag!("project-root", project_root)
156
+ env.tag!("environment-name", environment_name)
157
+ end
158
+ end
159
+ xml.to_s
160
+ end
161
+
162
+ # Determines if this notice should be ignored
163
+ def ignore?
164
+ ignored_class_names.include?(error_class) ||
165
+ ignore_by_filters.any? {|filter| filter.call(self) }
166
+ end
167
+
168
+ # Allows properties to be accessed using a hash-like syntax
169
+ #
170
+ # @example
171
+ # notice[:error_message]
172
+ # @param [String] method The given key for an attribute
173
+ # @return The attribute value, or self if given +:request+
174
+ def [](method)
175
+ case method
176
+ when :request
177
+ self
178
+ else
179
+ send(method)
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ attr_writer :exception, :api_key, :backtrace, :error_class, :error_message,
186
+ :backtrace_filters, :parameters, :params_filters,
187
+ :environment_filters, :session_data, :project_root, :url, :ignore,
188
+ :ignore_by_filters, :notifier_name, :notifier_url, :notifier_version,
189
+ :component, :action, :cgi_data, :environment_name
190
+
191
+ # Arguments given in the initializer
192
+ attr_accessor :args
193
+
194
+ # Gets a property named +attribute+ of an exception, either from an actual
195
+ # exception or a hash.
196
+ #
197
+ # If an exception is available, #from_exception will be used. Otherwise,
198
+ # a key named +attribute+ will be used from the #args.
199
+ #
200
+ # If no exception or hash key is available, +default+ will be used.
201
+ def exception_attribute(attribute, default = nil, &block)
202
+ (exception && from_exception(attribute, &block)) || args[attribute] || default
203
+ end
204
+
205
+ # Gets a property named +attribute+ from an exception.
206
+ #
207
+ # If a block is given, it will be used when getting the property from an
208
+ # exception. The block should accept and exception and return the value for
209
+ # the property.
210
+ #
211
+ # If no block is given, a method with the same name as +attribute+ will be
212
+ # invoked for the value.
213
+ def from_exception(attribute)
214
+ if block_given?
215
+ yield(exception)
216
+ else
217
+ exception.send(attribute)
218
+ end
219
+ end
220
+
221
+ # Removes non-serializable data from the given attribute.
222
+ # See #clean_unserializable_data
223
+ def clean_unserializable_data_from(attribute)
224
+ self.send(:"#{attribute}=", clean_unserializable_data(send(attribute)))
225
+ end
226
+
227
+ # Removes non-serializable data. Allowed data types are strings, arrays,
228
+ # and hashes. All other types are converted to strings.
229
+ # TODO: move this onto Hash
230
+ def clean_unserializable_data(data)
231
+ if data.respond_to?(:to_hash)
232
+ data.to_hash.inject({}) do |result, (key, value)|
233
+ result.merge(key => clean_unserializable_data(value))
234
+ end
235
+ elsif data.respond_to?(:to_ary)
236
+ data.collect do |value|
237
+ clean_unserializable_data(value)
238
+ end
239
+ else
240
+ data.to_s
241
+ end
242
+ end
243
+
244
+ # Replaces the contents of params that match params_filters.
245
+ # TODO: extract this to a different class
246
+ def clean_params
247
+ clean_unserializable_data_from(:parameters)
248
+ filter(parameters)
249
+ if cgi_data
250
+ clean_unserializable_data_from(:cgi_data)
251
+ filter(cgi_data)
252
+ end
253
+ if session_data
254
+ clean_unserializable_data_from(:session_data)
255
+ filter(session_data)
256
+ end
257
+ end
258
+
259
+ def clean_rack_request_data
260
+ if cgi_data
261
+ cgi_data.delete("rack.request.form_vars")
262
+ end
263
+ end
264
+
265
+ def filter(hash)
266
+ if params_filters
267
+ hash.each do |key, value|
268
+ if filter_key?(key)
269
+ hash[key] = "[FILTERED]"
270
+ elsif value.respond_to?(:to_hash)
271
+ filter(hash[key])
272
+ end
273
+ end
274
+ end
275
+ end
276
+
277
+ def filter_key?(key)
278
+ params_filters.any? do |filter|
279
+ key.to_s.include?(filter.to_s)
280
+ end
281
+ end
282
+
283
+ def find_session_data
284
+ self.session_data = args[:session_data] || args[:session] || rack_session || {}
285
+ self.session_data = session_data[:data] if session_data[:data]
286
+ end
287
+
288
+ # Converts the mixed class instances and class names into just names
289
+ # TODO: move this into Configuration or another class
290
+ def ignored_class_names
291
+ ignore.collect do |string_or_class|
292
+ if string_or_class.respond_to?(:name)
293
+ string_or_class.name
294
+ else
295
+ string_or_class
296
+ end
297
+ end
298
+ end
299
+
300
+ def xml_vars_for(builder, hash)
301
+ hash.each do |key, value|
302
+ if value.respond_to?(:to_hash)
303
+ builder.var(:key => key){|b| xml_vars_for(b, value.to_hash) }
304
+ else
305
+ builder.var(value.to_s, :key => key)
306
+ end
307
+ end
308
+ end
309
+
310
+ def rack_env(method)
311
+ rack_request.send(method) if rack_request
312
+ end
313
+
314
+ def rack_request
315
+ @rack_request ||= if args[:rack_env]
316
+ ::Rack::Request.new(args[:rack_env])
317
+ end
318
+ end
319
+
320
+ def action_dispatch_params
321
+ args[:rack_env]['action_dispatch.request.parameters'] if args[:rack_env]
322
+ end
323
+
324
+ def rack_session
325
+ args[:rack_env]['rack.session'] if args[:rack_env]
326
+ end
327
+
328
+ def also_use_rack_params_filters
329
+ if args[:rack_env]
330
+ @params_filters ||= []
331
+ @params_filters += rack_request.env["action_dispatch.parameter_filter"] || []
332
+ end
333
+ end
334
+
335
+ end
336
+ end