chatterbox 0.3.3

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/.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