Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'development-error-lookup'

  • Loading branch information...
commit 94f25c96219cbc16ce6f25220fefbb6e9cc8841f 2 parents 3963e93 + 2f1fed2
@tristandunn tristandunn authored
View
17 lib/hoptoad_notifier.rb
@@ -101,6 +101,23 @@ def notify_or_ignore(exception, opts = {})
send_notice(notice) unless notice.ignore?
end
+ def build_lookup_hash_for(exception, options = {})
+ notice = build_notice_for(exception, options)
+
+ result = {}
+ result[:action] = notice.action rescue nil
+ result[:component] = notice.component rescue nil
+ result[:error_class] = notice.error_class if notice.error_class
+ result[:environment_name] = 'production'
+
+ unless notice.backtrace.lines.empty?
+ result[:file] = notice.backtrace.lines.first.file
+ result[:line_number] = notice.backtrace.lines.first.number
+ end
+
+ result
+ end
+
private
def send_notice(notice)
View
19 lib/hoptoad_notifier/catcher.rb
@@ -7,6 +7,8 @@ def self.included(base) #:nodoc:
if base.instance_methods.map(&:to_s).include? 'rescue_action_in_public' and !base.instance_methods.map(&:to_s).include? 'rescue_action_in_public_without_hoptoad'
base.send(:alias_method, :rescue_action_in_public_without_hoptoad, :rescue_action_in_public)
base.send(:alias_method, :rescue_action_in_public, :rescue_action_in_public_with_hoptoad)
+ base.send(:alias_method, :rescue_action_locally_without_hoptoad, :rescue_action_locally)
+ base.send(:alias_method, :rescue_action_locally, :rescue_action_locally_with_hoptoad)
end
end
@@ -21,6 +23,23 @@ def rescue_action_in_public_with_hoptoad(exception)
rescue_action_in_public_without_hoptoad(exception)
end
+ def rescue_action_locally_with_hoptoad(exception)
+ result = rescue_action_locally_without_hoptoad(exception)
+
+ if HoptoadNotifier.configuration.development_lookup
+ path = "#{File.dirname(File.dirname(__FILE__))}/templates/rescue.erb"
+ notice = HoptoadNotifier.build_lookup_hash_for(exception, request_data_for_hoptoad)
+
+ result << @template.render(
+ :file => path,
+ :locals => { :host => HoptoadNotifier.configuration.host,
+ :api_key => HoptoadNotifier.configuration.api_key,
+ :notice => notice })
+ end
+
+ result
+ end
+
# This method should be used for sending manual notifications while you are still
# inside the controller. Otherwise it works like HoptoadNotifier.notify.
def notify_hoptoad(hash_or_exception)
View
14 lib/hoptoad_notifier/configuration.rb
@@ -3,11 +3,11 @@ module HoptoadNotifier
class Configuration
OPTIONS = [:api_key, :backtrace_filters, :development_environments,
- :environment_filters, :environment_name, :host, :http_open_timeout,
- :http_read_timeout, :ignore, :ignore_by_filters, :ignore_user_agent,
- :notifier_name, :notifier_url, :notifier_version, :params_filters,
- :project_root, :port, :protocol, :proxy_host, :proxy_pass, :proxy_port,
- :proxy_user, :secure].freeze
+ :development_lookup, :environment_filters, :environment_name, :host,
+ :http_open_timeout, :http_read_timeout, :ignore, :ignore_by_filters,
+ :ignore_user_agent, :notifier_name, :notifier_url, :notifier_version,
+ :params_filters, :project_root, :port, :protocol, :proxy_host,
+ :proxy_pass, :proxy_port, :proxy_user, :secure].freeze
# The API key for your project, found on the project edit form.
attr_accessor :api_key
@@ -63,6 +63,9 @@ class Configuration
# A list of environments in which notifications should not be sent.
attr_accessor :development_environments
+ # +true+ if you want to check for production errors matching development errors, +false+ otherwise.
+ attr_accessor :development_lookup
+
# The name of the environment the application is running in
attr_accessor :environment_name
@@ -123,6 +126,7 @@ def initialize
@ignore = IGNORE_DEFAULT.dup
@ignore_user_agent = []
@development_environments = %w(development test)
+ @development_lookup = true
@notifier_name = 'Hoptoad Notifier'
@notifier_version = VERSION
@notifier_url = 'http://hoptoadapp.com'
View
91 lib/templates/rescue.erb
@@ -0,0 +1,91 @@
+<script type="text/javascript">
+var Hoptoad = {
+ host : <%= host.to_json %>,
+ api_key : <%= api_key.to_json %>,
+ notice : <%= notice.to_json %>,
+ message : 'This error exists in production!',
+
+ initialize: function() {
+ if (this.initialized) {
+ return;
+ } else {
+ this.initialized = true;
+ }
+
+ var data = [];
+
+ for (var key in this.notice) {
+ data[data.length] = 'notice[' + key + ']=' + this.notice[key];
+ }
+
+ data[data.length] = 'notice[api_key]=' + this.api_key;
+ data[data.length] = 'callback=Hoptoad.onSuccess';
+ data[data.length] = '_=' + (new Date()).getTime();
+
+ var head = document.getElementsByTagName('head')[0];
+ var done = false;
+
+ var
+ script = document.createElement('script');
+ script.src = 'http://' + this.host + '/notices_api/v1/notices/exist?' +
+ data.join('&');
+ script.type = 'text/javascript';
+ script.onload = script.onreadystatechange = function(){
+ if (!done && (!this.readyState ||
+ this.readyState == 'loaded' || this.readyState == 'complete')) {
+
+ done = true;
+
+ // Handle memory leak in IE. (via jQuery)
+ script.onload = script.onreadystatechange = null;
+ head.removeChild(script);
+ }
+ };
+
+ head.appendChild(script);
+ },
+
+ onSuccess: function(error) {
+ var body = document.getElementsByTagName('body')[0];
+ var text = document.createTextNode(this.message);
+ var element = document.createElement('a');
+
+ element.id = 'hoptoad';
+ element.href = 'http://' + error.subdomain + '.' + this.host +
+ '/projects/' + error.project_id + '/errors/' + error.id;
+ element.appendChild(text);
+
+ body.insertBefore(element, body.firstChild);
+
+ var h1 = document.getElementsByTagName('h1')[0];
+ var pre = document.getElementsByTagName('pre')[0];
+ var wrapper = document.createElement('div');
+
+ wrapper.id = 'wrapper';
+ wrapper.appendChild(h1);
+ wrapper.appendChild(pre);
+
+ body.insertBefore(wrapper, body.children[1]);
+ }
+};
+
+window.onload = function() {
+ Hoptoad.initialize.apply(Hoptoad);
+};
+</script>
+
+<style type="text/css">
+#hoptoad {
+ background: #FFF url(http://hoptoadapp.com/images/fell-off-the-toad.gif) no-repeat top right;
+ color: #F00;
+ padding: 45px 101px 45px 12px;
+ font-size: 14px;
+ font-weight: bold;
+ display: block;
+ float: right;
+}
+
+#wrapper {
+ padding-right: 360px;
+}
+</style>
View
35 test/catcher_test.rb
@@ -219,4 +219,39 @@ def process_action_with_automatic_notification(args = {})
assert_sent_hash filtered_cgi, '/notice/request/cgi-data'
end
+ context "for a local error with development lookup enabled" do
+ setup do
+ HoptoadNotifier.configuration.development_lookup = true
+ HoptoadNotifier.stubs(:build_lookup_hash_for).returns({ :awesome => 2 })
+
+ @controller = process_action_with_automatic_notification(:local => true)
+ @response = @controller.response
+ end
+
+ should "append custom CSS and JS to response body for a local error" do
+ assert_match /text\/css/, @response.body
+ assert_match /text\/javascript/, @response.body
+ end
+
+ should "contain host, API key and notice JSON" do
+ assert_match HoptoadNotifier.configuration.host.to_json, @response.body
+ assert_match HoptoadNotifier.configuration.api_key.to_json, @response.body
+ assert_match ({ :awesome => 2 }).to_json, @response.body
+ end
+ end
+
+ context "for a local error with development lookup disabled" do
+ setup do
+ HoptoadNotifier.configuration.development_lookup = false
+
+ @controller = process_action_with_automatic_notification(:local => true)
+ @response = @controller.response
+ end
+
+ should "not append custom CSS and JS to response for a local error" do
+ assert_no_match /text\/css/, @response.body
+ assert_no_match /text\/javascript/, @response.body
+ end
+ end
+
end
View
4 test/configuration_test.rb
@@ -27,6 +27,7 @@ class ConfigurationTest < Test::Unit::TestCase
HoptoadNotifier::Configuration::DEFAULT_BACKTRACE_FILTERS
assert_config_default :ignore,
HoptoadNotifier::Configuration::IGNORE_DEFAULT
+ assert_config_default :development_lookup, true
end
should "provide default values for secure connections" do
@@ -66,6 +67,7 @@ class ConfigurationTest < Test::Unit::TestCase
assert_config_overridable :notifier_name
assert_config_overridable :notifier_url
assert_config_overridable :environment_name
+ assert_config_overridable :development_lookup
end
should "have an api key" do
@@ -80,7 +82,7 @@ class ConfigurationTest < Test::Unit::TestCase
:http_read_timeout, :ignore, :ignore_by_filters, :ignore_user_agent,
:notifier_name, :notifier_url, :notifier_version, :params_filters,
:project_root, :port, :protocol, :proxy_host, :proxy_pass, :proxy_port,
- :proxy_user, :secure].each do |option|
+ :proxy_user, :secure, :development_lookup].each do |option|
assert_equal config[option], hash[option], "Wrong value for #{option}"
end
end
View
47 test/notifier_test.rb
@@ -136,4 +136,51 @@ def set_development_env
end
end
+ context "building notice JSON for an exception" do
+ setup do
+ @params = { :controller => "users", :action => "create" }
+ @exception = build_exception
+ @hash = HoptoadNotifier.build_lookup_hash_for(@exception, @params)
+ end
+
+ should "set action" do
+ assert_equal @params[:action], @hash[:action]
+ end
+
+ should "set controller" do
+ assert_equal @params[:controller], @hash[:component]
+ end
+
+ should "set line number" do
+ assert @hash[:line_number] =~ /\d+/
+ end
+
+ should "set file" do
+ assert_equal './test/helper.rb', @hash[:file]
+ end
+
+ should "set rails_env to production" do
+ assert_equal 'production', @hash[:environment_name]
+ end
+
+ should "set error class" do
+ assert_equal 'RuntimeError', @hash[:error_class]
+ end
+
+ should "not set file or line number with no backtrace" do
+ @exception.stubs(:backtrace).returns([])
+
+ @hash = HoptoadNotifier.build_lookup_hash_for(@exception)
+
+ assert_nil @hash[:line_number]
+ assert_nil @hash[:file]
+ end
+
+ should "not set action or controller when not provided" do
+ @hash = HoptoadNotifier.build_lookup_hash_for(@exception)
+
+ assert_nil @hash[:action]
+ assert_nil @hash[:controller]
+ end
+ end
end
Please sign in to comment.
Something went wrong with that request. Please try again.