relevance-chatterbox 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .specification
2
+ 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,54 @@
1
+ Chatterbox
2
+ ==========================================
3
+
4
+ TODO summary
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
+
29
+
30
+
31
+
32
+
33
+ Rails Exception Integration
34
+ ---------------------------------------
35
+
36
+ Example 1
37
+ ---------------------------------------
38
+
39
+ Example 2
40
+ ---------------------------------------
41
+
42
+ Bugs & Patches
43
+ --------------
44
+
45
+ Links
46
+ -------------
47
+
48
+ Contributors
49
+ ------------
50
+ * Rob Sanheim
51
+
52
+ Copyrights
53
+ ------------
54
+ * Copyright &copy; 2008-2009 [Relevance, Inc.](http://www.thinkrelevance.com/), under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "chatterbox"
8
+ gem.summary = %Q{TODO}
9
+ gem.email = "rsanheim@gmail.com"
10
+ gem.homepage = "http://github.com/relevance/chatterbox"
11
+ gem.authors = ["Rob Sanheim"]
12
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
13
+ end
14
+
15
+ rescue LoadError
16
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
17
+ end
18
+
19
+ require 'micronaut/rake_task'
20
+ Micronaut::RakeTask.new(:examples) do |examples|
21
+ examples.pattern = 'examples/**/*_example.rb'
22
+ examples.ruby_opts << '-Ilib -Iexamples'
23
+ end
24
+
25
+ Micronaut::RakeTask.new(:rcov) do |examples|
26
+ examples.pattern = 'examples/**/*_example.rb'
27
+ examples.rcov_opts = '-Ilib -Iexamples'
28
+ examples.rcov = true
29
+ end
30
+
31
+
32
+ task :default => :examples
@@ -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,138 @@
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 convert exception to notice" do
18
+ exception = RuntimeError.new
19
+ notification = Chatterbox::Notification.new(exception)
20
+ notification.expects(:exception_to_notice).with(exception).returns({})
21
+ notification.notice
22
+ end
23
+
24
+ it "should use to_hash if message is not an exception and it responds_to possible" do
25
+ some_object = mock(:to_hash => {:foo => "bar"})
26
+ Chatterbox::Notification.new(some_object).notice.should include({:foo => "bar"})
27
+ end
28
+
29
+ it "should call to_s on anything that responds to it, as a last resort" do
30
+ some_object = mock(:to_s => "my to_s")
31
+ Chatterbox::Notification.new(some_object).notice.should include({:summary => "my to_s"})
32
+ end
33
+
34
+ it "merges hash passed in with default info" do
35
+ hash = {:message => "hey!"}
36
+ default_info = mock()
37
+ default_info.expects(:merge).with(hash)
38
+ notification = Chatterbox::Notification.new(hash)
39
+ notification.expects(:default_info).returns(default_info)
40
+ notification.notice
41
+ end
42
+
43
+ it "turns string notice into a hash keyed by notice" do
44
+ notification = Chatterbox::Notification.new("You have been placed on alert")
45
+ notification.notice.should include({:summary => "You have been placed on alert"})
46
+ end
47
+
48
+ it "always includes a summary" do
49
+ Chatterbox::Notification.new().notice.should include(:summary)
50
+ Chatterbox::Notification.new({}).notice.should include(:summary)
51
+ Chatterbox::Notification.new(RuntimeError.new).notice.should include(:summary)
52
+ end
53
+
54
+ it "should set summary to N/A if nothing provided" do
55
+ Chatterbox::Notification.new({}).notice.should include(:summary => "N/A")
56
+ Chatterbox::Notification.new({:foo => 'baz'}).notice.should include(:summary => "N/A")
57
+ end
58
+ end
59
+
60
+ describe "exceptions" do
61
+
62
+ def raised_exception
63
+ raise RuntimeError, "Your zing bats got mixed up with the snosh frazzles."
64
+ rescue => e
65
+ e
66
+ end
67
+
68
+ it "should extract exception info" do
69
+ exception = raised_exception
70
+ data = Chatterbox::Notification.new(exception).notice
71
+ data[:summary].should == "RuntimeError: Your zing bats got mixed up with the snosh frazzles."
72
+ data[:error_class].should == "RuntimeError"
73
+ data[:error_message].should == "Your zing bats got mixed up with the snosh frazzles."
74
+ data[:backtrace].should == exception.backtrace
75
+ end
76
+
77
+ it "merges rails info and ruby info into the exception info" do
78
+ notification = Chatterbox::Notification.new(raised_exception)
79
+ rails = stub_everything(:version => "2.0", :root => "/rails/root", :env => "production")
80
+ notification.stubs(:rails_configuration).returns(rails)
81
+ notification.notice.should include(:rails_version => "2.0")
82
+ notification.notice.should include(:rails_root => "/rails/root")
83
+ notification.notice.should include(:rails_env => "production")
84
+ end
85
+
86
+ end
87
+
88
+ describe "hashes" do
89
+
90
+ it "merges rails info and ruby info into the notification" do
91
+ notification = Chatterbox::Notification.new({})
92
+ rails = stub_everything(:version => "2.0", :root => "/rails/root", :env => "production")
93
+ notification.stubs(:rails_configuration).returns(rails)
94
+ notification.notice.should include(:rails_version => "2.0")
95
+ notification.notice.should include(:rails_root => "/rails/root")
96
+ notification.notice.should include(:rails_env => "production")
97
+ end
98
+
99
+ end
100
+
101
+ describe "default info to be included with every notification" do
102
+
103
+ it "should return full ENV" do
104
+ environment = { "USER" => "jdoe", "PATH" => "/usr/bin", "HOME" => "/usr/home/jdoe" }
105
+ notification = Chatterbox::Notification.new
106
+ notification.stubs(:env).returns(environment)
107
+ notification.default_info.should include(:environment => environment)
108
+ end
109
+
110
+ it "should return Ruby version and platform" do
111
+ notification = Chatterbox::Notification.new
112
+ notification.stubs(:ruby_version).returns("1.8.6")
113
+ notification.stubs(:ruby_platform).returns("Mac OS X blah")
114
+ data = notification.default_info
115
+ data.should include(:ruby_version => "1.8.6")
116
+ data.should include(:ruby_platform => "Mac OS X blah")
117
+ end
118
+
119
+ describe "when Rails is defined" do
120
+
121
+ it "should return Rails info" do
122
+ notification = Chatterbox::Notification.new
123
+ rails = stub
124
+ rails.stubs(:root).returns("/some/path")
125
+ rails.stubs(:env).returns("production")
126
+ rails.stubs(:version).returns("2.1.2")
127
+ notification.stubs(:rails_configuration).returns(rails)
128
+
129
+ notification.default_info.should include(:rails_root => "/some/path")
130
+ notification.default_info.should include(:rails_env => "production")
131
+ notification.default_info.should include(:rails_version => "2.1.2")
132
+ end
133
+
134
+ end
135
+
136
+ end
137
+
138
+ 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]))
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,84 @@
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
+ default_info.merge(hash)
14
+ end
15
+
16
+ def normalize_message_to_hash(message)
17
+ case
18
+ when Exception === message
19
+ exception_to_notice(message)
20
+ when message.respond_to?(:to_hash)
21
+ message.to_hash
22
+ when message.respond_to?(:to_s)
23
+ string_to_notice(message.to_s)
24
+ end
25
+ end
26
+
27
+ def default_info
28
+ default_info = {
29
+ :summary => "N/A",
30
+ :environment => env,
31
+ :ruby_version => ruby_version,
32
+ :ruby_platform => ruby_platform
33
+ }
34
+ default_info = add_ruby_info(default_info)
35
+ default_info = add_rails_info(default_info) if rails_configuration
36
+ default_info
37
+ end
38
+
39
+ def string_to_notice(message)
40
+ { :summary => message }
41
+ end
42
+
43
+ def exception_to_notice(exception)
44
+ {
45
+ :summary => "#{exception.class.name}: #{exception.message}",
46
+ :error_class => exception.class.name,
47
+ :error_message => exception.message,
48
+ :backtrace => exception.backtrace,
49
+ }
50
+ end
51
+
52
+ def add_rails_info(data)
53
+ data.merge({
54
+ :rails_env => rails_configuration.env,
55
+ :rails_root => rails_configuration.root,
56
+ :rails_version => rails_configuration.version
57
+ })
58
+ end
59
+
60
+ def add_ruby_info(data)
61
+ data.merge({
62
+ :ruby_version => ruby_version,
63
+ :ruby_platform => ruby_platform
64
+ })
65
+ end
66
+
67
+ def ruby_version
68
+ RUBY_VERSION
69
+ end
70
+
71
+ def ruby_platform
72
+ RUBY_PLATFORM
73
+ end
74
+
75
+ def env
76
+ ENV.to_hash
77
+ end
78
+
79
+ def rails_configuration
80
+ defined?(Rails) && Rails
81
+ end
82
+
83
+ end
84
+ 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/consumers.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'action_mailer'
2
+ require File.join(File.dirname(__FILE__), *%w[consumers email_consumer])
@@ -0,0 +1,55 @@
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
+ class Mailer < ActionMailer::Base
18
+ @@sender_address = %("Exception Notifier" <exception.notifier@default.com>)
19
+ cattr_accessor :sender_address
20
+
21
+ @@exception_recipients = []
22
+ cattr_accessor :exception_recipients
23
+
24
+ @@email_prefix = "[ERROR] "
25
+ cattr_accessor :email_prefix
26
+
27
+ self.template_root = File.expand_path(File.join(File.dirname(__FILE__), *%w[.. .. views]))
28
+
29
+ def self.reloadable?() false end
30
+
31
+ def exception_notification(data={})
32
+ content_type "text/plain"
33
+
34
+ subject "#{email_prefix} Error - #{data['summary']}"
35
+
36
+ recipients exception_recipients
37
+ from sender_address
38
+
39
+ body data
40
+ end
41
+
42
+ private
43
+
44
+ def sanitize_backtrace(trace)
45
+ re = Regexp.new(/^#{Regexp.escape(rails_root)}/)
46
+ trace.map { |line| Pathname.new(line.gsub(re, "[RAILS_ROOT]")).cleanpath.to_s }
47
+ end
48
+
49
+ def rails_root
50
+ @rails_root ||= Pathname.new(RAILS_ROOT).cleanpath.to_s
51
+ end
52
+
53
+ end
54
+
55
+ end
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/version.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 1
@@ -0,0 +1,11 @@
1
+ Error Message
2
+ --------------
3
+ <%= @error_class %>: <%= @error_message %>
4
+
5
+ Backtrace
6
+ ----------
7
+ <%= @backtrace.join "\n" %>
8
+
9
+ Environment
10
+ ------------
11
+ <%= @environment %>
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: relevance-chatterbox
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob Sanheim
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-20 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: rsanheim@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - LICENSE
24
+ - README.markdown
25
+ files:
26
+ - .gitignore
27
+ - .treasure_map.rb
28
+ - LICENSE
29
+ - README.markdown
30
+ - Rakefile
31
+ - examples/chatterbox_example.rb
32
+ - examples/example_helper.rb
33
+ - examples/lib/chatterbox/notification_example.rb
34
+ - examples/lib/chatterbox/rails_catcher_example.rb
35
+ - init.rb
36
+ - lib/chatterbox.rb
37
+ - lib/chatterbox/notification.rb
38
+ - lib/chatterbox/rails_catcher.rb
39
+ - lib/consumers.rb
40
+ - lib/consumers/email_consumer.rb
41
+ - rails/init.rb
42
+ - version.yml
43
+ - views/chatterbox/mailer/exception_notification.erb
44
+ has_rdoc: false
45
+ homepage: http://github.com/relevance/chatterbox
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --charset=UTF-8
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.2.0
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: TODO
70
+ test_files:
71
+ - examples/chatterbox_example.rb
72
+ - examples/example_helper.rb
73
+ - examples/lib/chatterbox/notification_example.rb
74
+ - examples/lib/chatterbox/rails_catcher_example.rb