square-hoptoad_notifier 2.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/CHANGELOG +427 -0
  2. data/INSTALL +25 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.md +435 -0
  5. data/README_FOR_HEROKU_ADDON.md +93 -0
  6. data/Rakefile +227 -0
  7. data/SUPPORTED_RAILS_VERSIONS +10 -0
  8. data/TESTING.rdoc +8 -0
  9. data/generators/hoptoad/hoptoad_generator.rb +88 -0
  10. data/generators/hoptoad/lib/insert_commands.rb +34 -0
  11. data/generators/hoptoad/lib/rake_commands.rb +24 -0
  12. data/generators/hoptoad/templates/capistrano_hook.rb +6 -0
  13. data/generators/hoptoad/templates/hoptoad_notifier_tasks.rake +25 -0
  14. data/generators/hoptoad/templates/initializer.rb +6 -0
  15. data/lib/hoptoad_notifier.rb +153 -0
  16. data/lib/hoptoad_notifier/backtrace.rb +99 -0
  17. data/lib/hoptoad_notifier/capistrano.rb +20 -0
  18. data/lib/hoptoad_notifier/configuration.rb +242 -0
  19. data/lib/hoptoad_notifier/notice.rb +337 -0
  20. data/lib/hoptoad_notifier/rack.rb +42 -0
  21. data/lib/hoptoad_notifier/rails.rb +41 -0
  22. data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +30 -0
  23. data/lib/hoptoad_notifier/rails/controller_methods.rb +68 -0
  24. data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
  25. data/lib/hoptoad_notifier/rails/javascript_notifier.rb +42 -0
  26. data/lib/hoptoad_notifier/rails3_tasks.rb +82 -0
  27. data/lib/hoptoad_notifier/railtie.rb +32 -0
  28. data/lib/hoptoad_notifier/sender.rb +83 -0
  29. data/lib/hoptoad_notifier/shared_tasks.rb +29 -0
  30. data/lib/hoptoad_notifier/tasks.rb +83 -0
  31. data/lib/hoptoad_notifier/user_informer.rb +23 -0
  32. data/lib/hoptoad_notifier/version.rb +3 -0
  33. data/lib/hoptoad_tasks.rb +44 -0
  34. data/lib/rails/generators/hoptoad/hoptoad_generator.rb +94 -0
  35. data/lib/templates/javascript_notifier.erb +13 -0
  36. data/lib/templates/rescue.erb +91 -0
  37. data/rails/init.rb +1 -0
  38. data/script/integration_test.rb +38 -0
  39. data/test/backtrace_test.rb +118 -0
  40. data/test/catcher_test.rb +331 -0
  41. data/test/configuration_test.rb +216 -0
  42. data/test/helper.rb +248 -0
  43. data/test/hoptoad_tasks_test.rb +152 -0
  44. data/test/javascript_notifier_test.rb +52 -0
  45. data/test/logger_test.rb +85 -0
  46. data/test/notice_test.rb +448 -0
  47. data/test/notifier_test.rb +222 -0
  48. data/test/rack_test.rb +58 -0
  49. data/test/rails_initializer_test.rb +36 -0
  50. data/test/sender_test.rb +161 -0
  51. data/test/user_informer_test.rb +29 -0
  52. metadata +225 -0
@@ -0,0 +1,68 @@
1
+ module HoptoadNotifier
2
+ module Rails
3
+ module ControllerMethods
4
+ private
5
+
6
+ # This method should be used for sending manual notifications while you are still
7
+ # inside the controller. Otherwise it works like HoptoadNotifier.notify.
8
+ def notify_hoptoad(hash_or_exception)
9
+ unless hoptoad_local_request?
10
+ HoptoadNotifier.notify(hash_or_exception, hoptoad_request_data)
11
+ end
12
+ end
13
+
14
+ def hoptoad_local_request?
15
+ if defined?(::Rails.application.config)
16
+ ::Rails.application.config.consider_all_requests_local || request.local?
17
+ else
18
+ consider_all_requests_local || local_request?
19
+ end
20
+ end
21
+
22
+ def hoptoad_ignore_user_agent? #:nodoc:
23
+ # Rails 1.2.6 doesn't have request.user_agent, so check for it here
24
+ user_agent = request.respond_to?(:user_agent) ? request.user_agent : request.env["HTTP_USER_AGENT"]
25
+ HoptoadNotifier.configuration.ignore_user_agent.flatten.any? { |ua| ua === user_agent }
26
+ end
27
+
28
+ def hoptoad_request_data
29
+ { :parameters => hoptoad_filter_if_filtering(params.to_hash),
30
+ :session_data => hoptoad_filter_if_filtering(hoptoad_session_data),
31
+ :controller => params[:controller],
32
+ :action => params[:action],
33
+ :url => hoptoad_request_url,
34
+ :cgi_data => hoptoad_filter_if_filtering(request.env) }
35
+ end
36
+
37
+ def hoptoad_filter_if_filtering(hash)
38
+ return hash if ! hash.is_a?(Hash)
39
+
40
+ if respond_to?(:filter_parameters)
41
+ filter_parameters(hash) rescue hash
42
+ else
43
+ hash
44
+ end
45
+ end
46
+
47
+ def hoptoad_session_data
48
+ if session.respond_to?(:to_hash)
49
+ session.to_hash
50
+ else
51
+ session.data
52
+ end
53
+ end
54
+
55
+ def hoptoad_request_url
56
+ url = "#{request.protocol}#{request.host}"
57
+
58
+ unless [80, 443].include?(request.port)
59
+ url << ":#{request.port}"
60
+ end
61
+
62
+ url << request.request_uri
63
+ url
64
+ end
65
+ end
66
+ end
67
+ end
68
+
@@ -0,0 +1,33 @@
1
+ module HoptoadNotifier
2
+ module Rails
3
+ module ErrorLookup
4
+
5
+ # Sets up an alias chain to catch exceptions when Rails does
6
+ def self.included(base) #:nodoc:
7
+ base.send(:alias_method, :rescue_action_locally_without_hoptoad, :rescue_action_locally)
8
+ base.send(:alias_method, :rescue_action_locally, :rescue_action_locally_with_hoptoad)
9
+ end
10
+
11
+ private
12
+
13
+ def rescue_action_locally_with_hoptoad(exception)
14
+ result = rescue_action_locally_without_hoptoad(exception)
15
+
16
+ if HoptoadNotifier.configuration.development_lookup
17
+ path = File.join(File.dirname(__FILE__), '..', '..', 'templates', 'rescue.erb')
18
+ notice = HoptoadNotifier.build_lookup_hash_for(exception, hoptoad_request_data)
19
+
20
+ result << @template.render(
21
+ :file => path,
22
+ :use_full_path => false,
23
+ :locals => { :host => HoptoadNotifier.configuration.host,
24
+ :api_key => HoptoadNotifier.configuration.api_key,
25
+ :notice => notice })
26
+ end
27
+
28
+ result
29
+ end
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,42 @@
1
+ module HoptoadNotifier
2
+ module Rails
3
+ module JavascriptNotifier
4
+ def self.included(base) #:nodoc:
5
+ base.send :helper_method, :hoptoad_javascript_notifier
6
+ end
7
+
8
+ private
9
+
10
+ def hoptoad_javascript_notifier
11
+ return unless HoptoadNotifier.configuration.public?
12
+
13
+ path = File.join File.dirname(__FILE__), '..', '..', 'templates', 'javascript_notifier.erb'
14
+ host = HoptoadNotifier.configuration.host.dup
15
+ port = HoptoadNotifier.configuration.port
16
+ host << ":#{port}" unless [80, 443].include?(port)
17
+
18
+ options = {
19
+ :file => path,
20
+ :layout => false,
21
+ :use_full_path => false,
22
+ :locals => {
23
+ :host => host,
24
+ :api_key => HoptoadNotifier.configuration.api_key,
25
+ :environment => HoptoadNotifier.configuration.environment_name,
26
+ :action_name => action_name,
27
+ :controller_name => controller_name,
28
+ :url => request.url
29
+ }
30
+ }
31
+
32
+ if @template
33
+ @template.render(options)
34
+ else
35
+ render_to_string(options)
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,82 @@
1
+ require 'hoptoad_notifier'
2
+ require File.join(File.dirname(__FILE__), 'shared_tasks')
3
+
4
+ namespace :hoptoad do
5
+ desc "Verify your gem installation by sending a test exception to the hoptoad service"
6
+ task :test => [:environment] do
7
+ Rails.logger = Logger.new(STDOUT)
8
+ Rails.logger.level = Logger::DEBUG
9
+ HoptoadNotifier.configure(true) do |config|
10
+ config.logger = Rails.logger
11
+ end
12
+
13
+ require './app/controllers/application_controller'
14
+
15
+ class HoptoadTestingException < RuntimeError; end
16
+
17
+ unless HoptoadNotifier.configuration.api_key
18
+ puts "Hoptoad needs an API key configured! Check the README to see how to add it."
19
+ exit
20
+ end
21
+
22
+ HoptoadNotifier.configuration.development_environments = []
23
+
24
+ puts "Configuration:"
25
+ HoptoadNotifier.configuration.to_hash.each do |key, value|
26
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
27
+ end
28
+
29
+ unless defined?(ApplicationController)
30
+ puts "No ApplicationController found"
31
+ exit
32
+ end
33
+
34
+ puts 'Setting up the Controller.'
35
+ class ApplicationController
36
+ # This is to bypass any filters that may prevent access to the action.
37
+ prepend_before_filter :test_hoptoad
38
+ def test_hoptoad
39
+ puts "Raising '#{exception_class.name}' to simulate application failure."
40
+ raise exception_class.new, 'Testing hoptoad via "rake hoptoad:test". If you can see this, it works.'
41
+ end
42
+
43
+ # def rescue_action(exception)
44
+ # rescue_action_in_public exception
45
+ # end
46
+
47
+ # Ensure we actually have an action to go to.
48
+ def verify; end
49
+
50
+ # def consider_all_requests_local
51
+ # false
52
+ # end
53
+
54
+ # def local_request?
55
+ # false
56
+ # end
57
+
58
+ def exception_class
59
+ exception_name = ENV['EXCEPTION'] || "HoptoadTestingException"
60
+ Object.const_get(exception_name)
61
+ rescue
62
+ Object.const_set(exception_name, Class.new(Exception))
63
+ end
64
+
65
+ def logger
66
+ nil
67
+ end
68
+ end
69
+ class HoptoadVerificationController < ApplicationController; end
70
+
71
+ Rails.application.routes_reloader.execute_if_updated
72
+ Rails.application.routes.draw do
73
+ match 'verify' => 'application#verify', :as => 'verify'
74
+ end
75
+
76
+ puts 'Processing request.'
77
+ env = Rack::MockRequest.env_for("/verify")
78
+
79
+ Rails.application.call(env)
80
+ end
81
+ end
82
+
@@ -0,0 +1,32 @@
1
+ require 'hoptoad_notifier'
2
+ require 'rails'
3
+
4
+ module HoptoadNotifier
5
+ class Railtie < Rails::Railtie
6
+ rake_tasks do
7
+ require "hoptoad_notifier/rails3_tasks"
8
+ end
9
+
10
+ initializer "hoptoad.use_rack_middleware" do |app|
11
+ app.config.middleware.use "HoptoadNotifier::Rack"
12
+ app.config.middleware.insert 0, "HoptoadNotifier::UserInformer"
13
+ end
14
+
15
+ config.after_initialize do
16
+ HoptoadNotifier.configure(true) do |config|
17
+ config.logger ||= Rails.logger
18
+ config.environment_name ||= Rails.env
19
+ config.project_root ||= Rails.root
20
+ config.framework = "Rails: #{::Rails::VERSION::STRING}"
21
+ end
22
+
23
+ if defined?(::ActionController::Base)
24
+ require 'hoptoad_notifier/rails/javascript_notifier'
25
+ require 'hoptoad_notifier/rails/controller_methods'
26
+
27
+ ::ActionController::Base.send(:include, HoptoadNotifier::Rails::ControllerMethods)
28
+ ::ActionController::Base.send(:include, HoptoadNotifier::Rails::JavascriptNotifier)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,83 @@
1
+ module HoptoadNotifier
2
+ # Sends out the notice to Hoptoad
3
+ class Sender
4
+
5
+ NOTICES_URI = '/notifier_api/v2/notices/'.freeze
6
+ HTTP_ERRORS = [Timeout::Error,
7
+ Errno::EINVAL,
8
+ Errno::ECONNRESET,
9
+ EOFError,
10
+ Net::HTTPBadResponse,
11
+ Net::HTTPHeaderSyntaxError,
12
+ Net::ProtocolError,
13
+ Errno::ECONNREFUSED].freeze
14
+
15
+ def initialize(options = {})
16
+ [:proxy_host, :proxy_port, :proxy_user, :proxy_pass, :protocol,
17
+ :host, :port, :secure, :http_open_timeout, :http_read_timeout].each do |option|
18
+ instance_variable_set("@#{option}", options[option])
19
+ end
20
+ end
21
+
22
+ # Sends the notice data off to Hoptoad for processing.
23
+ #
24
+ # @param [String] data The XML notice to be sent off
25
+ def send_to_hoptoad(data)
26
+ logger.debug { "Sending request to #{url.to_s}:\n#{data}" } if logger
27
+
28
+ http =
29
+ Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass).
30
+ new(url.host, url.port)
31
+
32
+ http.read_timeout = http_read_timeout
33
+ http.open_timeout = http_open_timeout
34
+
35
+ if secure
36
+ http.use_ssl = true
37
+ http.ca_file = OpenSSL::X509::DEFAULT_CERT_FILE
38
+ http.verify_mode = OpenSSL::SSL::VERIFY_PEER
39
+ else
40
+ http.use_ssl = false
41
+ end
42
+
43
+ response = begin
44
+ http.post(url.path, data, HEADERS)
45
+ rescue *HTTP_ERRORS => e
46
+ log :error, "Timeout while contacting the Hoptoad server."
47
+ nil
48
+ end
49
+
50
+ case response
51
+ when Net::HTTPSuccess then
52
+ log :info, "Success: #{response.class}", response
53
+ else
54
+ log :error, "Failure: #{response.class}", response
55
+ end
56
+
57
+ if response && response.respond_to?(:body)
58
+ error_id = response.body.match(%r{<error-id[^>]*>(.*?)</error-id>})
59
+ error_id[1] if error_id
60
+ end
61
+ end
62
+
63
+ private
64
+
65
+ attr_reader :proxy_host, :proxy_port, :proxy_user, :proxy_pass, :protocol,
66
+ :host, :port, :secure, :http_open_timeout, :http_read_timeout
67
+
68
+ def url
69
+ URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
70
+ end
71
+
72
+ def log(level, message, response = nil)
73
+ logger.send level, LOG_PREFIX + message if logger
74
+ HoptoadNotifier.report_environment_info
75
+ HoptoadNotifier.report_response_body(response.body) if response && response.respond_to?(:body)
76
+ end
77
+
78
+ def logger
79
+ HoptoadNotifier.logger
80
+ end
81
+
82
+ end
83
+ end
@@ -0,0 +1,29 @@
1
+ namespace :hoptoad do
2
+ desc "Notify Hoptoad of a new deploy."
3
+ task :deploy => :environment do
4
+ require 'hoptoad_tasks'
5
+ HoptoadTasks.deploy(:rails_env => ENV['TO'],
6
+ :scm_revision => ENV['REVISION'],
7
+ :scm_repository => ENV['REPO'],
8
+ :local_username => ENV['USER'],
9
+ :api_key => ENV['API_KEY'])
10
+ end
11
+
12
+ task :log_stdout do
13
+ require 'logger'
14
+ RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
15
+ end
16
+
17
+ namespace :heroku do
18
+ desc "Install Heroku deploy notifications addon"
19
+ task :add_deploy_notification => [:environment] do
20
+ heroku_api_key = `heroku console 'puts ENV[%{HOPTOAD_API_KEY}]' | head -n 1`.strip
21
+ heroku_rails_env = `heroku console 'puts RAILS_ENV' | head -n 1`.strip
22
+
23
+ command = %Q(heroku addons:add deployhooks:http url="http://hoptoadapp.com/deploys.txt?deploy[rails_env]=#{heroku_rails_env}&api_key=#{heroku_api_key}")
24
+
25
+ puts "\nRunning:\n#{command}\n"
26
+ puts `#{command}`
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,83 @@
1
+ require 'hoptoad_notifier'
2
+ require File.join(File.dirname(__FILE__), 'shared_tasks')
3
+
4
+ namespace :hoptoad do
5
+ desc "Verify your gem installation by sending a test exception to the hoptoad service"
6
+ task :test => ['hoptoad:log_stdout', :environment] do
7
+ RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
8
+
9
+ require 'action_controller/test_process'
10
+
11
+ Dir["app/controllers/application*.rb"].each { |file| require(file) }
12
+
13
+ class HoptoadTestingException < RuntimeError; end
14
+
15
+ unless HoptoadNotifier.configuration.api_key
16
+ puts "Hoptoad needs an API key configured! Check the README to see how to add it."
17
+ exit
18
+ end
19
+
20
+ HoptoadNotifier.configuration.development_environments = []
21
+
22
+ catcher = HoptoadNotifier::Rails::ActionControllerCatcher
23
+ in_controller = ApplicationController.included_modules.include?(catcher)
24
+ in_base = ActionController::Base.included_modules.include?(catcher)
25
+ if !in_controller || !in_base
26
+ puts "Rails initialization did not occur"
27
+ exit
28
+ end
29
+
30
+ puts "Configuration:"
31
+ HoptoadNotifier.configuration.to_hash.each do |key, value|
32
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
33
+ end
34
+
35
+ unless defined?(ApplicationController)
36
+ puts "No ApplicationController found"
37
+ exit
38
+ end
39
+
40
+ puts 'Setting up the Controller.'
41
+ class ApplicationController
42
+ # This is to bypass any filters that may prevent access to the action.
43
+ prepend_before_filter :test_hoptoad
44
+ def test_hoptoad
45
+ puts "Raising '#{exception_class.name}' to simulate application failure."
46
+ raise exception_class.new, 'Testing hoptoad via "rake hoptoad:test". If you can see this, it works.'
47
+ end
48
+
49
+ def rescue_action(exception)
50
+ rescue_action_in_public exception
51
+ end
52
+
53
+ # Ensure we actually have an action to go to.
54
+ def verify; end
55
+
56
+ def consider_all_requests_local
57
+ false
58
+ end
59
+
60
+ def local_request?
61
+ false
62
+ end
63
+
64
+ def exception_class
65
+ exception_name = ENV['EXCEPTION'] || "HoptoadTestingException"
66
+ Object.const_get(exception_name)
67
+ rescue
68
+ Object.const_set(exception_name, Class.new(Exception))
69
+ end
70
+
71
+ def logger
72
+ nil
73
+ end
74
+ end
75
+ class HoptoadVerificationController < ApplicationController; end
76
+
77
+ puts 'Processing request.'
78
+ request = ActionController::TestRequest.new("REQUEST_URI" => "/hoptoad_verification_controller")
79
+ response = ActionController::TestResponse.new
80
+ HoptoadVerificationController.new.process(request, response)
81
+ end
82
+ end
83
+