ratchetio 0.5.5 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +2 -1
- data/CHANGELOG.md +9 -0
- data/THANKS +2 -0
- data/lib/ratchetio/exception_reporter.rb +24 -0
- data/lib/ratchetio/middleware/rack/builder.rb +21 -0
- data/lib/ratchetio/middleware/rack/test_session.rb +31 -0
- data/lib/ratchetio/middleware/rails/show_exceptions.rb +26 -0
- data/lib/ratchetio/rack.rb +9 -0
- data/lib/ratchetio/rails/controller_methods.rb +7 -85
- data/lib/ratchetio/railtie.rb +8 -9
- data/lib/ratchetio/request_data_extractor.rb +115 -0
- data/lib/ratchetio/sidekiq.rb +25 -0
- data/lib/ratchetio/version.rb +1 -1
- data/lib/ratchetio.rb +13 -10
- data/ratchetio.gemspec +2 -2
- data/spec/controllers/home_controller_spec.rb +23 -29
- data/spec/dummyapp/app/controllers/home_controller.rb +4 -0
- data/spec/dummyapp/app/controllers/users_controller.rb +5 -0
- data/spec/dummyapp/app/models/user.rb +0 -1
- data/spec/dummyapp/config/routes.rb +4 -1
- data/spec/ratchetio_spec.rb +5 -5
- data/spec/requests/home_spec.rb +48 -0
- data/spec/spec_helper.rb +3 -3
- metadata +16 -8
- data/lib/ratchetio/rails/middleware/exception_catcher.rb +0 -37
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
**0.6.0**
|
4
|
+
- BREAKING CHANGE: Ratchetio.report_exception now returns 'ignored', 'disabled', or 'error' instead of nil when the exception is not reported for one of those reasons. It still returns the payload upon success.
|
5
|
+
- Request data is now parsed from the rack environment instead of from within the controller, addressing issue #10.
|
6
|
+
- Add Sidekiq middleware for catching workers' exceptions
|
7
|
+
- Replaced activesupport dependency with multi_json
|
8
|
+
|
9
|
+
**0.5.5**
|
10
|
+
- Added activesupport dependency for use without Rails
|
11
|
+
|
3
12
|
**0.5.4**
|
4
13
|
- Added new default scrub params
|
5
14
|
|
data/THANKS
CHANGED
@@ -0,0 +1,24 @@
|
|
1
|
+
module Ratchetio
|
2
|
+
module ExceptionReporter
|
3
|
+
include RequestDataExtractor
|
4
|
+
|
5
|
+
def report_exception_to_ratchetio(env, exception)
|
6
|
+
ratchetio_debug "[Ratchet.io] Reporting exception: #{exception.try(:message)}", :error
|
7
|
+
request_data = extract_request_data_from_rack(env)
|
8
|
+
person_data = extract_person_data_from_controller(env)
|
9
|
+
exception_data = Ratchetio.report_exception(exception, request_data, person_data)
|
10
|
+
env['ratchetio.exception_uuid'] = exception_data[:uuid]
|
11
|
+
ratchetio_debug "[Ratchet.io] Exception uuid saved in env: #{exception_data[:uuid]}"
|
12
|
+
rescue => e
|
13
|
+
ratchetio_debug "[Ratchet.io] Exception while reporting exception to Ratchet.io: #{e.try(:message)}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def ratchetio_debug(message, level = :debug)
|
17
|
+
if defined?(Rails)
|
18
|
+
::Rails.logger.send(level, message)
|
19
|
+
else
|
20
|
+
puts message
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Ratchetio
|
2
|
+
module Middleware
|
3
|
+
module Rack
|
4
|
+
module Builder
|
5
|
+
include ExceptionReporter
|
6
|
+
|
7
|
+
def call_with_ratchetio(env)
|
8
|
+
call_without_ratchetio(env)
|
9
|
+
rescue => exception
|
10
|
+
report_exception_to_ratchetio(env, exception)
|
11
|
+
raise exception
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.send(:alias_method, :call_without_ratchetio, :call)
|
16
|
+
base.send(:alias_method, :call, :call_with_ratchetio)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Ratchetio
|
2
|
+
module Middleware
|
3
|
+
module Rack
|
4
|
+
module TestSession
|
5
|
+
include ExceptionReporter
|
6
|
+
|
7
|
+
def process_request_with_ratchetio(uri, env, &block)
|
8
|
+
process_request_without_ratchetio(uri, env, &block)
|
9
|
+
rescue => exception
|
10
|
+
report_exception_to_ratchetio(env, exception)
|
11
|
+
raise exception
|
12
|
+
end
|
13
|
+
|
14
|
+
def env_for_with_ratchetio(path, env)
|
15
|
+
env_for_without_ratchetio(path, env)
|
16
|
+
rescue => exception
|
17
|
+
report_exception_to_ratchetio(env, exception)
|
18
|
+
raise exception
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.included(base)
|
22
|
+
base.send(:alias_method, :process_request_without_ratchetio, :process_request)
|
23
|
+
base.send(:alias_method, :process_request, :process_request_with_ratchetio)
|
24
|
+
|
25
|
+
base.send(:alias_method, :env_for_without_ratchetio, :env_for)
|
26
|
+
base.send(:alias_method, :env_for, :env_for_with_ratchetio)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Ratchetio
|
2
|
+
module Middleware
|
3
|
+
module Rails
|
4
|
+
module ShowExceptions
|
5
|
+
include ExceptionReporter
|
6
|
+
|
7
|
+
def render_exception_with_ratchetio(env, exception)
|
8
|
+
report_exception_to_ratchetio(env, exception)
|
9
|
+
render_exception_without_ratchetio(env, exception)
|
10
|
+
end
|
11
|
+
|
12
|
+
def call_with_ratchetio(env)
|
13
|
+
call_without_ratchetio(env)
|
14
|
+
rescue => exception
|
15
|
+
report_exception_to_ratchetio(env, exception)
|
16
|
+
raise exception
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(base)
|
20
|
+
base.send(:alias_method_chain, :render_exception, :ratchetio)
|
21
|
+
base.send(:alias_method_chain, :call, :ratchetio)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
if defined?(Rack::Builder)
|
2
|
+
require 'ratchetio/middleware/rack/builder'
|
3
|
+
Rack::Builder.send(:include, Ratchetio::Middleware::Rack::Builder)
|
4
|
+
end
|
5
|
+
|
6
|
+
if defined?(Rack::Test::Session)
|
7
|
+
require 'ratchetio/middleware/rack/test_session'
|
8
|
+
Rack::Test::Session.send(:include, Ratchetio::Middleware::Rack::TestSession)
|
9
|
+
end
|
@@ -1,28 +1,16 @@
|
|
1
1
|
module Ratchetio
|
2
2
|
module Rails
|
3
3
|
module ControllerMethods
|
4
|
-
|
5
|
-
def ratchetio_request_data
|
6
|
-
{
|
7
|
-
:params => ratchetio_filter_params(params),
|
8
|
-
:url => ratchetio_request_url,
|
9
|
-
:user_ip => ratchetio_user_ip,
|
10
|
-
:headers => ratchetio_request_headers,
|
11
|
-
:GET => request.GET.to_hash,
|
12
|
-
# leaving out POST for now
|
13
|
-
:session => ratchetio_session_data,
|
14
|
-
:method => request.method,
|
15
|
-
}
|
16
|
-
end
|
4
|
+
include RequestDataExtractor
|
17
5
|
|
18
6
|
def ratchetio_person_data
|
19
7
|
user = send(Ratchetio.configuration.person_method)
|
20
8
|
# include id, username, email if non-empty
|
21
9
|
if user
|
22
|
-
{
|
23
|
-
:id =>
|
24
|
-
:username =>
|
25
|
-
:email =>
|
10
|
+
{
|
11
|
+
:id => (user.send(Ratchetio.configuration.person_id_method) rescue nil),
|
12
|
+
:username => (user.send(Ratchetio.configuration.person_username_method) rescue nil),
|
13
|
+
:email => (user.send(Ratchetio.configuration.person_email_method) rescue nil)
|
26
14
|
}
|
27
15
|
else
|
28
16
|
{}
|
@@ -31,74 +19,8 @@ module Ratchetio
|
|
31
19
|
{}
|
32
20
|
end
|
33
21
|
|
34
|
-
|
35
|
-
|
36
|
-
def ratchetio_filter_params(params)
|
37
|
-
filtered = {}
|
38
|
-
|
39
|
-
params.to_hash.each_pair do |k,v|
|
40
|
-
if v.is_a? ActionDispatch::Http::UploadedFile
|
41
|
-
# only save content_type, original_filename, and length
|
42
|
-
begin
|
43
|
-
filtered[k] = {
|
44
|
-
:content_type => v.content_type,
|
45
|
-
:original_filename => v.original_filename,
|
46
|
-
:size => v.tempfile.size
|
47
|
-
}
|
48
|
-
rescue
|
49
|
-
filtered[k] = 'Uploaded file'
|
50
|
-
end
|
51
|
-
elsif v.is_a? Hash
|
52
|
-
filtered[k] = ratchetio_filter_params v
|
53
|
-
elsif Ratchetio.configuration.scrub_fields.include? k.to_sym
|
54
|
-
filtered[k] = "*" * v.length
|
55
|
-
else
|
56
|
-
filtered[k] = v
|
57
|
-
end
|
58
|
-
end
|
59
|
-
filtered
|
60
|
-
end
|
61
|
-
|
62
|
-
def ratchetio_request_url
|
63
|
-
url = "#{request.protocol}#{request.host}"
|
64
|
-
unless [80, 443].include?(request.port)
|
65
|
-
url << ":#{request.port}"
|
66
|
-
end
|
67
|
-
url << request.fullpath
|
68
|
-
url
|
69
|
-
end
|
70
|
-
|
71
|
-
def ratchetio_user_ip
|
72
|
-
# priority: X-Real-Ip, then X-Forwarded-For, then request.remote_ip
|
73
|
-
real_ip = request.env['HTTP_X_REAL_IP']
|
74
|
-
if real_ip
|
75
|
-
return real_ip
|
76
|
-
end
|
77
|
-
forwarded_for = request.env['HTTP_X_FORWARDED_FOR']
|
78
|
-
if forwarded_for
|
79
|
-
return forwarded_for
|
80
|
-
end
|
81
|
-
request.remote_ip
|
82
|
-
end
|
83
|
-
|
84
|
-
def ratchetio_request_headers
|
85
|
-
headers = {}
|
86
|
-
request.env.each_pair do |k,v|
|
87
|
-
if k.match(/^HTTP_/)
|
88
|
-
# convert HTTP_CONTENT_TYPE to Content-Type, etc.
|
89
|
-
name = k.split("_", 2)[1].sub("_", "-").split(/(\W)/).map(&:capitalize).join
|
90
|
-
headers[name] = v
|
91
|
-
end
|
92
|
-
end
|
93
|
-
headers
|
94
|
-
end
|
95
|
-
|
96
|
-
def ratchetio_session_data
|
97
|
-
if session.respond_to?(:to_hash)
|
98
|
-
ratchetio_filter_params(session.to_hash)
|
99
|
-
else
|
100
|
-
session.data
|
101
|
-
end
|
22
|
+
def ratchetio_request_data
|
23
|
+
extract_request_data_from_rack(request.env)
|
102
24
|
end
|
103
25
|
|
104
26
|
end
|
data/lib/ratchetio/railtie.rb
CHANGED
@@ -22,16 +22,15 @@ module Ratchetio
|
|
22
22
|
include Ratchetio::Rails::ControllerMethods
|
23
23
|
end
|
24
24
|
|
25
|
-
if defined?(
|
26
|
-
#
|
27
|
-
require 'ratchetio/rails/
|
28
|
-
|
29
|
-
elsif defined?(
|
30
|
-
#
|
31
|
-
require 'ratchetio/rails/
|
32
|
-
|
25
|
+
if defined?(ActionDispatch::DebugExceptions)
|
26
|
+
# Rails 3.2.x
|
27
|
+
require 'ratchetio/middleware/rails/show_exceptions'
|
28
|
+
ActionDispatch::DebugExceptions.send(:include, Ratchetio::Middleware::Rails::ShowExceptions)
|
29
|
+
elsif defined?(ActionDispatch::ShowExceptions)
|
30
|
+
# Rails 3.0.x and 3.1.x
|
31
|
+
require 'ratchetio/middleware/rails/show_exceptions'
|
32
|
+
ActionDispatch::ShowExceptions.send(:include, Ratchetio::Middleware::Rails::ShowExceptions)
|
33
33
|
end
|
34
|
-
|
35
34
|
end
|
36
35
|
end
|
37
36
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Ratchetio
|
2
|
+
module RequestDataExtractor
|
3
|
+
ATTACHMENT_CLASSES = %w[
|
4
|
+
ActionDispatch::Http::UploadedFile
|
5
|
+
Rack::Multipart::UploadedFile
|
6
|
+
].freeze
|
7
|
+
|
8
|
+
def extract_person_data_from_controller(env)
|
9
|
+
controller = env['action_controller.instance']
|
10
|
+
person_data = controller ? controller.try(:ratchetio_person_data) : {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def extract_request_data_from_rack(env)
|
14
|
+
sensitive_params = sensitive_params_list(env)
|
15
|
+
request_params = ratchetio_request_params(env)
|
16
|
+
cookies = ratchetio_filtered_params(sensitive_params, ratchetio_request_cookies(env))
|
17
|
+
get_params = ratchetio_filtered_params(sensitive_params, ratchetio_get_params(env))
|
18
|
+
post_params = ratchetio_filtered_params(sensitive_params, ratchetio_post_params(env))
|
19
|
+
|
20
|
+
{
|
21
|
+
:params => get_params.merge(post_params).merge(request_params),
|
22
|
+
:url => ratchetio_url(env),
|
23
|
+
:user_ip => ratchetio_user_ip(env),
|
24
|
+
:headers => ratchetio_headers(env),
|
25
|
+
:GET => get_params,
|
26
|
+
:POST => post_params,
|
27
|
+
:cookies => cookies,
|
28
|
+
:session => env['rack.session.options'],
|
29
|
+
:method => ratchetio_request_method(env)
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def ratchetio_request_method(env)
|
36
|
+
env['REQUEST_METHOD'] || env[:method]
|
37
|
+
end
|
38
|
+
|
39
|
+
def ratchetio_headers(env)
|
40
|
+
env.keys.grep(/^HTTP_/).map do |header|
|
41
|
+
name = header.gsub(/^HTTP_/, '').split('_').map(&:capitalize).join('-')
|
42
|
+
{ name => env[header] }
|
43
|
+
end.inject(:merge)
|
44
|
+
end
|
45
|
+
|
46
|
+
def ratchetio_url(env)
|
47
|
+
scheme = env['rack.url_scheme']
|
48
|
+
host = env['HTTP_HOST'] || env['SERVER_NAME']
|
49
|
+
path = env['ORIGINAL_FULLPATH'] || env['REQUEST_URI']
|
50
|
+
unless path.nil? || path.empty?
|
51
|
+
path = '/' + path.to_s if path.to_s.slice(0, 1) != '/'
|
52
|
+
end
|
53
|
+
|
54
|
+
[scheme, '://', host, path].join
|
55
|
+
end
|
56
|
+
|
57
|
+
def ratchetio_user_ip(env)
|
58
|
+
(env['action_dispatch.remote_ip'] || env['HTTP_X_REAL_IP'] || env['HTTP_X_FORWARDED_FOR'] || env['REMOTE_ADDR']).to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
def ratchetio_request_params(env)
|
62
|
+
route = ::Rails.application.routes.recognize_path(env['PATH_INFO']) rescue {}
|
63
|
+
{
|
64
|
+
:controller => route[:controller],
|
65
|
+
:action => route[:action],
|
66
|
+
:format => route[:format],
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
def ratchetio_get_params(env)
|
71
|
+
rack_request(env).GET
|
72
|
+
rescue
|
73
|
+
{}
|
74
|
+
end
|
75
|
+
|
76
|
+
def ratchetio_post_params(env)
|
77
|
+
rack_request(env).POST
|
78
|
+
rescue
|
79
|
+
{}
|
80
|
+
end
|
81
|
+
|
82
|
+
def ratchetio_request_cookies(env)
|
83
|
+
rack_request(env).cookie
|
84
|
+
rescue
|
85
|
+
{}
|
86
|
+
end
|
87
|
+
|
88
|
+
def rack_request(env)
|
89
|
+
@rack_request ||= Rack::Request.new(env)
|
90
|
+
end
|
91
|
+
|
92
|
+
def ratchetio_filtered_params(sensitive_params, params)
|
93
|
+
params.inject({}) do |result, (key, value)|
|
94
|
+
if key.to_sym.in?(sensitive_params)
|
95
|
+
result[key] = '*' * (value.length rescue 8)
|
96
|
+
elsif value.is_a?(Hash)
|
97
|
+
result[key] = ratchetio_filtered_params(sensitive_params, value)
|
98
|
+
elsif value.class.name.in?(ATTACHMENT_CLASSES)
|
99
|
+
result[key] = {
|
100
|
+
:content_type => value.content_type,
|
101
|
+
:original_filename => value.original_filename,
|
102
|
+
:size => value.tempfile.size
|
103
|
+
} rescue 'Uploaded file'
|
104
|
+
else
|
105
|
+
result[key] = value
|
106
|
+
end
|
107
|
+
result
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def sensitive_params_list(env)
|
112
|
+
Ratchetio.configuration.scrub_fields |= Array(env['action_dispatch.parameter_filter'])
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Ratchetio
|
4
|
+
class Sidekiq
|
5
|
+
def call(worker, msg, queue)
|
6
|
+
begin
|
7
|
+
yield
|
8
|
+
rescue => e
|
9
|
+
msg.delete('backtrace')
|
10
|
+
msg.delete('error_backtrace')
|
11
|
+
msg.delete('error_message')
|
12
|
+
msg.delete('error_class')
|
13
|
+
|
14
|
+
Ratchetio.report_exception(e, :params => msg)
|
15
|
+
raise
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Sidekiq.configure_server do |config|
|
22
|
+
config.server_middleware do |chain|
|
23
|
+
chain.add Ratchetio::Sidekiq
|
24
|
+
end
|
25
|
+
end
|
data/lib/ratchetio/version.rb
CHANGED
data/lib/ratchetio.rb
CHANGED
@@ -6,15 +6,21 @@ require 'uri'
|
|
6
6
|
|
7
7
|
require "girl_friday" if defined?(GirlFriday)
|
8
8
|
|
9
|
+
require 'ratchetio/version'
|
9
10
|
require 'ratchetio/configuration'
|
11
|
+
require 'ratchetio/request_data_extractor'
|
12
|
+
require 'ratchetio/exception_reporter'
|
13
|
+
|
10
14
|
require 'ratchetio/delayed_job' if defined?(Delayed) && defined?(Delayed::Plugins)
|
15
|
+
require 'ratchetio/sidekiq' if defined?(Sidekiq)
|
11
16
|
require 'ratchetio/goalie' if defined?(Goalie)
|
17
|
+
require 'ratchetio/rack' if defined?(Rack)
|
12
18
|
require 'ratchetio/railtie' if defined?(Rails)
|
13
|
-
require 'ratchetio/version'
|
14
19
|
|
15
20
|
module Ratchetio
|
16
21
|
class << self
|
17
22
|
attr_writer :configuration
|
23
|
+
attr_reader :last_report
|
18
24
|
|
19
25
|
# Configures the gem.
|
20
26
|
#
|
@@ -57,24 +63,21 @@ module Ratchetio
|
|
57
63
|
# @param person_data [Hash] Data describing the affected person. Should be the result of calling
|
58
64
|
# `ratchetio_person_data`
|
59
65
|
def report_exception(exception, request_data = nil, person_data = nil)
|
60
|
-
unless configuration.enabled
|
61
|
-
|
62
|
-
end
|
63
|
-
|
64
|
-
if ignored?(exception)
|
65
|
-
return
|
66
|
-
end
|
66
|
+
return 'disabled' unless configuration.enabled
|
67
|
+
return 'ignored' if ignored?(exception)
|
67
68
|
|
68
69
|
data = exception_data(exception, filtered_level(exception))
|
69
70
|
data[:request] = request_data if request_data
|
70
71
|
data[:person] = person_data if person_data
|
71
72
|
|
73
|
+
@last_report = data
|
74
|
+
|
72
75
|
payload = build_payload(data)
|
73
76
|
schedule_payload(payload)
|
74
77
|
data
|
75
78
|
rescue => e
|
76
79
|
logger.error "[Ratchet.io] Error reporting exception to Ratchet.io: #{e}"
|
77
|
-
|
80
|
+
'error'
|
78
81
|
end
|
79
82
|
|
80
83
|
# Reports an arbitrary message to Ratchet.io
|
@@ -274,7 +277,7 @@ module Ratchetio
|
|
274
277
|
:access_token => configuration.access_token,
|
275
278
|
:data => data
|
276
279
|
}
|
277
|
-
|
280
|
+
MultiJson.dump(payload)
|
278
281
|
end
|
279
282
|
|
280
283
|
def base_data(level = 'error')
|
data/ratchetio.gemspec
CHANGED
@@ -14,9 +14,9 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.require_paths = ["lib"]
|
15
15
|
gem.version = Ratchetio::VERSION
|
16
16
|
|
17
|
-
gem.add_runtime_dependency '
|
17
|
+
gem.add_runtime_dependency 'multi_json', '~> 1.5.0'
|
18
18
|
|
19
|
-
gem.add_development_dependency 'rails', '~> 3.2.
|
19
|
+
gem.add_development_dependency 'rails', '~> 3.2.11'
|
20
20
|
gem.add_development_dependency 'devise', '>= 2.1.2'
|
21
21
|
gem.add_development_dependency 'rspec-rails', '~> 2.12.0'
|
22
22
|
gem.add_development_dependency 'database_cleaner', '>= 0.9.1'
|
@@ -1,7 +1,8 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe HomeController do
|
4
|
-
|
4
|
+
let(:logger_mock) { double("Rails.logger").as_null_object }
|
5
|
+
|
5
6
|
before(:each) do
|
6
7
|
reset_configuration
|
7
8
|
Ratchetio.configure do |config|
|
@@ -12,8 +13,6 @@ describe HomeController do
|
|
12
13
|
config.logger = logger_mock
|
13
14
|
end
|
14
15
|
end
|
15
|
-
|
16
|
-
let(:logger_mock) { double("Rails.logger").as_null_object }
|
17
16
|
|
18
17
|
context "ratchetio controller methods" do
|
19
18
|
# TODO run these for a a more-real request
|
@@ -43,14 +42,14 @@ describe HomeController do
|
|
43
42
|
:tempfile => "dummy"
|
44
43
|
}
|
45
44
|
file = ActionDispatch::Http::UploadedFile.new(file_hash)
|
46
|
-
|
45
|
+
|
47
46
|
params = {
|
48
47
|
:name => name,
|
49
48
|
:a_file => file
|
50
49
|
}
|
51
50
|
|
52
|
-
filtered = controller.send(:
|
53
|
-
|
51
|
+
filtered = controller.send(:ratchetio_filtered_params, Ratchetio.configuration.scrub_fields, params)
|
52
|
+
|
54
53
|
filtered[:name].should == name
|
55
54
|
filtered[:a_file].should be_a_kind_of(Hash)
|
56
55
|
filtered[:a_file][:content_type].should == file_hash[:type]
|
@@ -67,7 +66,7 @@ describe HomeController do
|
|
67
66
|
:tempfile => "dummy"
|
68
67
|
}
|
69
68
|
file = ActionDispatch::Http::UploadedFile.new(file_hash)
|
70
|
-
|
69
|
+
|
71
70
|
params = {
|
72
71
|
:name => name,
|
73
72
|
:wrapper => {
|
@@ -78,18 +77,18 @@ describe HomeController do
|
|
78
77
|
}
|
79
78
|
}
|
80
79
|
|
81
|
-
filtered = controller.send(:
|
82
|
-
|
80
|
+
filtered = controller.send(:ratchetio_filtered_params, Ratchetio.configuration.scrub_fields, params)
|
81
|
+
|
83
82
|
filtered[:name].should == name
|
84
83
|
filtered[:wrapper][:wrapper2][:foo].should == "bar"
|
85
|
-
|
84
|
+
|
86
85
|
filtered_file = filtered[:wrapper][:wrapper2][:a_file]
|
87
86
|
filtered_file.should be_a_kind_of(Hash)
|
88
87
|
filtered_file[:content_type].should == file_hash[:type]
|
89
88
|
filtered_file[:original_filename].should == file_hash[:filename]
|
90
89
|
filtered_file[:size].should == file_hash[:tempfile].size
|
91
90
|
end
|
92
|
-
|
91
|
+
|
93
92
|
it "should scrub the default scrub_fields" do
|
94
93
|
params = {
|
95
94
|
:passwd => "hidden",
|
@@ -97,29 +96,29 @@ describe HomeController do
|
|
97
96
|
:secret => "hidden",
|
98
97
|
:notpass => "visible"
|
99
98
|
}
|
100
|
-
|
101
|
-
filtered = controller.send(:
|
102
|
-
|
99
|
+
|
100
|
+
filtered = controller.send(:ratchetio_filtered_params, Ratchetio.configuration.scrub_fields, params)
|
101
|
+
|
103
102
|
filtered[:passwd].should == "******"
|
104
103
|
filtered[:password].should == "******"
|
105
104
|
filtered[:secret].should == "******"
|
106
105
|
filtered[:notpass].should == "visible"
|
107
106
|
end
|
108
|
-
|
107
|
+
|
109
108
|
it "should scrub custom scrub_fields" do
|
110
109
|
Ratchetio.configure do |config|
|
111
110
|
config.scrub_fields = [:notpass, :secret]
|
112
111
|
end
|
113
|
-
|
112
|
+
|
114
113
|
params = {
|
115
114
|
:passwd => "visible",
|
116
115
|
:password => "visible",
|
117
116
|
:secret => "hidden",
|
118
117
|
:notpass => "hidden"
|
119
118
|
}
|
120
|
-
|
121
|
-
filtered = controller.send(:
|
122
|
-
|
119
|
+
|
120
|
+
filtered = controller.send(:ratchetio_filtered_params, Ratchetio.configuration.scrub_fields, params)
|
121
|
+
|
123
122
|
filtered[:passwd].should == "visible"
|
124
123
|
filtered[:password].should == "visible"
|
125
124
|
filtered[:secret].should == "******"
|
@@ -132,7 +131,7 @@ describe HomeController do
|
|
132
131
|
req = controller.request
|
133
132
|
req.host = 'ratchet.io'
|
134
133
|
|
135
|
-
controller.send(:
|
134
|
+
controller.send(:ratchetio_request_data)[:url].should == 'http://ratchet.io'
|
136
135
|
end
|
137
136
|
end
|
138
137
|
|
@@ -140,16 +139,16 @@ describe HomeController do
|
|
140
139
|
it "should use X-Real-Ip when set" do
|
141
140
|
controller.request.env["HTTP_X_REAL_IP"] = '1.1.1.1'
|
142
141
|
controller.request.env["HTTP_X_FORWARDED_FOR"] = '1.2.3.4'
|
143
|
-
controller.send(:
|
142
|
+
controller.send(:ratchetio_request_data)[:user_ip].should == '1.1.1.1'
|
144
143
|
end
|
145
144
|
|
146
145
|
it "should use X-Forwarded-For when set" do
|
147
146
|
controller.request.env["HTTP_X_FORWARDED_FOR"] = '1.2.3.4'
|
148
|
-
controller.send(:
|
147
|
+
controller.send(:ratchetio_request_data)[:user_ip].should == '1.2.3.4'
|
149
148
|
end
|
150
149
|
|
151
150
|
it "should use the remote_addr when neither is set" do
|
152
|
-
controller.send(:
|
151
|
+
controller.send(:ratchetio_request_data)[:user_ip].should == '0.0.0.0'
|
153
152
|
end
|
154
153
|
end
|
155
154
|
|
@@ -172,15 +171,10 @@ describe HomeController do
|
|
172
171
|
end
|
173
172
|
end
|
174
173
|
|
175
|
-
# TODO need to figure out how to make a test request that uses enough of the middleware
|
176
|
-
# that it invokes the ratchetio exception catcher. just plain "get 'some_url'" doesn't
|
177
|
-
# seem to work.
|
178
|
-
it "should report uncaught exceptions"
|
179
|
-
|
180
174
|
after(:each) do
|
181
175
|
Ratchetio.configure do |config|
|
182
176
|
config.logger = ::Rails.logger
|
183
177
|
end
|
184
178
|
end
|
185
|
-
|
179
|
+
|
186
180
|
end
|
@@ -4,8 +4,11 @@ Dummy::Application.routes.draw do
|
|
4
4
|
end
|
5
5
|
root :to => "home#index"
|
6
6
|
devise_for :users
|
7
|
-
resources :users
|
7
|
+
resources :users do
|
8
|
+
member { post :start_session }
|
9
|
+
end
|
8
10
|
|
9
11
|
match "/cause_exception" => "home#cause_exception"
|
10
12
|
match "/report_exception" => "home#report_exception"
|
13
|
+
match "/current_user" => "home#current_user"
|
11
14
|
end
|
data/spec/ratchetio_spec.rb
CHANGED
@@ -93,7 +93,7 @@ describe Ratchetio do
|
|
93
93
|
it 'should report exception objects with no backtrace' do
|
94
94
|
payload = nil
|
95
95
|
Ratchetio.stub(:schedule_payload) do |*args|
|
96
|
-
payload =
|
96
|
+
payload = MultiJson.load(args[0])
|
97
97
|
end
|
98
98
|
Ratchetio.report_exception(StandardError.new("oops"))
|
99
99
|
payload["data"]["body"]["trace"]["frames"].should == []
|
@@ -227,6 +227,8 @@ describe Ratchetio do
|
|
227
227
|
let(:logger_mock) { double("Rails.logger").as_null_object }
|
228
228
|
|
229
229
|
it 'should send the payload using the default asynchronous handler girl_friday' do
|
230
|
+
logger_mock.should_receive(:info).with('[Ratchet.io] Scheduling payload')
|
231
|
+
logger_mock.should_receive(:info).with('[Ratchet.io] Sending payload')
|
230
232
|
logger_mock.should_receive(:info).with('[Ratchet.io] Success')
|
231
233
|
|
232
234
|
Ratchetio.configure do |config|
|
@@ -361,15 +363,13 @@ describe Ratchetio do
|
|
361
363
|
context 'build_payload' do
|
362
364
|
it 'should build valid json' do
|
363
365
|
json = Ratchetio.send(:build_payload, {:foo => {:bar => "baz"}})
|
364
|
-
hash =
|
366
|
+
hash = MultiJson.load(json)
|
365
367
|
hash["data"]["foo"]["bar"].should == "baz"
|
366
368
|
end
|
367
369
|
end
|
368
370
|
|
369
371
|
context 'base_data' do
|
370
|
-
before(:each)
|
371
|
-
configure
|
372
|
-
end
|
372
|
+
before(:each) { configure }
|
373
373
|
|
374
374
|
it 'should have the correct notifier name' do
|
375
375
|
Ratchetio.send(:base_data)[:notifier][:name].should == 'ratchetio-gem'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HomeController do
|
4
|
+
let(:logger_mock) { double("Rails.logger").as_null_object }
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
reset_configuration
|
8
|
+
Ratchetio.configure do |config|
|
9
|
+
config.access_token = 'aaaabbbbccccddddeeeeffff00001111'
|
10
|
+
config.environment = ::Rails.env
|
11
|
+
config.root = ::Rails.root
|
12
|
+
config.framework = "Rails: #{::Rails::VERSION::STRING}"
|
13
|
+
config.logger = logger_mock
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "with broken request" do
|
18
|
+
it "should report uncaught exceptions" do
|
19
|
+
expect{ get 'current_user', nil, :cookie => '8%B' }.to raise_exception
|
20
|
+
|
21
|
+
exception_info = Ratchetio.last_report[:body][:trace][:exception]
|
22
|
+
exception_info[:class].should == 'ArgumentError'
|
23
|
+
exception_info[:message].should == 'invalid %-encoding (8%B)'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "with error hiding deep inside" do
|
28
|
+
let!(:cookie_method_name){ :[] }
|
29
|
+
let!(:original_cookie_method){ ActionDispatch::Cookies::CookieJar.instance_method(cookie_method_name) }
|
30
|
+
let!(:broken_cookie_method){ Proc.new{ |name| "1" - 1 } }
|
31
|
+
|
32
|
+
before(:each) do
|
33
|
+
ActionDispatch::Cookies::CookieJar.send(:define_method, cookie_method_name, broken_cookie_method)
|
34
|
+
end
|
35
|
+
|
36
|
+
after(:each) do
|
37
|
+
ActionDispatch::Cookies::CookieJar.send(:define_method, cookie_method_name, original_cookie_method)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should report uncaught exceptions" do
|
41
|
+
expect{ get 'current_user' }.to raise_exception
|
42
|
+
|
43
|
+
exception_info = Ratchetio.last_report[:body][:trace][:exception]
|
44
|
+
exception_info[:class].should == 'NoMethodError'
|
45
|
+
# exception_info[:message].should == 'undefined method `-\' for "1":String'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
|
3
|
-
ENV['RAILS_ENV']
|
3
|
+
ENV['RAILS_ENV'] = 'test'
|
4
4
|
require File.expand_path('../dummyapp/config/environment', __FILE__)
|
5
5
|
require 'rspec/rails'
|
6
6
|
require 'database_cleaner'
|
@@ -26,10 +26,10 @@ RSpec.configure do |config|
|
|
26
26
|
config.after(:each) do
|
27
27
|
DatabaseCleaner.clean
|
28
28
|
end
|
29
|
-
|
29
|
+
|
30
30
|
end
|
31
31
|
|
32
32
|
def reset_configuration
|
33
33
|
Ratchetio.reconfigure do |config|
|
34
34
|
end
|
35
|
-
end
|
35
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ratchetio
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,16 +9,16 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-29 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: multi_json
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 1.5.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
24
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -26,7 +26,7 @@ dependencies:
|
|
26
26
|
requirements:
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
29
|
+
version: 1.5.0
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
31
|
name: rails
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -34,7 +34,7 @@ dependencies:
|
|
34
34
|
requirements:
|
35
35
|
- - ~>
|
36
36
|
- !ruby/object:Gem::Version
|
37
|
-
version: 3.2.
|
37
|
+
version: 3.2.11
|
38
38
|
type: :development
|
39
39
|
prerelease: false
|
40
40
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -42,7 +42,7 @@ dependencies:
|
|
42
42
|
requirements:
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
|
-
version: 3.2.
|
45
|
+
version: 3.2.11
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
47
|
name: devise
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -127,12 +127,18 @@ files:
|
|
127
127
|
- lib/ratchetio.rb
|
128
128
|
- lib/ratchetio/configuration.rb
|
129
129
|
- lib/ratchetio/delayed_job.rb
|
130
|
+
- lib/ratchetio/exception_reporter.rb
|
130
131
|
- lib/ratchetio/goalie.rb
|
132
|
+
- lib/ratchetio/middleware/rack/builder.rb
|
133
|
+
- lib/ratchetio/middleware/rack/test_session.rb
|
134
|
+
- lib/ratchetio/middleware/rails/show_exceptions.rb
|
135
|
+
- lib/ratchetio/rack.rb
|
131
136
|
- lib/ratchetio/rails.rb
|
132
137
|
- lib/ratchetio/rails/controller_methods.rb
|
133
|
-
- lib/ratchetio/rails/middleware/exception_catcher.rb
|
134
138
|
- lib/ratchetio/railtie.rb
|
135
139
|
- lib/ratchetio/rake_tasks.rb
|
140
|
+
- lib/ratchetio/request_data_extractor.rb
|
141
|
+
- lib/ratchetio/sidekiq.rb
|
136
142
|
- lib/ratchetio/version.rb
|
137
143
|
- ratchetio.gemspec
|
138
144
|
- spec/controllers/home_controller_spec.rb
|
@@ -188,6 +194,7 @@ files:
|
|
188
194
|
- spec/dummyapp/public/favicon.ico
|
189
195
|
- spec/dummyapp/script/rails
|
190
196
|
- spec/ratchetio_spec.rb
|
197
|
+
- spec/requests/home_spec.rb
|
191
198
|
- spec/spec_helper.rb
|
192
199
|
- spec/support/devise.rb
|
193
200
|
homepage: https://github.com/ratchetio/ratchetio-gem
|
@@ -268,6 +275,7 @@ test_files:
|
|
268
275
|
- spec/dummyapp/public/favicon.ico
|
269
276
|
- spec/dummyapp/script/rails
|
270
277
|
- spec/ratchetio_spec.rb
|
278
|
+
- spec/requests/home_spec.rb
|
271
279
|
- spec/spec_helper.rb
|
272
280
|
- spec/support/devise.rb
|
273
281
|
has_rdoc:
|
@@ -1,37 +0,0 @@
|
|
1
|
-
module Ratchetio
|
2
|
-
module Rails
|
3
|
-
module Middleware
|
4
|
-
module ExceptionCatcher
|
5
|
-
def self.included(base)
|
6
|
-
base.send(:alias_method_chain, :render_exception, :ratchetio)
|
7
|
-
end
|
8
|
-
|
9
|
-
def render_exception_with_ratchetio(env, exception)
|
10
|
-
exception_data = nil
|
11
|
-
begin
|
12
|
-
controller = env['action_controller.instance']
|
13
|
-
request_data = controller.try(:ratchetio_request_data)
|
14
|
-
person_data = controller.try(:ratchetio_person_data)
|
15
|
-
exception_data = Ratchetio.report_exception(exception, request_data, person_data)
|
16
|
-
rescue => e
|
17
|
-
# TODO use logger here?
|
18
|
-
puts "[Ratchet.io] Exception while reporting exception to Ratchet.io: #{e}"
|
19
|
-
end
|
20
|
-
|
21
|
-
# if an exception was reported, save uuid in the env
|
22
|
-
# so it can be displayed to the user on the error page
|
23
|
-
if exception_data
|
24
|
-
begin
|
25
|
-
env['ratchetio.exception_uuid'] = exception_data[:uuid]
|
26
|
-
rescue => e
|
27
|
-
puts "[Ratchet.io] Exception saving uuid in env: #{e}"
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# now continue as normal
|
32
|
-
render_exception_without_ratchetio(env, exception)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|