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 CHANGED
@@ -3,7 +3,6 @@ rvm:
3
3
  - 1.8.7
4
4
  - 1.9.3
5
5
  - 1.9.2
6
- - ree
7
6
  - ruby-head
8
7
  - jruby-18mode
9
8
  - jruby-19mode
@@ -14,3 +13,5 @@ matrix:
14
13
  allow_failures:
15
14
  - rvm: ruby-head
16
15
  - rvm: rbx-18mode
16
+ - rvm: jruby-18mode
17
+ - rvm: jruby-19mode
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
@@ -2,6 +2,8 @@ Huge thanks to the following contributors (by github username). For the most up-
2
2
 
3
3
  arr-ee
4
4
  kavu
5
+ magnolia-fan
5
6
  mipearson
6
7
  trisweb
7
8
  tysontate
9
+ wbond
@@ -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 => begin user.send(Ratchetio.configuration.person_id_method) rescue nil end,
24
- :username => begin user.send(Ratchetio.configuration.person_username_method) rescue nil end,
25
- :email => begin user.send(Ratchetio.configuration.person_email_method) rescue nil end
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
- private
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
@@ -22,16 +22,15 @@ module Ratchetio
22
22
  include Ratchetio::Rails::ControllerMethods
23
23
  end
24
24
 
25
- if defined?(::ActionDispatch::DebugExceptions)
26
- # rails 3.2.x
27
- require 'ratchetio/rails/middleware/exception_catcher'
28
- ::ActionDispatch::DebugExceptions.send(:include, Ratchetio::Rails::Middleware::ExceptionCatcher)
29
- elsif defined?(::ActionDispatch::ShowExceptions)
30
- # rails 3.0.x and 3.1.x
31
- require 'ratchetio/rails/middleware/exception_catcher'
32
- ::ActionDispatch::ShowExceptions.send(:include, Ratchetio::Rails::Middleware::ExceptionCatcher)
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
@@ -1,3 +1,3 @@
1
1
  module Ratchetio
2
- VERSION = "0.5.5"
2
+ VERSION = "0.6.0"
3
3
  end
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
- return
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
- nil
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
- ActiveSupport::JSON.encode(payload)
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 'activesupport', '~> 3.2.9'
17
+ gem.add_runtime_dependency 'multi_json', '~> 1.5.0'
18
18
 
19
- gem.add_development_dependency 'rails', '~> 3.2.9'
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(:ratchetio_filter_params, params)
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(:ratchetio_filter_params, params)
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(:ratchetio_filter_params, params)
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(:ratchetio_filter_params, params)
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(:ratchetio_request_url).should == 'http://ratchet.io'
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(:ratchetio_user_ip).should == '1.1.1.1'
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(:ratchetio_user_ip).should == '1.2.3.4'
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(:ratchetio_user_ip).should == '0.0.0.0'
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
@@ -18,4 +18,8 @@ class HomeController < ApplicationController
18
18
  def cause_exception
19
19
  foo = bar
20
20
  end
21
+
22
+ def current_user
23
+ User.find_by_encrypted_password(cookies[:session_id])
24
+ end
21
25
  end
@@ -9,4 +9,9 @@ class UsersController < ApplicationController
9
9
  @user = User.find(params[:id])
10
10
  end
11
11
 
12
+ def start_session
13
+ @user = User.find(params[:id])
14
+ cookies[:session_id] = @user.encrypted_password
15
+ end
16
+
12
17
  end
@@ -7,5 +7,4 @@ class User < ActiveRecord::Base
7
7
 
8
8
  # Setup accessible (or protected) attributes for your model
9
9
  attr_accessible :name, :email, :password, :password_confirmation, :remember_me
10
-
11
10
  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
@@ -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 = JSON.parse( args[0] )
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 = ActiveSupport::JSON.decode(json)
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) do
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'] ||= 'test'
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.5.5
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-26 00:00:00.000000000 Z
12
+ date: 2013-01-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: activesupport
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: 3.2.9
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: 3.2.9
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.9
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.9
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