cloudtrapper 0.0.2.pre

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 (81) hide show
  1. data/CHANGELOG +823 -0
  2. data/Gemfile +12 -0
  3. data/Guardfile +6 -0
  4. data/INSTALL +20 -0
  5. data/MIT-LICENSE +22 -0
  6. data/README.md +465 -0
  7. data/README_FOR_HEROKU_ADDON.md +94 -0
  8. data/Rakefile +223 -0
  9. data/SUPPORTED_RAILS_VERSIONS +23 -0
  10. data/TESTING.md +33 -0
  11. data/cloudtrapper.gemspec +35 -0
  12. data/features/metal.feature +18 -0
  13. data/features/rack.feature +56 -0
  14. data/features/rails.feature +211 -0
  15. data/features/rails_with_js_notifier.feature +97 -0
  16. data/features/rake.feature +27 -0
  17. data/features/sinatra.feature +29 -0
  18. data/features/step_definitions/file_steps.rb +10 -0
  19. data/features/step_definitions/metal_steps.rb +23 -0
  20. data/features/step_definitions/rack_steps.rb +23 -0
  21. data/features/step_definitions/rails_application_steps.rb +433 -0
  22. data/features/step_definitions/rake_steps.rb +17 -0
  23. data/features/support/airbrake_shim.rb.template +11 -0
  24. data/features/support/env.rb +18 -0
  25. data/features/support/matchers.rb +35 -0
  26. data/features/support/rails.rb +201 -0
  27. data/features/support/rake/Rakefile +68 -0
  28. data/features/support/terminal.rb +107 -0
  29. data/features/user_informer.feature +63 -0
  30. data/generators/cloudtrapper/airbrake_generator.rb +94 -0
  31. data/generators/cloudtrapper/lib/insert_commands.rb +34 -0
  32. data/generators/cloudtrapper/lib/rake_commands.rb +24 -0
  33. data/generators/cloudtrapper/templates/capistrano_hook.rb +6 -0
  34. data/generators/cloudtrapper/templates/cloudtrapper_tasks.rake +25 -0
  35. data/generators/cloudtrapper/templates/initializer.rb +6 -0
  36. data/install.rb +1 -0
  37. data/lib/cloudtrapper/backtrace.rb +100 -0
  38. data/lib/cloudtrapper/capistrano.rb +44 -0
  39. data/lib/cloudtrapper/configuration.rb +281 -0
  40. data/lib/cloudtrapper/notice.rb +348 -0
  41. data/lib/cloudtrapper/rack.rb +55 -0
  42. data/lib/cloudtrapper/rails/action_controller_catcher.rb +30 -0
  43. data/lib/cloudtrapper/rails/controller_methods.rb +74 -0
  44. data/lib/cloudtrapper/rails/error_lookup.rb +33 -0
  45. data/lib/cloudtrapper/rails/javascript_notifier.rb +48 -0
  46. data/lib/cloudtrapper/rails/middleware/exceptions_catcher.rb +29 -0
  47. data/lib/cloudtrapper/rails.rb +40 -0
  48. data/lib/cloudtrapper/rails3_tasks.rb +85 -0
  49. data/lib/cloudtrapper/railtie.rb +48 -0
  50. data/lib/cloudtrapper/rake_handler.rb +66 -0
  51. data/lib/cloudtrapper/sender.rb +116 -0
  52. data/lib/cloudtrapper/shared_tasks.rb +36 -0
  53. data/lib/cloudtrapper/tasks.rb +83 -0
  54. data/lib/cloudtrapper/user_informer.rb +27 -0
  55. data/lib/cloudtrapper/version.rb +3 -0
  56. data/lib/cloudtrapper.rb +155 -0
  57. data/lib/cloudtrapper_tasks.rb +65 -0
  58. data/lib/rails/generators/cloudtrapper/cloudtrapper_generator.rb +100 -0
  59. data/lib/templates/javascript_notifier.erb +15 -0
  60. data/lib/templates/rescue.erb +91 -0
  61. data/rails/init.rb +1 -0
  62. data/resources/README.md +34 -0
  63. data/resources/ca-bundle.crt +3376 -0
  64. data/script/integration_test.rb +38 -0
  65. data/test/backtrace_test.rb +162 -0
  66. data/test/capistrano_test.rb +34 -0
  67. data/test/catcher_test.rb +333 -0
  68. data/test/cloudtrapper_2_2.xsd +78 -0
  69. data/test/cloudtrapper_tasks_test.rb +170 -0
  70. data/test/configuration_test.rb +221 -0
  71. data/test/helper.rb +263 -0
  72. data/test/javascript_notifier_test.rb +52 -0
  73. data/test/logger_test.rb +73 -0
  74. data/test/notice_test.rb +468 -0
  75. data/test/notifier_test.rb +246 -0
  76. data/test/rack_test.rb +58 -0
  77. data/test/rails_initializer_test.rb +36 -0
  78. data/test/recursion_test.rb +10 -0
  79. data/test/sender_test.rb +261 -0
  80. data/test/user_informer_test.rb +29 -0
  81. metadata +301 -0
@@ -0,0 +1,348 @@
1
+ require 'builder'
2
+ require 'socket'
3
+
4
+ module Cloudtrapper
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 "Cloudtrapper 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
+ # The host name where this error occurred (if any)
70
+ attr_reader :hostname
71
+
72
+ def initialize(args)
73
+ self.args = args
74
+ self.exception = args[:exception]
75
+ self.api_key = args[:api_key]
76
+ self.project_root = args[:project_root]
77
+ self.url = args[:url] || rack_env(:url)
78
+
79
+ self.notifier_name = args[:notifier_name]
80
+ self.notifier_version = args[:notifier_version]
81
+ self.notifier_url = args[:notifier_url]
82
+
83
+ self.ignore = args[:ignore] || []
84
+ self.ignore_by_filters = args[:ignore_by_filters] || []
85
+ self.backtrace_filters = args[:backtrace_filters] || []
86
+ self.params_filters = args[:params_filters] || []
87
+ self.parameters = args[:parameters] ||
88
+ action_dispatch_params ||
89
+ rack_env(:params) ||
90
+ {}
91
+ self.component = args[:component] || args[:controller] || parameters['controller']
92
+ self.action = args[:action] || parameters['action']
93
+
94
+ self.environment_name = args[:environment_name]
95
+ self.cgi_data = args[:cgi_data] || args[:rack_env]
96
+ self.backtrace = Backtrace.parse(exception_attribute(:backtrace, caller), :filters => self.backtrace_filters)
97
+ self.error_class = exception_attribute(:error_class) {|exception| exception.class.name }
98
+ self.error_message = exception_attribute(:error_message, 'Notification') do |exception|
99
+ "#{exception.class.name}: #{exception.message}"
100
+ end
101
+
102
+ self.hostname = local_hostname
103
+
104
+ also_use_rack_params_filters
105
+ find_session_data
106
+ clean_params
107
+ clean_rack_request_data
108
+ end
109
+
110
+ # Converts the given notice to XML
111
+ def to_xml
112
+ builder = Builder::XmlMarkup.new
113
+ builder.instruct!
114
+ xml = builder.notice(:version => Cloudtrapper::API_VERSION) do |notice|
115
+ notice.tag!("api-key", api_key)
116
+ notice.notifier do |notifier|
117
+ notifier.name(notifier_name)
118
+ notifier.version(notifier_version)
119
+ notifier.url(notifier_url)
120
+ end
121
+ notice.error do |error|
122
+ error.tag!('class', error_class)
123
+ error.message(error_message)
124
+ error.backtrace do |backtrace|
125
+ self.backtrace.lines.each do |line|
126
+ backtrace.line(:number => line.number,
127
+ :file => line.file,
128
+ :method => line.method)
129
+ end
130
+ end
131
+ end
132
+ if url ||
133
+ controller ||
134
+ action ||
135
+ !parameters.blank? ||
136
+ !cgi_data.blank? ||
137
+ !session_data.blank?
138
+ notice.request do |request|
139
+ request.url(url)
140
+ request.component(controller)
141
+ request.action(action)
142
+ unless parameters.nil? || parameters.empty?
143
+ request.params do |params|
144
+ xml_vars_for(params, parameters)
145
+ end
146
+ end
147
+ unless session_data.nil? || session_data.empty?
148
+ request.session do |session|
149
+ xml_vars_for(session, session_data)
150
+ end
151
+ end
152
+ unless cgi_data.nil? || cgi_data.empty?
153
+ request.tag!("cgi-data") do |cgi_datum|
154
+ xml_vars_for(cgi_datum, cgi_data)
155
+ end
156
+ end
157
+ end
158
+ end
159
+ notice.tag!("server-environment") do |env|
160
+ env.tag!("project-root", project_root)
161
+ env.tag!("environment-name", environment_name)
162
+ env.tag!("hostname", hostname)
163
+ end
164
+ end
165
+ xml.to_s
166
+ end
167
+
168
+ # Determines if this notice should be ignored
169
+ def ignore?
170
+ ignored_class_names.include?(error_class) ||
171
+ ignore_by_filters.any? {|filter| filter.call(self) }
172
+ end
173
+
174
+ # Allows properties to be accessed using a hash-like syntax
175
+ #
176
+ # @example
177
+ # notice[:error_message]
178
+ # @param [String] method The given key for an attribute
179
+ # @return The attribute value, or self if given +:request+
180
+ def [](method)
181
+ case method
182
+ when :request
183
+ self
184
+ else
185
+ send(method)
186
+ end
187
+ end
188
+
189
+ private
190
+
191
+ attr_writer :exception, :api_key, :backtrace, :error_class, :error_message,
192
+ :backtrace_filters, :parameters, :params_filters,
193
+ :environment_filters, :session_data, :project_root, :url, :ignore,
194
+ :ignore_by_filters, :notifier_name, :notifier_url, :notifier_version,
195
+ :component, :action, :cgi_data, :environment_name, :hostname
196
+
197
+ # Arguments given in the initializer
198
+ attr_accessor :args
199
+
200
+ # Gets a property named +attribute+ of an exception, either from an actual
201
+ # exception or a hash.
202
+ #
203
+ # If an exception is available, #from_exception will be used. Otherwise,
204
+ # a key named +attribute+ will be used from the #args.
205
+ #
206
+ # If no exception or hash key is available, +default+ will be used.
207
+ def exception_attribute(attribute, default = nil, &block)
208
+ (exception && from_exception(attribute, &block)) || args[attribute] || default
209
+ end
210
+
211
+ # Gets a property named +attribute+ from an exception.
212
+ #
213
+ # If a block is given, it will be used when getting the property from an
214
+ # exception. The block should accept and exception and return the value for
215
+ # the property.
216
+ #
217
+ # If no block is given, a method with the same name as +attribute+ will be
218
+ # invoked for the value.
219
+ def from_exception(attribute)
220
+ if block_given?
221
+ yield(exception)
222
+ else
223
+ exception.send(attribute)
224
+ end
225
+ end
226
+
227
+ # Removes non-serializable data from the given attribute.
228
+ # See #clean_unserializable_data
229
+ def clean_unserializable_data_from(attribute)
230
+ self.send(:"#{attribute}=", clean_unserializable_data(send(attribute)))
231
+ end
232
+
233
+ # Removes non-serializable data. Allowed data types are strings, arrays,
234
+ # and hashes. All other types are converted to strings.
235
+ # TODO: move this onto Hash
236
+ def clean_unserializable_data(data, stack = [])
237
+ return "[possible infinite recursion halted]" if stack.any?{|item| item == data.object_id }
238
+
239
+ if data.respond_to?(:to_hash)
240
+ data.to_hash.inject({}) do |result, (key, value)|
241
+ result.merge(key => clean_unserializable_data(value, stack + [data.object_id]))
242
+ end
243
+ elsif data.respond_to?(:to_ary)
244
+ data.to_ary.collect do |value|
245
+ clean_unserializable_data(value, stack + [data.object_id])
246
+ end
247
+ else
248
+ data.to_s
249
+ end
250
+ end
251
+
252
+ # Replaces the contents of params that match params_filters.
253
+ # TODO: extract this to a different class
254
+ def clean_params
255
+ clean_unserializable_data_from(:parameters)
256
+ filter(parameters)
257
+ if cgi_data
258
+ clean_unserializable_data_from(:cgi_data)
259
+ filter(cgi_data)
260
+ end
261
+ if session_data
262
+ clean_unserializable_data_from(:session_data)
263
+ filter(session_data)
264
+ end
265
+ end
266
+
267
+ def clean_rack_request_data
268
+ if cgi_data
269
+ cgi_data.delete("rack.request.form_vars")
270
+ end
271
+ end
272
+
273
+ def filter(hash)
274
+ if params_filters
275
+ hash.each do |key, value|
276
+ if filter_key?(key)
277
+ hash[key] = "[FILTERED]"
278
+ elsif value.respond_to?(:to_hash)
279
+ filter(hash[key])
280
+ end
281
+ end
282
+ end
283
+ end
284
+
285
+ def filter_key?(key)
286
+ params_filters.any? do |filter|
287
+ key.to_s.eql?(filter.to_s)
288
+ end
289
+ end
290
+
291
+ def find_session_data
292
+ self.session_data = args[:session_data] || args[:session] || rack_session || {}
293
+ self.session_data = session_data[:data] if session_data[:data]
294
+ end
295
+
296
+ # Converts the mixed class instances and class names into just names
297
+ # TODO: move this into Configuration or another class
298
+ def ignored_class_names
299
+ ignore.collect do |string_or_class|
300
+ if string_or_class.respond_to?(:name)
301
+ string_or_class.name
302
+ else
303
+ string_or_class
304
+ end
305
+ end
306
+ end
307
+
308
+ def xml_vars_for(builder, hash)
309
+ hash.each do |key, value|
310
+ if value.respond_to?(:to_hash)
311
+ builder.var(:key => key){|b| xml_vars_for(b, value.to_hash) }
312
+ else
313
+ builder.var(value.to_s, :key => key)
314
+ end
315
+ end
316
+ end
317
+
318
+ def rack_env(method)
319
+ rack_request.send(method) if rack_request
320
+ end
321
+
322
+ def rack_request
323
+ @rack_request ||= if args[:rack_env]
324
+ ::Rack::Request.new(args[:rack_env])
325
+ end
326
+ end
327
+
328
+ def action_dispatch_params
329
+ args[:rack_env]['action_dispatch.request.parameters'] if args[:rack_env]
330
+ end
331
+
332
+ def rack_session
333
+ args[:rack_env]['rack.session'] if args[:rack_env]
334
+ end
335
+
336
+ def also_use_rack_params_filters
337
+ if args[:rack_env]
338
+ @params_filters ||= []
339
+ @params_filters += rack_request.env["action_dispatch.parameter_filter"] || []
340
+ end
341
+ end
342
+
343
+ def local_hostname
344
+ Socket.gethostname
345
+ end
346
+
347
+ end
348
+ end
@@ -0,0 +1,55 @@
1
+ module Cloudtrapper
2
+ # Middleware for Rack applications. Any errors raised by the upstream
3
+ # application will be delivered to Cloudtrapper and re-raised.
4
+ #
5
+ # Synopsis:
6
+ #
7
+ # require 'rack'
8
+ # require 'cloudtrapper'
9
+ #
10
+ # Cloudtrapper.configure do |config|
11
+ # config.api_key = 'my_api_key'
12
+ # end
13
+ #
14
+ # app = Rack::Builder.app do
15
+ # run lambda { |env| raise "Rack down" }
16
+ # end
17
+ #
18
+ # use Cloudtrapper::Rack
19
+ # run app
20
+ #
21
+ # Use a standard Cloudtrapper.configure call to configure your api key.
22
+ class Rack
23
+ def initialize(app)
24
+ @app = app
25
+ Cloudtrapper.configuration.logger ||= Logger.new STDOUT
26
+ end
27
+
28
+ def ignored_user_agent?(env)
29
+ true if Cloudtrapper.
30
+ configuration.
31
+ ignore_user_agent.
32
+ flatten.
33
+ any? { |ua| ua === env['HTTP_USER_AGENT'] }
34
+ end
35
+
36
+ def notify_cloudtrapper(exception,env)
37
+ Cloudtrapper.notify_or_ignore(exception,:rack_env => env) unless ignored_user_agent?(env)
38
+ end
39
+
40
+ def call(env)
41
+ begin
42
+ response = @app.call(env)
43
+ rescue Exception => raised
44
+ env['cloudtrapper.error_id'] = notify_cloudtrapper(raised,env)
45
+ raise
46
+ end
47
+
48
+ if env['rack.exception']
49
+ env['cloudtrapper.error_id'] = notify_cloudtrapper(env['rack.exception'],env)
50
+ end
51
+
52
+ response
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,30 @@
1
+ module Cloudtrapper
2
+ module Rails
3
+ module ActionControllerCatcher
4
+
5
+ # Sets up an alias chain to catch exceptions when Rails does
6
+ def self.included(base) #:nodoc:
7
+ base.send(:alias_method, :rescue_action_in_public_without_cloudtrapper, :rescue_action_in_public)
8
+ base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_cloudtrapper)
9
+ end
10
+
11
+ private
12
+
13
+ # Overrides the rescue_action method in ActionController::Base, but does not inhibit
14
+ # any custom processing that is defined with Rails 2's exception helpers.
15
+ def rescue_action_in_public_with_cloudtrapper(exception)
16
+ unless cloudtrapper_ignore_user_agent?
17
+ error_id = Cloudtrapper.notify_or_ignore(exception, cloudtrapper_request_data)
18
+ request.env['cloudtrapper.error_id'] = error_id
19
+ end
20
+ rescue_action_in_public_without_cloudtrapper(exception)
21
+ end
22
+
23
+ def cloudtrapper_ignore_user_agent? #:nodoc:
24
+ # Rails 1.2.6 doesn't have request.user_agent, so check for it here
25
+ user_agent = request.respond_to?(:user_agent) ? request.user_agent : request.env["HTTP_USER_AGENT"]
26
+ Cloudtrapper.configuration.ignore_user_agent.flatten.any? { |ua| ua === user_agent }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,74 @@
1
+ module Cloudtrapper
2
+ module Rails
3
+ module ControllerMethods
4
+
5
+ def cloudtrapper_request_data
6
+ { :parameters => cloudtrapper_filter_if_filtering(params.to_hash),
7
+ :session_data => cloudtrapper_filter_if_filtering(cloudtrapper_session_data),
8
+ :controller => params[:controller],
9
+ :action => params[:action],
10
+ :url => cloudtrapper_request_url,
11
+ :cgi_data => cloudtrapper_filter_if_filtering(request.env) }
12
+ end
13
+
14
+ private
15
+
16
+ # This method should be used for sending manual notifications while you are still
17
+ # inside the controller. Otherwise it works like Cloudtrapper.notify.
18
+ def notify_cloudtrapper(hash_or_exception)
19
+ unless cloudtrapper_local_request?
20
+ Cloudtrapper.notify(hash_or_exception, cloudtrapper_request_data)
21
+ end
22
+ end
23
+
24
+ def cloudtrapper_local_request?
25
+ if defined?(::Rails.application.config)
26
+ ::Rails.application.config.consider_all_requests_local || (request.local? && (!request.env["HTTP_X_FORWARDED_FOR"]))
27
+ else
28
+ consider_all_requests_local || (local_request? && (!request.env["HTTP_X_FORWARDED_FOR"]))
29
+ end
30
+ end
31
+
32
+ def cloudtrapper_ignore_user_agent? #:nodoc:
33
+ # Rails 1.2.6 doesn't have request.user_agent, so check for it here
34
+ user_agent = request.respond_to?(:user_agent) ? request.user_agent : request.env["HTTP_USER_AGENT"]
35
+ Cloudtrapper.configuration.ignore_user_agent.flatten.any? { |ua| ua === user_agent }
36
+ end
37
+
38
+
39
+ def cloudtrapper_filter_if_filtering(hash)
40
+ return hash if ! hash.is_a?(Hash)
41
+
42
+
43
+ if respond_to?(:filter_parameters) # Rails 2
44
+ filter_parameters(hash)
45
+ elsif defined?(ActionDispatch::Http::ParameterFilter) # Rails 3
46
+ ActionDispatch::Http::ParameterFilter.new(::Rails.application.config.filter_parameters).filter(hash)
47
+ else
48
+ hash
49
+ end rescue hash
50
+
51
+ end
52
+
53
+ def cloudtrapper_session_data
54
+ if session.respond_to?(:to_hash)
55
+ session.to_hash
56
+ else
57
+ session.data
58
+ end
59
+ end
60
+
61
+ def cloudtrapper_request_url
62
+ url = "#{request.protocol}#{request.host}"
63
+
64
+ unless [80, 443].include?(request.port)
65
+ url << ":#{request.port}"
66
+ end
67
+
68
+ url << request.fullpath
69
+ url
70
+ end
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,33 @@
1
+ module Cloudtrapper
2
+ module Rails
3
+ module ErrorLookup
4
+
5
+ # Sets up an alias chain to catch exceptions when Rails does
6
+ def self.included(base) #:nodoc:
7
+ base.send(:alias_method, :rescue_action_locally_without_cloudtrapper, :rescue_action_locally)
8
+ base.send(:alias_method, :rescue_action_locally, :rescue_action_locally_with_cloudtrapper)
9
+ end
10
+
11
+ private
12
+
13
+ def rescue_action_locally_with_cloudtrapper(exception)
14
+ result = rescue_action_locally_without_cloudtrapper(exception)
15
+
16
+ if Cloudtrapper.configuration.development_lookup
17
+ path = File.join(File.dirname(__FILE__), '..', '..', 'templates', 'rescue.erb')
18
+ notice = Cloudtrapper.build_lookup_hash_for(exception, cloudtrapper_request_data)
19
+
20
+ result << @template.render(
21
+ :file => path,
22
+ :use_full_path => false,
23
+ :locals => { :host => Cloudtrapper.configuration.host,
24
+ :api_key => Cloudtrapper.configuration.api_key,
25
+ :notice => notice })
26
+ end
27
+
28
+ result
29
+ end
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,48 @@
1
+ module Cloudtrapper
2
+ module Rails
3
+ module JavascriptNotifier
4
+ def self.included(base) #:nodoc:
5
+ base.send :helper_method, :cloudtrapper_javascript_notifier
6
+ end
7
+
8
+ private
9
+
10
+ def cloudtrapper_javascript_notifier
11
+ return unless Cloudtrapper.configuration.public?
12
+
13
+ path = File.join File.dirname(__FILE__), '..', '..', 'templates', 'javascript_notifier.erb'
14
+ host = Cloudtrapper.configuration.host.dup
15
+ port = Cloudtrapper.configuration.port
16
+ host << ":#{port}" unless [80, 443].include?(port)
17
+
18
+ options = {
19
+ :file => path,
20
+ :layout => false,
21
+ :use_full_path => false,
22
+ :locals => {
23
+ :host => host,
24
+ :api_key => Cloudtrapper.configuration.js_api_key,
25
+ :environment => Cloudtrapper.configuration.environment_name,
26
+ :action_name => action_name,
27
+ :controller_name => controller_name,
28
+ :url => request.url
29
+ }
30
+ }
31
+
32
+ res = if @template
33
+ @template.render(options)
34
+ else
35
+ render_to_string(options)
36
+ end
37
+
38
+ if res.respond_to?(:html_safe)
39
+ res.html_safe
40
+ else
41
+ res
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,29 @@
1
+ module Cloudtrapper
2
+ module Rails
3
+ module Middleware
4
+ module ExceptionsCatcher
5
+ def self.included(base)
6
+ base.send(:alias_method_chain,:render_exception,:cloudtrapper)
7
+ end
8
+
9
+ def skip_user_agent?(env)
10
+ user_agent = env["HTTP_USER_AGENT"]
11
+ ::Cloudtrapper.configuration.ignore_user_agent.flatten.any? { |ua| ua === user_agent }
12
+ rescue
13
+ false
14
+ end
15
+
16
+ def render_exception_with_cloudtrapper(env,exception)
17
+ controller = env['action_controller.instance']
18
+ env['cloudtrapper.error_id'] = Cloudtrapper.
19
+ notify_or_ignore(exception,
20
+ controller.try(:cloudtrapper_request_data) || :rack_env => env) unless skip_user_agent?(env)
21
+ if defined?(controller.rescue_action_in_public_without_cloudtrapper)
22
+ controller.rescue_action_in_public_without_cloudtrapper(exception)
23
+ end
24
+ render_exception_without_cloudtrapper(env,exception)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ require 'cloudtrapper'
2
+ require 'cloudtrapper/rails/controller_methods'
3
+ require 'cloudtrapper/rails/action_controller_catcher'
4
+ require 'cloudtrapper/rails/error_lookup'
5
+ require 'cloudtrapper/rails/javascript_notifier'
6
+
7
+ module Cloudtrapper
8
+ module Rails
9
+ def self.initialize
10
+ if defined?(ActionController::Base)
11
+ ActionController::Base.send(:include, Cloudtrapper::Rails::ActionControllerCatcher)
12
+ ActionController::Base.send(:include, Cloudtrapper::Rails::ErrorLookup)
13
+ ActionController::Base.send(:include, Cloudtrapper::Rails::ControllerMethods)
14
+ ActionController::Base.send(:include, Cloudtrapper::Rails::JavascriptNotifier)
15
+ end
16
+
17
+ rails_logger = if defined?(::Rails.logger)
18
+ ::Rails.logger
19
+ elsif defined?(RAILS_DEFAULT_LOGGER)
20
+ RAILS_DEFAULT_LOGGER
21
+ end
22
+
23
+ if defined?(::Rails.configuration) && ::Rails.configuration.respond_to?(:middleware)
24
+ ::Rails.configuration.middleware.insert_after 'ActionController::Failsafe',
25
+ Cloudtrapper::Rack
26
+ ::Rails.configuration.middleware.insert_after 'Rack::Lock',
27
+ Cloudtrapper::UserInformer
28
+ end
29
+
30
+ Cloudtrapper.configure(true) do |config|
31
+ config.logger = rails_logger
32
+ config.environment_name = defined?(::Rails.env) && ::Rails.env || defined?(RAILS_ENV) && RAILS_ENV
33
+ config.project_root = defined?(::Rails.root) && ::Rails.root || defined?(RAILS_ROOT) && RAILS_ROOT
34
+ config.framework = defined?(::Rails.version) && "Rails: #{::Rails.version}" || defined?(::Rails::VERSION::STRING) && "Rails: #{::Rails::VERSION::STRING}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ Cloudtrapper::Rails.initialize