chatterbox 0.5.1 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.markdown CHANGED
@@ -1,4 +1,9 @@
1
1
  HEAD
2
+ 0.5.3
3
+ - exception filtering
4
+
5
+ 0.5.2
6
+ - add RAILS_ROOT filtering
2
7
 
3
8
  0.5.1
4
9
  - to_s the env and root from Rails
data/README.markdown CHANGED
@@ -1,10 +1,9 @@
1
1
  Chatterbox
2
2
  ==========================================
3
3
 
4
- Simple Notifications. Publishing and subscribing to notifications is decoupled by default, so bring your own message queue, web service, database, or whatever to act as an intermediary.
4
+ Simple Notifications. Publishing and subscribing to notifications is decoupled by default, so bring your own message queue, web service, database, or whatever to act as an intermediary. Of course, you can wire Chatterbox to work directly if you like.
5
5
 
6
- Installing and Running
7
- ---------------------------------------
6
+ ## Installing and Running
8
7
 
9
8
  For plain old gem install:
10
9
 
@@ -18,6 +17,12 @@ Then run:
18
17
 
19
18
  rake gems:install
20
19
 
20
+ ## Exception Notification
21
+
22
+ One of the first use cases that Chatterbox was developed for was exception notification. To setup Chatterbox for exception notification, install it as a gem with the instructions above, then configure a service.
23
+
24
+ then wire the `RailsCatcher` in your `ApplicationController`:
25
+
21
26
  To enable standard Rails exception catching for your controllers, add the following to `application_controller`
22
27
 
23
28
  class ApplicationController < ActionController::Base
data/chatterbox.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{chatterbox}
8
- s.version = "0.5.1"
8
+ s.version = "0.5.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Rob Sanheim"]
12
- s.date = %q{2009-11-07}
12
+ s.date = %q{2009-11-13}
13
13
  s.description = %q{Send notifications and messages. However you want.}
14
14
  s.email = %q{rsanheim@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -25,9 +25,10 @@ Gem::Specification.new do |s|
25
25
  "Rakefile",
26
26
  "chatterbox.gemspec",
27
27
  "examples/example_helper.rb",
28
- "examples/lib/chatterbox/exception_notification/filter_example.rb",
28
+ "examples/lib/chatterbox/exception_notification/extracter_example.rb",
29
29
  "examples/lib/chatterbox/exception_notification/presenter_example.rb",
30
30
  "examples/lib/chatterbox/exception_notification/rails_extracter_example.rb",
31
+ "examples/lib/chatterbox/rails_catcher_controller_example.rb",
31
32
  "examples/lib/chatterbox/rails_catcher_example.rb",
32
33
  "examples/lib/chatterbox/services/email/mailer_example.rb",
33
34
  "examples/lib/chatterbox/services/email_example.rb",
@@ -56,9 +57,10 @@ Gem::Specification.new do |s|
56
57
  s.summary = %q{Notifications and messages}
57
58
  s.test_files = [
58
59
  "examples/example_helper.rb",
59
- "examples/lib/chatterbox/exception_notification/filter_example.rb",
60
+ "examples/lib/chatterbox/exception_notification/extracter_example.rb",
60
61
  "examples/lib/chatterbox/exception_notification/presenter_example.rb",
61
62
  "examples/lib/chatterbox/exception_notification/rails_extracter_example.rb",
63
+ "examples/lib/chatterbox/rails_catcher_controller_example.rb",
62
64
  "examples/lib/chatterbox/rails_catcher_example.rb",
63
65
  "examples/lib/chatterbox/services/email/mailer_example.rb",
64
66
  "examples/lib/chatterbox/services/email_example.rb",
@@ -36,6 +36,40 @@ describe Chatterbox::ExceptionNotification::RailsExtracter do
36
36
  details[:request][:remote_ip].should == "192.5.5.0"
37
37
  end
38
38
  end
39
+
40
+ describe "cleaning RAILS_ROOT" do
41
+ it "does nothing if there is no Rails configuration" do
42
+ backtrace = %w[/some/path/here.rb]
43
+ lambda {
44
+ Chatterbox::ExceptionNotification::RailsExtracter.wrap({:backtrace => backtrace})
45
+ }.should_not raise_error
46
+ end
47
+
48
+ it "does nothing if there is no rails_root on Rails" do
49
+ Chatterbox::ExceptionNotification::RailsExtracter.any_instance.stubs(:rails_configuration).returns(stub_everything)
50
+ backtrace = %w[/some/path/here.rb]
51
+ lambda {
52
+ Chatterbox::ExceptionNotification::RailsExtracter.wrap({:backtrace => backtrace})
53
+ }.should_not raise_error
54
+ end
55
+
56
+ it "should replace the Rails root from the backtrace with RAILS_ROOT" do
57
+ rails = stub_everything({ :root => "/var/apps/dogs.com" })
58
+ Chatterbox::ExceptionNotification::RailsExtracter.any_instance.stubs(:rails_configuration).returns(rails)
59
+ backtrace = %w[
60
+ /Users/rsanheim/.rvm/gems/ruby/1.9.1/gems/actionpack-2.3.4/lib/action_controller/test_process.rb:398:in `get'
61
+ /var/apps/dogs.com/app/controllers/users_controller.rb:5:in `index'
62
+ /var/apps/dogs.com/app/controllers/users_controller.rb:27:in `foo_baz'
63
+ /var/apps/dogs.com/lib/some_module.rb:10:in `something_else']
64
+ details = Chatterbox::ExceptionNotification::RailsExtracter.wrap({:backtrace => backtrace})
65
+ rails_lines = details[:backtrace].select { |line| line.include?("users_controller.rb") }
66
+ rails_lines.should_not be_empty
67
+ rails_lines.each do |line|
68
+ line.should_not match(%r{^/var/apps/dogs.com/})
69
+ line.should match(%r{^\[RAILS_ROOT\]})
70
+ end
71
+ end
72
+ end
39
73
  end
40
74
 
41
75
  describe "rails_configuration" do
@@ -0,0 +1,109 @@
1
+ require 'example_helper'
2
+ require 'chatterbox/exception_notification'
3
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. .. rails init]))
4
+
5
+ ActionController::Routing::Routes.draw { |map| map.connect ':controller/:action/:id' }
6
+
7
+ class WidgetException < RuntimeError; end
8
+ class WidgetsController < ActionController::Base
9
+ include Chatterbox::RailsCatcher
10
+
11
+ def rescue_action(exception)
12
+ rescue_action_in_public(exception)
13
+ end
14
+
15
+ def rescue_action_in_public_without_chatterbox(exception)
16
+ raise exception
17
+ end
18
+
19
+ def index
20
+ raise_exception
21
+ render :text => "hi"
22
+ end
23
+
24
+ def raise_exception
25
+ raise WidgetException, "Bad dog!"
26
+ end
27
+ end
28
+
29
+ describe WidgetsController do
30
+
31
+ describe "controller haxing" do
32
+ it "chains the chatterbox method to rescue_action_in_public" do
33
+ exception = RuntimeError.new
34
+ @controller.expects(:rescue_action_in_public_without_chatterbox).with(exception)
35
+ @controller.stubs(:extract_exception_details)
36
+ @controller.rescue_action_in_public(exception)
37
+ end
38
+
39
+ it "should have the catcher included in ApplicationController" do
40
+ WidgetsController.ancestors.should include(Chatterbox::RailsCatcher)
41
+ end
42
+
43
+ it "should hide aliased methods so they are not exposed as actions" do
44
+ WidgetsController.hidden_actions.should include("rescue_action_in_public_with_chatterbox")
45
+ WidgetsController.hidden_actions.should include("rescue_action_in_public_without_chatterbox")
46
+ end
47
+ end
48
+
49
+ describe "exception handling" do
50
+ it "should raise on index" do
51
+ lambda {
52
+ get :index
53
+ }.should raise_error(WidgetException, "Bad dog!")
54
+ end
55
+
56
+ it "should send exception notice as hash" do
57
+ Chatterbox.expects(:handle_notice).with(instance_of(Hash))
58
+ get :index rescue nil
59
+ end
60
+ end
61
+
62
+ describe "ignoring exceptions" do
63
+ describe "when configured to ignore RuntimeError (as class)" do
64
+ before do
65
+ Chatterbox::RailsCatcher.configure { |c| c.ignore << RuntimeError }
66
+ end
67
+
68
+ after do
69
+ Chatterbox::RailsCatcher.configure { |c| c.ignore = Chatterbox::RailsCatcher.default_ignored_exceptions }
70
+ end
71
+
72
+ it "handles exceptions normally" do
73
+ lambda {
74
+ @controller.rescue_action_in_public(RuntimeError.new)
75
+ }.should raise_error(RuntimeError)
76
+ end
77
+
78
+ it "ignores anything configured on the ignore list" do
79
+ Chatterbox.expects(:handle_notice).never
80
+ begin
81
+ @controller.rescue_action_in_public(RuntimeError.new)
82
+ rescue RuntimeError; end
83
+ end
84
+ end
85
+
86
+ describe "when configured to ignore RuntimeError (as String)" do
87
+ before do
88
+ Chatterbox::RailsCatcher.configure { |c| c.ignore << "RuntimeError" }
89
+ end
90
+
91
+ after do
92
+ Chatterbox::RailsCatcher.configure { |c| c.ignore = Chatterbox::RailsCatcher.default_ignored_exceptions }
93
+ end
94
+
95
+ it "handles exceptions normally" do
96
+ lambda {
97
+ @controller.rescue_action_in_public(RuntimeError.new)
98
+ }.should raise_error(RuntimeError)
99
+ end
100
+
101
+ it "ignores anything configured on the ignore list" do
102
+ Chatterbox.expects(:handle_notice).never
103
+ begin
104
+ @controller.rescue_action_in_public(RuntimeError.new)
105
+ rescue RuntimeError; end
106
+ end
107
+ end
108
+ end
109
+ end
@@ -2,60 +2,35 @@ require 'example_helper'
2
2
  require 'chatterbox/exception_notification'
3
3
  require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. .. rails init]))
4
4
 
5
- ActionController::Routing::Routes.draw { |map| map.connect ':controller/:action/:id' }
6
-
7
- class WidgetException < RuntimeError; end
8
- class WidgetsController < ActionController::Base
9
- include Chatterbox::RailsCatcher
10
-
11
- def rescue_action e
12
- rescue_action_in_public e
13
- end
14
-
15
- def rescue_action_in_public_without_chatterbox e
16
- raise e
17
- end
5
+ describe Chatterbox::RailsCatcher do
18
6
 
19
- def index
20
- raise_exception
21
- render :text => "hi"
7
+ def helper
8
+ @helper ||= Class.new {
9
+ include Chatterbox::RailsCatcher
10
+ }.new
22
11
  end
23
12
 
24
- def raise_exception
25
- raise WidgetException, "Bad dog!"
26
- end
27
- end
28
-
29
- describe WidgetsController do
30
-
31
- describe "controller haxing" do
32
- it "chains the chatterbox method to rescue_action_in_public" do
33
- exception = RuntimeError.new
34
- @controller.expects(:rescue_action_in_public_without_chatterbox).with(exception)
35
- @controller.stubs(:extract_exception_details)
36
- @controller.rescue_action_in_public(exception)
37
- end
38
-
39
- it "should have the catcher included in ApplicationController" do
40
- WidgetsController.ancestors.should include(Chatterbox::RailsCatcher)
41
- end
42
-
43
- it "should hide aliased methods so they are not exposed as actions" do
44
- WidgetsController.hidden_actions.should include("rescue_action_in_public_with_chatterbox")
45
- WidgetsController.hidden_actions.should include("rescue_action_in_public_without_chatterbox")
13
+ describe "logger" do
14
+ it "should delegate to Chatterbox#logger" do
15
+ Chatterbox.expects(:logger)
16
+ helper.logger
46
17
  end
47
18
  end
48
19
 
49
- describe "exception handling" do
50
- it "should raise on index" do
51
- lambda {
52
- get :index
53
- }.should raise_error(WidgetException, "Bad dog!")
20
+ describe "configuration" do
21
+ after do
22
+ Chatterbox::RailsCatcher.configure { |c| c.ignore = Chatterbox::RailsCatcher.default_ignored_exceptions }
23
+ end
24
+
25
+ it "ignores common Rails exceptions by default" do
26
+ Chatterbox::RailsCatcher.configuration.ignore.should == Chatterbox::RailsCatcher.default_ignored_exceptions
54
27
  end
55
28
 
56
- it "should send exception notice as hash" do
57
- Chatterbox.expects(:handle_notice).with(instance_of(Hash))
58
- get :index rescue nil
29
+ it "allows adding exceptions to the ignore list" do
30
+ Chatterbox::RailsCatcher.configure do |config|
31
+ config.ignore << "SomeOtherException"
32
+ end
33
+ Chatterbox::RailsCatcher.configuration.ignore.should include("SomeOtherException")
59
34
  end
60
35
  end
61
36
  end
@@ -11,11 +11,17 @@ module Chatterbox::ExceptionNotification
11
11
  def notice
12
12
  hsh = extract_rails_info(@message)
13
13
  hsh = extract_request_info(hsh)
14
+ hsh = filter_rails_root(hsh)
14
15
  hsh
15
16
  end
16
-
17
- def rails_configuration
18
- Object.const_get("Rails") if Object.const_defined?("Rails")
17
+
18
+ def filter_rails_root(hsh)
19
+ return hsh unless hsh[:backtrace]
20
+ cleaner = ActiveSupport::BacktraceCleaner.new
21
+ cleaner.add_filter { |line| line.gsub(rails_root, "[RAILS_ROOT]") }
22
+ backtrace = cleaner.clean(hsh[:backtrace])
23
+ hsh[:backtrace] = backtrace
24
+ hsh
19
25
  end
20
26
 
21
27
  def extract_rails_info(message)
@@ -23,7 +29,7 @@ module Chatterbox::ExceptionNotification
23
29
  message.merge({
24
30
  :rails_info => {
25
31
  :rails_env => rails_configuration.env.to_s,
26
- :rails_root => rails_configuration.root.to_s,
32
+ :rails_root => rails_root,
27
33
  :rails_version => rails_configuration.version
28
34
  }
29
35
  })
@@ -40,12 +46,14 @@ module Chatterbox::ExceptionNotification
40
46
  }
41
47
  })
42
48
  end
43
- end
44
- end
45
-
49
+
50
+ def rails_root
51
+ rails_configuration.try(:root).to_s
52
+ end
46
53
 
47
- #
48
- # * URL : <%= @request.protocol %><%= @host %><%= @request.request_uri %>
49
- # * IP address: <%= @request.env["HTTP_X_FORWARDED_FOR"] || @request.env["REMOTE_ADDR"] %>
50
- # * Parameters: <%= filter_sensitive_post_data_parameters(@request.parameters).inspect %>
51
- # * Rails root: <%= @rails_root %>
54
+ def rails_configuration
55
+ Object.const_get("Rails") if Object.const_defined?("Rails")
56
+ end
57
+
58
+ end
59
+ end
@@ -1,6 +1,24 @@
1
+ require 'ostruct'
2
+
1
3
  module Chatterbox
2
4
 
3
5
  module RailsCatcher
6
+ delegate :logger, :to => Chatterbox
7
+ delegate :configuration, :to => self
8
+
9
+ def self.default_ignored_exceptions
10
+ ['ActiveRecord::RecordNotFound', 'ActionController::RoutingError',
11
+ 'ActionController::InvalidAuthenticityToken', 'ActionController::UnknownAction',
12
+ 'CGI::Session::CookieStore::TamperedWithCookie' ]
13
+ end
14
+
15
+ def self.configuration
16
+ @configuration ||= OpenStruct.new(:ignore => default_ignored_exceptions)
17
+ end
18
+
19
+ def self.configure
20
+ yield(configuration)
21
+ end
4
22
 
5
23
  def self.included(base)
6
24
  if base.instance_methods.map(&:to_s).include? 'rescue_action_in_public' and !base.instance_methods.map(&:to_s).include? 'rescue_action_in_public_without_chatterbox'
@@ -12,15 +30,26 @@ module Chatterbox
12
30
 
13
31
  # Overrides the rescue_action method in ActionController::Base, but does not inhibit
14
32
  # any custom processing that is defined with Rails 2's exception helpers.
15
- def rescue_action_in_public_with_chatterbox exception
16
- Chatterbox.logger.debug { "#{self.class}#rescue_action_in_public_with_chatterbox: caught exception #{exception} - about to handle"}
17
- options = extract_exception_details(exception)
18
- Chatterbox.handle_notice(options)
19
- Chatterbox.logger.debug { "#{self.class}#rescue_action_in_public_with_chatterbox: handing exception #{exception} off to normal rescue handling"}
20
-
33
+ def rescue_action_in_public_with_chatterbox(exception)
34
+ logger.debug { "#{log_prefix} caught exception #{exception} - about to handle" }
35
+ unless on_ignore_list?(exception)
36
+ Chatterbox.handle_notice(extract_exception_details(exception))
37
+ end
38
+ logger.debug { "#{log_prefix} handing exception #{exception} off to normal rescue handling" }
21
39
  rescue_action_in_public_without_chatterbox(exception)
22
40
  end
23
41
 
42
+ private
43
+
44
+ def log_prefix
45
+ "#{self.class}#rescue_action_in_public_with_chatterbox:"
46
+ end
47
+
48
+ def on_ignore_list?(exception)
49
+ configuration.ignore.include?(exception.class) ||
50
+ configuration.ignore.include?(exception.class.to_s)
51
+ end
52
+
24
53
  def extract_exception_details(exception)
25
54
  options = { :exception => exception, :request => request }
26
55
  options = Chatterbox::ExceptionNotification::Extracter.wrap(options)
data/version.yml CHANGED
@@ -2,4 +2,4 @@
2
2
  :major: 0
3
3
  :minor: 5
4
4
  :build:
5
- :patch: 1
5
+ :patch: 3
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chatterbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rob Sanheim
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-07 00:00:00 -05:00
12
+ date: 2009-11-13 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -70,9 +70,10 @@ files:
70
70
  - Rakefile
71
71
  - chatterbox.gemspec
72
72
  - examples/example_helper.rb
73
- - examples/lib/chatterbox/exception_notification/filter_example.rb
73
+ - examples/lib/chatterbox/exception_notification/extracter_example.rb
74
74
  - examples/lib/chatterbox/exception_notification/presenter_example.rb
75
75
  - examples/lib/chatterbox/exception_notification/rails_extracter_example.rb
76
+ - examples/lib/chatterbox/rails_catcher_controller_example.rb
76
77
  - examples/lib/chatterbox/rails_catcher_example.rb
77
78
  - examples/lib/chatterbox/services/email/mailer_example.rb
78
79
  - examples/lib/chatterbox/services/email_example.rb
@@ -123,9 +124,10 @@ specification_version: 3
123
124
  summary: Notifications and messages
124
125
  test_files:
125
126
  - examples/example_helper.rb
126
- - examples/lib/chatterbox/exception_notification/filter_example.rb
127
+ - examples/lib/chatterbox/exception_notification/extracter_example.rb
127
128
  - examples/lib/chatterbox/exception_notification/presenter_example.rb
128
129
  - examples/lib/chatterbox/exception_notification/rails_extracter_example.rb
130
+ - examples/lib/chatterbox/rails_catcher_controller_example.rb
129
131
  - examples/lib/chatterbox/rails_catcher_example.rb
130
132
  - examples/lib/chatterbox/services/email/mailer_example.rb
131
133
  - examples/lib/chatterbox/services/email_example.rb