merb_hoptoad_notifier-secure 1.0.10

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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Corey Donohoe<atmos@atmos.org>, Joakim Kolsjö<trejje@gmail.com>
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 ADDED
@@ -0,0 +1,59 @@
1
+ merb_hoptoad_notifier
2
+ ---------------------------------------------
3
+ This is merb plugin for exception notification with hoptoad. It should work with
4
+ any merb app that's based on merb 1.0 and higher
5
+
6
+ This plugin: http://github.com/atmos/merb_hoptoad_notifier/tree/master
7
+ The original: http://github.com/thoughtbot/hoptoad_notifier/tree/master
8
+
9
+ Usage:
10
+
11
+ 1) Get your api key for your app from hoptoadapp.com
12
+
13
+ 2) Add the api key to config/hoptoad.yml with a similar syntax as the following
14
+ ---
15
+ :development: &defaults
16
+ :api_key: ZOMGLOLROFLMAO
17
+
18
+ :rake:
19
+ <<: *defaults
20
+
21
+ :test:
22
+ <<: *defaults
23
+
24
+ :production:
25
+ :api_key: UBERSECRETSHIT
26
+
27
+
28
+ 3) Require hoptoad in init.rb
29
+ require 'merb_hoptoad_notifier'
30
+
31
+ 4) Add the following method to your Exceptions controller. Depending on your merb version you'll need to use the exceptions,standard_error, or internal_server error as the action name. Kinda weak, but the API changed a lot in 0.9.x
32
+
33
+ class Exceptions < Merb::Controller
34
+ if %w( staging production ).include?(Merb.env)
35
+ def standard_error
36
+ HoptoadNotifier.notify_hoptoad(request, session)
37
+ render
38
+ end
39
+ end
40
+ end
41
+
42
+ 5) Restart the server, trigger an error(in staging or prod) and check that it arrived at hoptoad :)
43
+
44
+
45
+ Filtersing your post environment
46
+ --------------------------------
47
+ If you have environmental variables set in your ruby process that should not be sent to hoptoad,
48
+ there's a mechanism for filtering those attributes now. Throw something like this in
49
+ config/init.rb
50
+
51
+ Merb::BootLoader.after_app_loads do
52
+ HoptoadNotifier.environment_filters = %w(^AWS ^EC2 SECRET PRIVATE KEY)
53
+ end
54
+
55
+ Each of these words will be compiled into a regex so you should be able to use anchors if needed.
56
+
57
+ Thanks to the following GitHubbers
58
+ ----------------------------------
59
+ joakimk, fairchild and cv.
data/Rakefile ADDED
@@ -0,0 +1,74 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'rubygems/specification'
4
+ require 'date'
5
+ require 'merb-core/version'
6
+ require 'merb-core/tasks/merb_rake_helper'
7
+ require 'spec/rake/spectask'
8
+
9
+ install_home = ENV['GEM_HOME'] ? "-i #{ENV['GEM_HOME']}" : ""
10
+
11
+ def sudo
12
+ windows = (PLATFORM =~ /win32|cygwin/) rescue nil
13
+ ENV['MERB_SUDO'] ||= "sudo"
14
+ sudo = windows ? "" : ENV['MERB_SUDO']
15
+ end
16
+
17
+ NAME = "merb_hoptoad_notifier"
18
+ GEM_VERSION = "1.0.9.1"
19
+ AUTHOR = "Corey Donohoe"
20
+ EMAIL = 'atmos@atmos.org'
21
+ HOMEPAGE = "http://github.com/atmos/merb_hoptoad_notifier"
22
+ SUMMARY = "Merb plugin that provides hoptoad exception notification"
23
+
24
+ spec = Gem::Specification.new do |s|
25
+ s.rubyforge_project = 'merb'
26
+ s.name = NAME
27
+ s.version = GEM_VERSION
28
+ s.platform = Gem::Platform::RUBY
29
+ s.has_rdoc = true
30
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
31
+ s.summary = SUMMARY
32
+ s.description = s.summary
33
+ s.author = AUTHOR
34
+ s.email = EMAIL
35
+ s.homepage = HOMEPAGE
36
+ s.add_dependency('merb-core', '>= 1.0.9')
37
+ s.require_path = 'lib'
38
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
39
+ end
40
+
41
+ Rake::GemPackageTask.new(spec) do |pkg|
42
+ pkg.gem_spec = spec
43
+ end
44
+
45
+ desc "install the plugin locally"
46
+ task :install => [:package] do
47
+ sh %{#{sudo} gem install #{install_home} pkg/#{NAME}-#{GEM_VERSION} --no-update-sources}
48
+ end
49
+
50
+ desc "create a gemspec file"
51
+ task :make_spec do
52
+ File.open("#{NAME}.gemspec", "w") do |file|
53
+ file.puts spec.to_ruby
54
+ end
55
+ end
56
+
57
+ namespace :jruby do
58
+ desc "Run :package and install the resulting .gem with jruby"
59
+ task :install => :package do
60
+ sh %{#{sudo} jruby -S gem install #{install_home} pkg/#{NAME}-#{GEM_VERSION}.gem --no-rdoc --no-ri}
61
+ end
62
+ end
63
+
64
+ Spec::Rake::SpecTask.new(:default) do |t|
65
+ t.spec_opts << %w(-fs --color) << %w(-O spec/spec.opts)
66
+ t.spec_opts << '--loadby' << 'random'
67
+ t.spec_files = Dir["spec/*_spec.rb"]
68
+ t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
69
+ t.rcov_opts << '--exclude' << '.gem/'
70
+
71
+ t.rcov_opts << '--text-summary'
72
+ t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
73
+ t.rcov_opts << '--only-uncovered'
74
+ end
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ TODO:
2
+ See if there's any more rich information we could provide to hoptoad
@@ -0,0 +1,10 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/merb_hoptoad_notifier/hoptoad_notifier')
2
+ require File.expand_path(File.dirname(__FILE__)+'/merb_hoptoad_notifier/hoptoad_mixin')
3
+
4
+ # make sure we're running inside Merb
5
+ if defined?(Merb::Plugins)
6
+ Merb::BootLoader.after_app_loads do
7
+ HoptoadNotifier.configure
8
+ end
9
+ Merb::Plugins.add_rakefiles "merb_hoptoad_notifier/merbtasks"
10
+ end
@@ -0,0 +1,17 @@
1
+ require File.expand_path(File.dirname(__FILE__)+'/hoptoad_notifier')
2
+
3
+ module HoptoadMixin
4
+ def notify_hoptoad(request=nil, session=nil)
5
+ request ||= self.request
6
+ session ||= self.session
7
+
8
+ HoptoadNotifier.notify_hoptoad(request, session)
9
+ end
10
+
11
+ def warn_hoptoad(message, request=nil, session=nil, options={})
12
+ request ||= self.request
13
+ session ||= self.session
14
+
15
+ HoptoadNotifier.warn_hoptoad(message, request, session, options)
16
+ end
17
+ end
@@ -0,0 +1,181 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+
4
+ module HoptoadNotifier
5
+ class << self
6
+ attr_accessor :api_key, :logger, :secure
7
+
8
+ alias_method :secure?, :secure
9
+
10
+ def configure
11
+ key = YAML.load_file(Merb.root / 'config' / 'hoptoad.yml')
12
+ if key
13
+ env = key[Merb.env.to_sym]
14
+ env ? @api_key = env[:api_key] : raise(ArgumentError, "No hoptoad key for Merb environment #{Merb.env}")
15
+ end
16
+ self.secure = Merb::Plugins.config[:merb_hoptoad_notifier][:secure]
17
+ end
18
+
19
+ def url
20
+ @url ||= begin
21
+ if secure?
22
+ URI.parse("https://hoptoadapp.com:443/notices/")
23
+ else
24
+ URI.parse("http://hoptoadapp.com:80/notices/")
25
+ end
26
+ end
27
+ end
28
+
29
+ def logger
30
+ @logger || Merb.logger
31
+ end
32
+
33
+ def environment_filters
34
+ @environment_filters ||= %w(AWS_ACCESS_KEY AWS_SECRET_ACCESS_KEY AWS_ACCOUNT SSH_AUTH_SOCK)
35
+ end
36
+
37
+ def warn_hoptoad(message, request, session, options={})
38
+ return if request.nil?
39
+ params = request.params
40
+
41
+ data = {
42
+ :api_key => HoptoadNotifier.api_key,
43
+ :error_class => options[:error_class] || message,
44
+ :error_message => message,
45
+ :backtrace => caller,
46
+ :environment => ENV.to_hash
47
+ }
48
+
49
+ data[:request] = {
50
+ :params => params
51
+ }
52
+
53
+ data[:environment] = clean_hoptoad_environment(ENV.to_hash.merge(request.env))
54
+ data[:environment][:RAILS_ENV] = Merb.env
55
+
56
+ data[:session] = {
57
+ :key => session.instance_variable_get("@session_id"),
58
+ :data => session.to_hash
59
+ }
60
+
61
+ send_to_hoptoad :notice => default_notice_options.merge(data)
62
+ end
63
+
64
+ def notify_hoptoad(request, session)
65
+ return if request.nil?
66
+ params = request.params
67
+ ids = []
68
+
69
+ request.exceptions.each do |exception|
70
+ data = {
71
+ :api_key => HoptoadNotifier.api_key,
72
+ :error_class => Extlib::Inflection.camelize(exception.class.name),
73
+ :error_message => "#{Extlib::Inflection.camelize(exception.class.name)}: #{exception.message}",
74
+ :backtrace => exception.backtrace,
75
+ :environment => ENV.to_hash
76
+ }
77
+
78
+ data[:request] = {
79
+ :params => params
80
+ }
81
+
82
+ data[:environment] = clean_hoptoad_environment(ENV.to_hash.merge(request.env))
83
+ data[:environment][:RAILS_ENV] = Merb.env
84
+
85
+ data[:session] = {
86
+ :key => session.instance_variable_get("@session_id"),
87
+ :data => session.to_hash
88
+ }
89
+
90
+ id = send_to_hoptoad :notice => default_notice_options.merge(data)
91
+ ids << id if id
92
+ end
93
+ ids.empty? ? false : ids
94
+ end
95
+
96
+ def notify_hoptoad_exception(exception)
97
+ data = {
98
+ :api_key => HoptoadNotifier.api_key,
99
+ :error_class => Extlib::Inflection.camelize(exception.class.name),
100
+ :error_message => "#{Extlib::Inflection.camelize(exception.class.name)}: #{exception.message}",
101
+ :backtrace => exception.backtrace,
102
+ :environment => ENV.to_hash
103
+ }
104
+
105
+ data[:environment][:RAILS_ENV] = Merb.env
106
+
107
+ send_to_hoptoad :notice => default_notice_options.merge(data)
108
+ end
109
+
110
+
111
+ def send_to_hoptoad(data) #:nodoc:
112
+ url = self.url
113
+
114
+ connection = Net::HTTP.new(url.host, url.port)
115
+ connection.read_timeout = 5 # seconds
116
+ connection.open_timeout = 2 # seconds
117
+ connection.use_ssl = !!HoptoadNotifier.secure
118
+ connection.verify_mode = OpenSSL::SSL::VERIFY_NONE
119
+ connection.start do |http|
120
+ headers = {
121
+ 'Content-type' => 'application/x-yaml',
122
+ 'Accept' => 'text/xml, application/xml'
123
+ }
124
+
125
+ response = begin
126
+ http.post(url.path, clean_non_serializable_data(data).to_yaml, headers)
127
+ rescue TimeoutError => e
128
+ logger.error "Timeout while contacting the Hoptoad server."
129
+ nil
130
+ end
131
+ case response
132
+ when Net::HTTPSuccess then
133
+ logger.info "Hoptoad Success: #{response.class}"
134
+ return response.body[/<id type=.integer.>(\d+)<\/id>/, 1].to_i
135
+ else
136
+ logger.error "Hoptoad Failure: #{response.class}\n#{response.body if response.respond_to? :body}"
137
+ end
138
+ end
139
+ end
140
+
141
+ def default_notice_options #:nodoc:
142
+ {
143
+ :api_key => HoptoadNotifier.api_key,
144
+ :error_message => 'Notification',
145
+ :backtrace => nil,
146
+ :request => {},
147
+ :session => {},
148
+ :environment => {}
149
+ }
150
+ end
151
+
152
+ def clean_non_serializable_data(notice) #:nodoc:
153
+ notice.select{|k,v| serializable?(v) }.inject({}) do |h, pair|
154
+ h[pair.first] = pair.last.is_a?(Hash) ? clean_non_serializable_data(pair.last) : pair.last
155
+ h
156
+ end
157
+ end
158
+
159
+ def serializable?(value) #:nodoc:
160
+ value.is_a?(Fixnum) ||
161
+ value.is_a?(Array) ||
162
+ value.is_a?(String) ||
163
+ value.is_a?(Hash) ||
164
+ value.is_a?(Bignum)
165
+ end
166
+
167
+ def stringify_keys(hash) #:nodoc:
168
+ hash.inject({}) do |h, pair|
169
+ h[pair.first.to_s] = pair.last.is_a?(Hash) ? stringify_keys(pair.last) : pair.last
170
+ h
171
+ end
172
+ end
173
+ def clean_hoptoad_environment(env) #:nodoc:
174
+ env.each do |k, v|
175
+ env[k] = "[FILTERED]" if HoptoadNotifier.environment_filters.any? do |filter|
176
+ k.to_s.match(/#{filter}/)
177
+ end
178
+ end
179
+ end
180
+ end
181
+ end
@@ -0,0 +1,2 @@
1
+ namespace :merb_hoptoad_notifier do
2
+ end
@@ -0,0 +1,9 @@
1
+ ---
2
+ :development: &defaults
3
+ :api_key: ZOMGLOLROFLMAO
4
+
5
+ :test:
6
+ <<: *defaults
7
+
8
+ :production:
9
+ :api_key: UBERSECRETSHIT
@@ -0,0 +1,113 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "HoptoadNotifier" do
4
+ include Merb::Spec::Helpers
5
+
6
+ before(:each) do
7
+ stub(Merb).env { :production }
8
+ stub(Merb).root { Dir.tmpdir }
9
+
10
+ @http = Net::HTTP.new('hoptoadapp.com')
11
+ @headers = { "Content-type" => "application/x-yaml", "Accept" => "text/xml, application/xml" }
12
+ @config = {:development => {:api_key=>"ZOMGLOLROFLMAO"}, :production => {:api_key=>"UBERSECRETSHIT"}, :test => {:api_key=>"ZOMGLOLROFLMAO"}}
13
+ end
14
+
15
+ it "should define a constant" do
16
+ HoptoadNotifier.should_not be_nil
17
+ end
18
+
19
+ describe ".configure" do
20
+ before(:each) do
21
+ mock(YAML).load_file(File.join(Merb.root / 'config' / 'hoptoad.yml')) { @config }
22
+ HoptoadNotifier.configure
23
+ end
24
+ it "should know the api key after configuring" do
25
+ HoptoadNotifier.api_key.should == 'UBERSECRETSHIT'
26
+ end
27
+ end
28
+
29
+ describe ".stringify_key" do
30
+ it "should turn string keys into symbols" do
31
+ HoptoadNotifier.stringify_keys({'foo' => 'bar', :baz => 'foo', :bar => 'foo'}).should == { 'foo' => 'bar', 'baz' => 'foo', 'bar' => 'foo'}
32
+ end
33
+ end
34
+
35
+
36
+ describe "notification" do
37
+ before(:each) do
38
+ stub(Net::HTTP).new('hoptoadapp.com', 80, nil, nil, nil, nil) { @http }
39
+ mock(YAML).load_file(File.join(Merb.root / 'config' / 'hoptoad.yml')) { @config }
40
+ HoptoadNotifier.configure
41
+ end
42
+
43
+ describe ".default_notice_options" do
44
+ it "should return sane defaults" do
45
+ HoptoadNotifier.default_notice_options.should == {
46
+ :api_key => HoptoadNotifier.api_key,
47
+ :error_message => 'Notification',
48
+ :backtrace => nil,
49
+ :request => {},
50
+ :session => {},
51
+ :environment => {}
52
+ }
53
+ end
54
+ end
55
+
56
+ describe ".notify_hoptoad" do
57
+ describe "bad input" do
58
+ it "should handle nil input" do
59
+ HoptoadNotifier.notify_hoptoad(nil, nil).should be_nil
60
+ end
61
+ end
62
+
63
+ describe "good input" do
64
+ before(:each) do
65
+ mock(HoptoadNotifier).send_to_hoptoad(anything).times(2) { true }
66
+ @request = setup_merb_request
67
+ mock(@request).exceptions { [RuntimeError.new('ZOMG'), RuntimeError.new('ORLY')]}
68
+ mock(@request).params { {"q"=>"0017000000SmnJ0"} }
69
+ end
70
+ it "should return true" do
71
+ HoptoadNotifier.notify_hoptoad(@request, {}).should be_true
72
+ end
73
+ end
74
+ end
75
+
76
+ describe ".send_to_hoptoad" do
77
+ describe "any 2XX response" do
78
+ before(:each) do
79
+ response = Net::HTTPOK.new('1.1', 200, 'Wazzup?')
80
+ mock(@http).post("/notices/", "--- {}\n\n", @headers) { response }
81
+ end
82
+ it "should log success" do
83
+ mock(HoptoadNotifier.logger).info "Hoptoad Success: Net::HTTPOK"
84
+ HoptoadNotifier.send_to_hoptoad({})
85
+ end
86
+ end
87
+ describe "any non 2XX response" do
88
+ before(:each) do
89
+ response = Net::HTTPInternalServerError.new('1.1', 500, 'Upstream unavailable')
90
+ mock(response).body { 'Upstream unavailable' }
91
+
92
+ stub(@http.instance_variable_get('@socket')).closed? { true }
93
+ mock(@http).post("/notices/", "--- {}\n\n", @headers) { response }
94
+ end
95
+ it "should log failure" do
96
+ mock(HoptoadNotifier.logger).error "Hoptoad Failure: Net::HTTPInternalServerError\nUpstream unavailable"
97
+ HoptoadNotifier.send_to_hoptoad({})
98
+ end
99
+ end
100
+ describe "a timeout exception is thrown" do
101
+ before(:each) do
102
+ response = TimeoutError.new("It took too fucking long")
103
+ mock(@http).post("/notices/", "--- {}\n\n", @headers) { raise response }
104
+ end
105
+ it "should log failure" do
106
+ mock(HoptoadNotifier.logger).error("Hoptoad Failure: NilClass\n")
107
+ mock(HoptoadNotifier.logger).error("Timeout while contacting the Hoptoad server.")
108
+ HoptoadNotifier.send_to_hoptoad({})
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
data/spec/spec.opts ADDED
File without changes
@@ -0,0 +1,33 @@
1
+ $TESTING=true
2
+ $:.push File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'rr'
4
+ require 'merb-core'
5
+ require 'merb_hoptoad_notifier'
6
+ require 'tmpdir'
7
+ require 'pp'
8
+
9
+ Spec::Runner.configure do |config|
10
+ config.mock_with :rr
11
+ end
12
+
13
+ module Merb
14
+ module Spec
15
+ module Helpers
16
+ def setup_merb_request
17
+ rack_env = {"SERVER_NAME"=>"localhost",
18
+ "rack.run_once"=>false, "rack.url_scheme"=>"http", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_5; en-us) AppleWebKit/526.1+ (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1",
19
+ "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "PATH_INFO"=>"/search/", "HTTP_CACHE_CONTROL"=>"max-age=0",
20
+ "HTTP_ACCEPT_LANGUAGE"=>"en-us", "HTTP_HOST"=>"localhost:4000", "SERVER_PROTOCOL"=>"HTTP/1.1", "SCRIPT_NAME"=>"",
21
+ "REQUEST_PATH"=>"/search/", "SERVER_SOFTWARE"=>"Mongrel 1.1.5", "REMOTE_ADDR"=>"127.0.0.1", "rack.streaming"=>true,
22
+ "rack.version"=>[0, 1], "rack.multithread"=>true, "HTTP_VERSION"=>"HTTP/1.1", "rack.multiprocess"=>false,
23
+ "REQUEST_URI"=>"/search/?q=0017000000SmnJ0", "SERVER_PORT"=>"4000", "QUERY_STRING"=>"q=0017000000SmnJ0",
24
+ "GATEWAY_INTERFACE"=>"CGI/1.2", "HTTP_ACCEPT"=>"text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
25
+ "REQUEST_METHOD"=>"GET", "HTTP_CONNECTION"=>"keep-alive"}
26
+
27
+ request = Merb::Request.new(rack_env)
28
+ stub(request).env { rack_env }
29
+ request
30
+ end
31
+ end
32
+ end
33
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: merb_hoptoad_notifier-secure
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.10
5
+ platform: ruby
6
+ authors:
7
+ - Corey Donohoe
8
+ - Glen Mailer
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2009-03-07 00:00:00 +00:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: merb-core
18
+ type: :runtime
19
+ version_requirement:
20
+ version_requirements: !ruby/object:Gem::Requirement
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 1.0.9
25
+ version:
26
+ description: Merb plugin that provides secure hoptoad exception notification
27
+ email: atmos@atmos.org
28
+ executables: []
29
+
30
+ extensions: []
31
+
32
+ extra_rdoc_files:
33
+ - README
34
+ - LICENSE
35
+ - TODO
36
+ files:
37
+ - LICENSE
38
+ - README
39
+ - Rakefile
40
+ - TODO
41
+ - lib/merb_hoptoad_notifier/hoptoad_mixin.rb
42
+ - lib/merb_hoptoad_notifier/hoptoad_notifier.rb
43
+ - lib/merb_hoptoad_notifier/merbtasks.rb
44
+ - lib/merb_hoptoad_notifier.rb
45
+ - spec/fixtures/hoptoad.yml
46
+ - spec/merb_hoptoad_notifier_spec.rb
47
+ - spec/spec.opts
48
+ - spec/spec_helper.rb
49
+ has_rdoc: true
50
+ homepage: http://github.com/glenjamin/merb_hoptoad_notifier
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project: merb
73
+ rubygems_version: 1.3.4
74
+ signing_key:
75
+ specification_version: 2
76
+ summary: Merb plugin that provides secure hoptoad exception notification
77
+ test_files: []
78
+