honeybadger 1.0.0

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.
Files changed (65) hide show
  1. data/Gemfile +13 -0
  2. data/Gemfile.lock +114 -0
  3. data/Guardfile +5 -0
  4. data/MIT-LICENSE +22 -0
  5. data/README.md +271 -0
  6. data/Rakefile +261 -0
  7. data/SUPPORTED_RAILS_VERSIONS +26 -0
  8. data/TESTING.md +33 -0
  9. data/features/metal.feature +18 -0
  10. data/features/rack.feature +56 -0
  11. data/features/rails.feature +211 -0
  12. data/features/rake.feature +27 -0
  13. data/features/sinatra.feature +29 -0
  14. data/features/step_definitions/file_steps.rb +10 -0
  15. data/features/step_definitions/metal_steps.rb +23 -0
  16. data/features/step_definitions/rack_steps.rb +23 -0
  17. data/features/step_definitions/rails_application_steps.rb +394 -0
  18. data/features/step_definitions/rake_steps.rb +17 -0
  19. data/features/support/env.rb +17 -0
  20. data/features/support/honeybadger_shim.rb.template +8 -0
  21. data/features/support/rails.rb +201 -0
  22. data/features/support/rake/Rakefile +68 -0
  23. data/features/support/terminal.rb +107 -0
  24. data/generators/honeybadger/honeybadger_generator.rb +94 -0
  25. data/generators/honeybadger/lib/insert_commands.rb +34 -0
  26. data/generators/honeybadger/lib/rake_commands.rb +24 -0
  27. data/generators/honeybadger/templates/capistrano_hook.rb +6 -0
  28. data/generators/honeybadger/templates/honeybadger_tasks.rake +25 -0
  29. data/generators/honeybadger/templates/initializer.rb +6 -0
  30. data/honeybadger.gemspec +109 -0
  31. data/lib/honeybadger.rb +162 -0
  32. data/lib/honeybadger/backtrace.rb +123 -0
  33. data/lib/honeybadger/capistrano.rb +43 -0
  34. data/lib/honeybadger/configuration.rb +273 -0
  35. data/lib/honeybadger/notice.rb +314 -0
  36. data/lib/honeybadger/rack.rb +55 -0
  37. data/lib/honeybadger/rails.rb +34 -0
  38. data/lib/honeybadger/rails/action_controller_catcher.rb +30 -0
  39. data/lib/honeybadger/rails/controller_methods.rb +69 -0
  40. data/lib/honeybadger/rails/middleware/exceptions_catcher.rb +29 -0
  41. data/lib/honeybadger/rails3_tasks.rb +84 -0
  42. data/lib/honeybadger/railtie.rb +45 -0
  43. data/lib/honeybadger/rake_handler.rb +65 -0
  44. data/lib/honeybadger/sender.rb +120 -0
  45. data/lib/honeybadger/shared_tasks.rb +36 -0
  46. data/lib/honeybadger/tasks.rb +82 -0
  47. data/lib/honeybadger_tasks.rb +65 -0
  48. data/lib/rails/generators/honeybadger/honeybadger_generator.rb +99 -0
  49. data/rails/init.rb +1 -0
  50. data/resources/README.md +34 -0
  51. data/resources/ca-bundle.crt +3376 -0
  52. data/script/integration_test.rb +38 -0
  53. data/test/test_helper.rb +143 -0
  54. data/test/unit/backtrace_test.rb +180 -0
  55. data/test/unit/capistrano_test.rb +34 -0
  56. data/test/unit/configuration_test.rb +201 -0
  57. data/test/unit/honeybadger_tasks_test.rb +163 -0
  58. data/test/unit/logger_test.rb +72 -0
  59. data/test/unit/notice_test.rb +406 -0
  60. data/test/unit/notifier_test.rb +245 -0
  61. data/test/unit/rack_test.rb +56 -0
  62. data/test/unit/rails/action_controller_catcher_test.rb +300 -0
  63. data/test/unit/rails_test.rb +35 -0
  64. data/test/unit/sender_test.rb +257 -0
  65. metadata +315 -0
@@ -0,0 +1,8 @@
1
+ require 'sham_rack'
2
+
3
+ ShamRack.at("api.honeybadger.io") do |env|
4
+ response = <<-end_json
5
+ {"id":"123456789"}
6
+ end_json
7
+ ["200 OK", { "Content-type" => "application/json" }, [response]]
8
+ end
@@ -0,0 +1,201 @@
1
+ module RailsHelpers
2
+ def rails_root_exists?
3
+ File.exists?(environment_path)
4
+ end
5
+
6
+ def application_controller_filename
7
+ controller_filename = File.join(rails_root, 'app', 'controllers', "application_controller.rb")
8
+ end
9
+
10
+ def rails3?
11
+ rails_version =~ /^3/
12
+ end
13
+
14
+ def rails_root
15
+ LOCAL_RAILS_ROOT
16
+ end
17
+
18
+ def rails_uses_rack?
19
+ rails3? || rails_version =~ /^2\.3/
20
+ end
21
+
22
+ def rails_version
23
+ @rails_version ||= begin
24
+ if ENV["RAILS_VERSION"]
25
+ ENV["RAILS_VERSION"]
26
+ elsif bundler_manages_gems?
27
+ rails_version = open(gemfile_path).read.match(/gem.*rails["'].*["'](.+)["']/)[1]
28
+ else
29
+ environment_file = File.join(rails_root, 'config', 'environment.rb')
30
+ rails_version = `grep RAILS_GEM_VERSION #{environment_file}`.match(/[\d.]+/)[0]
31
+ end
32
+ end
33
+ end
34
+
35
+ def bundler_manages_gems?
36
+ File.exists?(gemfile_path)
37
+ end
38
+
39
+ def gemfile_path
40
+ gemfile = File.join(rails_root, 'Gemfile')
41
+ end
42
+
43
+ def rails_manages_gems?
44
+ rails_version =~ /^2\.[123]/
45
+ end
46
+
47
+ def rails_supports_initializers?
48
+ rails3? || rails_version =~ /^2\./
49
+ end
50
+
51
+ def rails_finds_generators_in_gems?
52
+ rails3? || rails_version =~ /^2\./
53
+ end
54
+
55
+ def version_string
56
+ ENV['RAILS_VERSION'] || `tail -n 1 SUPPORTED_RAILS_VERSIONS` # use latest version if ENV["RAILS_VERSION"] is undefined
57
+ end
58
+
59
+ def environment_path
60
+ File.join(rails_root, 'config', 'environment.rb')
61
+ end
62
+
63
+ def rakefile_path
64
+ File.join(rails_root, 'Rakefile')
65
+ end
66
+
67
+ def bundle_gem(gem_name, version = nil)
68
+ File.open(gemfile_path, 'a') do |file|
69
+ gem = "gem '#{gem_name}'"
70
+ gem += ", '#{version}'" if version
71
+ file.puts(gem)
72
+ end
73
+ end
74
+
75
+ def config_gem(gem_name, version = nil)
76
+ run = "Rails::Initializer.run do |config|"
77
+ insert = " config.gem '#{gem_name}'"
78
+ insert += ", :version => '#{version}'" if version
79
+ content = File.read(environment_path)
80
+ content = "require 'thread'\n#{content}"
81
+ if content.sub!(run, "#{run}\n#{insert}")
82
+ File.open(environment_path, 'wb') { |file| file.write(content) }
83
+ else
84
+ raise "Couldn't find #{run.inspect} in #{environment_path}"
85
+ end
86
+ end
87
+
88
+ def config_gem_dependencies
89
+ insert = <<-END
90
+ if Gem::VERSION >= "1.3.6"
91
+ module Rails
92
+ class GemDependency
93
+ def requirement
94
+ r = super
95
+ (r == Gem::Requirement.default) ? nil : r
96
+ end
97
+ end
98
+ end
99
+ end
100
+ END
101
+ run = "Rails::Initializer.run do |config|"
102
+ content = File.read(environment_path)
103
+ if content.sub!(run, "#{insert}\n#{run}")
104
+ File.open(environment_path, 'wb') { |file| file.write(content) }
105
+ else
106
+ raise "Couldn't find #{run.inspect} in #{environment_path}"
107
+ end
108
+ end
109
+
110
+ def require_thread
111
+ content = File.read(rakefile_path)
112
+ content = "require 'thread'\n#{content}"
113
+ File.open(rakefile_path, 'wb') { |file| file.write(content) }
114
+ end
115
+
116
+ def perform_request(uri, environment = 'production')
117
+ if rails3?
118
+ request_script = <<-SCRIPT
119
+ require File.expand_path('../config/environment', __FILE__)
120
+
121
+
122
+ env = Rack::MockRequest.env_for(#{uri.inspect})
123
+ response = RailsRoot::Application.call(env)
124
+
125
+
126
+ response = response.last if response.last.is_a?(ActionDispatch::Response)
127
+
128
+ if response.is_a?(Array)
129
+ puts response.join
130
+ else
131
+ puts response.body
132
+ end
133
+ SCRIPT
134
+ File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
135
+ @terminal.cd(rails_root)
136
+ @terminal.run("ruby -rthread ./script/rails runner -e #{environment} request.rb")
137
+ elsif rails_uses_rack?
138
+ request_script = <<-SCRIPT
139
+ require File.expand_path('../config/environment', __FILE__)
140
+
141
+ env = Rack::MockRequest.env_for(#{uri.inspect})
142
+ app = Rack::Lint.new(ActionController::Dispatcher.new)
143
+
144
+ status, headers, body = app.call(env)
145
+
146
+ response = ""
147
+ if body.respond_to?(:to_str)
148
+ response << body
149
+ else
150
+ body.each { |part| response << part }
151
+ end
152
+
153
+ puts response
154
+ SCRIPT
155
+ File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
156
+ @terminal.cd(rails_root)
157
+ @terminal.run("ruby -rthread ./script/runner -e #{environment} request.rb")
158
+ else
159
+ uri = URI.parse(uri)
160
+ request_script = <<-SCRIPT
161
+ require 'cgi'
162
+ class CGIWrapper < CGI
163
+ def initialize(*args)
164
+ @env_table = {}
165
+ @stdinput = $stdin
166
+ super(*args)
167
+ end
168
+ attr_reader :env_table
169
+ end
170
+ $stdin = StringIO.new("")
171
+ cgi = CGIWrapper.new
172
+ cgi.env_table.update({
173
+ 'HTTPS' => 'off',
174
+ 'REQUEST_METHOD' => "GET",
175
+ 'HTTP_HOST' => #{[uri.host, uri.port].join(':').inspect},
176
+ 'SERVER_PORT' => #{uri.port.inspect},
177
+ 'REQUEST_URI' => #{uri.request_uri.inspect},
178
+ 'PATH_INFO' => #{uri.path.inspect},
179
+ 'QUERY_STRING' => #{uri.query.inspect}
180
+ })
181
+ require 'dispatcher' unless defined?(ActionController::Dispatcher)
182
+ Dispatcher.dispatch(cgi)
183
+ SCRIPT
184
+ File.open(File.join(rails_root, 'request.rb'), 'w') { |file| file.write(request_script) }
185
+ @terminal.cd(rails_root)
186
+ @terminal.run("ruby -rthread ./script/runner -e #{environment} request.rb")
187
+ end
188
+ end
189
+
190
+ def monkeypatch_old_version
191
+ monkeypatchin= <<-MONKEYPATCHIN
192
+
193
+ MissingSourceFile::REGEXPS << [/^cannot load such file -- (.+)$/i, 1]
194
+
195
+ MONKEYPATCHIN
196
+
197
+ File.open(File.join(rails_root,"config","initializers", 'monkeypatchin.rb'), 'w') { |file| file.write(monkeypatchin) }
198
+ end
199
+ end
200
+
201
+ World(RailsHelpers)
@@ -0,0 +1,68 @@
1
+ # A test harness for RakeHandler
2
+ #
3
+ require 'rake'
4
+ require 'rubygems'
5
+ require 'honeybadger'
6
+ require 'honeybadger/rake_handler'
7
+
8
+ Honeybadger.configure do |c|
9
+ end
10
+
11
+ # Should catch exception
12
+ task :honeybadger do
13
+ Honeybadger.configuration.rescue_rake_exceptions = true
14
+ stub_tty_output(true)
15
+ raise_exception
16
+ end
17
+
18
+ # Should not catch exception
19
+ task :honeybadger_disabled do
20
+ Honeybadger.configuration.rescue_rake_exceptions = false
21
+ stub_tty_output(true)
22
+ raise_exception
23
+ end
24
+
25
+ # Should not catch exception as tty_output is true
26
+ task :honeybadger_autodetect_from_terminal do
27
+ Honeybadger.configuration.rescue_rake_exceptions = nil
28
+ stub_tty_output(true)
29
+ raise_exception
30
+ end
31
+
32
+ # Should catch exception as tty_output is false
33
+ task :honeybadger_autodetect_not_from_terminal do
34
+ Honeybadger.configuration.rescue_rake_exceptions = nil
35
+ stub_tty_output(false)
36
+ raise_exception
37
+ end
38
+
39
+ task :honeybadger_not_yet_configured do
40
+ Honeybadger.configuration.rescue_rake_exceptions = true
41
+ stub_tty_output(true)
42
+ stub_empty_sender
43
+ raise_exception
44
+ end
45
+
46
+ module Honeybadger
47
+ def self.notify_or_ignore(*args)
48
+ # TODO if you need to check more params, you'll have to use json.dump or something
49
+ $stderr.puts "honeybadger #{args[1][:component]}"
50
+ end
51
+ end
52
+
53
+ def stub_empty_sender
54
+ Honeybadger.sender = nil
55
+ end
56
+
57
+ def stub_tty_output(value)
58
+ Rake.application.instance_eval do
59
+ @tty_output_stub = value
60
+ def tty_output?
61
+ @tty_output_stub
62
+ end
63
+ end
64
+ end
65
+
66
+ def raise_exception
67
+ raise 'TEST'
68
+ end
@@ -0,0 +1,107 @@
1
+ require 'fileutils'
2
+
3
+ Before do
4
+ @terminal = Terminal.new
5
+ end
6
+
7
+ After do |story|
8
+ if story.failed?
9
+ # puts @terminal.output
10
+ end
11
+ end
12
+
13
+ class Terminal
14
+ attr_reader :output, :status
15
+ attr_accessor :environment_variables, :invoke_heroku_rake_tasks_locally
16
+
17
+ def initialize
18
+ @cwd = FileUtils.pwd
19
+ @output = ""
20
+ @status = 0
21
+ @logger = Logger.new(File.join(TEMP_DIR, 'terminal.log'))
22
+
23
+ @invoke_heroku_rake_tasks_locally = false
24
+
25
+ @environment_variables = {
26
+ "GEM_HOME" => LOCAL_GEM_ROOT,
27
+ "GEM_PATH" => "#{LOCAL_GEM_ROOT}:#{BUILT_GEM_ROOT}",
28
+ "PATH" => "#{gem_bin_path}:#{ENV['PATH']}"
29
+ }
30
+ end
31
+
32
+ def cd(directory)
33
+ @cwd = directory
34
+ end
35
+
36
+ def run(command)
37
+ command = optionally_invoke_heroku_rake_tasks_locally(command)
38
+
39
+ output << "#{command}\n"
40
+ FileUtils.cd(@cwd) do
41
+ # The ; forces ruby to shell out so the env settings work right
42
+ cmdline = "#{environment_settings} #{command} 2>&1 ; "
43
+ logger.debug(cmdline)
44
+ result = `#{cmdline}`
45
+ logger.debug(result)
46
+ output << result
47
+ end
48
+ @status = $?
49
+ end
50
+
51
+ def optionally_invoke_heroku_rake_tasks_locally(command)
52
+ if invoke_heroku_rake_tasks_locally
53
+ command.sub(/^heroku /, '')
54
+ else
55
+ command
56
+ end
57
+ end
58
+
59
+ def echo(string)
60
+ logger.debug(string)
61
+ end
62
+
63
+ def flush!
64
+ @output = ""
65
+ end
66
+
67
+ def build_and_install_gem(gemspec)
68
+ pkg_dir = File.join(TEMP_DIR, 'pkg')
69
+ FileUtils.mkdir_p(pkg_dir)
70
+ output = `gem build #{gemspec} 2>&1`
71
+ gem_file = Dir.glob("*.gem").first
72
+ unless gem_file
73
+ raise "Gem didn't build:\n#{output}"
74
+ end
75
+ target = File.join(pkg_dir, gem_file)
76
+ FileUtils.mv(gem_file, target)
77
+ install_gem_to(LOCAL_GEM_ROOT, target)
78
+ end
79
+
80
+ def install_gem(gem)
81
+ install_gem_to(LOCAL_GEM_ROOT, gem)
82
+ end
83
+
84
+ def uninstall_gem(gem)
85
+ `gem uninstall -i #{LOCAL_GEM_ROOT} #{gem}`
86
+ end
87
+
88
+ def prepend_path(path)
89
+ @environment_variables['PATH'] = path + ":" + @environment_variables['PATH']
90
+ end
91
+
92
+ private
93
+
94
+ def install_gem_to(root, gem)
95
+ `gem install -i #{root} --no-ri --no-rdoc #{gem}`
96
+ end
97
+
98
+ def environment_settings
99
+ @environment_variables.map { |key, value| "#{key}=#{value}" }.join(' ')
100
+ end
101
+
102
+ def gem_bin_path
103
+ File.join(LOCAL_GEM_ROOT, "bin")
104
+ end
105
+
106
+ attr_reader :logger
107
+ end
@@ -0,0 +1,94 @@
1
+ require File.expand_path(File.dirname(__FILE__) + "/lib/insert_commands.rb")
2
+ require File.expand_path(File.dirname(__FILE__) + "/lib/rake_commands.rb")
3
+
4
+ class HoneybadgerGenerator < Rails::Generator::Base
5
+ def add_options!(opt)
6
+ opt.on('-k', '--api-key=key', String, "Your Honeybadger API key") { |v| options[:api_key] = v}
7
+ opt.on('-h', '--heroku', "Use the Heroku addon to provide your Honeybadger API key") { |v| options[:heroku] = v}
8
+ opt.on('-a', '--app=myapp', String, "Your Heroku app name (only required if deploying to >1 Heroku app)") { |v| options[:app] = v}
9
+ end
10
+
11
+ def manifest
12
+ if !api_key_configured? && !options[:api_key] && !options[:heroku]
13
+ puts "Must pass --api-key or --heroku or create config/initializers/honeybadger.rb"
14
+ exit
15
+ end
16
+ if plugin_is_present?
17
+ puts "You must first remove the honeybadger plugin. Please run: script/plugin remove honeybadger"
18
+ exit
19
+ end
20
+ record do |m|
21
+ m.directory 'lib/tasks'
22
+ m.file 'honeybadger_tasks.rake', 'lib/tasks/honeybadger_tasks.rake'
23
+ if ['config/deploy.rb', 'Capfile'].all? { |file| File.exists?(file) }
24
+ m.append_to 'config/deploy.rb', capistrano_hook
25
+ end
26
+ if api_key_expression
27
+ if use_initializer?
28
+ m.template 'initializer.rb', 'config/initializers/honeybadger.rb',
29
+ :assigns => {:api_key => api_key_expression}
30
+ else
31
+ m.template 'initializer.rb', 'config/honeybadger.rb',
32
+ :assigns => {:api_key => api_key_expression}
33
+ m.append_to 'config/environment.rb', "require 'config/honeybadger'"
34
+ end
35
+ end
36
+ determine_api_key if heroku?
37
+ m.rake "honeybadger:test --trace", :generate_only => true
38
+ end
39
+ end
40
+
41
+ def api_key_expression
42
+ s = if options[:api_key]
43
+ "'#{options[:api_key]}'"
44
+ elsif options[:heroku]
45
+ "ENV['HONEYBADGER_API_KEY']"
46
+ end
47
+ end
48
+
49
+ def determine_api_key
50
+ puts "Attempting to determine your API Key from Heroku..."
51
+ ENV['HONEYBADGER_API_KEY'] = heroku_api_key
52
+ if ENV['HONEYBADGER_API_KEY'].blank?
53
+ puts "... Failed."
54
+ puts "WARNING: We were unable to detect the Honeybadger API Key from your Heroku environment."
55
+ puts "Your Heroku application environment may not be configured correctly."
56
+ exit 1
57
+ else
58
+ puts "... Done."
59
+ puts "Heroku's Honeybadger API Key is '#{ENV['HONEYBADGER_API_KEY']}'"
60
+ end
61
+ end
62
+
63
+ def heroku_var(var,app_name = nil)
64
+ app = app_name ? "--app #{app_name}" : ''
65
+ `heroku config #{app} | grep -E "#{var.upcase}" | awk '{ print $3; }'`.strip
66
+ end
67
+
68
+ def heroku_api_key
69
+ heroku_var("(hoptoad|honeybadger)_api_key",options[:app]).split.find {|x| x unless x.blank?}
70
+ end
71
+
72
+ def heroku?
73
+ options[:heroku] ||
74
+ system("grep HONEYBADGER_API_KEY config/initializers/honeybadger.rb") ||
75
+ system("grep HONEYBADGER_API_KEY config/environment.rb")
76
+ end
77
+
78
+ def use_initializer?
79
+ Rails::VERSION::MAJOR > 1
80
+ end
81
+
82
+ def api_key_configured?
83
+ File.exists?('config/initializers/honeybadger.rb') ||
84
+ system("grep Honeybadger config/environment.rb")
85
+ end
86
+
87
+ def capistrano_hook
88
+ IO.read(source_path('capistrano_hook.rb'))
89
+ end
90
+
91
+ def plugin_is_present?
92
+ File.exists?('vendor/plugins/honeybadger')
93
+ end
94
+ end