We’ve been working with Heroku to bring you an even easier and more convenient way to add Hoptoad to your Heroku apps:
$ heroku addons:add hoptoad:plus
$ heroku addons:add hoptoad:basic
Or, if you prefer, you can add Hoptoad to your app with a single click over on the Hoptoad Heroku Addon page.
Once you’ve configured the addon, you’ll want to install the Hoptoad Notifier in your project, and you can use a shiny, new, Heroku-specific configuration flag in the generator:
$ rails generate hoptoad --heroku
Then, you can click through from your Heroku dashboard to the Hoptoad addon, and get the same Hoptoad interface you’re come to know and love:


I would highly recommend building your own Heroku addon. The experience is getting very polished, and there is a large amount of documentation at the Heroku Addon Provider site. They take care of billing and provide a single-sign on API for addon providers, and generally make the process seamless and pain-free. We’ve been working with them for a while, through the development of their addon platform, and they have a very solid offering now. Go forth and develop!
… in Python.
Hoptoad, our app error app, is a Web service that accepts XML. No Rails-specific tricks going on here; you can have runtime errors in any programming language and Hoptoad can handle that for you.
Let’s build a notifier in some language I don’t know. How about Python?
The main step is to actually send the data to Hoptoad. Subsequent steps (integration testing, plugins for your framework, and so on) are either outside the scope of this tutorial or may be covered in a future tutorial.
Sending the data is two parts: generating the data to send, then sending it. To generate the data we can use any library that will produce XML for us.
import traceback
import sys
import urllib2
import xmlbuilder
from xmlbuilder import XMLBuilder
def api_key():
return "YOUR API KEY"
def generate_xml(exn):
_,_,trace = sys.exc_info()
xml = XMLBuilder()
with xml.notice(version = 2.0):
xml << ('api-key', api_key())
with xml.notifier:
xml << ('name', 'Dennis')
xml << ('version', '0.1')
xml << ('url', 'http://hoptoadapp.com')
with xml('server-environment'):
xml << ('environment-name', 'REPL')
with xml.error:
xml << ('class', exn.__class__.__name__)
xml << ('message', str(exn))
with xml.backtrace:
[xml << ('line', {'file':filename, 'number':line_number, 'method':function_name}) \
for filename, line_number, function_name, _ in traceback.extract_tb(trace)]
return str(xml)
What we’ve written here is a method that takes an exception and produces the XML that should be sent to Hoptoad. Nothing especially tricky happens here; we pull out the class of exception and the message describing it, then grab and generate the XML for the backtrace.
Next up: sending this data.
Sending the data isn’t too tricky. It’s a HTTP POST with the text/xml content type to /notifier_api/v2/notices. The only edge case is that paid accounts support SSL.
def hoptoad_url(use_ssl):
if use_ssl:
return "https://hoptoadapp.com/notifier_api/v2/notices"
else:
return "http://hoptoadapp.com/notifier_api/v2/notices"
def send_to_hoptoad(xml, use_ssl):
headers = { 'Content-Type': 'text/xml' }
request = urllib2.Request(hoptoad_url(use_ssl), xml, headers)
response = urllib2.urlopen(request)
status = response.getcode()
if status == 200:
pass
if status == 403:
raise Exception("Cannot use SSL")
if status == 422:
raise Exception("Invalid XML sent to Hoptoad")
if status == 500:
raise Exception("Hoptoad has hopped the toad")
def notify_hoptoad(exn, use_ssl):
send_to_hoptoad(generate_xml(exn), use_ssl)
The HTTP response indicates how well it worked; the exceptional cases are 403 (the customer does not have a Hoptoad account that supports SSL), 422 (invalid XML), and 500 (server error).
So that’s it:
try:
1/0
except ZeroDivisionError as exn:
notify_hoptoad(exn, True)
That is what is needed to send an exception to Hoptoad from Python. To use Hoptoad with Django there is the full django-hoptoad plugin; a full list of alternative plugins exists too. Perhaps yours can be there soon too!
A common pattern in our apps is handling failure by notifying Airbrake. A good example is hitting a third party web service such as Twitter.
def fetch_tweets(search)
search.fetch
rescue *(HTTP_ERRORS + Twitter::Search::ERRORS) => error
HoptoadNotifier.notify(error)
[]
end
We send rescued errors to Airbrake, shielding our users from 500s with relevant copy.

This uses the Twitter gem. It separates building a query (object initialization) and making the HTTP request (fetch method).
Somewhere else, we do heavy-duty query building. We then isolate the HTTP request inside it’s own method and rescue an expected class of errors.
Aside from testing failure, this isolation makes our other tests easier. We can stub the fetch_tweets method and forget about the interface to the Twitter library.
HTTP_ERRORS comes from the Suspenders’ errors initializer and Twitter::Search::ERRORS is something custom.
We use Bourne for its test spies.
*(HTTP_ERRORS + Twitter::Search::ERRORS).each do |error|
should "notify hoptoad upon #{error} on fetch tweets" do
brand = build(:brand)
search = stub('search')
search.stubs(:fetch).raises(error.new(''))
Airbrake.stubs(:notify)
brand.fetch_tweets(search)
assert_received(Airbrake, :notify) do |expects|
expects.with(is_a(error))
end
end
end
This is a classic stub-and-spy test. It uses the normal Four-Phase Test structure. The extra newlines separate the setup, exercise, and verification phases. The test is flat; it does not use a context block.
Written by Dan Croak.