ratchetio 0.5.5 → 0.6.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.
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