riscfuture-hoptoad_notifier 2.3.6

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 (47) hide show
  1. data/CHANGELOG +218 -0
  2. data/INSTALL +25 -0
  3. data/MIT-LICENSE +22 -0
  4. data/README.rdoc +394 -0
  5. data/Rakefile +217 -0
  6. data/SUPPORTED_RAILS_VERSIONS +10 -0
  7. data/TESTING.rdoc +8 -0
  8. data/generators/hoptoad/hoptoad_generator.rb +63 -0
  9. data/generators/hoptoad/lib/insert_commands.rb +34 -0
  10. data/generators/hoptoad/lib/rake_commands.rb +24 -0
  11. data/generators/hoptoad/templates/capistrano_hook.rb +6 -0
  12. data/generators/hoptoad/templates/hoptoad_notifier_tasks.rake +25 -0
  13. data/generators/hoptoad/templates/initializer.rb +6 -0
  14. data/lib/hoptoad_notifier.rb +148 -0
  15. data/lib/hoptoad_notifier/backtrace.rb +99 -0
  16. data/lib/hoptoad_notifier/capistrano.rb +20 -0
  17. data/lib/hoptoad_notifier/configuration.rb +236 -0
  18. data/lib/hoptoad_notifier/notice.rb +334 -0
  19. data/lib/hoptoad_notifier/rack.rb +40 -0
  20. data/lib/hoptoad_notifier/rails.rb +39 -0
  21. data/lib/hoptoad_notifier/rails/action_controller_catcher.rb +29 -0
  22. data/lib/hoptoad_notifier/rails/controller_methods.rb +60 -0
  23. data/lib/hoptoad_notifier/rails/error_lookup.rb +33 -0
  24. data/lib/hoptoad_notifier/rails/javascript_notifier.rb +43 -0
  25. data/lib/hoptoad_notifier/rails3_tasks.rb +91 -0
  26. data/lib/hoptoad_notifier/railtie.rb +29 -0
  27. data/lib/hoptoad_notifier/sender.rb +63 -0
  28. data/lib/hoptoad_notifier/tasks.rb +97 -0
  29. data/lib/hoptoad_notifier/version.rb +3 -0
  30. data/lib/hoptoad_tasks.rb +44 -0
  31. data/lib/rails/generators/hoptoad/hoptoad_generator.rb +69 -0
  32. data/lib/templates/javascript_notifier.erb +6 -0
  33. data/lib/templates/rescue.erb +91 -0
  34. data/rails/init.rb +1 -0
  35. data/script/integration_test.rb +38 -0
  36. data/test/backtrace_test.rb +118 -0
  37. data/test/catcher_test.rb +329 -0
  38. data/test/configuration_test.rb +209 -0
  39. data/test/helper.rb +239 -0
  40. data/test/hoptoad_tasks_test.rb +152 -0
  41. data/test/logger_test.rb +85 -0
  42. data/test/notice_test.rb +457 -0
  43. data/test/notifier_test.rb +222 -0
  44. data/test/rack_test.rb +58 -0
  45. data/test/rails_initializer_test.rb +36 -0
  46. data/test/sender_test.rb +123 -0
  47. metadata +197 -0
@@ -0,0 +1,40 @@
1
+ module HoptoadNotifier
2
+ # Middleware for Rack applications. Any errors raised by the upstream
3
+ # application will be delivered to Hoptoad and re-raised.
4
+ #
5
+ # Synopsis:
6
+ #
7
+ # require 'rack'
8
+ # require 'hoptoad_notifier'
9
+ #
10
+ # HoptoadNotifier.configure do |config|
11
+ # config.api_key = 'my_api_key'
12
+ # end
13
+ #
14
+ # app = Rack::Builder.app do
15
+ # use HoptoadNotifier::Rack
16
+ # run lambda { |env| raise "Rack down" }
17
+ # end
18
+ #
19
+ # Use a standard HoptoadNotifier.configure call to configure your api key.
20
+ class Rack
21
+ def initialize(app)
22
+ @app = app
23
+ end
24
+
25
+ def call(env)
26
+ begin
27
+ response = @app.call(env)
28
+ rescue Exception => raised
29
+ HoptoadNotifier.notify_or_ignore(raised, :rack_env => env)
30
+ raise
31
+ end
32
+
33
+ if env['rack.exception']
34
+ HoptoadNotifier.notify_or_ignore(env['rack.exception'], :rack_env => env)
35
+ end
36
+
37
+ response
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ require 'hoptoad_notifier'
2
+ require 'hoptoad_notifier/rails/controller_methods'
3
+ require 'hoptoad_notifier/rails/action_controller_catcher'
4
+ require 'hoptoad_notifier/rails/error_lookup'
5
+ require 'hoptoad_notifier/rails/javascript_notifier'
6
+
7
+ module HoptoadNotifier
8
+ module Rails
9
+ def self.initialize
10
+ if defined?(ActionController::Base)
11
+ ActionController::Base.send(:include, HoptoadNotifier::Rails::ActionControllerCatcher)
12
+ ActionController::Base.send(:include, HoptoadNotifier::Rails::ErrorLookup)
13
+ ActionController::Base.send(:include, HoptoadNotifier::Rails::ControllerMethods)
14
+ ActionController::Base.send(:include, HoptoadNotifier::Rails::JavascriptNotifier)
15
+ end
16
+
17
+ rails_logger = if defined?(::Rails.logger)
18
+ ::Rails.logger
19
+ elsif defined?(RAILS_DEFAULT_LOGGER)
20
+ RAILS_DEFAULT_LOGGER
21
+ end
22
+
23
+ if defined?(::Rails.configuration) && ::Rails.configuration.respond_to?(:middleware)
24
+ ::Rails.configuration.middleware.insert_after 'ActionController::Failsafe',
25
+ HoptoadNotifier::Rack
26
+ end
27
+
28
+ HoptoadNotifier.configure(true) do |config|
29
+ config.logger = rails_logger
30
+ config.environment_name = RAILS_ENV if defined?(RAILS_ENV)
31
+ config.project_root = RAILS_ROOT if defined?(RAILS_ROOT)
32
+ config.framework = "Rails: #{::Rails::VERSION::STRING}" if defined?(::Rails::VERSION)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ HoptoadNotifier::Rails.initialize
39
+
@@ -0,0 +1,29 @@
1
+ module HoptoadNotifier
2
+ module Rails
3
+ module ActionControllerCatcher
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_in_public_without_hoptoad, :rescue_action_in_public)
8
+ base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_hoptoad)
9
+ end
10
+
11
+ private
12
+
13
+ # Overrides the rescue_action method in ActionController::Base, but does not inhibit
14
+ # any custom processing that is defined with Rails 2's exception helpers.
15
+ def rescue_action_in_public_with_hoptoad(exception)
16
+ unless hoptoad_ignore_user_agent?
17
+ HoptoadNotifier.notify_or_ignore(exception, hoptoad_request_data)
18
+ end
19
+ rescue_action_in_public_without_hoptoad(exception)
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
+ end
28
+ end
29
+ end
@@ -0,0 +1,60 @@
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 consider_all_requests_local || local_request?
10
+ HoptoadNotifier.notify(hash_or_exception, hoptoad_request_data)
11
+ end
12
+ end
13
+
14
+ def hoptoad_ignore_user_agent? #:nodoc:
15
+ # Rails 1.2.6 doesn't have request.user_agent, so check for it here
16
+ user_agent = request.respond_to?(:user_agent) ? request.user_agent : request.env["HTTP_USER_AGENT"]
17
+ HoptoadNotifier.configuration.ignore_user_agent.flatten.any? { |ua| ua === user_agent }
18
+ end
19
+
20
+ def hoptoad_request_data
21
+ { :parameters => hoptoad_filter_if_filtering(params.to_hash),
22
+ :session_data => hoptoad_filter_if_filtering(hoptoad_session_data),
23
+ :controller => params[:controller],
24
+ :action => params[:action],
25
+ :url => hoptoad_request_url,
26
+ :cgi_data => hoptoad_filter_if_filtering(request.env) }
27
+ end
28
+
29
+ def hoptoad_filter_if_filtering(hash)
30
+ return hash if ! hash.is_a?(Hash)
31
+
32
+ if respond_to?(:filter_parameters)
33
+ filter_parameters(hash) rescue hash
34
+ else
35
+ hash
36
+ end
37
+ end
38
+
39
+ def hoptoad_session_data
40
+ if session.respond_to?(:to_hash)
41
+ session.to_hash
42
+ else
43
+ session.data
44
+ end
45
+ end
46
+
47
+ def hoptoad_request_url
48
+ url = "#{request.protocol}#{request.host}"
49
+
50
+ unless [80, 443].include?(request.port)
51
+ url << ":#{request.port}"
52
+ end
53
+
54
+ url << request.request_uri
55
+ url
56
+ end
57
+ end
58
+ end
59
+ end
60
+
@@ -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,43 @@
1
+ module HoptoadNotifier
2
+ module Rails
3
+ module JavascriptNotifier
4
+ def self.included(base) #:nodoc:
5
+ base.send(:after_filter, :insert_hoptoad_javascript_notifier)
6
+ end
7
+
8
+ private
9
+
10
+ def insert_hoptoad_javascript_notifier
11
+ return unless HoptoadNotifier.configuration.public?
12
+ return unless HoptoadNotifier.configuration.js_notifier
13
+
14
+ path = File.join(File.dirname(__FILE__), '..', '..', 'templates', 'javascript_notifier.erb')
15
+ host = HoptoadNotifier.configuration.host.dup
16
+ port = HoptoadNotifier.configuration.port
17
+ host << ":#{port}" unless [80, 443].include?(port)
18
+
19
+ options = {
20
+ :file => path,
21
+ :layout => false,
22
+ :use_full_path => false,
23
+ :locals => {
24
+ :host => host,
25
+ :secure => HoptoadNotifier.configuration.secure,
26
+ :api_key => HoptoadNotifier.configuration.api_key,
27
+ :environment => HoptoadNotifier.configuration.environment_name
28
+ }
29
+ }
30
+
31
+ if @template
32
+ javascript = @template.render(options)
33
+ else
34
+ javascript = render_to_string(options)
35
+ end
36
+
37
+ if response.body.respond_to?(:gsub)
38
+ response.body = response.body.gsub(/<(head)>/i, "<\\1>\n" + javascript)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,91 @@
1
+ require 'hoptoad_notifier'
2
+
3
+ namespace :hoptoad do
4
+ desc "Notify Hoptoad of a new deploy."
5
+ task :deploy => :environment do
6
+ require 'hoptoad_tasks'
7
+ HoptoadTasks.deploy(:rails_env => ENV['TO'],
8
+ :scm_revision => ENV['REVISION'],
9
+ :scm_repository => ENV['REPO'],
10
+ :local_username => ENV['USER'],
11
+ :api_key => ENV['API_KEY'])
12
+ end
13
+
14
+ desc "Verify your gem installation by sending a test exception to the hoptoad service"
15
+ task :test => [:environment] do
16
+ Rails.logger = Logger.new(STDOUT)
17
+ Rails.logger.level = Logger::DEBUG
18
+ HoptoadNotifier.configure(true) do |config|
19
+ config.logger = Rails.logger
20
+ end
21
+
22
+ require Rails.root + 'app/controllers/application_controller'
23
+
24
+ class HoptoadTestingException < RuntimeError; end
25
+
26
+ unless HoptoadNotifier.configuration.api_key
27
+ puts "Hoptoad needs an API key configured! Check the README to see how to add it."
28
+ exit
29
+ end
30
+
31
+ HoptoadNotifier.configuration.development_environments = []
32
+
33
+ puts "Configuration:"
34
+ HoptoadNotifier.configuration.to_hash.each do |key, value|
35
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
36
+ end
37
+
38
+ unless defined?(ApplicationController)
39
+ puts "No ApplicationController found"
40
+ exit
41
+ end
42
+
43
+ puts 'Setting up the Controller.'
44
+ class ApplicationController
45
+ # This is to bypass any filters that may prevent access to the action.
46
+ prepend_before_filter :test_hoptoad
47
+ def test_hoptoad
48
+ puts "Raising '#{exception_class.name}' to simulate application failure."
49
+ raise exception_class.new, 'Testing hoptoad via "rake hoptoad:test". If you can see this, it works.'
50
+ end
51
+
52
+ # def rescue_action(exception)
53
+ # rescue_action_in_public exception
54
+ # end
55
+
56
+ # Ensure we actually have an action to go to.
57
+ def verify; end
58
+
59
+ # def consider_all_requests_local
60
+ # false
61
+ # end
62
+
63
+ # def local_request?
64
+ # false
65
+ # end
66
+
67
+ def exception_class
68
+ exception_name = ENV['EXCEPTION'] || "HoptoadTestingException"
69
+ Object.const_get(exception_name)
70
+ rescue
71
+ Object.const_set(exception_name, Class.new(Exception))
72
+ end
73
+
74
+ def logger
75
+ nil
76
+ end
77
+ end
78
+ class HoptoadVerificationController < ApplicationController; end
79
+
80
+ Rails.application.routes_reloader.execute_if_updated
81
+ Rails.application.routes.draw do
82
+ match 'verify' => 'application#verify', :as => 'verify'
83
+ end
84
+
85
+ puts 'Processing request.'
86
+ env = Rack::MockRequest.env_for("/verify")
87
+
88
+ Rails.application.call(env)
89
+ end
90
+ end
91
+
@@ -0,0 +1,29 @@
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
+ end
13
+
14
+ config.after_initialize do
15
+ HoptoadNotifier.configure(true) do |config|
16
+ config.logger ||= Rails.logger
17
+ config.environment_name ||= Rails.env
18
+ config.project_root ||= Rails.root
19
+ config.framework = "Rails: #{::Rails::VERSION::STRING}"
20
+ end
21
+
22
+ if defined?(::ActionController::Base)
23
+ require 'hoptoad_notifier/rails/javascript_notifier'
24
+
25
+ ::ActionController::Base.send(:include, HoptoadNotifier::Rails::JavascriptNotifier)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,63 @@
1
+ module HoptoadNotifier
2
+ # Sends out the notice to Hoptoad
3
+ class Sender
4
+
5
+ NOTICES_URI = '/notifier_api/v2/notices/'.freeze
6
+
7
+ def initialize(options = {})
8
+ [:proxy_host, :proxy_port, :proxy_user, :proxy_pass, :protocol,
9
+ :host, :port, :secure, :http_open_timeout, :http_read_timeout].each do |option|
10
+ instance_variable_set("@#{option}", options[option])
11
+ end
12
+ end
13
+
14
+ # Sends the notice data off to Hoptoad for processing.
15
+ #
16
+ # @param [String] data The XML notice to be sent off
17
+ def send_to_hoptoad(data)
18
+ logger.debug { "Sending request to #{url.to_s}:\n#{data}" } if logger
19
+
20
+ http =
21
+ Net::HTTP::Proxy(proxy_host, proxy_port, proxy_user, proxy_pass).
22
+ new(url.host, url.port)
23
+
24
+ http.read_timeout = http_read_timeout
25
+ http.open_timeout = http_open_timeout
26
+ http.use_ssl = secure
27
+
28
+ response = begin
29
+ http.post(url.path, data, HEADERS)
30
+ rescue TimeoutError => e
31
+ log :error, "Timeout while contacting the Hoptoad server."
32
+ nil
33
+ end
34
+
35
+ case response
36
+ when Net::HTTPSuccess then
37
+ log :info, "Success: #{response.class}", response
38
+ else
39
+ log :error, "Failure: #{response.class}", response
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :proxy_host, :proxy_port, :proxy_user, :proxy_pass, :protocol,
46
+ :host, :port, :secure, :http_open_timeout, :http_read_timeout
47
+
48
+ def url
49
+ URI.parse("#{protocol}://#{host}:#{port}").merge(NOTICES_URI)
50
+ end
51
+
52
+ def log(level, message, response = nil)
53
+ logger.send level, LOG_PREFIX + message if logger
54
+ HoptoadNotifier.report_environment_info
55
+ HoptoadNotifier.report_response_body(response.body) if response && response.respond_to?(:body)
56
+ end
57
+
58
+ def logger
59
+ HoptoadNotifier.logger
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,97 @@
1
+ require 'hoptoad_notifier'
2
+
3
+ namespace :hoptoad do
4
+ desc "Notify Hoptoad of a new deploy."
5
+ task :deploy => :environment do
6
+ require 'hoptoad_tasks'
7
+ HoptoadTasks.deploy(:rails_env => ENV['TO'],
8
+ :scm_revision => ENV['REVISION'],
9
+ :scm_repository => ENV['REPO'],
10
+ :local_username => ENV['USER'],
11
+ :api_key => ENV['API_KEY'])
12
+ end
13
+
14
+ task :log_stdout do
15
+ require 'logger'
16
+ RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
17
+ end
18
+
19
+ desc "Verify your gem installation by sending a test exception to the hoptoad service"
20
+ task :test => ['hoptoad:log_stdout', :environment] do
21
+ RAILS_DEFAULT_LOGGER.level = Logger::DEBUG
22
+
23
+ require 'action_controller/test_process'
24
+
25
+ Dir["app/controllers/application*.rb"].each { |file| require(file) }
26
+
27
+ class HoptoadTestingException < RuntimeError; end
28
+
29
+ unless HoptoadNotifier.configuration.api_key
30
+ puts "Hoptoad needs an API key configured! Check the README to see how to add it."
31
+ exit
32
+ end
33
+
34
+ HoptoadNotifier.configuration.development_environments = []
35
+
36
+ catcher = HoptoadNotifier::Rails::ActionControllerCatcher
37
+ in_controller = ApplicationController.included_modules.include?(catcher)
38
+ in_base = ActionController::Base.included_modules.include?(catcher)
39
+ if !in_controller || !in_base
40
+ puts "Rails initialization did not occur"
41
+ exit
42
+ end
43
+
44
+ puts "Configuration:"
45
+ HoptoadNotifier.configuration.to_hash.each do |key, value|
46
+ puts sprintf("%25s: %s", key.to_s, value.inspect.slice(0, 55))
47
+ end
48
+
49
+ unless defined?(ApplicationController)
50
+ puts "No ApplicationController found"
51
+ exit
52
+ end
53
+
54
+ puts 'Setting up the Controller.'
55
+ class ApplicationController
56
+ # This is to bypass any filters that may prevent access to the action.
57
+ prepend_before_filter :test_hoptoad
58
+ def test_hoptoad
59
+ puts "Raising '#{exception_class.name}' to simulate application failure."
60
+ raise exception_class.new, 'Testing hoptoad via "rake hoptoad:test". If you can see this, it works.'
61
+ end
62
+
63
+ def rescue_action(exception)
64
+ rescue_action_in_public exception
65
+ end
66
+
67
+ # Ensure we actually have an action to go to.
68
+ def verify; end
69
+
70
+ def consider_all_requests_local
71
+ false
72
+ end
73
+
74
+ def local_request?
75
+ false
76
+ end
77
+
78
+ def exception_class
79
+ exception_name = ENV['EXCEPTION'] || "HoptoadTestingException"
80
+ Object.const_get(exception_name)
81
+ rescue
82
+ Object.const_set(exception_name, Class.new(Exception))
83
+ end
84
+
85
+ def logger
86
+ nil
87
+ end
88
+ end
89
+ class HoptoadVerificationController < ApplicationController; end
90
+
91
+ puts 'Processing request.'
92
+ request = ActionController::TestRequest.new
93
+ response = ActionController::TestResponse.new
94
+ HoptoadVerificationController.new.process(request, response)
95
+ end
96
+ end
97
+