exception_engine 0.2.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.
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