<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

 <title>Jeff Balogh</title>
 <link href="http://jbalogh.me/atom.xml" rel="self"/>
 <link href="http://jbalogh.me/"/>
 <updated>2026-07-16T10:42:04+00:00</updated>
 <id>http://jbalogh.me/</id>
 <author>
   <name>Jeff Balogh</name>
   <email>blog@jbalogh.me</email>
 </author>

 
 <entry>
   <title>The Story of Glow</title>
   <link href="http://jbalogh.me/2013/01/22/the-story-of-glow/"/>
   <updated>2013-01-22T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2013/01/22/the-story-of-glow</id>
   <content type="html">&lt;p&gt;One of the most fun projects I worked on at Mozilla was
&lt;a href=&quot;http://glow.mozilla.org&quot;&gt;glow.mozilla.org&lt;/a&gt;. It was a couple weeks before the launch of Firefox 4
and my frontend buddy &lt;a href=&quot;https://twitter.com/potch&quot;&gt;@potch&lt;/a&gt; and I decided we should make a map to track
downloads in real-time.  This was back when Firefox releases were a big deal.
It turned out to be a really compelling visual. We saw a bunch of tweets about
people loving the map, it was featured in &lt;a href=&quot;http://flowingdata.com/2011/03/22/firefox-4-downloads-in-real-time/&quot;&gt;Flowing Data&lt;/a&gt; and nytimes.com, and
somebody even made a &lt;a href=&quot;http://www.youtube.com/watch?v=ummATvv6Nc0&quot;&gt;video&lt;/a&gt; about it.&lt;/p&gt;

&lt;p&gt;Potch started working on the frontend map with SVG and canvas while I figured
out how to serve the download data; we would meet in the middle. We didn’t have
much experience with node.js and didn’t know what kind of traffic we’d see, so
I decided to play it safe and use static json data files for a near-real-time
experience. Each minute of downloads was stored in a separate file in a
predictable location. When the page loaded it looked for the data file two
minutes before the current time, since we knew (hoped) that it would be written
and ready to serve.&lt;/p&gt;

&lt;p&gt;Our script loaded the record of the downloads and started dropping dots on the
world map wherever the download came from (according to IP addresses). No one
knew the data was two minutes old and our web servers were pretty good at
serving static files.&lt;/p&gt;

&lt;p&gt;The download data was already flowing into HBase (set up by someone else), so I
wrote a Python script that connected to HBase every minute and retrieved the
latest data. I massaged it into a structure easier for the js to consume and
wrote it all out to a per-minute json file. That script ran on a cron job since
we like old reliable technology.&lt;/p&gt;

&lt;p&gt;The dots were animated on a transparent canvas overlaid on the SVG world map.
We had a lot of trouble making the animation performant since we were working
with old browsers, Firefox 3.6 and Chrome 10. Everything worked, but it was
super slow.&lt;/p&gt;

&lt;p&gt;The Sunday before the release, I curled up with the Chrome profiler and
optimized the script. The original version was thrashing the GC by removing
items in the middle of our large array of data points, so I rewrote it to sort
the data first and use a moving cursor to traverse the array, animating dots as
it went along. Our only garbage collection came when we were done with a
minute’s worth of data. I made some more tweaks to fix slow canvas and DOM
interactions and we were good to go. We went from slowing down around 500
points per minute to slowing down at around 70,000 points per minute. At peak
we saw 6,500 downloads per minute, so the page worked beautifully.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>How to Add Push Notifcations to your Website</title>
   <link href="http://jbalogh.me/2012/04/05/how-to-add-push-notifications-to-your-site/"/>
   <updated>2012-04-05T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2012/04/05/how-to-add-push-notifications-to-your-site</id>
   <content type="html">&lt;p&gt;This week I released &lt;a href=&quot;https://github-notifications.herokuapp.com/&quot;&gt;https://github-notifications.herokuapp.com/&lt;/a&gt;, a site that
demos &lt;a href=&quot;http://jbalogh.me/2012/01/30/push-notifications/&quot;&gt;push notifications in Firefox&lt;/a&gt;. It uses Github web hooks to send
notifications when there’s a commit to one of your repositories. Push
notifications are currently implemented in Firefox as an experimental
&lt;a href=&quot;https://github.com/jbalogh/push-addon/&quot;&gt;add-on&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post shows the code I used to send push notifications from the site.&lt;/p&gt;

&lt;h2 id=&quot;get-a-push-url&quot;&gt;Get a Push URL&lt;/h2&gt;

&lt;p&gt;When you give a website permission to send push notifications, Firefox asks the
Push Notification Service to create a URL for the site to contact you. That URL
is returned through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mozNotification&lt;/code&gt; javascript API.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;notification&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mozNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;notification&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;requestRemotePermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;requestRemotePermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onsuccess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;jQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/add-push-url&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;push-url&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;This code checks that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;navigator.mozNotification&lt;/code&gt; exists and then asks for
permission to send notifications using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requestRemotePermission()&lt;/code&gt;. When the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onsuccess&lt;/code&gt; event fires, the callback &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt;s your push URL back to the server.&lt;/p&gt;

&lt;p&gt;(You can play with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mozNotification&lt;/code&gt; API by installing the &lt;a href=&quot;https://github.com/jbalogh/push-addon/&quot;&gt;add-on&lt;/a&gt;.)&lt;/p&gt;

&lt;h2 id=&quot;save-the-push-url&quot;&gt;Save the Push URL&lt;/h2&gt;

&lt;p&gt;The backend of my site is a simple &lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;Flask&lt;/a&gt; app. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;User&lt;/code&gt; model stores the
username and push URL:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;nb&quot;&gt;id&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Integer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;primary_key&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;unique&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;push_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Column&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;256&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nullable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Users are created with an empty &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push_url&lt;/code&gt; after they connect to Github through
OAuth.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push_url&lt;/code&gt; is filled in by calling the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/add-push-url&lt;/code&gt; view:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;o&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;app&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;route&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;/add-push-url&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;methods&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;POST&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_push_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;username&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;User&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;first_or_404&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push_url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;form&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;push-url&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;db&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;session&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;commit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;s&quot;&gt;&apos;Welcome to Github Notifications!&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;s&quot;&gt;&apos;So glad to have you %s.&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;username&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;h2 id=&quot;send-a-notifcation&quot;&gt;Send a Notifcation&lt;/h2&gt;

&lt;p&gt;Sending a notification is as easy as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;POST&lt;/code&gt;ing to the push URL.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;notify&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action_url&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;title&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
           &lt;span class=&quot;s&quot;&gt;&apos;body&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;body&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;actionUrl&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;action_url&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;requests&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;push_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;msg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;Using the &lt;a href=&quot;http://python-requests.org&quot;&gt;requests&lt;/a&gt; library, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notify&lt;/code&gt; sends a message back to the user’s
push URL as a string of url-encoded parameters.  After that, the push
notification system takes care of getting the message to the user’s browser.&lt;/p&gt;

&lt;p&gt;That’s all the code needed to send push notifications to Firefox; we’re trying
to keep the system as simple as possible for developers. In the coming months
the add-on will be integrated into the browser and our Push Notification
Service will go live. If you have questions, feel free to contact me over email
or twitter.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Introducing Push Notifications</title>
   <link href="http://jbalogh.me/2012/01/30/push-notifications/"/>
   <updated>2012-01-30T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2012/01/30/push-notifications</id>
   <content type="html">&lt;p&gt;Push notifications are a way for websites to send small messages to users when
the user is not on the site. iOS and Android devices already support their own
push notification services, but we want to make notifications available to the
whole web.  We’re making prototypes and designing the API right now and want to
share our progress.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;The website gets a URL where it can send notifications to the user. The URL
points to the Notification Service, and is a secret between the user and the
website.&lt;/li&gt;
  &lt;li&gt;The site sends a notification to the Notification Service.&lt;/li&gt;
  &lt;li&gt;The Notification Service delivers the message to Firefox on the desktop, on
Android, on Boot to Gecko, or on iOS through Firefox Home; we’ll find the
right place to deliver the message.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To start sending push notifications, a website needs to ask the user for
permission. Here’s some example code:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-javascript&quot; data-lang=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;notification&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;navigator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;mozNotification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;notification&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;requestRemotePermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// Ask the user to allow notifications.&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;notification&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;requestRemotePermission&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;onsuccess&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;result&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;New push URL: &lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// We got a new push URL, store it on the server.&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;jQuery&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;/push-urls/&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The notification API will live at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;navigator.mozNotification&lt;/code&gt; until it gets
standardized. First we get the API object and check that it exists. If it’s
there we ask the user for permission to send notifications using
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;notification.requestRemotePermission()&lt;/code&gt;, which returns an object we use to
watch for events.&lt;/p&gt;

&lt;p&gt;If the user grants permission, the browser will talk to the Notification
Service and grab a new URL that links our site to the user.  Every site/user
pair gets a unique URL.&lt;/p&gt;

&lt;p&gt;The URL is available in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;onsuccess&lt;/code&gt; callback as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;request.result&lt;/code&gt; and should
be sent back to the server and stored for future use.&lt;/p&gt;

&lt;h2 id=&quot;on-the-server&quot;&gt;On the Server&lt;/h2&gt;

&lt;p&gt;Now that we have a URL, we can send messages from our servers to the
Notification Service.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;POST /some-queue-url HTTP/1.1
Content-Type: application/json

{&quot;iconUrl&quot;: &quot;http://example.com/shipped.png&quot;,
 &quot;title&quot;: &quot;Your package has shipped.&quot;,
 &quot;body&quot;: &quot;We shipped your package at 10am this morning.&quot;,
 &quot;actionUrl&quot;: &quot;http://example.com/order-status&quot;,
 &quot;replaceId&quot;: &quot;order-status&quot;}
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;strong&gt;iconUrl&lt;/strong&gt;: URL of the icon to be shown with this notification.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;title&lt;/strong&gt;: Primary text of the notification.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;body&lt;/strong&gt;: Secondary text of the notification.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;actionUrl&lt;/strong&gt;: URL to be opened if the user clicks on the notification.&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;replaceId&lt;/strong&gt;: A string which identifies a group of like messages. If the
user is offline, only the last message with the same &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;replaceId&lt;/code&gt; will be sent
when the user comes back online.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once the notification is in the system, we’ll deliver it to the recipient on
all the devices they have Firefox installed, but we’ll try not to show
duplicate notifications on different devices.&lt;/p&gt;

&lt;p&gt;For a more detailed description, please check out our &lt;a href=&quot;https://wiki.mozilla.org/Services/Notifications/Push/API&quot;&gt;wiki page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update&lt;/strong&gt;: If you’d like to give feedback please email me, find me on
twitter, or reply to &lt;a href=&quot;http://groups.google.com/group/mozilla.dev.platform/browse_thread/thread/52f0c86c6e164d84#&quot;&gt;this post&lt;/a&gt; on mozilla.dev.platform.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Cache Machine: Automatic caching for your Django models</title>
   <link href="http://jbalogh.me/2010/02/09/cache-machine/"/>
   <updated>2010-02-09T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2010/02/09/cache-machine</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://github.com/jbalogh/django-cache-machine&quot;&gt;Cache Machine&lt;/a&gt; hooks into Django’s ORM to provide easy object caching and
invalidation.&lt;/p&gt;

&lt;p&gt;One of our primary goals with the rewrite of
&lt;a href=&quot;https://addons.mozilla.org&quot;&gt;addons.mozilla.org&lt;/a&gt; was to improve our cache
management.  Large sites like &lt;abbr title=&quot;addons.mozilla.org&quot;&gt;AMO&lt;/abbr&gt; rely
on layers of caching to stay afloat, and caching database queries in memcached
is one of our favorite tools.&lt;/p&gt;

&lt;p&gt;AMO heavily favors reads over writes, so we have great cache performance; the
hit rate ranges from 90%-98%.  However, once something is in the cache, it’s
stuck there until timeout (60 minutes).  Combined with front-end caching, this
can mean it’s a couple of hours before add-on developers see their changes roll
out to the site.  We don’t like that.&lt;/p&gt;

&lt;p&gt;For &lt;a href=&quot;http://github.com/jbalogh/zamboni&quot;&gt;zamboni&lt;/a&gt;, our Django-based rewrite,
seamless object caching and invalidation was my first project.  Today we
released &lt;a href=&quot;http://github.com/jbalogh/django-cache-machine&quot;&gt;Cache Machine&lt;/a&gt; as a drop-in library for use in any Django
application.  The package is available on &lt;a href=&quot;http://pypi.python.org/pypi/django-cache-machine&quot;&gt;pypi&lt;/a&gt; and the code is on
&lt;a href=&quot;http://github.com/jbalogh/django-cache-machine&quot;&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;usage&quot;&gt;Usage&lt;/h2&gt;

&lt;p&gt;Here’s a cache-enabled model:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;

&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;caching.base&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Zomg&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;caching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CachingMixin&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IntegerField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;objects&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;caching&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CachingManager&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;The first step is to inherit from &lt;a href=&quot;http://jbalogh.me/projects/cache-machine/#caching.base.CachingMixin&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CachingMixin&lt;/code&gt;&lt;/a&gt;.  This mixin gives your
model &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post_sync&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post_delete&lt;/code&gt; signal handlers that will flush the
object from cache.  It also adds a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cache_key&lt;/code&gt; property that helps invalidate
the object properly.&lt;/p&gt;

&lt;p&gt;Then you replace the default manager with &lt;a href=&quot;http://jbalogh.me/projects/cache-machine/#caching.base.CachingManager&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CachingManager&lt;/code&gt;&lt;/a&gt;.  Instead of
a normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QuerySet&lt;/code&gt;, this manager returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CachingQuerySets&lt;/code&gt; which try to
pull objects from cache before performing a database lookup.&lt;/p&gt;

&lt;h2 id=&quot;how-it-works&quot;&gt;How it works&lt;/h2&gt;

&lt;p&gt;Cache Machine knows how to cache normal &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QuerySets&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RawQuerySets&lt;/code&gt;.
Each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;QuerySet&lt;/code&gt; is keyed by the active locale and the SQL of the underlying
query.  The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;CachingQuerySet&lt;/code&gt; wraps around  &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Queryset.iterator()&lt;/code&gt; to check
cache before hitting the database.&lt;/p&gt;

&lt;p&gt;Invalidation is the interesting part.  As we iterate through a set of objects
in a database result, we create a “flush list” for each object.  The flush list
maps each object to a list of cached queries it’s a member of.  When an object
is invalidated in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;post_sync&lt;/code&gt; signal, all of the queries it was a part of
are immediately invalidated.&lt;/p&gt;

&lt;p&gt;Parent and child foreign-key relationships are also tracked in the flush lists.
If a parent object changes, its flush list will be invalidated along with all
children that point to it, and vice versa.&lt;/p&gt;

&lt;h2 id=&quot;issues&quot;&gt;Issues&lt;/h2&gt;

&lt;p&gt;Only the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;memcached&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;locmem&lt;/code&gt; backends are supported.  Cache Machine
relies on infinite cache timeouts for storing flush lists, but none of Django’s
builtin backends support this (even though the memcached server does).  We wrap
the memcached and locmem backends to fix the infinite timeout issue, but file
and database backends aren’t implemented since they’re not useful to us.&lt;/p&gt;

&lt;p&gt;Cache Machine does not cache &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;values()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;values_list()&lt;/code&gt; calls.  Since
these methods don’t return full objects, we can’t know how to invalidate them
properly.  They could be overridden to do normal lookups and then pull out the
results, but I haven’t gotten around to that yet.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;count()&lt;/code&gt; queries will not be cached.  These can’t be invalidated
efficiently.  I recommend denormalizing your tables and adding a count field if
you need to access it often. &lt;em&gt;Update: limited count caching was enabled in
&lt;a href=&quot;http://github.com/jbalogh/django-cache-machine/commit/c1e871f4d7&quot;&gt;this commit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Cache Machine has a few &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;log.debug&lt;/code&gt; calls in its caching and invalidation
internals.  These work fine with zamboni, since we set up our logging on
startup.  I don’t know if these calls will be problematic without or logging
config.  Let me know.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Highlights from DjangoCon 2009</title>
   <link href="http://jbalogh.me/2009/10/20/djangocon-2009/"/>
   <updated>2009-10-20T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2009/10/20/djangocon-2009</id>
   <content type="html">&lt;p&gt;Long (long!) overdue, here’s a bunch of links that point to the good things I
learned at djangocon.&lt;/p&gt;

&lt;h2 id=&quot;restful-ponies&quot;&gt;&lt;a href=&quot;http://immike.net/files/restful_ponies.pdf&quot;&gt;Restful Ponies&lt;/a&gt;&lt;/h2&gt;

&lt;ul&gt;
  &lt;li&gt;Started with a basic REST overview, gets interesting when it shows how you can
easily expose resources using &lt;a href=&quot;http://bitbucket.org/jespern/django-piston&quot;&gt;Piston&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://code.welldev.org/django-roa/src/&quot;&gt;django-roa&lt;/a&gt; builds on Piston to give models a basic REST API for free&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://github.com/sixapart/remoteobjects/&quot;&gt;remoteobjects&lt;/a&gt; is an object-restational mapper that maps objects to REST
apis on the web.  It’s really cool and I’m playing with it for the new
Bugzilla rest view.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;deploying-django&quot;&gt;&lt;a href=&quot;http://oebfare.com/files/DeployingDjangoDC2009.pdf&quot;&gt;Deploying Django&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Talks about how they’re doing “repeatable, automated, isolated” deployments
using Python tools.  Don’t you wish we were rocking this on AMO?&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;virtualenv and pip to keep the deployment environment isolated&lt;/li&gt;
  &lt;li&gt;fabric to script vcs checkout and installation&lt;/li&gt;
  &lt;li&gt;using lightweight tags in git to mark deployment versions&lt;/li&gt;
  &lt;li&gt;up &lt;em&gt;and&lt;/em&gt; down migrations with &lt;a href=&quot;http://south.aeracode.org/&quot;&gt;South&lt;/a&gt; to stay safe&lt;/li&gt;
  &lt;li&gt;mod_wsgi daemon mode is preferred, fastcgi is cool too&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;scaling-django&quot;&gt;&lt;a href=&quot;http://immike.net/files/scaling_django.pdf&quot;&gt;Scaling Django&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Pownce was serving hundreds of request/sec, thousands of db ops/sec, it can be
done.  It’s simple to do automatic caching and invalidation when your queries
are going through the ORM, Django’s signals decouple the invalidation process
from your update code.  Using multiple databases with Django is not
straightforward, but it’s getting easier with an SoC project that’s ready to
merged in.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://pypi.python.org/pypi/sqlparse/0.1.1&quot;&gt;sqlparse&lt;/a&gt; pretty prints SQL queries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;the-realtime-web&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/err/the-realtime-web-and-other-buzzwords&quot;&gt;The Realtime Web&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Showed how they created an IRC client in the browser over comet with Django on
the backend.  The browser and the app keep “persistent” connections to an
&lt;a href=&quot;http://orbited.org/&quot;&gt;orbited&lt;/a&gt; server that handles all the messy details so the app can pretend
it’s really connected directly to the browser.&lt;/p&gt;

&lt;p&gt;This coincided with the release of FriendFeed’s &lt;a href=&quot;http://www.tornadoweb.org/&quot;&gt;Tornado&lt;/a&gt; web server, which
led to an interesting focus on async during the conference.&lt;/p&gt;

&lt;h2 id=&quot;using-django-in-non-standard-ways&quot;&gt;&lt;a href=&quot;http://media.eflorenzano.com/dropbox/UsingDjangoInNonStandardWays.pdf&quot;&gt;Using Django in Non-Standard Ways&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Covers a lot of little hurdles that people might consider show-stoppers when
using Django, how to overcome them.  WSGI middleware is fun:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;repoze.bitblt: automatically scales images&lt;/li&gt;
  &lt;li&gt;repoze.squeeze: Merges JS/CSS automatically based on statistical analysis&lt;/li&gt;
  &lt;li&gt;repoze.profile:  Aggregates Python profiling data across all requests, and
provides an html frontend for viewing the data&lt;/li&gt;
  &lt;li&gt;repoze.slicer: extract/filter pieces of an html response&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;pinax-tutorial&quot;&gt;&lt;a href=&quot;http://www.slideshare.net/pydanny/pinax-tutorial-090909&quot;&gt;Pinax Tutorial&lt;/a&gt;&lt;/h2&gt;

&lt;p&gt;Pinax tries to be a collection of reusable apps that work well together, but it
looks like you spend more time trying to configure things than actually making
useful apps.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>The worst schema versioning system, ever?</title>
   <link href="http://jbalogh.me/2009/05/23/schematic/"/>
   <updated>2009-05-23T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2009/05/23/schematic</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://github.com/jbalogh/schematic&quot;&gt;schematic&lt;/a&gt; talks to your database over stdin on the command line.  Thus, it
supports all DBMSs that have a command line interface and doesn’t care what
programming language you worship.  Win!&lt;/p&gt;

&lt;p&gt;It only looks for files in the same directory as itself so you should put this
script, settings.py, and all migrations in the same directory.&lt;/p&gt;

&lt;p&gt;Configuration is done in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;settings.py&lt;/code&gt;, which should look something like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;# How to connect to the database
db = &apos;mysql --silent -p blam -D pow&apos;
# The table where version info is stored.
table = &apos;schema_version&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It’s python so you can do whatever crazy things you want, and it’s a separate
file so you can keep local settings out of version control.&lt;/p&gt;

&lt;p&gt;Migrations are just sql in files whose names start with a number, like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;001-adding-awesome.sql&lt;/code&gt;.  They’re matched against &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&apos;^\d+&apos;&lt;/code&gt; so you can put
zeros in front to keep ordering in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt; happy, and whatever you want after the
migration number, such as text describing the migration.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/jbalogh/schematic&quot;&gt;schematic&lt;/a&gt; creates a table (named in settings.py) with one column, that
holds one row, which describes the current version of the database.  Any
migration file with a number greater than the current version will be applied
to the database and the version tracker will be upgraded.  The migration and
version bump are performed in a transaction.&lt;/p&gt;

&lt;p&gt;The version-tracking table will initially be set to 0, so the 0th migration
could be a script that creates all your tables (for reference).  Migration
numbers are not required to increase linearly.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://github.com/jbalogh/schematic&quot;&gt;schematic&lt;/a&gt; doesn’t pretend to be intelligent. Running migrations manually
without upgrading the version tracking will throw things off.&lt;/p&gt;

&lt;p&gt;Tested on sqlite any mysql.&lt;/p&gt;

&lt;p&gt;NOTE: any superfluous output, like column headers, will cause an error.  On
mysql, this is fixed by using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--silent&lt;/code&gt; parameter.&lt;/p&gt;

&lt;p&gt;Things that might be nice: downgrades, running python files.&lt;/p&gt;

</content>
 </entry>
 
 <entry>
   <title>Introducing poboy</title>
   <link href="http://jbalogh.me/2009/05/14/poboy/"/>
   <updated>2009-05-14T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2009/05/14/poboy</id>
   <content type="html">&lt;p&gt;I’d be surprised if &lt;a href=&quot;http://github.com/jbalogh/poboy/tree/master&quot;&gt;poboy&lt;/a&gt; is useful to anyone I don’t work with, but I
wrote a README, so that should be shared with the internet.&lt;/p&gt;

&lt;p&gt;Finds all the gettext calls that have an inline fallback and moves that
fallback into the messages.po file.  Thus, you can use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;___(&apos;msgid&apos;, &apos;msgstr&apos;)&lt;/code&gt;
when you’re writing new code and use this script to clean up afterwards.&lt;/p&gt;

&lt;p&gt;poboy won’t edit any code files.  Instead, it prints out a unified diff that
you can check for correctness and send to patch.  I didn’t want to deal with
rewriting files safely.&lt;/p&gt;

&lt;h2 id=&quot;how-i-use-it&quot;&gt;How I use it&lt;/h2&gt;

&lt;p&gt;Find all the strings that have a fallback:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;poboy locale/en_US/LC_MESSAGES/messages.po --find
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Find the strings with a fallback that aren’t already in messages.po:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;poboy locale/en_US/LC_MESSAGES/messages.po -an
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;That’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-a&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--add&lt;/code&gt; (to the .po file) and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-n&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--dry_run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Show the strings that will be added and the cleanup patch:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;poboy locale/en_US/LC_MESSAGES/messages.po -n
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And the fun one, add the strings to messages.po and generate a cleanup patch:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;poboy locale/en_US/LC_MESSAGES/messages.po &amp;gt; poboy.patch
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</content>
 </entry>
 
 <entry>
   <title>pyquery: a jquery-like library for python</title>
   <link href="http://jbalogh.me/2009/03/24/pyquery/"/>
   <updated>2009-03-24T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2009/03/24/pyquery</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://pyquery.org/&quot;&gt;pyquery&lt;/a&gt; is a fantastic little library for dealing with
XML and HTML documents.  It brings the power and ease of jQuery into Python,
letting you deal with CSS selectors and functions instead of a clunky DOM.  I
try to avoid dealing with XML as much as possible, but slinging around pyquery
almost makes XML fun.&lt;/p&gt;

&lt;h2 id=&quot;building-lxml&quot;&gt;Building lxml&lt;/h2&gt;
&lt;p&gt;The hardest part of working with pyquery is getting it installed.  pyquery gets
all of its XML power from &lt;a href=&quot;http://codespeak.net/lxml/&quot;&gt;lxml&lt;/a&gt;, which has a reputation for being difficult.
&lt;a href=&quot;http://blog.ianbicking.org/2008/12/10/lxml-an-underappreciated-web-scraping-library/&quot;&gt;Ian Bicking&lt;/a&gt; mentioned that lxml2.2 has become much easier to install by
providing an option to compile the troublesome C libs as static libraries,
which has avoided any problems for me.  All you need to do is define
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;STATIC_DEPS=true&lt;/code&gt; in the build environment:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;STATIC_DEPS=true pip install pyquery
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This has worked for me on OS X with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;easy_install&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buildout&lt;/code&gt;, and
probably anything else based on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;distutils&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;web-scraping&quot;&gt;Web Scraping&lt;/h2&gt;
&lt;p&gt;Web scraping is ridiculously easy with pyquery.  Grabbing a &lt;a href=&quot;http://www.pangloss.com/seidel/Shaker/&quot;&gt;Shakespearean
insult&lt;/a&gt; from the web is as simple as&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;import pyquery

p = pyquery.PyQuery(&apos;http://www.pangloss.com/seidel/Shaker/&apos;)
insult = p(&apos;font&apos;).text()
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Finding the insult on that page is aided by the author’s semantic &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;font&lt;/code&gt; tag.&lt;/p&gt;

&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;
&lt;p&gt;I like to make sure that my views are working correctly, another task in which
I’m finding pyquery indispensable.  I’ve seen regexen used for the same task,
but examining a real DOM is much more resilient than trying to pick out pieces
by matching strings.  Testing views is especially useful when dealing with
template systems like Django’s and Jinja’s which silently hide errors instead
of raising exceptions.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;assert d(&apos;#stats&apos;).text() == &apos;5 tests: +2 -3&apos;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;I’ve noticed that testing the HTML in this manner has improved my semantic
markup.  Pulling out and testing pieces of the page forces me to add meaningful
ids and classes to the elements.&lt;/p&gt;

&lt;h2 id=&quot;bonus&quot;&gt;Bonus&lt;/h2&gt;
&lt;p&gt;For extra HTML goodness, the tests submit response pages to the &lt;a href=&quot;http://validator.w3.org/&quot;&gt;w3c
Validator&lt;/a&gt; using this &lt;a href=&quot;http://github.com/jbalogh/bosley/blob/f64cfeb739954311c36b357d657c227e6d9646a2/bosley/tests/multipart.py&quot;&gt;multipart form encoder&lt;/a&gt;.  Then, of course, I use
pyquery to make sure all is well.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;validator = post_multipart(&apos;validator.w3.org&apos;, &apos;/check&apos;,
                           {&apos;fragment&apos;: response.data})
assert pyquery.PyQuery(validator)(&apos;#congrats&apos;)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 
 <entry>
   <title>Nose Test Runner for Django</title>
   <link href="http://jbalogh.me/2008/11/02/nose-test-runner-for-django/"/>
   <updated>2008-11-02T00:00:00+00:00</updated>
   <id>http://jbalogh.me/2008/11/02/nose-test-runner-for-django</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Update: you can now find django-nose on &lt;a href=&quot;http://pypi.python.org/pypi/django-nose&quot;&gt;pypi&lt;/a&gt; and &lt;a href=&quot;http://github.com/jbalogh/django-nose&quot;&gt;github&lt;/a&gt; with much
better documentation.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I am not a big fan of Python’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;unittest&lt;/code&gt; library.  The Java-inspired API and
the difficulty of running tests are too much for me to deal with.  That’s why I
love &lt;a href=&quot;http://www.somethingaboutorange.com/mrl/projects/nose/&quot;&gt;nose&lt;/a&gt;: I can use regular &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;assert&lt;/code&gt;s (or the Pythonic helpers in
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nose.tools&lt;/code&gt;) and running all my tests is as simple as calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nosetests&lt;/code&gt; from
the command line.  On top of that, nose also supports cool plugins like
generating coverage reports and running tests interactively, test fixtures at
any granularity level, and simple selection of tests to run, making me a happy
tester.&lt;/p&gt;

&lt;p&gt;Which is why I wrote a custom &lt;a href=&quot;http://docs.djangoproject.com/en/dev/topics/testing/#defining-a-test-runner&quot;&gt;test runner&lt;/a&gt; as soon as I started working on
&lt;a href=&quot;basieproject.org&quot;&gt;basie&lt;/a&gt;.  Django provides its own test runner framework, but it’s far less
advanced than nose.&lt;/p&gt;

&lt;p&gt;I haven’t packaged it up for PyPI yet, but you can download
&lt;a href=&quot;http://code.basieproject.org/trunk/apps/django_nose/nose_runner.py&quot;&gt;nose_runner.py&lt;/a&gt; from our repository.  Here’s the documentation:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Django test runner that invokes nose.

Usage:
    ./manage.py test DJANGO_ARGS -- NOSE_ARGS

The &apos;test&apos; argument, and any other args before &apos;--&apos;, will not be passed to
nose, allowing django args and nose args to coexist.

You can use

    NOSE_ARGS = [&apos;list&apos;, &apos;of&apos;, &apos;args&apos;]

in settings.py for arguments that you always want passed to nose.
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</content>
 </entry>
 

</feed>
