square-hoptoad_notifier 2.4.8

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 (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
+