chatterbox 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ coverage
data/.treasure_map.rb ADDED
@@ -0,0 +1,22 @@
1
+ map_for(:chatterbox) do |map|
2
+
3
+ map.keep_a_watchful_eye_for 'lib', 'examples', 'rails'
4
+
5
+ # map.add_mapping %r%examples/(.*)_example\.rb% do |match|
6
+ # ["examples/#{match[1]}_example.rb"]
7
+ # end
8
+ #
9
+ # map.add_mapping %r%examples/example_helper\.rb% do |match|
10
+ # Dir["examples/**/*_example.rb"]
11
+ # end
12
+ #
13
+ # map.add_mapping %r%lib/(.*)\.rb% do |match|
14
+ # Dir["examples/#{match[1]}_example.rb"]
15
+ # end
16
+
17
+ map.add_mapping %r%rails/(.*)\.rb% do |match|
18
+ ["examples/#{match[1]}_example.rb"]
19
+ end
20
+
21
+
22
+ end
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Rob Sanheim
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.markdown ADDED
@@ -0,0 +1,59 @@
1
+ Chatterbox
2
+ ==========================================
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.
5
+
6
+ Installing and Running
7
+ ---------------------------------------
8
+
9
+ To install within a Rails app:
10
+
11
+ Add the following to your environment.rb file:
12
+
13
+ config.gem "relevance-chatterbox"
14
+
15
+ Then run:
16
+
17
+ rake gems:install
18
+
19
+ To enable standard Rails exception catching for your controllers, add the following to `application_controller`
20
+
21
+ class ApplicationController < ActionController::Base
22
+ include Chatterbox::RailsCatcher
23
+ # ...
24
+ end
25
+
26
+ Then, wire up a producer
27
+
28
+ Example 1
29
+ ---------------------------------------
30
+
31
+ Wiring messages to be sent by Email service
32
+
33
+ Chatterbox::Publishers.register do |notice|
34
+ Chatterbox::Email.deliver(notice)
35
+ end
36
+
37
+ Example 2
38
+ ---------------------------------------
39
+
40
+ Wiring up notices to be sent to an exceptions queue, defined in RosettaQueue
41
+
42
+ Chatterbox::Publishers.register do |notice|
43
+ RosettaQueue::Producer.publish(:exceptions, notice)
44
+ end
45
+
46
+
47
+ Bugs & Patches
48
+ --------------
49
+
50
+ Links
51
+ -------------
52
+
53
+ Contributors
54
+ ------------
55
+ * Rob Sanheim
56
+
57
+ Copyrights
58
+ ------------
59
+ * Copyright &copy; 2008-2009 [Relevance, Inc.](http://www.thinkrelevance.com/), under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |gem|
6
+ gem.name = "chatterbox"
7
+ gem.summary = %Q{Notifications and messages}
8
+ gem.email = "rsanheim@gmail.com"
9
+ gem.homepage = "http://github.com/rsanheim/chatterbox"
10
+ gem.authors = ["Rob Sanheim"]
11
+ gem.add_development_dependency "mocha"
12
+ gem.add_development_dependency "actioncontroller"
13
+ gem.add_development_dependency "spicycode-micronaut"
14
+ gem.add_development_dependency "spicycode-micronaut-rails"
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'micronaut/rake_task'
22
+ Micronaut::RakeTask.new(:examples) do |examples|
23
+ examples.pattern = 'examples/**/*_example.rb'
24
+ examples.ruby_opts << '-Ilib -Iexamples'
25
+ end
26
+
27
+ Micronaut::RakeTask.new(:rcov) do |examples|
28
+ examples.pattern = 'examples/**/*_example.rb'
29
+ examples.rcov_opts = '-Ilib -Iexamples'
30
+ examples.rcov = true
31
+ end
32
+
33
+ task :default => :examples
34
+
35
+ require 'rake/rdoctask'
36
+ Rake::RDocTask.new do |rdoc|
37
+ if File.exist?('VERSION')
38
+ version = File.read('VERSION')
39
+ else
40
+ version = ""
41
+ end
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "chatterbox-email #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
@@ -0,0 +1,75 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{chatterbox}
8
+ s.version = "0.3.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Rob Sanheim"]
12
+ s.date = %q{2009-10-05}
13
+ s.email = %q{rsanheim@gmail.com}
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.markdown"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ ".treasure_map.rb",
21
+ "LICENSE",
22
+ "README.markdown",
23
+ "Rakefile",
24
+ "chatterbox.gemspec",
25
+ "examples/chatterbox_example.rb",
26
+ "examples/example_helper.rb",
27
+ "examples/lib/chatterbox/consumers/email_consumer_example.rb",
28
+ "examples/lib/chatterbox/notification_example.rb",
29
+ "examples/lib/chatterbox/rails_catcher_example.rb",
30
+ "init.rb",
31
+ "lib/chatterbox.rb",
32
+ "lib/chatterbox/notification.rb",
33
+ "lib/chatterbox/rails_catcher.rb",
34
+ "lib/consumers.rb",
35
+ "lib/consumers/email_consumer.rb",
36
+ "rails/init.rb",
37
+ "todo.markdown",
38
+ "version.yml",
39
+ "views/chatterbox/mailer/exception_notification.erb"
40
+ ]
41
+ s.homepage = %q{http://github.com/rsanheim/chatterbox}
42
+ s.rdoc_options = ["--charset=UTF-8"]
43
+ s.require_paths = ["lib"]
44
+ s.rubygems_version = %q{1.3.5}
45
+ s.summary = %q{Notifications and messages}
46
+ s.test_files = [
47
+ "examples/chatterbox_example.rb",
48
+ "examples/example_helper.rb",
49
+ "examples/lib/chatterbox/consumers/email_consumer_example.rb",
50
+ "examples/lib/chatterbox/notification_example.rb",
51
+ "examples/lib/chatterbox/rails_catcher_example.rb"
52
+ ]
53
+
54
+ if s.respond_to? :specification_version then
55
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
56
+ s.specification_version = 3
57
+
58
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
59
+ s.add_development_dependency(%q<mocha>, [">= 0"])
60
+ s.add_development_dependency(%q<actioncontroller>, [">= 0"])
61
+ s.add_development_dependency(%q<spicycode-micronaut>, [">= 0"])
62
+ s.add_development_dependency(%q<spicycode-micronaut-rails>, [">= 0"])
63
+ else
64
+ s.add_dependency(%q<mocha>, [">= 0"])
65
+ s.add_dependency(%q<actioncontroller>, [">= 0"])
66
+ s.add_dependency(%q<spicycode-micronaut>, [">= 0"])
67
+ s.add_dependency(%q<spicycode-micronaut-rails>, [">= 0"])
68
+ end
69
+ else
70
+ s.add_dependency(%q<mocha>, [">= 0"])
71
+ s.add_dependency(%q<actioncontroller>, [">= 0"])
72
+ s.add_dependency(%q<spicycode-micronaut>, [">= 0"])
73
+ s.add_dependency(%q<spicycode-micronaut-rails>, [">= 0"])
74
+ end
75
+ end
@@ -0,0 +1,73 @@
1
+ require File.join(File.dirname(__FILE__), *%w[example_helper])
2
+
3
+ describe Chatterbox do
4
+
5
+ before do
6
+ Chatterbox.logger = Logger.new(nil)
7
+ Chatterbox::Publishers.clear!
8
+ end
9
+
10
+ after do
11
+ Chatterbox.logger = nil
12
+ end
13
+
14
+ describe "handle_notice" do
15
+ include Chatterbox
16
+
17
+ it "should create Notification and return the notice" do
18
+ notification = mock(:notice => {:hash => 'of awesomeness'})
19
+ Chatterbox::Notification.expects(:new).returns(notification)
20
+ handle_notice("message")
21
+ end
22
+
23
+ it "should publish the notice" do
24
+ notification = stub(:notice => {:hash => 'of awesomeness'})
25
+ Chatterbox::Notification.stubs(:new).returns(notification)
26
+ expects(:publish_notice).with({:hash => 'of awesomeness'})
27
+ handle_notice("message")
28
+ end
29
+
30
+ end
31
+
32
+ describe "logger" do
33
+
34
+ it "uses STDOUT logger if Rails not available" do
35
+ Chatterbox.logger = nil
36
+
37
+ Logger.expects(:new).with(STDOUT).returns("logger")
38
+ Chatterbox.stubs(:rails_default_logger).returns(nil)
39
+ Chatterbox.logger.should == "logger"
40
+ end
41
+ end
42
+
43
+ describe "publish" do
44
+
45
+ include Chatterbox
46
+
47
+ it "should call each publisher with the notice" do
48
+ notice = stub
49
+ publisher = Chatterbox::Publishers.register { "i'm in your block" }
50
+ publisher.expects(:call).with(notice)
51
+
52
+ publish_notice(notice)
53
+ end
54
+
55
+ end
56
+
57
+ describe "publishers" do
58
+
59
+ it "should allow clearing all publishers" do
60
+ Chatterbox::Publishers.register { "sending your messages" }
61
+ Chatterbox::Publishers.publishers.size.should == 1
62
+ Chatterbox::Publishers.clear!
63
+ Chatterbox::Publishers.publishers.size.should == 0
64
+ end
65
+
66
+ it "should allow registering with a block" do
67
+ pub1 = Chatterbox::Publishers.register { "sending your messages" }
68
+ pub2 = Chatterbox::Publishers.register { "announcing your news" }
69
+ Chatterbox::Publishers.publishers.should == [pub1, pub2]
70
+ end
71
+ end
72
+
73
+ end
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require 'action_controller'
3
+ require 'micronaut'
4
+ require 'micronaut-rails'
5
+ require 'mocha'
6
+
7
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
8
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
9
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
10
+
11
+ require 'chatterbox'
12
+
13
+ def not_in_editor?
14
+ !(ENV.has_key?('TM_MODE') || ENV.has_key?('EMACS') || ENV.has_key?('VIM'))
15
+ end
16
+
17
+ Micronaut.configure do |c|
18
+ c.mock_with :mocha
19
+ c.color_enabled = not_in_editor?
20
+ c.filter_run :focused => true
21
+ c.alias_example_to :fit, :focused => true
22
+ c.enable_controller_support :behaviour => { :describes => lambda { |dt| dt < ::ActionController::Base } }
23
+ end
@@ -0,0 +1,65 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. .. example_helper]))
2
+ require 'chatterbox'
3
+
4
+ describe Chatterbox::Mailer do
5
+
6
+ it "displays environment vars sorted" do
7
+ notice = {
8
+ :environment => {
9
+ "PATH" => "/usr/bin",
10
+ "PS1" => "$",
11
+ "TMPDIR" => "/tmp"
12
+ }
13
+ }
14
+ expected = <<EOL
15
+ PATH => /usr/bin
16
+ PS1 => $
17
+ TMPDIR => /tmp
18
+ EOL
19
+ mail = Chatterbox::Mailer.create_exception_notification(notice)
20
+ mail.body.should include(expected.strip)
21
+ end
22
+
23
+ it "displays any other details in the hash in the email body" do
24
+ notice = {
25
+ :details => { "message_id" => "user01-create", "current_user" => "Chris Liebing" },
26
+ :environment => { "foo" => "path" }
27
+ }
28
+ Chatterbox::Mailer.create_exception_notification(notice)
29
+ expected = <<EOL
30
+ Details
31
+ -------
32
+ current_user => Chris Liebing
33
+ message_id => user01-create
34
+ EOL
35
+ mail = Chatterbox::Mailer.create_exception_notification(notice)
36
+ mail.body.should include(expected.strip)
37
+ end
38
+
39
+ it "does not mutate the provided hash" do
40
+ notice = {'foo' => 'bar', :environment => {}}
41
+ Chatterbox::Mailer.create_exception_notification(notice)
42
+ notice.should == {'foo' => 'bar', :environment => {}}
43
+ end
44
+
45
+ describe "subject" do
46
+
47
+ it "extracts the subject from the given data hash when the key is a symbol" do
48
+ mail = Chatterbox::Mailer.create_exception_notification(:environment => {}, :summary => 'foo')
49
+ mail.subject.should include('foo')
50
+ end
51
+
52
+ it "extracts the subject from the given data hash when the key is a string" do
53
+ mail = Chatterbox::Mailer.create_exception_notification(:environment => {}, 'summary' => 'foo')
54
+ mail.subject.should include('foo')
55
+ end
56
+
57
+ it "includes the given prefix" do
58
+ Chatterbox::Mailer.email_prefix = '[super important email]'
59
+ mail = Chatterbox::Mailer.create_exception_notification(:environment => {})
60
+ mail.subject.should match(/\[super important email\]/)
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,147 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. example_helper]))
2
+
3
+ describe Chatterbox::Notification do
4
+
5
+ before do
6
+ Chatterbox.logger = Logger.new(nil)
7
+ end
8
+
9
+ describe "creating the notice" do
10
+
11
+ it "should safely handle nil" do
12
+ lambda {
13
+ Chatterbox::Notification.new(nil).notice
14
+ }.should_not raise_error
15
+ end
16
+
17
+ it "should use to_hash if message is not an exception and it responds_to possible" do
18
+ some_object = mock(:to_hash => {:foo => "bar"})
19
+ Chatterbox::Notification.new(some_object).notice.should include({:foo => "bar"})
20
+ end
21
+
22
+ it "should call to_s on anything that responds to it, as a last resort" do
23
+ some_object = mock(:to_s => "my to_s")
24
+ Chatterbox::Notification.new(some_object).notice.should include({:summary => "my to_s"})
25
+ end
26
+
27
+ it "merges hash passed in with default info" do
28
+ hash = {:message => "hey!"}
29
+ default_info = mock()
30
+ default_info.expects(:merge).with(hash)
31
+ notification = Chatterbox::Notification.new(hash)
32
+ notification.expects(:default_info).returns(default_info)
33
+ notification.notice
34
+ end
35
+
36
+ it "turns string notice into a hash keyed by notice" do
37
+ notification = Chatterbox::Notification.new("You have been placed on alert")
38
+ notification.notice.should include({:summary => "You have been placed on alert"})
39
+ end
40
+
41
+ it "always includes a summary" do
42
+ Chatterbox::Notification.new().notice.should include(:summary)
43
+ Chatterbox::Notification.new({}).notice.should include(:summary)
44
+ Chatterbox::Notification.new(RuntimeError.new).notice.should include(:summary)
45
+ end
46
+
47
+ it "should set summary to N/A if nothing provided" do
48
+ Chatterbox::Notification.new({}).notice.should include(:summary => "N/A")
49
+ Chatterbox::Notification.new({:foo => 'baz'}).notice.should include(:summary => "N/A")
50
+ end
51
+ end
52
+
53
+ describe "exceptions" do
54
+
55
+ def raised_exception
56
+ raise RuntimeError, "Your zing bats got mixed up with the snosh frazzles."
57
+ rescue => e
58
+ e
59
+ end
60
+
61
+ it "should extract exception info" do
62
+ exception = raised_exception
63
+ data = Chatterbox::Notification.new(exception).notice
64
+ data[:summary].should == "RuntimeError: Your zing bats got mixed up with the snosh frazzles."
65
+ data[:error_class].should == "RuntimeError"
66
+ data[:error_message].should == "Your zing bats got mixed up with the snosh frazzles."
67
+ data[:backtrace].should == exception.backtrace
68
+ end
69
+
70
+ it "should extract exception info from an exception in a hash" do
71
+ exception = raised_exception
72
+ data = Chatterbox::Notification.new(:exception => exception, :other_info => "yo dawg").notice
73
+ data[:summary].should == "RuntimeError: Your zing bats got mixed up with the snosh frazzles."
74
+ data[:error_class].should == "RuntimeError"
75
+ data[:error_message].should == "Your zing bats got mixed up with the snosh frazzles."
76
+ data[:backtrace].should == exception.backtrace
77
+ data[:other_info].should == "yo dawg"
78
+ end
79
+
80
+ it "should let extra data win over auto extracted exception data" do
81
+ exception = raised_exception
82
+ data = Chatterbox::Notification.new(:exception => exception, :summary => "I know what I'm doing, and we got an error").notice
83
+ data[:summary].should == "I know what I'm doing, and we got an error"
84
+ end
85
+
86
+ it "merges rails info and ruby info into the exception info" do
87
+ notification = Chatterbox::Notification.new(raised_exception)
88
+ rails = stub_everything(:version => "2.0", :root => "/rails/root", :env => "production")
89
+ notification.stubs(:rails_configuration).returns(rails)
90
+ notification.notice.should include(:rails_version => "2.0")
91
+ notification.notice.should include(:rails_root => "/rails/root")
92
+ notification.notice.should include(:rails_env => "production")
93
+ end
94
+
95
+ end
96
+
97
+ describe "hashes" do
98
+
99
+ it "merges rails info and ruby info into the notification" do
100
+ notification = Chatterbox::Notification.new({})
101
+ rails = stub_everything(:version => "2.0", :root => "/rails/root", :env => "production")
102
+ notification.stubs(:rails_configuration).returns(rails)
103
+ notification.notice.should include(:rails_version => "2.0")
104
+ notification.notice.should include(:rails_root => "/rails/root")
105
+ notification.notice.should include(:rails_env => "production")
106
+ end
107
+
108
+ end
109
+
110
+ describe "default info to be included with every notification" do
111
+
112
+ it "should return full ENV" do
113
+ environment = { "USER" => "jdoe", "PATH" => "/usr/bin", "HOME" => "/usr/home/jdoe" }
114
+ notification = Chatterbox::Notification.new
115
+ notification.stubs(:env).returns(environment)
116
+ notification.default_info.should include(:environment => environment)
117
+ end
118
+
119
+ it "should return Ruby version and platform" do
120
+ notification = Chatterbox::Notification.new
121
+ notification.stubs(:ruby_version).returns("1.8.6")
122
+ notification.stubs(:ruby_platform).returns("Mac OS X blah")
123
+ data = notification.default_info
124
+ data.should include(:ruby_version => "1.8.6")
125
+ data.should include(:ruby_platform => "Mac OS X blah")
126
+ end
127
+
128
+ describe "when Rails is defined" do
129
+
130
+ it "should return Rails info" do
131
+ notification = Chatterbox::Notification.new
132
+ rails = stub
133
+ rails.stubs(:root).returns("/some/path")
134
+ rails.stubs(:env).returns("production")
135
+ rails.stubs(:version).returns("2.1.2")
136
+ notification.stubs(:rails_configuration).returns(rails)
137
+
138
+ notification.default_info.should include(:rails_root => "/some/path")
139
+ notification.default_info.should include(:rails_env => "production")
140
+ notification.default_info.should include(:rails_version => "2.1.2")
141
+ end
142
+
143
+ end
144
+
145
+ end
146
+
147
+ end
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. example_helper]))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. .. rails init]))
3
+
4
+ ActionController::Routing::Routes.draw { |map| map.connect ':controller/:action/:id' }
5
+
6
+ class WidgetException < RuntimeError; end
7
+ class WidgetsController < ActionController::Base
8
+ include Chatterbox::RailsCatcher
9
+
10
+ def rescue_action e
11
+ rescue_action_in_public e
12
+ end
13
+
14
+ def rescue_action_in_public_without_chatterbox e
15
+ raise e
16
+ end
17
+
18
+ def index
19
+ raise_exception
20
+ render :text => "hi"
21
+ end
22
+
23
+ def raise_exception
24
+ raise WidgetException, "Bad dog!"
25
+ end
26
+ end
27
+
28
+ describe WidgetsController do
29
+
30
+ describe "rescue aliasing" do
31
+
32
+ it "should alias method chain" do
33
+ exception = RuntimeError.new
34
+ @controller.expects(:rescue_action_in_public_without_chatterbox).with(exception)
35
+ @controller.rescue_action_in_public(exception)
36
+ end
37
+ end
38
+
39
+ describe "exception handling" do
40
+
41
+ it "should raise on index" do
42
+ lambda {
43
+ get :index
44
+ }.should raise_error(WidgetException, "Bad dog!")
45
+ end
46
+
47
+ it "should send exception to handle_notice" do
48
+ Chatterbox.expects(:handle_notice).with(instance_of(WidgetException))
49
+ get :index rescue nil
50
+ end
51
+
52
+ end
53
+
54
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ # For backwards compatibility with old versions of Rails and plugins
2
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w[rails init]))
@@ -0,0 +1,87 @@
1
+ module Chatterbox
2
+
3
+ class Notification
4
+
5
+ attr_reader :message
6
+
7
+ def initialize(message = nil)
8
+ @message = message
9
+ end
10
+
11
+ def notice
12
+ hash = normalize_message_to_hash(message)
13
+ hash = exception_to_notice(hash)
14
+ default_info.merge(hash)
15
+ end
16
+
17
+ def normalize_message_to_hash(message)
18
+ case
19
+ when Exception === message
20
+ { :exception => message }
21
+ when message.respond_to?(:to_hash)
22
+ message.to_hash
23
+ when message.respond_to?(:to_s)
24
+ string_to_notice(message.to_s)
25
+ end
26
+ end
27
+
28
+ def default_info
29
+ default_info = {
30
+ :summary => "N/A",
31
+ :environment => env,
32
+ :ruby_version => ruby_version,
33
+ :ruby_platform => ruby_platform
34
+ }
35
+ default_info = add_ruby_info(default_info)
36
+ default_info = add_rails_info(default_info) if rails_configuration
37
+ default_info
38
+ end
39
+
40
+ def string_to_notice(message)
41
+ { :summary => message }
42
+ end
43
+
44
+ def exception_to_notice(hash)
45
+ return hash unless hash.key?(:exception)
46
+ exception = hash[:exception]
47
+ {
48
+ :summary => "#{exception.class.name}: #{exception.message}",
49
+ :error_class => exception.class.name,
50
+ :error_message => exception.message,
51
+ :backtrace => exception.backtrace,
52
+ }.merge(hash)
53
+ end
54
+
55
+ def add_rails_info(data)
56
+ data.merge({
57
+ :rails_env => rails_configuration.env,
58
+ :rails_root => rails_configuration.root,
59
+ :rails_version => rails_configuration.version
60
+ })
61
+ end
62
+
63
+ def add_ruby_info(data)
64
+ data.merge({
65
+ :ruby_version => ruby_version,
66
+ :ruby_platform => ruby_platform
67
+ })
68
+ end
69
+
70
+ def ruby_version
71
+ RUBY_VERSION
72
+ end
73
+
74
+ def ruby_platform
75
+ RUBY_PLATFORM
76
+ end
77
+
78
+ def env
79
+ ENV.to_hash
80
+ end
81
+
82
+ def rails_configuration
83
+ defined?(Rails) && Rails
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,24 @@
1
+ module Chatterbox
2
+
3
+ module RailsCatcher
4
+
5
+ def self.included(base)
6
+ 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'
7
+ base.send(:alias_method, :rescue_action_in_public_without_chatterbox, :rescue_action_in_public)
8
+ base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_chatterbox)
9
+ end
10
+ end
11
+
12
+ # Overrides the rescue_action method in ActionController::Base, but does not inhibit
13
+ # any custom processing that is defined with Rails 2's exception helpers.
14
+ def rescue_action_in_public_with_chatterbox exception
15
+ Chatterbox.logger.debug { "#{self.class}#rescue_action_in_public_with_chatterbox: caught exception #{exception} - about to handle"}
16
+ Chatterbox.handle_notice(exception)
17
+ Chatterbox.logger.debug { "#{self.class}#rescue_action_in_public_with_chatterbox: handing exception #{exception} off to normal rescue handling"}
18
+
19
+ rescue_action_in_public_without_chatterbox(exception)
20
+ end
21
+
22
+ end
23
+
24
+ end
data/lib/chatterbox.rb ADDED
@@ -0,0 +1,52 @@
1
+ require File.join(File.dirname(__FILE__), *%w[chatterbox notification])
2
+ require File.join(File.dirname(__FILE__), *%w[consumers])
3
+
4
+ module Chatterbox
5
+
6
+ def handle_notice(message)
7
+ notice = Notification.new(message).notice
8
+ publish_notice(notice)
9
+ end
10
+
11
+ def publish_notice(notice)
12
+ Publishers.publishers.each { |p| p.call(notice) }
13
+ end
14
+
15
+ def logger
16
+ @logger ||= rails_default_logger || Logger.new(STDOUT)
17
+ end
18
+
19
+ def logger=(logger)
20
+ @logger = logger
21
+ end
22
+
23
+ def rails_default_logger
24
+ defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : nil
25
+ end
26
+
27
+ extend self
28
+
29
+ module Publishers
30
+
31
+ class << self
32
+
33
+ def publishers
34
+ @publishers ||= []
35
+ end
36
+
37
+ def register(&blk)
38
+ Chatterbox.logger.debug { "Registering publisher: #{blk}"}
39
+ publishers << blk
40
+ blk
41
+ end
42
+
43
+ def clear!
44
+ @publishers = []
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
@@ -0,0 +1,64 @@
1
+ module Chatterbox
2
+ class EmailConsumer
3
+
4
+ attr_reader :notice
5
+
6
+ def initialize(notice)
7
+ @notice = notice
8
+ end
9
+
10
+ def process
11
+ Chatterbox.logger.debug { "Mailing notification #{notice[:summary]}"}
12
+ Mailer.deliver_exception_notification(notice)
13
+ end
14
+
15
+ end
16
+
17
+ # Things taken out of the hash for exception emails:
18
+ #
19
+ # :details => a hash of details about the context of the error -- ie current state, request info, etc...any info
20
+ # related to the exception that is domain specific
21
+ # :error_class => taken from the Exception
22
+ # :error_message => taken from the Exception
23
+ # :backtrace => taken from the Exception
24
+ class Mailer < ActionMailer::Base
25
+ @@sender_address = %("Exception Notifier" <exception.notifier@default.com>)
26
+ cattr_accessor :sender_address
27
+
28
+ @@exception_recipients = []
29
+ cattr_accessor :exception_recipients
30
+
31
+ @@email_prefix = "[ERROR] "
32
+ cattr_accessor :email_prefix
33
+
34
+ self.template_root = File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. views]))
35
+
36
+ def self.reloadable?() false end
37
+
38
+ def exception_notification(data={})
39
+ data = data.dup.symbolize_keys
40
+
41
+ content_type "text/plain"
42
+
43
+ subject "#{email_prefix} Error - #{data.delete(:summary)}"
44
+
45
+ recipients exception_recipients
46
+ from sender_address
47
+
48
+ body data
49
+ end
50
+
51
+ private
52
+
53
+ def sanitize_backtrace(trace)
54
+ re = Regexp.new(/^#{Regexp.escape(rails_root)}/)
55
+ trace.map { |line| Pathname.new(line.gsub(re, "[RAILS_ROOT]")).cleanpath.to_s }
56
+ end
57
+
58
+ def rails_root
59
+ @rails_root ||= Pathname.new(RAILS_ROOT).cleanpath.to_s
60
+ end
61
+
62
+ end
63
+
64
+ end
data/lib/consumers.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'action_mailer'
2
+ require File.join(File.dirname(__FILE__), *%w[consumers email_consumer])
data/rails/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. lib chatterbox]))
2
+ require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. lib chatterbox rails_catcher]))
data/todo.markdown ADDED
@@ -0,0 +1,7 @@
1
+ * shouldn't require _action_mailer_ unless really needed by the mailer
2
+
3
+ # rails catcher
4
+ * make sure we push through request info for exceptions
5
+ * wire ignore exceptions in rails catcher
6
+ * wire up common idioms (or easy ability to) to grab current_user from request, current_url, etc
7
+
data/version.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 3
4
+ :patch: 3
@@ -0,0 +1,19 @@
1
+ Error Message
2
+ --------------
3
+ <%= @error_class %>: <%= @error_message %>
4
+
5
+ Details
6
+ -------
7
+ <% @details.keys.map(&:to_s).sort.each do |key| -%>
8
+ <%= %[#{key} => #{@details[key]}] %>
9
+ <% end if @details -%>
10
+
11
+ Backtrace
12
+ ----------
13
+ <%= @backtrace.join "\n" if @backtrace %>
14
+
15
+ Environment
16
+ ------------
17
+ <% @environment.keys.sort.each do |key| -%>
18
+ <%= %[#{key} => #{@environment[key]}] %>
19
+ <% end -%>
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chatterbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.3
5
+ platform: ruby
6
+ authors:
7
+ - Rob Sanheim
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-05 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: mocha
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: actioncontroller
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: spicycode-micronaut
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: spicycode-micronaut-rails
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ description:
56
+ email: rsanheim@gmail.com
57
+ executables: []
58
+
59
+ extensions: []
60
+
61
+ extra_rdoc_files:
62
+ - LICENSE
63
+ - README.markdown
64
+ files:
65
+ - .gitignore
66
+ - .treasure_map.rb
67
+ - LICENSE
68
+ - README.markdown
69
+ - Rakefile
70
+ - chatterbox.gemspec
71
+ - examples/chatterbox_example.rb
72
+ - examples/example_helper.rb
73
+ - examples/lib/chatterbox/consumers/email_consumer_example.rb
74
+ - examples/lib/chatterbox/notification_example.rb
75
+ - examples/lib/chatterbox/rails_catcher_example.rb
76
+ - init.rb
77
+ - lib/chatterbox.rb
78
+ - lib/chatterbox/notification.rb
79
+ - lib/chatterbox/rails_catcher.rb
80
+ - lib/consumers.rb
81
+ - lib/consumers/email_consumer.rb
82
+ - rails/init.rb
83
+ - todo.markdown
84
+ - version.yml
85
+ - views/chatterbox/mailer/exception_notification.erb
86
+ has_rdoc: true
87
+ homepage: http://github.com/rsanheim/chatterbox
88
+ licenses: []
89
+
90
+ post_install_message:
91
+ rdoc_options:
92
+ - --charset=UTF-8
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: "0"
100
+ version:
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: "0"
106
+ version:
107
+ requirements: []
108
+
109
+ rubyforge_project:
110
+ rubygems_version: 1.3.5
111
+ signing_key:
112
+ specification_version: 3
113
+ summary: Notifications and messages
114
+ test_files:
115
+ - examples/chatterbox_example.rb
116
+ - examples/example_helper.rb
117
+ - examples/lib/chatterbox/consumers/email_consumer_example.rb
118
+ - examples/lib/chatterbox/notification_example.rb
119
+ - examples/lib/chatterbox/rails_catcher_example.rb