<?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>2015-04-17T01:07:24+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&gt;mozNotification&lt;/code&gt; javascript API.&lt;/p&gt;
&lt;div class=&#39;highlight&#39;&gt;&lt;pre&gt;&lt;code class=&#39;language-javascript&#39; data-lang=&#39;javascript&#39;&gt;&lt;span class=&#39;kd&#39;&gt;var&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;notification&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;navigator&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;mozNotification&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;;&lt;/span&gt;
&lt;span class=&#39;k&#39;&gt;if&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;notification&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;notification&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;requestRemotePermission&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;{&lt;/span&gt;
  &lt;span class=&#39;kd&#39;&gt;var&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;request&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;notification&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;requestRemotePermission&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;();&lt;/span&gt;
  &lt;span class=&#39;nx&#39;&gt;request&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;onsuccess&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;kd&#39;&gt;function&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;()&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;{&lt;/span&gt;
    &lt;span class=&#39;kd&#39;&gt;var&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;url&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;request&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;result&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;url&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;;&lt;/span&gt;
    &lt;span class=&#39;nx&#39;&gt;jQuery&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;post&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;s2&#39;&gt;&amp;quot;/add-push-url&amp;quot;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;{&lt;/span&gt;&lt;span class=&#39;s2&#39;&gt;&amp;quot;push-url&amp;quot;&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;:&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;url&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;});&lt;/span&gt;
  &lt;span class=&#39;p&#39;&gt;}&lt;/span&gt;
&lt;span class=&#39;p&#39;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This code checks that &lt;code&gt;navigator.mozNotification&lt;/code&gt; exists and then asks for permission to send notifications using &lt;code&gt;requestRemotePermission()&lt;/code&gt;. When the &lt;code&gt;onsuccess&lt;/code&gt; event fires, the callback &lt;code&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&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&gt;User&lt;/code&gt; model stores the username and push URL:&lt;/p&gt;
&lt;div class=&#39;highlight&#39;&gt;&lt;pre&gt;&lt;code class=&#39;language-python&#39; data-lang=&#39;python&#39;&gt;&lt;span class=&#39;k&#39;&gt;class&lt;/span&gt; &lt;span class=&#39;nc&#39;&gt;User&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;Model&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;Model&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;):&lt;/span&gt;
    &lt;span class=&#39;nb&#39;&gt;id&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;Column&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;Integer&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;primary_key&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;=&lt;/span&gt;&lt;span class=&#39;bp&#39;&gt;True&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;username&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;Column&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;String&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;mi&#39;&gt;80&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;),&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;unique&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;=&lt;/span&gt;&lt;span class=&#39;bp&#39;&gt;True&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;push_url&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;Column&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;String&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;mi&#39;&gt;256&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;),&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;nullable&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;=&lt;/span&gt;&lt;span class=&#39;bp&#39;&gt;True&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Users are created with an empty &lt;code&gt;push_url&lt;/code&gt; after they connect to Github through OAuth. The &lt;code&gt;push_url&lt;/code&gt; is filled in by calling the &lt;code&gt;/add-push-url&lt;/code&gt; view:&lt;/p&gt;
&lt;div class=&#39;highlight&#39;&gt;&lt;pre&gt;&lt;code class=&#39;language-python&#39; data-lang=&#39;python&#39;&gt;&lt;span class=&#39;nd&#39;&gt;@app.route&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;s&#39;&gt;&amp;#39;/add-push-url&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;methods&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;=&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;[&lt;/span&gt;&lt;span class=&#39;s&#39;&gt;&amp;#39;POST&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;])&lt;/span&gt;
&lt;span class=&#39;k&#39;&gt;def&lt;/span&gt; &lt;span class=&#39;nf&#39;&gt;add_push_url&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;():&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;username&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;session&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;[&lt;/span&gt;&lt;span class=&#39;s&#39;&gt;&amp;#39;username&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;]&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;user&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;User&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;query&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;filter_by&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;username&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;=&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;username&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;first_or_404&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;()&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;user&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;push_url&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;request&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;form&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;[&lt;/span&gt;&lt;span class=&#39;s&#39;&gt;&amp;#39;push-url&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;]&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;session&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;add&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;user&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;db&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;session&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;commit&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;()&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;notify&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;user&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;push_url&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt;
           &lt;span class=&#39;s&#39;&gt;&amp;#39;Welcome to Github Notifications!&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt;
           &lt;span class=&#39;s&#39;&gt;&amp;#39;So glad to have you &lt;/span&gt;&lt;span class=&#39;si&#39;&gt;%s&lt;/span&gt;&lt;span class=&#39;s&#39;&gt;.&amp;#39;&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;%&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;user&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;username&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt;
    &lt;span class=&#39;k&#39;&gt;return&lt;/span&gt; &lt;span class=&#39;s&#39;&gt;&amp;#39;&amp;#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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&gt;POST&lt;/code&gt;ing to the push URL.&lt;/p&gt;
&lt;div class=&#39;highlight&#39;&gt;&lt;pre&gt;&lt;code class=&#39;language-python&#39; data-lang=&#39;python&#39;&gt;&lt;span class=&#39;k&#39;&gt;def&lt;/span&gt; &lt;span class=&#39;nf&#39;&gt;notify&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;push_url&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;title&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;body&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;action_url&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;=&lt;/span&gt;&lt;span class=&#39;bp&#39;&gt;None&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;):&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;msg&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;{&lt;/span&gt;&lt;span class=&#39;s&#39;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;:&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;title&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt;
           &lt;span class=&#39;s&#39;&gt;&amp;#39;body&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;:&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;body&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;}&lt;/span&gt;
    &lt;span class=&#39;k&#39;&gt;if&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;action&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;:&lt;/span&gt;
        &lt;span class=&#39;n&#39;&gt;msg&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;[&lt;/span&gt;&lt;span class=&#39;s&#39;&gt;&amp;#39;actionUrl&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;]&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;action_url&lt;/span&gt;
    &lt;span class=&#39;n&#39;&gt;requests&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;post&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;push_url&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;msg&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Using the &lt;a href=&quot;http://python-requests.org&quot;&gt;requests&lt;/a&gt; library, &lt;code&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;div class=&#39;highlight&#39;&gt;&lt;pre&gt;&lt;code class=&#39;language-javascript&#39; data-lang=&#39;javascript&#39;&gt;&lt;span class=&#39;kd&#39;&gt;var&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;notification&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;navigator&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;mozNotification&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;;&lt;/span&gt;
&lt;span class=&#39;k&#39;&gt;if&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;notification&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;notification&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;requestRemotePermission&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;)&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;{&lt;/span&gt;
  &lt;span class=&#39;c1&#39;&gt;// Ask the user to allow notifications.&lt;/span&gt;
  &lt;span class=&#39;kd&#39;&gt;var&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;request&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;notification&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;requestRemotePermission&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;();&lt;/span&gt;
  &lt;span class=&#39;nx&#39;&gt;request&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;onsuccess&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;kd&#39;&gt;function&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;()&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;{&lt;/span&gt;
    &lt;span class=&#39;kd&#39;&gt;var&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;url&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;request&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;result&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;;&lt;/span&gt;
    &lt;span class=&#39;nx&#39;&gt;console&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;log&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;s1&#39;&gt;&amp;#39;New push URL: &amp;#39;&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;+&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;url&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;);&lt;/span&gt;
    &lt;span class=&#39;c1&#39;&gt;// We got a new push URL, store it on the server.&lt;/span&gt;
    &lt;span class=&#39;nx&#39;&gt;jQuery&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;post&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;(&lt;/span&gt;&lt;span class=&#39;s1&#39;&gt;&amp;#39;/push-urls/&amp;#39;&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;,&lt;/span&gt; &lt;span class=&#39;p&#39;&gt;{&lt;/span&gt;&lt;span class=&#39;nx&#39;&gt;url&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;:&lt;/span&gt; &lt;span class=&#39;nx&#39;&gt;url&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;});&lt;/span&gt;
  &lt;span class=&#39;p&#39;&gt;};&lt;/span&gt;
&lt;span class=&#39;p&#39;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The notification API will live at &lt;code&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&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&gt;onsuccess&lt;/code&gt; callback as &lt;code&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;pre&gt;&lt;code&gt;POST /some-queue-url HTTP/1.1
Content-Type: application/json

{&amp;quot;iconUrl&amp;quot;: &amp;quot;http://example.com/shipped.png&amp;quot;,
 &amp;quot;title&amp;quot;: &amp;quot;Your package has shipped.&amp;quot;,
 &amp;quot;body&amp;quot;: &amp;quot;We shipped your package at 10am this morning.&amp;quot;,
 &amp;quot;actionUrl&amp;quot;: &amp;quot;http://example.com/order-status&amp;quot;,
 &amp;quot;replaceId&amp;quot;: &amp;quot;order-status&amp;quot;}&lt;/code&gt;&lt;/pre&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&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=&#39;addons.mozilla.org&#39;&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;div class=&#39;highlight&#39;&gt;&lt;pre&gt;&lt;code class=&#39;language-python&#39; data-lang=&#39;python&#39;&gt;&lt;span class=&#39;kn&#39;&gt;from&lt;/span&gt; &lt;span class=&#39;nn&#39;&gt;django.db&lt;/span&gt; &lt;span class=&#39;kn&#39;&gt;import&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;models&lt;/span&gt;

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

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

    &lt;span class=&#39;n&#39;&gt;objects&lt;/span&gt; &lt;span class=&#39;o&#39;&gt;=&lt;/span&gt; &lt;span class=&#39;n&#39;&gt;caching&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;base&lt;/span&gt;&lt;span class=&#39;o&#39;&gt;.&lt;/span&gt;&lt;span class=&#39;n&#39;&gt;CachingManager&lt;/span&gt;&lt;span class=&#39;p&#39;&gt;()&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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&gt;CachingMixin&lt;/code&gt;&lt;/a&gt;. This mixin gives your model &lt;code&gt;post_sync&lt;/code&gt; and &lt;code&gt;post_delete&lt;/code&gt; signal handlers that will flush the object from cache. It also adds a &lt;code&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&gt;CachingManager&lt;/code&gt;&lt;/a&gt;. Instead of a normal &lt;code&gt;QuerySet&lt;/code&gt;, this manager returns &lt;code&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&gt;QuerySets&lt;/code&gt; and &lt;code&gt;RawQuerySets&lt;/code&gt;. Each &lt;code&gt;QuerySet&lt;/code&gt; is keyed by the active locale and the SQL of the underlying query. The &lt;code&gt;CachingQuerySet&lt;/code&gt; wraps around &lt;code&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&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&gt;memcached&lt;/code&gt; and &lt;code&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&gt;values()&lt;/code&gt; and &lt;code&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&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&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_nonstandard_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&gt;settings.py&lt;/code&gt;, which should look something like:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;# How to connect to the database
db = &amp;#39;mysql --silent -p blam -D pow&amp;#39;
# The table where version info is stored.
table = &amp;#39;schema_version&amp;#39;&lt;/code&gt;&lt;/pre&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&gt;001-adding-awesome.sql&lt;/code&gt;. They’re matched against &lt;code&gt;&amp;#39;^\d+&amp;#39;&lt;/code&gt; so you can put zeros in front to keep ordering in &lt;code&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&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&gt;___(&amp;#39;msgid&amp;#39;, &amp;#39;msgstr&amp;#39;)&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;pre&gt;&lt;code&gt;poboy locale/en_US/LC_MESSAGES/messages.po --find&lt;/code&gt;&lt;/pre&gt;

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

&lt;pre&gt;&lt;code&gt;poboy locale/en_US/LC_MESSAGES/messages.po -an&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;That’s &lt;code&gt;-a&lt;/code&gt; for &lt;code&gt;--add&lt;/code&gt; (to the .po file) and &lt;code&gt;-n&lt;/code&gt; for &lt;code&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;pre&gt;&lt;code&gt;poboy locale/en_US/LC_MESSAGES/messages.po -n&lt;/code&gt;&lt;/pre&gt;

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

&lt;pre&gt;&lt;code&gt;poboy locale/en_US/LC_MESSAGES/messages.po &amp;gt; poboy.patch&lt;/code&gt;&lt;/pre&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&gt;STATIC_DEPS=true&lt;/code&gt; in the build environment:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;STATIC_DEPS=true pip install pyquery&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This has worked for me on OS X with &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;easy_install&lt;/code&gt;, &lt;code&gt;buildout&lt;/code&gt;, and probably anything else based on &lt;code&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;pre&gt;&lt;code&gt;import pyquery

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

&lt;p&gt;Finding the insult on that page is aided by the author’s semantic &lt;code&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;pre&gt;&lt;code&gt;assert d(&amp;#39;#stats&amp;#39;).text() == &amp;#39;5 tests: +2 -3&amp;#39;&lt;/code&gt;&lt;/pre&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;pre&gt;&lt;code&gt;validator = post_multipart(&amp;#39;validator.w3.org&amp;#39;, &amp;#39;/check&amp;#39;,
                           {&amp;#39;fragment&amp;#39;: response.data})
assert pyquery.PyQuery(validator)(&amp;#39;#congrats&amp;#39;)&lt;/code&gt;&lt;/pre&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&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&gt;assert&lt;/code&gt;s (or the Pythonic helpers in &lt;code&gt;nose.tools&lt;/code&gt;) and running all my tests is as simple as calling &lt;code&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;pre&gt;&lt;code&gt;Django test runner that invokes nose.

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

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

You can use

    NOSE_ARGS = [&amp;#39;list&amp;#39;, &amp;#39;of&amp;#39;, &amp;#39;args&amp;#39;]

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

</feed>
