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

 <title>Stephen McDonald</title>
 <link href="http://blog.jupo.org/atom.xml" rel="self"/>
 <link href="http://blog.jupo.org"/>
 <updated>2012-04-08T23:31:52-07:00</updated>
 <id>http://blog.jupo.org</id>
 <author>
   <name>Stephen McDonald</name>
   <email>steve@jupo.org</email>
 </author>

 
 <entry>
   <title>Topological Sorting Acyclic Directed Graphs</title>
   <link href="http://blog.jupo.org/2012/04/06/topological-sorting-acyclic-directed-graphs/"/>
   <updated>2012-04-06T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2012/04/06/topological-sorting-acyclic-directed-graphs</id>
   <content type="html">&lt;p&gt;The actual code written when developing a typical web application, ranks pretty lowly on the complexity scale, when compared to the rest of the software engineering industry. Of course, web development has its own set of interesting complexities around architecture and scaling, but the application code itself is relatively simple. Glue one library to another, update a row in a database, print some more rows out, send out an email, and so on.&lt;/p&gt;

&lt;p&gt;As a web developer, I always enjoy the chance to dig deeper into more formal data structures and algorithms when the opportunity arises. At &lt;a href=&quot;http://fairfax.com.au&quot;&gt;Fairfax&lt;/a&gt; I&amp;rsquo;ve recently had two distinct situations come up where I&amp;rsquo;ve needed to work through sets of data, with each of the items in the set containing references to other items that they depends on. The desired outcome was to sort the items so that I could process each item, knowing that each time I reached an item with dependencies, those dependent items would already have been processed.&lt;/p&gt;

&lt;p&gt;The first scenario was one with serialized &lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/db/models/&quot;&gt;Django models&lt;/a&gt;. I had a number of model instances that would be instantiated and serialized, with the aim of persisting them to the database at a later point. These models contained relationships with other models that were also being serialized. Temporary primary keys were used in order to build their relationships, allowing all of the models to then be serialized with their relationships intact.&lt;/p&gt;

&lt;p&gt;The problem then arose after deserializing these instances, and persisting them to the database. Each model with a foreign key to another couldn&amp;rsquo;t be processed, until the model it was related to had been persisted to the database first, as real primary keys are required for foreign key relationships to be defined. My first solution was a quick hack that manually sorted the objects correctly, knowing in advance which classes of models I was dealing with. This of course meant the code couldn&amp;rsquo;t be reused in a general manner, and as more classes of models were added to the mix down the track, this would need to be addressed.&lt;/p&gt;

&lt;p&gt;The second case like this came up in an entirely different situation. At Fairfax we&amp;rsquo;re building an impressive distributed system built on many &lt;a href=&quot;http://en.wikipedia.org/wiki/Representational_state_transfer&quot;&gt;RESTful APIs&lt;/a&gt;. Each of these APIs contains its own schema, and we set out to build a tool that could introspect these schemas, and generate client objects for interacting with them. These schemas also contain relationships between resources, and these resource relationships are also modelled in our client objects. Again this situation was one where I needed to process an unordered set of resources in the correct order, so that their relationships could correctly reference resources already handled.&lt;/p&gt;

&lt;p&gt;Shyly fool my bitten shame twice, or something like that. When this problem came around the second time, I didn&amp;rsquo;t have the luxury of knowing up front what the exact classes of data I&amp;rsquo;d be dealing with were, as they were due to change quite quickly as we iterated. I then set out to implement the correct solution for the problem, that could be applied to both of these situations.&lt;/p&gt;

&lt;p&gt;A quick Google search for &amp;ldquo;dependency resolution&amp;rdquo; brought me to the Wikipedia page for &lt;a href=&quot;http://en.wikipedia.org/wiki/Topological_sorting&quot;&gt;topological sorting&lt;/a&gt;, which was the solution I was looking for. In both cases, we have &lt;a href=&quot;http://en.wikipedia.org/wiki/Graph_(mathematics)&quot;&gt;graphs&lt;/a&gt;. Think of a graph as points on a map called nodes, with each node connected by lines called edges, to other nodes in the graph.&lt;/p&gt;

&lt;p&gt;&lt;em class=&quot;center&quot;&gt;&lt;img src=&quot;/static/img/topological-sorting-graph.png&quot; /&gt;&lt;br /&gt;&lt;br /&gt;A simple graph&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A &lt;a href=&quot;http://en.wikipedia.org/wiki/Directed_graph&quot;&gt;directed graph&lt;/a&gt; is one where each of the edges contain a direction, so each of the lines contain an arrow pointing one way or the other. An &lt;a href=&quot;http://en.wikipedia.org/wiki/Directed_acyclic_graph&quot;&gt;acyclic directed graph&lt;/a&gt; is where each of the edges only point in one direction, so that it&amp;rsquo;s not possible to follow the edges from one node to another, returning back to the original node. If this last condition is not satisfied, then the graph is said to contain &lt;a href=&quot;http://en.wikipedia.org/wiki/Directed_cycle&quot;&gt;directed cycles&lt;/a&gt;, that is, a path can be followed from one node to others, and back to the original node again.&lt;/p&gt;

&lt;p&gt;&lt;em class=&quot;center&quot;&gt;&lt;img src=&quot;/static/img/topological-sorting-da-graph.png&quot; /&gt;&lt;br /&gt;&lt;br /&gt;An acyclic directed graph&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In both scenarios I faced, my graph structure was different from those commonly found in examples of topological sorting. I had the outgoing edges defined, rather than incoming edges. The gist of the topological sort I needed, is to repeatedly go through all of the nodes in the graph, moving each of the nodes that has all of its edges resolved, onto a sequence that forms our sorted graph. A node has all of its edges resolved and can be moved, once all the nodes its edges point to, have been moved from the unsorted graph onto the sorted one.&lt;/p&gt;

&lt;p&gt;Consider the graph above from left to right, as pairs of nodes and their outgoing edges:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;graph_unsorted&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;mi&quot;&gt;2&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;span class=&quot;mi&quot;&gt;5&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;mi&quot;&gt;11&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;mi&quot;&gt;11&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;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&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;mi&quot;&gt;7&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;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&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;mi&quot;&gt;9&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;span class=&quot;mi&quot;&gt;10&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;span class=&quot;mi&quot;&gt;8&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;mi&quot;&gt;9&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;mi&quot;&gt;3&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;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Our expected output from a topological sort function would be as follows, with no nodes containing edges pointing to nodes before themselves:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;pprint&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pprint&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;pprint&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;topolgical_sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_unsorted&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;mi&quot;&gt;2&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;span class=&quot;mi&quot;&gt;9&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;span class=&quot;mi&quot;&gt;10&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;span class=&quot;mi&quot;&gt;11&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;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;9&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&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;mi&quot;&gt;5&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;mi&quot;&gt;11&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;mi&quot;&gt;8&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;mi&quot;&gt;9&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;mi&quot;&gt;3&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;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&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;mi&quot;&gt;7&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;mi&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])]&lt;/span&gt;
 
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Note that the ordering need not be precisely the same each time. In the result above, node 9 could come before node 2, as neither of these contain edges, so they equally belong first in the sorted graph.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s an implementation of my topological sort in Python:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;topolgical_sort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_unsorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    Repeatedly go through all of the nodes in the graph, moving each of&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    the nodes that has all its edges resolved, onto a sequence that&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    forms our sorted graph. A node has all of its edges resolved and&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    can be moved once all the nodes its edges point to, have been moved&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    from the unsorted graph onto the sorted one.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# This is the list we&amp;#39;ll return, that stores each node/edges pair&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# in topological order.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;graph_sorted&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;c&quot;&gt;# Convert the unsorted graph into a hash table. This gives us&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# constant-time lookup for checking if edges are unresolved, and&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# for removing nodes from the unsorted graph.&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;graph_unsorted&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;graph_unsorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

    &lt;span class=&quot;c&quot;&gt;# Run until the unsorted graph is empty.&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;graph_unsorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;

        &lt;span class=&quot;c&quot;&gt;# Go through each of the node/edges pairs in the unsorted&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# graph. If a set of edges doesn&amp;#39;t contain any nodes that&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# haven&amp;#39;t been resolved, that is, that are still in the&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# unsorted graph, remove the pair from the unsorted graph,&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# and append it to the sorted graph. Note here that by using&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# using the items() method for iterating, a copy of the&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# unsorted graph is used, allowing us to modify the unsorted&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# graph as we move through it. We also keep a flag for&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# checking that that graph is acyclic, which is true if any&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# nodes are resolved during each pass through the graph. If&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# not, we need to bail out as the graph therefore can&amp;#39;t be&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# sorted.&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;acyclic&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;graph_unsorted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edge&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&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;edge&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;graph_unsorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                    &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;acyclic&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;k&quot;&gt;del&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;graph_unsorted&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;graph_sorted&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;edges&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;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acyclic&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;# Uh oh, we&amp;#39;ve passed through all the unsorted nodes and&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;# weren&amp;#39;t able to resolve any of them, which means there&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;# are nodes with cyclic edges that will never be resolved,&lt;/span&gt;
            &lt;span class=&quot;c&quot;&gt;# so we bail out with an error.&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;raise&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;RuntimeError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;A cyclic dependency occurred&amp;quot;&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;n&quot;&gt;graph_sorted&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;I imagine some other uses for topological sorting would be task queues, where certain tasks are dependent on other tasks being completed first. It could also be used by package managers that install software libraries, to ensure each library has its dependencies met before it&amp;rsquo;s installed.&lt;/p&gt;

</content>
   
   <category term="data structures"></category>
   
   <category term="algorithms"></category>
   
   <category term="python"></category>
   
 </entry>
 
 <entry>
   <title>Mezzanine 1.0: The Aftermath</title>
   <link href="http://blog.jupo.org/2012/03/05/mezzanine-1.0-the-aftermath/"/>
   <updated>2012-03-05T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2012/03/05/mezzanine-1.0-the-aftermath</id>
   <content type="html">&lt;p&gt;This weekend I finally launched &lt;a href=&quot;http://mezzanine.jupo.org&quot;&gt;Mezzanine 1.0&lt;/a&gt; after two years in development.&lt;/p&gt;

&lt;p&gt;Rather than talk about Mezzanine itself and the lead up to 1.0, I thought it would be fun to look at the online reach of the release announcement, as well as some of the growth that has occurred over the last 18 months since &lt;a href=&quot;/2010/06/11/mezzanine-just-another-django-cms/&quot;&gt;I first made Mezzanine available&lt;/a&gt;. For more detail on Mezzanine and the release itself, check out the &lt;a href=&quot;http://groups.google.com/group/django-users/browse_thread/thread/c798413197b6f29b&quot;&gt;1.0 announcement to the wider Django community&lt;/a&gt;, as well as the &lt;a href=&quot;http://groups.google.com/group/mezzanine-users/browse_thread/thread/a3376410b4afdde5#&quot;&gt;discussion leading up to the 1.0 release&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;active-growth&quot;&gt;Active Growth&lt;/h4&gt;

&lt;p&gt;At the end of 2010 I wrote my annual &lt;a href=&quot;/2011/01/01/2010-my-year-in-review/&quot;&gt;year in review post&lt;/a&gt;, where I talked about Mezzanine and the activity that had occurred in its first 6 months. With the new 1.0 release, I thought it would be interesting to take a fresh look at some of the stats I talked about back then. We can see below that growth has continued all the way through at a steady pace.&lt;/p&gt;

&lt;table class=&quot;zebra-striped&quot;&gt;
&lt;tr&gt;
    &lt;th&gt;&amp;nbsp;&lt;/th&gt;
    &lt;th class=&quot;r&quot;&gt;Version 0.9.1&lt;em&gt;6 months&lt;/em&gt;&lt;/th&gt;
    &lt;th class=&quot;r&quot;&gt;Version 1.0.0&lt;em&gt;2 years&lt;/em&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Project followers&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;120&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;430&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Project forks&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;30&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;130&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Project contributors&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;10&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;40&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Mailing list subscribers&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;60&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;190&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Mailing list messages&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;300&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;1,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;PyPI downloads&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;4,000&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;38,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;Homepage visitors&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;9,000&lt;/td&gt;
    &lt;td class=&quot;r&quot;&gt;47,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;&lt;em class=&quot;center&quot;&gt;Combined follower/fork count for GitHub and Bitbucket brought to you by &lt;a href=&quot;http://otr.jupo.org&quot;&gt;One True Repo&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4 id=&quot;the-launch-party&quot;&gt;The Launch Party&lt;/h4&gt;

&lt;p&gt;The launch party we held for the release was a raging success. Just to be clear, by we I mean me, and by party I mean sitting at my computer all day with several browser tabs open, feverishly refreshing them with the hope of some good exposure and interest around the release. And there was plenty!&lt;/p&gt;

&lt;p&gt;After first announcing the release to the &lt;a href=&quot;http://groups.google.com/group/django-users&quot;&gt;django-users&lt;/a&gt; mailing list, I then posted it to a handful of popular channels in the programming community. All around the responses were positive, ranging from congratulation and praise, to all sorts of questions from curious people in the Django space who hadn&amp;rsquo;t heard of Mezzanine before.&lt;/p&gt;

&lt;h4 id=&quot;django-users&quot;&gt;django-users&lt;/h4&gt;

&lt;p&gt;As mentioned, I made the &lt;a href=&quot;http://groups.google.com/group/django-users/browse_thread/thread/c798413197b6f29b&quot;&gt;initial announcement&lt;/a&gt; to the django-users mailing list. Here I gave a general overview of Mezzanine and Cartridge, and went through their core features. The follow-up responses were positive, and included some questions about how Cartridge compares to other more popular ecommerce Django apps. I then gave a short version of my original post from &lt;a href=&quot;/2010/09/21/plugging-in-cartridge/&quot;&gt;when I first made Cartridge available&lt;/a&gt;, which covers that area in more detail.&lt;/p&gt;

&lt;h4 id=&quot;hacker-news&quot;&gt;Hacker News&lt;/h4&gt;

&lt;p&gt;I&amp;rsquo;ve posted a few articles to Hacker News before, but they&amp;rsquo;ve never been promoted to the front page, which occurs after receiving enough votes from the community. So I was delighted when 10 minutes or so after &lt;a href=&quot;http://news.ycombinator.com/item?id=3661574&quot;&gt;posting the announcement to Hacker News&lt;/a&gt;, it had reached enough votes to hit the front page. After that the votes came pouring through, with the announcement eventually making its way to 4th place on the front page, where it remained for most of the day.&lt;/p&gt;

&lt;p&gt;&lt;em class=&quot;center&quot;&gt;&lt;img class=&quot;full&quot; src=&quot;/static/img/mezzanine-1.0-release-hn-home.png&quot; /&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;When I woke up the next day, the article had reached over 90 votes, and a good range of questions had been posted, from looking for help getting started, to comparisons against other CMS projects. By then the news of the latest &lt;a href=&quot;https://github.com/rails/rails/commit/b83965785db1eec019edf1fc272b1aa393e6dc57&quot;&gt;Rails exploit&lt;/a&gt; had flooded the front page, and the Mezzanine release had been pushed down into oblivion.&lt;/p&gt;

&lt;p&gt;&lt;em class=&quot;center&quot;&gt;&lt;img class=&quot;full&quot; src=&quot;/static/img/mezzanine-1.0-release-hn-final.png&quot; /&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h4 id=&quot;reddit&quot;&gt;Reddit&lt;/h4&gt;

&lt;p&gt;At the same time, &lt;a href=&quot;http://www.reddit.com/r/programming/comments/qgk7h/mezzanine_a_django_powered_cms_two_years_in/&quot;&gt;I posted the announcement to Reddit&lt;/a&gt;. It also reached the front page of &lt;a href=&quot;http://www.reddit.com/r/programming/&quot;&gt;proggit&lt;/a&gt;, the programming sub-reddit I posted it to. Again there were a good number of comments with praise and questions. What was interesting about Reddit is that the number of votes both for and against the article are visibly displayed.&lt;/p&gt;

&lt;p&gt;&lt;em class=&quot;center&quot;&gt;&lt;img src=&quot;/static/img/mezzanine-1.0-release-reddit.png&quot; /&gt;&lt;br /&gt;&lt;a href=&quot;http://www.reddit.com/r/circlejerk/comments/nu3nd/edit_downvotes_really/&quot;&gt;Downvotes, really!!?&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It was amusing to think about what would cause people to vote it down. Perhaps they were fanatical supporters of another language, framework or CMS. I&amp;rsquo;ll never know! I think it&amp;rsquo;s a safe bet though that it had nothing to do with Mezzanine itself, based on the actual comments posted there and elsewhere.&lt;/p&gt;

&lt;h4 id=&quot;twitter&quot;&gt;Twitter&lt;/h4&gt;

&lt;p&gt;Of course I &lt;a href=&quot;https://twitter.com/stephen_mcd/status/176093637040549888&quot;&gt;tweeted the announcement&lt;/a&gt; once it was made. After a handful of direct and indirect retweets, the tweets started coming through with a lot of lovely praise, particularly for the responsive layout of &lt;a href=&quot;http://mezzanine.jupo.org&quot;&gt;Mezzanine&amp;rsquo;s new project homepage&lt;/a&gt;, something that all credit goes to the &lt;a href=&quot;http://twitter.github.com/bootstrap/&quot;&gt;Bootstrap&lt;/a&gt; team for. The most &lt;a href=&quot;http://www.urbandictionary.com/define.php?term=Humble%20Brag&quot;&gt;humbling&lt;/a&gt; moment though was when &lt;a href=&quot;https://twitter.com/#!/antrod/status/176129760836063232&quot;&gt;a tweet&lt;/a&gt; came through from &lt;a href=&quot;http://an.ton.io/&quot;&gt;Antonio Rodriguez&lt;/a&gt;, the former CTO of &lt;a href=&quot;http://hp.com&quot;&gt;HP&lt;/a&gt; of all people!&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;This looks like it may be Django&amp;rsquo;s killer app&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Very cool.&lt;/p&gt;

&lt;p&gt;After the announcement had hit the front pages of Hacker News and proggit, an army of Twitter bots that are connected to those sites then tweeted links to the announcement, resulting in hundreds of tweets and thousands of new visitors. The final assault was then triggered by &lt;a href=&quot;http://www.smashingmagazine.com/&quot;&gt;Smashing Magazine&lt;/a&gt;, who &lt;a href=&quot;https://twitter.com/smashingmag/status/177427252944969728&quot;&gt;tweeted Mezzanine&lt;/a&gt; to their &lt;em&gt;half a million&lt;/em&gt; followers.&lt;/p&gt;

&lt;h4 id=&quot;github&quot;&gt;GitHub&lt;/h4&gt;

&lt;p&gt;While GitHub wasn&amp;rsquo;t somewhere I explicitly made the announcement to, like the channels above, the reaction on there was probably the most important. Mezzanine and Cartridge received over 100 new developers following the projects.&lt;/p&gt;

&lt;p&gt;This resulted in Mezzanine being the most watched Python project on GitHub for both the day and the week! For the first time it also entered into the top 100 Python projects on GitHub of all time.&lt;/p&gt;

&lt;p&gt;&lt;em class=&quot;center&quot;&gt;&lt;img class=&quot;full&quot; src=&quot;/static/img/mezzanine-1.0-release-github-day-week.png&quot; /&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A quick burst of small contributions followed, from spelling corrections in &lt;a href=&quot;http://mezzanine.rtfd.org&quot;&gt;the documentation&lt;/a&gt;, to patches for getting things &lt;a href=&quot;https://github.com/stephenmcd/mezzanine/pull/148&quot;&gt;running smoothly on Windows&lt;/a&gt;. I then released version 1.0.1, and 1.0 became history.&lt;/p&gt;

&lt;h4 id=&quot;final-toast&quot;&gt;Final Toast&lt;/h4&gt;

&lt;p&gt;It&amp;rsquo;s been a hard slog over the last two years. With a full-time job and a family to take care of, the biggest challenge has always been finding the time to build new features and work with the community to ensure Mezzanine stays on the right track for its users. In that regard, Mezzanine wouldn&amp;rsquo;t be what it is today without the contribution of all the developers who have written features, fixed bugs, and most importantly, helped out new-comers on the mailing list. A special thanks goes out to all of you:&lt;/p&gt;

&lt;table class=&quot;zebra-striped&quot;&gt;
&lt;tr&gt;&lt;td&gt;
Lex Hider&lt;/td&gt;&lt;td&gt;
Van Lindberg&lt;/td&gt;&lt;td&gt;
Timur Bobrus&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Toby White&lt;/td&gt;&lt;td&gt;
Eric Floehr&lt;/td&gt;&lt;td&gt;
Tom von Schwerdtner&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Brad Montgomery&lt;/td&gt;&lt;td&gt;
Andrew Fisher&lt;/td&gt;&lt;td&gt;
Carlos David Marrero&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Lee Matos&lt;/td&gt;&lt;td&gt;
Josh de Blank&lt;/td&gt;&lt;td&gt;
Dominique Guardiola Falco&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Michał Oleniec&lt;/td&gt;&lt;td&gt;
John Campbell&lt;/td&gt;&lt;td&gt;
Andrew Grigorev&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Audrey Roy&lt;/td&gt;&lt;td&gt;
Josh Cartmell&lt;/td&gt;&lt;td&gt;
Osiloke Emoekpere&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Eduardo Gutierrez&lt;/td&gt;&lt;td&gt;
Rich Atkinson&lt;/td&gt;&lt;td&gt;
Brett Clouser&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Brent Hoover&lt;/td&gt;&lt;td&gt;
Owen Nelson&lt;/td&gt;&lt;td&gt;
Zeke Harris&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Ken Bolton&lt;/td&gt;&lt;td&gt;
Eli Spizzichino&lt;/td&gt;&lt;td&gt;
Michael Delaney&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
David Prusaczyk&lt;/td&gt;&lt;td&gt;
Alexey Makarenya&lt;/td&gt;&lt;td&gt;
Sebastián Magrí&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Kevin Levenstein&lt;/td&gt;&lt;td&gt;
Josh Batchelor&lt;/td&gt;&lt;td&gt;
John Barham&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Luke Plant&lt;/td&gt;&lt;td&gt;
Zdeněk Softič&lt;/td&gt;&lt;td&gt;
Alvin Mites&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Jason Kowaleski&lt;/td&gt;&lt;td&gt;
Nicola Larosa&lt;/td&gt;&lt;td&gt;
Anders Hofstee&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;
Chris Trengove&lt;/td&gt;&lt;td&gt;
Chris Smith&lt;/td&gt;&lt;td&gt;
Tommy Wolber&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;p&gt;Here&amp;rsquo;s looking forward to the next two years, and keeping Mezzanine and Cartridge a lean, mean, site building machine.&lt;/p&gt;

</content>
   
   <category term="mezzanine"></category>
   
   <category term="django"></category>
   
 </entry>
 
 <entry>
   <title>My Baby Sinatra Apps</title>
   <link href="http://blog.jupo.org/2012/02/27/my-baby-sinatra-apps/"/>
   <updated>2012-02-27T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2012/02/27/my-baby-sinatra-apps</id>
   <content type="html">&lt;p&gt;It was almost a year ago that I took up a new role using &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt;. I&amp;rsquo;ve previously talked about my &lt;a href=&quot;/2011/07/30/rails-quick-start-for-djangonauts/&quot;&gt;thoughts on Rails&lt;/a&gt;, and given my experience with &lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; I probably wouldn&amp;rsquo;t consider using Rails for my own projects. What I did explore in my time with &lt;a href=&quot;http://www.ruby-lang.org/&quot;&gt;Ruby&lt;/a&gt; was another framework called &lt;a href=&quot;http://www.sinatrarb.com/&quot;&gt;Sinatra&lt;/a&gt; which I used to build several apps. Firstly I&amp;rsquo;ll go over Sinatra and some of the related pieces in the stack, and then I&amp;rsquo;ll cover the apps I built.&lt;/p&gt;

&lt;h4 id=&quot;sinatra&quot;&gt;Sinatra&lt;/h4&gt;

&lt;p&gt;Sinatra is a micro-framework, which differs from mega-frameworks like Rails and Django, in that Sinatra is bare-bones. It mostly deals with mapping URLs to request handlers, and not much more beyond that. No templating, no &lt;a href=&quot;http://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORM&lt;/a&gt;, no middleware. All of these features can be slotted in using third party libraries where required. This makes for a very pleasant development experience with smaller sized apps - instead of having to do everything the &lt;em&gt;Django/Rails way&lt;/em&gt;, you&amp;rsquo;re free to pick and choose the parts you need, and weave them together in the best way you see fit. You&amp;rsquo;re working at a relatively lower level, with much less scaffolding, and a lot more flexibility and control.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://python.org/&quot;&gt;Python&lt;/a&gt; has its own counterparts in this space as well, such as &lt;a href=&quot;http://bottlepy.org/&quot;&gt;Bottle&lt;/a&gt; and &lt;a href=&quot;http://flask.pocoo.org/&quot;&gt;Flask&lt;/a&gt;. However at the time I was looking to dive further into Ruby, and Sinatra seemed like a great way to lean into it.&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;re new to web development, or an experienced developer coming to Ruby or Python from &lt;a href=&quot;/2010/09/28/on-modern-web-development/&quot;&gt;older stacks like ASP.NET or PHP&lt;/a&gt;, I&amp;rsquo;d highly recommend starting out with a micro framework like Sinatra or Flask, before moving onto their bigger siblings Rails and Django. You&amp;rsquo;ll get a great feel for their respective languages, without getting bogged down in the frameworks themselves.&lt;/p&gt;

&lt;h4 id=&quot;datamapper&quot;&gt;DataMapper&lt;/h4&gt;

&lt;p&gt;You can&amp;rsquo;t go very far these days developing a web application, without needing some form of persistent storage such as a database, and a library to work with it that goes beyond hand-written SQL. &lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/db/models/&quot;&gt;Django has its own ORM&lt;/a&gt; which is very powerful, but suffers from lacking a blessed, seamless migration tool. Rails has &lt;a href=&quot;http://ar.rubyonrails.org/&quot;&gt;Active Record&lt;/a&gt;, which has grown into the defacto ORM in the Ruby eco-system, and has its own set of problems. The main issue I had with Active Record was that there was no clear definition of what fields a particular model implemented, aside from diving directly into the database itself. It coincides clearly with the notion that &lt;a href=&quot;http://bens.me.uk/2009/going-off-the-rails&quot;&gt;Rails contains too much magic&lt;/a&gt;. Compare this to Django&amp;rsquo;s declarative ORM, where each model&amp;rsquo;s class contains an explicit blueprint of which fields and methods the model implements. The value of this in quickly picking up a new code base is highly under-stated, if the popularity of Active Record is anything to go by.&lt;/p&gt;

&lt;p&gt;I began looking for an ORM solution for Sinatra and quickly came across a project called &lt;a href=&quot;http://datamapper.org/&quot;&gt;DataMapper&lt;/a&gt;, and was incredibly pleased. Not only does it provide the same declarative style that Django&amp;rsquo;s ORM does, it goes above and beyond that with features that blow both Django&amp;rsquo;s ORM and Active Record out of the water.&lt;/p&gt;

&lt;p&gt;Firstly it provides the ability to automatically migrate models. Arguments against magic aside, this is an amazing feature. Simply change the model, and the changes are migrated to the database when next instantiated.&lt;/p&gt;

&lt;p&gt;Secondly, DataMapper completely eliminates the &lt;a href=&quot;http://www.pbell.com/index.cfm/2006/9/17/Understanding-the-n1-query-problem&quot;&gt;N+1 query problem&lt;/a&gt;. When iterating through data and accessing other models via related fields, DataMapper will query the database when the first relationship is accessed, retrieving all required data pertaining to the outer loop, building up all of the instance relationships on the fly prior to accessing them. Yes, it sees into the future and protects you from obliterating your database, a mistake all too common in web development. It&amp;rsquo;s worth noting that Django introduced this feature in the yet to be released 1.4 with the &lt;a href=&quot;https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related&quot;&gt;prefetch_related&lt;/a&gt; method. This is another great example of the explicitness of Python compared to the implicitness of Ruby.&lt;/p&gt;

&lt;p&gt;Whether you&amp;rsquo;re building a web application or not, if you&amp;rsquo;re accessing a database from Ruby, consider using DataMapper. It&amp;rsquo;s a great piece of software.&lt;/p&gt;

&lt;h4 id=&quot;heroku&quot;&gt;Heroku&lt;/h4&gt;

&lt;p&gt;The explosion in Ruby and Python development frameworks over the last half decade has been a boon to web development. Security, modularity, shelf life, and time to market have all dramatically improved thanks to dynamic languages, the frameworks that have developed around them, and the open source communities that make them possible. It&amp;rsquo;s not all fun and games however. Deployment of these applications has grown considerably more complex. Gone are the days of using FTP to upload some PHP scripts to a server, and hitting refresh on the web page to test your changes. We now have to deal with a wide variety of deployment tasks, from reloading application processes, database migrations, dependency management and much more.&lt;/p&gt;

&lt;p&gt;Naturally the community has risen to solve these problems, a movement sometimes referred to as &lt;a href=&quot;http://en.wikipedia.org/wiki/DevOps&quot;&gt;DevOps&lt;/a&gt;, with tools in place such as &lt;a href=&quot;https://github.com/capistrano/capistrano&quot;&gt;Ruby&amp;rsquo;s Capistrano&lt;/a&gt;, &lt;a href=&quot;http://docs.fabfile.org/&quot;&gt;Python&amp;rsquo;s Fabric&lt;/a&gt;, and configuration management tools to map complex deployments such as &lt;a href=&quot;http://www.opscode.com/chef/&quot;&gt;Chef&lt;/a&gt; and &lt;a href=&quot;http://puppetlabs.com/&quot;&gt;Puppet&lt;/a&gt;. While the learning curve is steep, with enough time invested up front, deployments can become as simple as pushing a button, and are more robust and integrated with quality assurance than ever before.&lt;/p&gt;

&lt;p&gt;Modern deployments such as these require experts. This is where &lt;a href=&quot;http://en.wikipedia.org/wiki/Platform_as_a_service&quot;&gt;Platform as a Service (PaaS)&lt;/a&gt; offerings come in. PaaS providers are modern hosting companies that take care of all the dirty work in configuring servers and automating deployments for you. Typically they&amp;rsquo;ll expose a distributed version control server using &lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; or &lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt; that you can push code commits to. The process of pushing commits then triggers deployment, automatically performing all of the required tasks. PaaS providers will also provide all of the related services required, such as databases, message queues, caching servers and so forth.&lt;/p&gt;

&lt;p&gt;Are PaaS providers a magic bullet? Absolutely not. Every application will have a tipping point where it grows beyond the &amp;ldquo;one size fits all&amp;rdquo; approach provided by PaaS offerings. You&amp;rsquo;re also at the mercy of the provider when it comes to uptime, so mission critical applications with strict service level agreements may require much more fine grained control in their hosting environments. Never forget that &lt;a href=&quot;http://www.whoownsmyavailability.com/&quot;&gt;you are the only one responsible for your service&amp;rsquo;s availability&lt;/a&gt;. However for smaller, stock, non-critical applications, PaaS providers are a dream come true, that remove most of the complexities around service provisioning, configuration and application deployment.&lt;/p&gt;

&lt;p&gt;The most well known of these providers is &lt;a href=&quot;http://www.heroku.com/&quot;&gt;Heroku&lt;/a&gt;, who were the first to popularise the PaaS architecture. Not only does Heroku offer a &lt;a href=&quot;https://addons.heroku.com/&quot;&gt;rich variety of add-on services&lt;/a&gt; that I was looking for, like &lt;a href=&quot;https://www.varnish-cache.org/&quot;&gt;Varnish&lt;/a&gt;, &lt;a href=&quot;http://memcached.org/&quot;&gt;Memcached&lt;/a&gt;, and &lt;a href=&quot;http://www.postgresql.org/&quot;&gt;PostgreSQL&lt;/a&gt;, they also provide free hosting for low capacity sites - ideal for the types of applications I ended up building with Sinatra.&lt;/p&gt;

&lt;p&gt;So with Sinatra, DataMapper and Heroku combined, I developed several small applications that scratched particular itches for me, in order to build up a good working knowledge of Ruby.&lt;/p&gt;

&lt;h4 id=&quot;linked-out&quot;&gt;Linked Out&lt;/h4&gt;

&lt;p&gt;I don&amp;rsquo;t keep an up to date CV anymore. If I pick up a new skill, or start a new role, I&amp;rsquo;ll update &lt;a href=&quot;http://www.linkedin.com/in/stephenmcd&quot;&gt;my LinkedIn profile&lt;/a&gt;. It&amp;rsquo;s the quickest and easiest way to keep my professional information up to date. For better or worse though, over the last few years LinkedIn has turned into a mass hunting ground for recruiters. I used take the time to enter into a dialogue with each and every recruiter that contacted me, after all anything less would be rude, but over time I realised the futility in this, as the practice by recruiters  to blast out boilerplate introductions to anyone who matched a keyword search, became more and more common. But I digress. The state of recruitment aside, these conversations would inevitably lead to recruiters asking for a CV they could present to their clients. LinkedIn profiles contain a &amp;ldquo;download as PDF&amp;rdquo; feature, which I would always refer recruiters to, but LinkedIn embeds their logo within the PDF, and over time as they&amp;rsquo;ve added new profile features, the PDF download hasn&amp;rsquo;t picked these up. What I&amp;rsquo;d always wanted was an easy way to export my profile as a clean PDF, containing only the information relevant to a CV. I also wanted to be able to share the tools with anyone else who wanted to use it, so a baby Sinatra app seemed like the perfect fit.&lt;/p&gt;

&lt;p&gt;There&amp;rsquo;s a great &lt;a href=&quot;https://github.com/pengwynn/linkedin&quot;&gt;Ruby library for interacting with LinkedIn&amp;rsquo;s API&lt;/a&gt;, and &lt;a href=&quot;https://github.com/jdpace/PDFKit&quot;&gt;PDFKit&lt;/a&gt; for converting HTML to PDF, which meant that I could format the CV using HTML and CSS, and all I then needed to do was convert that directly to PDF.&lt;/p&gt;

&lt;p&gt;The app I ended up building is called &lt;a href=&quot;http://linkedout.jupo.org/&quot;&gt;Linked Out&lt;/a&gt;. It has a very simple flow to it. You first authenticate via LinkedIn&amp;rsquo;s &lt;a href=&quot;oauth&quot;&gt;oAuth&lt;/a&gt; service, and then you&amp;rsquo;re redirected back to Linked Out, where you can create a PDF version of your profile. As an added bonus I hooked into LinkedIn&amp;rsquo;s connections API, so you also get the option of creating a CV for any of your LinkedIn connections - no need for recruiters to bother people with CV requests, they can create the CVs themselves.&lt;/p&gt;

&lt;p&gt;LinkedIn is somewhat lacking when it comes to formatting large blobs of text in profiles. People tend to create all sorts of formatting themselves, typically to create bulleted lists. So Linked Out contains some smarts to look for these different types of free-text formats, converting them into proper lists and headings where appropriate.&lt;/p&gt;

&lt;p&gt;Need to fend off pesky recruiters with a nice looking CV? Go and update your LinkedIn profile, and give &lt;a href=&quot;http://linkedout.jupo.org/&quot;&gt;Linked Out&lt;/a&gt; a try.&lt;/p&gt;

&lt;h4 id=&quot;klout-feed&quot;&gt;Klout Feed&lt;/h4&gt;

&lt;p&gt;If you&amp;rsquo;re unfamiliar with &lt;a href=&quot;http://klout.com/&quot;&gt;Klout&lt;/a&gt;, it&amp;rsquo;s a reputation measurement system that gives you a daily score based on your online interactions. It looks at your Twitter account as well as other social media services, and applies an algorithm based on the number of mentions you receive, retweets, and favourites, also taking into account the Klout score for each of the people who trigger these. It then assigns you a score out of 100 which you can measure on a daily basis to gauge how effectively you&amp;rsquo;re using Twitter. According to Klout at least.&lt;/p&gt;

&lt;p&gt;Klout has been described as many things, from a revolutionary game-changer in social media, to a vapid and narcissistic waste of time. Personally I found it to be an entertaining distraction. It did keep me coming back each day to check my score.&lt;/p&gt;

&lt;p&gt;Now I&amp;rsquo;m one of a &lt;a href=&quot;http://camendesign.com/blog/rss_is_dying&quot;&gt;dying breed&lt;/a&gt; that still uses RSS exclusively to keep track of everything going on online. Updates from my LinkedIn connections, contributions and issues for &lt;a href=&quot;https://github.com/stephenmcd&quot;&gt;my projects&lt;/a&gt; on GitHub, &lt;a href=&quot;http://groups.google.com/group/mezzanine-users&quot;&gt;Google Groups&lt;/a&gt; mailing lists, and of course various news sites and blogs. They all contain RSS feeds which I can catch up on, in one single interface. But not Klout. You need to log into their site each time you want to check you score. There&amp;rsquo;s an interesting point here. If you want to keep users coming back to your site and piss them off at the same time, create a great service but make sure you don&amp;rsquo;t include an RSS feed for the data your users are interested in.&lt;/p&gt;

&lt;p&gt;To solve this I put together an app called &lt;a href=&quot;http://klout-feed.jupo.org/&quot;&gt;Klout Feed&lt;/a&gt;. It uses Klout&amp;rsquo;s API to provide an RSS feed for each user, publishing their score and its change each day. The flow isn&amp;rsquo;t as seamless as Linked Out&amp;rsquo;s. Klout doesn&amp;rsquo;t provide any form of application integration the same way LinkedIn, Twitter and Facebook do. Just a per-user API key with daily limits assigned to it. So with Klout Feed you first need to head on over to Klout and grab an API key, then bring that back to Klout Feed to get the URL for your score&amp;rsquo;s RSS feed.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://iag.me/&quot;&gt;Ian Anderson&lt;/a&gt; has since gone ahead and written &lt;a href=&quot;http://iag.me/socialmedia/guides/how-to-get-your-klout-score-emailed-or-tweeted-to-you-every-day/&quot;&gt;a how-to article&lt;/a&gt; on combining Klout Feed with the &lt;a href=&quot;http://ifttt.com/&quot;&gt;If This Then That&lt;/a&gt; service. The result is that you can get email or SMS notifications each time your Klout score changes.&lt;/p&gt;

&lt;h4 id=&quot;one-true-repo&quot;&gt;One True Repo&lt;/h4&gt;

&lt;p&gt;Most open source projects use &lt;a href=&quot;http://git-scm.com/&quot;&gt;Git&lt;/a&gt; or &lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt; for version control, and are hosted on either &lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt; or &lt;a href=&quot;https://bitbucket.org/&quot;&gt;Bitbucket&lt;/a&gt; respectively. Some people like myself host their projects on both sites. I&amp;rsquo;ve talked about my setup for &lt;a href=&quot;/2011/12/31/announcing-hg-github/&quot;&gt;hosting on GitHub and Bitbucket&lt;/a&gt; before. Both sites provide totals for the number of interested developers following the project, and the number who have forked the project. A fork is when someone creates a copy of a project, usually with the intent of adding some news features or fixes, and pushing them back to the original source.&lt;/p&gt;

&lt;p&gt;What I&amp;rsquo;ve always wanted is a combined API for totaling followers and forks across both services for a single project hosted on both sites, and also for all projects for a given user on both sites. This is what I tackled for my next Ruby project, which I called &lt;a href=&quot;http://otr.jupo.org&quot;&gt;One True Repo (OTR)&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;OTR&amp;rsquo;s original form was as a library that other developers could embed in their project, so I built it as a Ruby gem that you can both include in your own project, or simply run from the command line and pipe the data it returns into other programs. The next step was to build a baby Sinatra app that provided a hosted version of the API that people could query. The project itself contains everything for all three of these forms - the library, the command-line tool, and the Sinatra web app.&lt;/p&gt;

&lt;p&gt;Querying the &lt;a href=&quot;http://developer.github.com/&quot;&gt;GitHub API&lt;/a&gt; was trivial and all the information I wanted was provided by it very easily. The &lt;a href=&quot;https://api.bitbucket.org/&quot;&gt;Bitbucket API&lt;/a&gt; wasn&amp;rsquo;t quite up to scratch for this however. Remarkably it doesn&amp;rsquo;t expose follower and fork count on each project. Some screen-scraping was therefore required to get these totals for each of a user&amp;rsquo;s projects.&lt;/p&gt;

&lt;p&gt;Do you mirror your open source projects across both GitHub and Bitbucket? Ever wonder how many people are following all your projects on both services? Give &lt;a href=&quot;http://otr.jupo.org&quot;&gt;One True Repo&lt;/a&gt; a try.&lt;/p&gt;

&lt;h4 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;As you can see from some of these apps, the Sinatra on Heroku combination is the perfect fit for small mashups that act as glue between other popular APIs. Free of charge, rapid development, and a great pool of libraries to choose from in the Ruby eco-system.&lt;/p&gt;

</content>
   
   <category term="ruby"></category>
   
   <category term="sinatra"></category>
   
   <category term="django"></category>
   
   <category term="ruby on rails"></category>
   
   <category term="orm"></category>
   
   <category term="n+1 queries"></category>
   
   <category term="heroku"></category>
   
   <category term="paas"></category>
   
   <category term="devops"></category>
   
   <category term="git"></category>
   
   <category term="github"></category>
   
   <category term="mercurial"></category>
   
   <category term="bitbucket"></category>
   
   <category term="open source"></category>
   
   <category term="linkedin"></category>
   
   <category term="klout"></category>
   
 </entry>
 
 <entry>
   <title>2011: My Year in Review</title>
   <link href="http://blog.jupo.org/2012/01/10/2011-my-year-in-review/"/>
   <updated>2012-01-10T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2012/01/10/2011-my-year-in-review</id>
   <content type="html">&lt;p&gt;Another year has gone by and another obligatory &lt;em&gt;year in review&lt;/em&gt; post is due. 2011 was a
year full of change for me. I changed jobs twice, spent half the year coding in a new language, moved interstate, and switched my primary operating system.&lt;/p&gt;

&lt;h4 id=&quot;ruby&quot;&gt;Ruby&lt;/h4&gt;

&lt;p&gt;After working at &lt;a href=&quot;http://citrus.com.au&quot;&gt;Citrus&lt;/a&gt; for almost nine years, I was well overdue for a change of scenery, so when I was approached by &lt;a href=&quot;http://impactdata.com.au&quot;&gt;Impact Data&lt;/a&gt; to come and work for them using &lt;a href=&quot;http://rubyonrails.org&quot;&gt;Ruby on Rails&lt;/a&gt;, I welcomed the opportunity. My time there was short and sweet however, as after six months I decided to move back home to Sydney, but it was a really rewarding experience working with an incredibly smart team on a technology stack that was new to me. I had a great time learning Ruby, forming a strong appreciation for its elegance, and it was very interesting along the way making plenty of &lt;a href=&quot;http://blog.jupo.org/2011/07/30/rails-quick-start-for-djangonauts/&quot;&gt;comparisons between Rails and Django&lt;/a&gt;. Naturally I started using Ruby in my own projects, getting to know &lt;a href=&quot;http://www.sinatrarb.com&quot;&gt;Sinatra&lt;/a&gt; for a handful of apps that I built. I&amp;rsquo;ll be writing a more detailed post about those soon, so stay tuned!&lt;/p&gt;

&lt;h4 id=&quot;django-dash&quot;&gt;Django Dash&lt;/h4&gt;

&lt;p&gt;For the second year in a row I entered the &lt;a href=&quot;http://djangodash.com&quot;&gt;Django Dash&lt;/a&gt; hackathon. This time around I wanted to do something that really pushed Django outside of its typical usage patterns. I&amp;rsquo;d recently read Cody Soyland&amp;rsquo;s introductory blog post on &lt;a href=&quot;http://codysoyland.com/2011/feb/6/evented-django-part-one-socketio-and-gevent/&quot;&gt;using WebSockets with Django&lt;/a&gt;, and so I came up with an idea I called &lt;a href=&quot;http://drawnby.jupo.org&quot;&gt;Drawn By&lt;/a&gt;, a collaborative drawing app where people could create sketches together in real-time, save them to an image they can download, and rate others&amp;rsquo; sketches in the gallery.&lt;/p&gt;

&lt;p&gt;I got to use a variety of technology I hadn&amp;rsquo;t used before which was really fun. I used &lt;a href=&quot;http://www.gevent.org&quot;&gt;gevent&lt;/a&gt; as the evented web server for maintaining open socket connections with the browser, the NoSQL database &lt;a href=&quot;http://redis.io&quot;&gt;Redis&lt;/a&gt; for queuing events and storing temporary pixel data, and the browser&amp;rsquo;s &lt;a href=&quot;http://en.wikipedia.org/wiki/Canvas_element&quot;&gt;Canvas API&lt;/a&gt; for front-end drawing interaction and rendering. I set a relatively high goal for ourselves this year with what we wanted to achieve, but we pulled it off nicely in the end.&lt;/p&gt;

&lt;p&gt;This year we came 3rd place out of around 50 entries, which was a great improvement on the previous year&amp;rsquo;s result of 8th. The most important result however was the creation of &lt;a href=&quot;https://github.com/stephenmcd/django-socketio&quot;&gt;django-socketio&lt;/a&gt;, which was extracted from Drawn By and released as open source. It brings together all of the scaffolding for using WebSockets with Django, and implements an events and channels system for building your own applications around it. &lt;a href=&quot;http://blog.jupo.org/2011/08/13/real-time-web-apps-with-django-and-websockets/&quot;&gt;I previously wrote about django-socketio&lt;/a&gt; right after releasing it, and since then it has gained quite a lot of traction, with a handful of developers contributing back fixes and new features.&lt;/p&gt;

&lt;h4 id=&quot;open-source&quot;&gt;Open Source&lt;/h4&gt;

&lt;p&gt;I actually spent less time this year contributing to open source than I did the previous year, but I steamed ahead nonetheless with a lot of new projects, as well as continued development and support for my major works, &lt;a href=&quot;http://mezzanine.jupo.org&quot;&gt;Mezzanine&lt;/a&gt; and &lt;a href=&quot;http://cartridge.jupo.org&quot;&gt;Cartridge&lt;/a&gt;. Both these projects have reached a very mature level over the course of 2011, thanks to tons of contributions from the &lt;a href=&quot;http://groups.google.com/group/mezzanine-users/topics&quot;&gt;Mezzanine and Cartridge community&lt;/a&gt;, which continues to grow steadily. Here&amp;rsquo;s a list of the projects I released as open source over the year:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;http://drawnby.jupo.org&quot;&gt;Drawn By&lt;/a&gt;: Collaborative real-time sketching. (Django / Python)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/stephenmcd/django-socketio&quot;&gt;django-socketio&lt;/a&gt;: WebSockets for Django. (Django / Python)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/stephenmcd/virtualboxing&quot;&gt;Virtualboxing&lt;/a&gt;: Comparison utilities for &lt;a href=&quot;http://basho.com/products/riak-overview/&quot;&gt;Riak&lt;/a&gt; and &lt;a href=&quot;http://www.mongodb.org&quot;&gt;MongoDB&lt;/a&gt;. (Ruby)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://chat.jupo.org&quot;&gt;Grillode&lt;/a&gt;: Multi-purpose chat server. (&lt;a href=&quot;http://nodejs.org&quot;&gt;Node&lt;/a&gt; / &lt;a href=&quot;http://coffeescript.org&quot;&gt;CoffeeScript&lt;/a&gt;)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://linkedout.jupo.org&quot;&gt;Linked Out&lt;/a&gt;: Create PDF resumes for &lt;a href=&quot;http://linkedin.com&quot;&gt;LinkedIn&lt;/a&gt;. (Sinatra / Ruby)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://klout-feed.jupo.org&quot;&gt;Klout Feed&lt;/a&gt;: Daily &lt;a href=&quot;http://klout.com&quot;&gt;Klout&lt;/a&gt; scores via RSS. (Sinatra / Ruby)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://github.com/stephenmcd/babbler&quot;&gt;Babbler&lt;/a&gt;: A &lt;a href=&quot;http://twitter.com&quot;&gt;Twitter&lt;/a&gt; bot. (Python)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://otr.jupo.org&quot;&gt;One True Repo&lt;/a&gt;: Combined &lt;a href=&quot;http://github.com&quot;&gt;GitHub&lt;/a&gt; and &lt;a href=&quot;http://bitbucket.org&quot;&gt;Bitbucket&lt;/a&gt; API. (Sinatra / Ruby)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://blog.jupo.org/2011/12/31/announcing-hg-github/&quot;&gt;hg-github&lt;/a&gt;: A &lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt; extension for GitHub. (Mercurial / Python)&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/stephenmcd/sphinx-me&quot;&gt;sphinx-me&lt;/a&gt;: A &lt;a href=&quot;http://sphinx.pocoo.org/&quot;&gt;Sphinx&lt;/a&gt; documentation generator. (Python)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;osx&quot;&gt;OSX&lt;/h4&gt;

&lt;p&gt;As I mentioned, towards the end of the year I moved back to Sydney. An opportunity came up to work with &lt;a href=&quot;http://fairfax.com.au&quot;&gt;Fairfax&lt;/a&gt;, the largest media organisation in Australia. Fairfax is building a new publishing platform using Django, so my experience with content management and Django was a natural fit. Mostly though, it was a chance for me to move back home and be closer to my family, after being away from them in Melbourne for a decade.&lt;/p&gt;

&lt;p&gt;When I started at Fairfax, I was surprised to find the entire team running OSX. I wasn&amp;rsquo;t surprised so much by the choice itself, as OSX is very popular in the Django and wider development community, but more so by my own lack of experience with it, having solely used Linux for the last half decade. I decided to give it a go and found it to be on par with Linux as a development environment.&lt;/p&gt;

&lt;p&gt;My biggest gripe was breaking down the mental muscle I&amp;rsquo;d built up around the shortcut keys for wrangling text. At first I was fumbling on OSX, but after a few days of using OSX during the day and Linux at night, I found that I wasn&amp;rsquo;t efficient with either of them - I needed consistency. I was also long overdue for a new machine. I originally had my eye on some of the MacBook Air clones like the &lt;a href=&quot;http://us.acer.com/ac/en/US/content/s-series-home&quot;&gt;Acer UltraBook&lt;/a&gt; and &lt;a href=&quot;http://zenbook.asus.com/au/design/&quot;&gt;Asus ZenBook&lt;/a&gt;, but I couldn&amp;rsquo;t find any information about running Linux on these, and I wasn&amp;rsquo;t prepared to go through the pain of working it out if anything went wrong. So I bit the bullet and picked up a 13 inch MacBook Air.&lt;/p&gt;

&lt;p&gt;All I need at the software level is visible application shortcuts, keyboard-driven application switching, a decent &lt;a href=&quot;http://www.sublimetext.com&quot;&gt;editor&lt;/a&gt;, &lt;a href=&quot;http://www.iterm2.com&quot;&gt;terminal&lt;/a&gt;, &lt;a href=&quot;http://www.google.com/chrome/&quot;&gt;web browser&lt;/a&gt; and &lt;a href=&quot;http://mxcl.github.com/homebrew/&quot;&gt;package manager&lt;/a&gt;, and I&amp;rsquo;m good to go. In that regard, OSX and Linux are equivalent for my use, each with their own minor flaws. What I am really loving is the hardware. The keyboard seems laid out in a way that lets me type faster than ever, and the solid state drive means everything is instantaneous - that paired with the best battery life I&amp;rsquo;ve ever experienced, and it&amp;rsquo;s a dream machine.&lt;/p&gt;

</content>
   
   <category term="osx"></category>
   
   <category term="ruby"></category>
   
   <category term="django"></category>
   
   <category term="django dash"></category>
   
   <category term="open source"></category>
   
 </entry>
 
 <entry>
   <title>Announcing hg-github</title>
   <link href="http://blog.jupo.org/2011/12/31/announcing-hg-github/"/>
   <updated>2011-12-31T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2011/12/31/announcing-hg-github</id>
   <content type="html">&lt;p&gt;The two front-runners in source code management these days are undoubtedly &lt;a href=&quot;http://git-scm.com&quot;&gt;Git&lt;/a&gt; and &lt;a href=&quot;http://mercurial.selenic.com/&quot;&gt;Mercurial&lt;/a&gt;. Distributed version control has clearly proven itself as the superior model over older centralised systems like &lt;a href=&quot;http://subversion.tigris.org/&quot;&gt;SVN&lt;/a&gt;, particularly in the context of open source development, where the ability to fork repositories and push and pull branches between them facilitates a much more efficient and streamlined work-flow.&lt;/p&gt;

&lt;p&gt;My personal opinion about the difference between the two is similar to &lt;a href=&quot;/2011/07/30/rails-quick-start-for-djangonauts&quot;&gt;my take on Django and Rails&lt;/a&gt;. Relative to their alternatives, the two are far more similar than different, essentially providing the same concepts and features, but with quite different underlying philosophies in their implementations. For this reason I choose the less popular Mercurial over Git to manage all of my projects. Firstly I feel it has a more natural and intuitive UI (I&amp;rsquo;m not referring to a graphical interface here, but the actual commands it implements and their arguments). Another key factor is that Mercurial is written in Python, which means hacking on it and building extensions for it is a breeze.&lt;/p&gt;

&lt;p&gt;What makes Git more popular than Mercurial? One major factor is their respective online hosting services, &lt;a href=&quot;http://github.com&quot;&gt;GitHub&lt;/a&gt; for Git and &lt;a href=&quot;http://bitbucket.org&quot;&gt;Bitbucket&lt;/a&gt; for Mercurial. If you&amp;rsquo;re unfamiliar with these sites, they&amp;rsquo;re like Facebook for programmers who share projects and collaborate together on code rather than post photos and videos. While both of these sites mostly implement the same core features, Github has always been several steps ahead of Bitbucket in terms of overall polish, and being first to market with new features. This difference has led GitHub to become far more popular than its Mercurial counterpart, and as a result, Git far more popular than Mercurial.&lt;/p&gt;

&lt;p&gt;With the majority of open source activity occurring on GitHub, what then is a Mercurial user to do? Limiting your projects to the audience of Bitbucket means missing out on a lot of potential collaboration. Fortunately some time ago, the team at GitHub developed a Mercurial extension called &lt;a href=&quot;http://hg-git.github.com/&quot;&gt;hg-git&lt;/a&gt;. It allows you to transfer code back and forth between a Mercurial repository on your machine to a Git repository on another machine, like GitHub for example, taking care of all the translation required between Git and Mercurial. After several years of using hg-git, it&amp;rsquo;s one of the only pieces of software that continues to amaze me. Think of &lt;a href=&quot;http://translate.google.com/&quot;&gt;Google&amp;rsquo;s language translator&lt;/a&gt;, which on a good day can provide some very quirky translations when converting text from one language to another. hg-git performs the same task, but has no room for error when translating a source code repository from Mercurial to Git, and back again. Admittedly hg-git has a much easier job to do than translating human languages with all their ambiguities. Still, I am constantly impressed by the task it performs.&lt;/p&gt;

&lt;p&gt;So hg-git allows me to develop my projects using Mercurial and have them shared on both Github and Bitbucket, allowing for maximum collaboration which is fantastic. It&amp;rsquo;s not entirely seamless however. I still need to perform a couple of manual steps such as adding GitHub paths to my repo configuration, and creating Mercurial tags that map to the Git branches I want to work with. I need to do this each time I set up a new repository, be it for starting a new project, or forking a project of my own or someone else&amp;rsquo;s. Recently I had to do this about half a dozen times in the space of an hour while working on a few different projects, and I thought to myself that I should be able to automate it. The result is a Mercurial extension I&amp;rsquo;ve called &lt;a href=&quot;https://github.com/stephenmcd/hg-github&quot;&gt;hg-github&lt;/a&gt; which automatically takes care of these manual steps required. It also wraps hg-git, so you don&amp;rsquo;t need to install both extensions, as hg-github pulls in and takes care of all the hard work that hg-git does.&lt;/p&gt;

&lt;h4 id=&quot;overview&quot;&gt;Overview&lt;/h4&gt;

&lt;p&gt;Once hg-github is installed, assuming the default remote location of your repository is on Bitbucket, the GitHub path is automatically added and given the name &lt;code&gt;github&lt;/code&gt;, so you can push to it with the following command:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;sh&quot;&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;hg push github
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;For other named Bitbucket locations, the name &lt;code&gt;github-NAME&lt;/code&gt; is given, where &lt;code&gt;NAME&lt;/code&gt; is the name of the path located on BitBucket. For example consider the following &lt;code&gt;.hg/hgrc&lt;/code&gt; repo config:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ini&quot;&gt;&lt;span class=&quot;k&quot;&gt;[paths]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh://hg@bitbucket.org/stephenmcd/hg-github&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;somefork&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh://hg@bitbucket.org/stephenmcd/hg-github-temp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;hg-git will add entries to the config file as follows. Note that the config file isn&amp;rsquo;t actually written to:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ini&quot;&gt;&lt;span class=&quot;k&quot;&gt;[paths]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh://hg@bitbucket.org/stephenmcd/hg-github&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;somefork&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;ssh://hg@bitbucket.org/stephenmcd/hg-github-temp&lt;/span&gt;

&lt;span class=&quot;na&quot;&gt;github&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;git+ssh://git@github.com/stephenmcd/hg-github.git&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;github-somefork&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;git+ssh://git@github.com/stephenmcd/hg-github-temp.git&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;hg-github assumes you have the same username on GitHub and Bitbucket. If you have a different GitHub username, you can specify it by adding the following section to your global &lt;code&gt;.hgrc&lt;/code&gt; file. For example my GitHub username is &lt;code&gt;stephenmcd&lt;/code&gt;:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ini&quot;&gt;&lt;span class=&quot;k&quot;&gt;[github]&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;username&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;stephenmcd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

</content>
   
   <category term="version control"></category>
   
   <category term="mercurial"></category>
   
   <category term="git"></category>
   
   <category term="github"></category>
   
   <category term="bitbucket"></category>
   
   <category term="open source"></category>
   
 </entry>
 
 <entry>
   <title>Django Model Field Injection</title>
   <link href="http://blog.jupo.org/2011/11/10/django-model-field-injection/"/>
   <updated>2011-11-10T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2011/11/10/django-model-field-injection</id>
   <content type="html">&lt;p&gt;As a creator and maintainer of several popular reusable &lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; applications, one of the most commonly requested features I&amp;rsquo;m asked for is the ability to customise the fields that a model implements. This topic comes up often on the &lt;a href=&quot;http://groups.google.com/group/mezzanine-users&quot;&gt;Mezzanine mailing list&lt;/a&gt;, and during &lt;a href=&quot;http://groups.google.com/group/mezzanine-users/browse_thread/thread/1f1669b0091a88d5&quot;&gt;this particular thread&lt;/a&gt; we researched ways that fields could be dynamically injected into models at run-time.&lt;/p&gt;

&lt;h4 id=&quot;other-approaches&quot;&gt;Other Approaches&lt;/h4&gt;

&lt;p&gt;It&amp;rsquo;s worth taking a look at other approaches to the general problem, and what their drawbacks are, in order to provide context for what the final solution needs to achieve.&lt;/p&gt;

&lt;p&gt;One approach is to implement as many of the model classes as possible as &lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes&quot;&gt;abstract base classes&lt;/a&gt;, so that users can subclass these with their own models. This approach makes sense for certain types of customisation, and it&amp;rsquo;s what I&amp;rsquo;ve done with &lt;a href=&quot;https://github.com/stephenmcd/django-forms-builder/blob/master/forms_builder/forms/models.py&quot;&gt;django-forms-builder&lt;/a&gt; for example. Some caveats exist with this approach however. Firstly, &lt;a href=&quot;https://docs.djangoproject.com/en/dev/ref/models/fields/#module-django.db.models.fields.related&quot;&gt;relationship fields&lt;/a&gt; can&amp;rsquo;t be defined on the abstract models, so these need to be implemented in concrete models either within the same app, or by the user implementing their own subclasses. Secondly, any functionality that references your models, such as &lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/http/views/&quot;&gt;views&lt;/a&gt; or &lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/http/middleware/&quot;&gt;middleware&lt;/a&gt;, needs to either have configurable settings for choosing which models to use, or be reimplemented entirely by the user to make use of their custom fields.&lt;/p&gt;

&lt;p&gt;Another approach is to simply recommend that users subclass the models that the app provides using &lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/db/models/#multi-table-inheritance&quot;&gt;multi-table inheritance&lt;/a&gt;. Unfortunately this will introduce unnecessary overhead with the extra database queries required when accessing the instances of the subclasses. Best case is that this amounts to an extra query or two in a view dealing with a single instance. Worst case is that when this approach is used with a queryset in a template, an extra query is performed for each instance returned - the classic &lt;a href=&quot;http://stackoverflow.com/questions/97197/what-is-the-n1-selects-problem&quot;&gt;N+1 query problem&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;dynamic-injection&quot;&gt;Dynamic Injection&lt;/h4&gt;

&lt;p&gt;The ideal approach would allow users to directly modify models with their own code, outside of the models&amp;rsquo; apps, without the models themselves having to implement any special hooks for customisation. The end result being an optimal database design, with no extra API requirements for the relevant models. It just so happens that this is possible by using several features that Django exposes, and combining them together in a particular way.&lt;/p&gt;

&lt;p&gt;The approach boils down to three concepts:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Dynamically adding fields to model classes&lt;/li&gt;
  &lt;li&gt;Ensuring Django&amp;rsquo;s model system respects the new fields&lt;/li&gt;
  &lt;li&gt;Getting the load ordering correct for the above to work&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Django&amp;rsquo;s model fields provide an undocumented &lt;code&gt;contribute_to_class&lt;/code&gt; method. This method serves as a fancy version of &lt;code&gt;setattr&lt;/code&gt; and takes a value and attribute name to use as arguments. Internally it then takes care of all the house-keeping required for a field to be added to a model.&lt;/p&gt;

&lt;p&gt;The other feature of Django we&amp;rsquo;ll use is the &lt;a href=&quot;https://docs.djangoproject.com/en/dev/ref/signals/#class-prepared&quot;&gt;&lt;code&gt;class_prepared&lt;/code&gt;&lt;/a&gt; signal. This signal is emitted each time a model class is declared and loaded for the first time by Django&amp;rsquo;s model system.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db.models&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django.db.models.signals&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;class_prepared&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;add_field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&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;kwargs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;sd&quot;&gt;&amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    class_prepared signal handler that checks for the model named&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    MyModel as the sender, and adds a CharField&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    to it.&lt;/span&gt;
&lt;span class=&quot;sd&quot;&gt;    &amp;quot;&amp;quot;&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__name__&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;MyModel&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;CharField&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;New field&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max_length&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;field&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;contribute_to_class&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sender&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;new_field&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;class_prepared&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;add_field&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The final consideration is connecting the &lt;code&gt;class_prepared&lt;/code&gt; signal at the correct time. It needs to occur prior to the relevant model class being declared, otherwise the signal will never be triggered when we want it to. A general way of achieving this is to connect the signal from within an app that is listed before the app containing the relevant model, in the &lt;code&gt;INSTALLED_APPS&lt;/code&gt; setting. Note that in the above code, we don&amp;rsquo;t explicitly import the model to use it as the signal&amp;rsquo;s sender, instead checking for the model&amp;rsquo;s class name, as importing it would break these load ordering requirements.&lt;/p&gt;

&lt;h4 id=&quot;caveats&quot;&gt;Caveats&lt;/h4&gt;

&lt;p&gt;Like the previously described approaches, dynamic injection also comes with a set of drawbacks. These drawbacks stem from the fact that the apps containing the models being customised don&amp;rsquo;t contain a definition for the fields being injected. This means that migration tools likes &lt;a href=&quot;http://south.aeracode.org/&quot;&gt;South&lt;/a&gt; are unable to detect the new fields, and workarounds such as creating manual migrations are required.&lt;/p&gt;

&lt;p&gt;Another related problem is when new admin classes containing references to the custom fields are registered and the fields haven&amp;rsquo;t yet been injected. A typical requirement for injected fields is to expose them via &lt;a href=&quot;https://docs.djangoproject.com/en/dev/ref/contrib/admin/&quot;&gt;Django&amp;rsquo;s admin interface&lt;/a&gt;, which can be achieved by unregistering existing admin classes for the models that fields are being injected into, subclassing these admin classes with new references to the injected fields, and registering the new admin classes. Unfortunately if this unregister/register dance occurs in an admin module, the fields may not have yet been injected. A quick work-around for this is to perform the unregister/register calls inside your project&amp;rsquo;s urlconf.&lt;/p&gt;

&lt;h4 id=&quot;mezzanine-support&quot;&gt;Mezzanine Support&lt;/h4&gt;

&lt;p&gt;Drawbacks aside, the field injection technique described above has characteristics that make it incredibly useful. As such the approach has first-class support in &lt;a href=&quot;http://mezzanine.jupo.org&quot;&gt;Mezzanine&lt;/a&gt; by way of the &lt;code&gt;EXTRA_MODEL_FIELDS&lt;/code&gt; setting. This setting allows you to define a sequence of all the custom fields you&amp;rsquo;d like to inject. Each item in the sequence contains four items: the dotted Python path to the model (including the field name to use for injection), the dotted Python path to the field class to use for the injected field, a sequence of the field&amp;rsquo;s position arguments, and finally a dict of its keyword arguments.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;EXTRA_MODEL_FIELDS&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;c&quot;&gt;# Add a custom image field from the fictitious somelib.fields module&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# to Mezzanine&amp;#39;s BlogPost model:&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Dotted path to field.&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;quot;mezzanine.blog.models.BlogPost.image&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Dotted path to field class.&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;quot;somelib.fields.ImageField&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Positional args for field class.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Image&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,),&lt;/span&gt;
        &lt;span class=&quot;c&quot;&gt;# Keyword args for field class.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;blank&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;s&quot;&gt;&amp;quot;upload_to&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;blog&amp;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;c&quot;&gt;# Example of adding a field to *all* of Mezzanine&amp;#39;s content types:&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;quot;mezzanine.pages.models.Page.another_field&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;s&quot;&gt;&amp;quot;IntegerField&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# &amp;#39;django.db.models.&amp;#39; is implied if path is omitted.&lt;/span&gt;
        &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;Another name&amp;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;s&quot;&gt;&amp;quot;blank&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;s&quot;&gt;&amp;quot;default&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&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;/div&gt;

&lt;p&gt;Mezzanine then uses this setting to inject all of the fields defined, using &lt;code&gt;class_prepared&lt;/code&gt; and &lt;code&gt;contribute_to_class&lt;/code&gt; as described earlier. It handles getting load order correct by performing the injection within the &lt;a href=&quot;https://github.com/stephenmcd/mezzanine/blob/master/mezzanine/boot/__init__.py&quot;&gt;&lt;code&gt;mezzanine.boot&lt;/code&gt;&lt;/a&gt; app, which is forced to the front of all apps defined in &lt;code&gt;INSTALLED_APPS&lt;/code&gt;. Django&amp;rsquo;s admin is also patched in the boot app, to defer certain calls to &lt;code&gt;unregister&lt;/code&gt; and &lt;code&gt;register&lt;/code&gt;, to correct the ordering issues described earlier.&lt;/p&gt;
</content>
   
   <category term="django"></category>
   
   <category term="mezzanine"></category>
   
   <category term="python"></category>
   
   <category term="orm"></category>
   
   <category term="n+1 queries"></category>
   
 </entry>
 
 <entry>
   <title>Open Source for You</title>
   <link href="http://blog.jupo.org/2011/09/12/open-source-for-you/"/>
   <updated>2011-09-12T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2011/09/12/open-source-for-you</id>
   <content type="html">&lt;p&gt;&lt;strong&gt;Update, Feb 14:&lt;/strong&gt; &lt;a href=&quot;https://twitter.com/rkJun&quot;&gt;@rkJun&lt;/a&gt; has &lt;a href=&quot;http://rkjun.wordpress.com/2012/02/13/open-source-for-you-korean_translate/&quot;&gt;translated this post into Korean&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I recently gave a presentational talk to the development team at my work, on what it&amp;rsquo;s like contributing to open source. A common perception by people not involved is that the open source community seems like one big socialist hippie commune, whose motivations are entirely altruistic with little reward for the individual. The approach I took was to dispel this myth and show that for many who participate, their motivations are mostly self serving, with many personal gains to be made. Despite some of the awkward truths I focused on, the talk was received really well.&lt;/p&gt;

&lt;h4 id=&quot;your-day-job-sucks-make-programming-fun-again&quot;&gt;Your Day Job Sucks, Make Programming Fun Again&lt;/h4&gt;

&lt;p&gt;If you have a regular 9 to 5 job as a software engineer at a typical company, chances are likely that your job sucks in several ways. You work on the same types of projects every day. You work with the same technology every day, and most likely it&amp;rsquo;s dated. There are more modern technology stacks out there that are much more elegant, yet for a variety of non-technical reasons, you&amp;rsquo;re not allowed to use them at work. You also have deadlines. Deadlines lead to knocking out features as quickly as possible, which directly conflicts with the quality of your work, so you&amp;rsquo;re often forced to take ugly shortcuts, leaving little to be proud of.&lt;/p&gt;

&lt;p&gt;If this is your only exposure to programming, it gets boring pretty quickly and isn&amp;rsquo;t much fun at all. Contrary to this, programming can actually be incredibly fun! Imagine your company had hundreds of different projects to choose from, and let you choose whichever one you wanted to work on. Then on top of that, they told you that you could use whichever technologies you wanted to, and that you could take as long as you needed to build it &lt;em&gt;correctly&lt;/em&gt; - so long as the end result was the most well designed and beautifully written software you&amp;rsquo;ve ever produced. As a professional programmer, that sounds like a dream, but that&amp;rsquo;s exactly how it is when you work on open source projects. Choose your problem domain. Choose your technology stack. Choose your pace. Choose your quality.&lt;/p&gt;

&lt;h4 id=&quot;become-a-better-developer-through-collaboration&quot;&gt;Become a Better Developer through Collaboration&lt;/h4&gt;

&lt;p&gt;There are two ways to become a better developer: writing code and reading code. This clearly doesn&amp;rsquo;t make sense in a silo on your own. There&amp;rsquo;s little to learn from reading your own code beyond reminiscent value, and you could write code on your own for years and the only lessons learned would be those you teach yourself. Naturally the real learning is to be had from interacting with other developers, writing code that they can review and give you feedback on, and reading code written by other developers with more experience and different approaches than you have.&lt;/p&gt;

&lt;p&gt;Drawing back to your day job, how many developers do you collaborate directly with. A few? A dozen? A small pool that will give you much to learn from, but is still relatively limited in scope. The open source community is made up of thousands of developers, who over many years have produced millions of lines of code. Code of quality much greater than you&amp;rsquo;d ever be exposed to without stepping outside of your workplace.&lt;/p&gt;

&lt;h4 id=&quot;acquire-cash-money&quot;&gt;Acquire Cash Money&lt;/h4&gt;

&lt;p&gt;One of the biggest misconceptions abut open source development is that it&amp;rsquo;s a financially fruitless labour. While true to a certain extent, there are actually several ways in which it can pay a monetary return, both indirectly and directly.&lt;/p&gt;

&lt;p&gt;You may work a steady, full-time job, or you&amp;rsquo;re looking for one, and think of yourself as an employee with a fixed salary, in total contrast to those wild freelancers and entrepreneurs who don&amp;rsquo;t know where their next meal will come from. Wrong. You are a business, just like your employer is. You have one customer, your employer. Sometimes you might feel as though you&amp;rsquo;re stuck in your day job, when in actual fact you&amp;rsquo;re simply stuck with one client.&lt;/p&gt;

&lt;p&gt;So how can you gain more clients? How can you gain a better client? By promoting your business of course. Open source provides a great way for you to showcase you and your ability as a developer. Your open source contributions can distinguish your CV from other developers, namely, your business&amp;rsquo;s competition. Graphic designers have always had the luxury of being able to display their portfolios, and developers can too. I&amp;rsquo;ve been on both sides of the hiring table, and when choosing between potential hires, someone with significant open source contributions has already demonstrated a clear passion for what they do, and therefore has a major advantage over those who are tasked with convincing people of the same, without having anything to show for it.&lt;/p&gt;

&lt;p&gt;Getting your open source projects to the level of quality and usefulness where people are actually building production systems with them is huge achievement in itself. Stemming from that are direct opportunities for paid work. Your corporate users may need your software customised for their use cases. You may even be lucky enough that they&amp;rsquo;d like features developed into the project that are great ideas - things you might not have thought of, and will take the project to the next level. Who better to develop these than the creator of the software themselves? I&amp;rsquo;ve had several instances where this has occurred, and companies have sponsored development on features that, had I had the insight to think of, would have developed freely on my own anyway.&lt;/p&gt;

&lt;h4 id=&quot;broaden-non-technical-experience&quot;&gt;Broaden Non-Technical Experience&lt;/h4&gt;

&lt;p&gt;Hands on coding is only one small aspect of software development. There are many other disciplines that make up the process of shipping a successful product. Project management, community building, formal writing, product development and marketing. While freelancers and one-man commercial studios may get the benefit of dipping their toes into these different areas, as a developer at a typical company your role often doesn&amp;rsquo;t take you outside of the day to day coding.&lt;/p&gt;

&lt;p&gt;Running an open source project properly will allow you to skill up in broad range of non-technical areas. I spend a lot of my time working on my most popular project, however these days only a small portion of this time is actually spent coding. Most of the time is spent in discussion with other developers on their feature ideas, coordinating the inclusion or exclusion of these, and providing support for new users. It&amp;rsquo;s hard work, but it&amp;rsquo;s a great and relevant experience.&lt;/p&gt;

&lt;p&gt;Corollary to this, many non-technical people who&amp;rsquo;d like to contribute to open source in some way, believe that they don&amp;rsquo;t have anything worthwhile to offer. With all that&amp;rsquo;s involved, some of the most beneficial contributions are those of a non-technical nature. If this describes you, then please don&amp;rsquo;t hesitate to dive in. Your help means more than you can imagine.&lt;/p&gt;

&lt;h4 id=&quot;feels-good-man&quot;&gt;Feels Good Man&lt;/h4&gt;

&lt;p&gt;The most obvious yet least tangible benefit in contributing to open source, is the idea I first alluded to around altruism and giving to others. As I mentioned, coordinating a non-trivial open source project can be hard work. It has its highs and lows, and sometimes it feels like there&amp;rsquo;s more of the latter. However every now and then, I&amp;rsquo;ll get a personal email of praise and gratitude. Not in any public forum where the soapbox can be a conduit for ulterior motives, just a private and genuine expression of thanks. I would never have thought of this as being particularly rewarding, until I actually experienced it. It really fills you with a true sense of pride and satisfaction, which makes it all worth while in the end.&lt;/p&gt;

&lt;h4 id=&quot;get-started-now&quot;&gt;Get Started Now&lt;/h4&gt;

&lt;p&gt;The Chinese philosopher &lt;a href=&quot;http://en.wikipedia.org/wiki/Laozi&quot;&gt;Lao Tzu&lt;/a&gt; said:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;A journey of a thousand miles begins with a single step&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Getting started might seem like a daunting task. Start small. Ignore the inner voice that whispers in your ear, arguing that your open source idea wouldn&amp;rsquo;t be of any use to anyone. That might even be true, but it doesn&amp;rsquo;t matter. Do it anyway. A lot of my projects are only a few dozen lines of code. People have found them useful in ways I couldn&amp;rsquo;t have imagined.&lt;/p&gt;

&lt;p&gt;Ideas can be hard to come by. Most of mine were itches I wanted to scratch. Product gaps in the technology stacks I&amp;rsquo;m interested in, or mundane processes I went through regularly, upon realising they could be automated and productised.&lt;/p&gt;

&lt;p&gt;Perhaps you&amp;rsquo;re an end user of open source software, and it isn&amp;rsquo;t perfect. A feature works differently than how you think it should, or something is undocumented and you struggled to find it. These days it seems that the modus operandi is to throw up your arms, and complain as loudly as possible about how the software you&amp;rsquo;re using for free, isn&amp;rsquo;t exactly the way you want it to be. Don&amp;rsquo;t. You have the choice to do something about it. Dive into the source code. Contribute something back. Improve yourself, and be a part of something.&lt;/p&gt;
</content>
   
   <category term="open source"></category>
   
 </entry>
 
 <entry>
   <title>Real-time Web Apps with Django and WebSockets</title>
   <link href="http://blog.jupo.org/2011/08/13/real-time-web-apps-with-django-and-websockets/"/>
   <updated>2011-08-13T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2011/08/13/real-time-web-apps-with-django-and-websockets</id>
   <content type="html">&lt;p&gt;I recently took part in &lt;a href=&quot;http://djangodash.com&quot;&gt;Django Dash&lt;/a&gt;, the annual
hackathon where teams of up to three compete to build the best
&lt;a href=&quot;http://djangoproject.com&quot;&gt;Django&lt;/a&gt; project they can within 48 hours. This year
I worked with &lt;a href=&quot;http://www.traviswhite.com.au/&quot;&gt;Travis White&lt;/a&gt; and &lt;a href=&quot;http://joshdeblank.com/&quot;&gt;Josh de
Blank&lt;/a&gt; to build &lt;a href=&quot;http://drawnby.jupo.org&quot;&gt;DrawnBy&lt;/a&gt; -
a collaborative drawing application that allows people to sketch together in
real-time.&lt;/p&gt;

&lt;p&gt;DrawnBy makes extensive use of
&lt;a href=&quot;http://en.wikipedia.org/wiki/WebSockets&quot;&gt;WebSockets&lt;/a&gt; which are mostly unheard
of in web stacks like Django and Rails, or even antiquated stacks like PHP and
ASP.NET, which are all designed around accepting a request and returning a
response to the browser as quickly as possible. This is in contrast to
WebSockets which allow full duplex communication between the browser and
server, and therefore require long running requests per user.&lt;/p&gt;

&lt;p&gt;A variety of patterns for dealing with WebSockets in Django emerged while
developing DrawnBy, and since the Dash I&amp;rsquo;ve been working on abstracting these
into a reusable Django application called &lt;a href=&quot;https://github.com/stephenmcd/django-socketio&quot;&gt;django-socketio&lt;/a&gt; which I&amp;rsquo;ve released
today. It&amp;rsquo;s available on &lt;a href=&quot;https://github.com/stephenmcd/django-
socketio&quot;&gt;Github&lt;/a&gt;, &lt;a href=&quot;https://bitbucket.org/stephenmcd/django-socketio&quot;&gt;Bitbucket&lt;/a&gt; and
&lt;a href=&quot;http://pypi.python.org/pypi/django-socketio/&quot;&gt;PyPI&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here&amp;rsquo;s an overview of the features.&lt;/p&gt;

&lt;h4 id=&quot;features&quot;&gt;Features&lt;/h4&gt;

&lt;ul&gt;
  &lt;li&gt;Installation of required packages from PyPI&lt;/li&gt;
  &lt;li&gt;A management command for running gevent&amp;rsquo;s pywsgi server with auto-reloading capabilities&lt;/li&gt;
  &lt;li&gt;A channel subscription and broadcast system that extends Socket.IO allowing WebSockets and events to be partitioned into separate concerns&lt;/li&gt;
  &lt;li&gt;A signals-like event system that abstracts away the various stages of a Socket.IO request&lt;/li&gt;
  &lt;li&gt;The required views, urlpatterns, templatetags and tests for all the above&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;channels&quot;&gt;Channels&lt;/h4&gt;

&lt;p&gt;The WebSocket implemented by gevent-websocket provides two methods for sending
data to other clients, &lt;code&gt;socket.send&lt;/code&gt; which sends data to the given socket
instance, and &lt;code&gt;socket.broadcast&lt;/code&gt; which sends data to all socket instances
other than itself.&lt;/p&gt;

&lt;p&gt;A common requirement for WebSocket based applications is to divide
communications up into separate channels. For example a chat site may have
multiple chat rooms and rather than using &lt;code&gt;broadcast&lt;/code&gt; which would send a chat
message to all chat rooms, each room would need a reference to each of the
connected sockets so that &lt;code&gt;send&lt;/code&gt; can be called on each socket when a new
message arrives for that room.&lt;/p&gt;

&lt;p&gt;django-socketio extends Socket.IO both on the client and server to provide
channels that can be subscribed and broadcast to.&lt;/p&gt;

&lt;p&gt;To subscribe to a channel client-side in JavaScript use the &lt;code&gt;socket.subscribe&lt;/code&gt;
method:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&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;nx&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;my&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;channel&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;/div&gt;

&lt;p&gt;Once the socket is subscribed to a channel, you can then broadcast to the
channel server-side in Python using the &lt;code&gt;socket.broadcast_channel&lt;/code&gt; method:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;broadcast_channel&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;my message&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;events&quot;&gt;Events&lt;/h4&gt;

&lt;p&gt;The &lt;code&gt;django_socketio.events&lt;/code&gt; module provides a handful of events that can be
subscribed to, very much like connecting receiver functions to Django signals.
Each of these events are raised throughout the relevant stages of a Socket.IO
request.&lt;/p&gt;

&lt;p&gt;Events are subscribed to by applying each event as a decorator to your event
handler functions:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;django_socketio.events&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;on_message&lt;/span&gt;

&lt;span class=&quot;nd&quot;&gt;@on_message&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;my_message_handler&lt;/span&gt;&lt;span class=&quot;p&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;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Each event handler takes at least three arguments: the current Django
&lt;code&gt;request&lt;/code&gt;, the Socket.IO &lt;code&gt;socket&lt;/code&gt; the event occurred for, and a &lt;code&gt;context&lt;/code&gt;,
which is simply a dictionary that can be used to persist variables across all
events throughout the life-cycle of a single WebSocket connection.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code&gt;on_connect&lt;/code&gt; - occurs once when the WebSocket connection is first established.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;on_message&lt;/code&gt; - occurs every time data is sent to the WebSocket. Takes an extra &lt;code&gt;message&lt;/code&gt; argument which contains the data sent.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;on_subscribe&lt;/code&gt; - occurs when a channel is subscribed to. Takes an extra &lt;code&gt;channel&lt;/code&gt; argument which contains the channel subscribed to.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;on_unsubscribe&lt;/code&gt; - occurs when a channel is unsubscribed from. Takes an extra &lt;code&gt;channel&lt;/code&gt; argument which contains the channel unsubscribed from.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;on_error&lt;/code&gt; - occurs when an error is raised. Takes an extra &lt;code&gt;exception&lt;/code&gt; argument which contains the exception for the error.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;on_disconnect&lt;/code&gt; - occurs once when the WebSocket disconnects.&lt;/li&gt;
  &lt;li&gt;&lt;code&gt;on_finish&lt;/code&gt; - occurs once when the Socket.IO request is finished.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Like Django signals, event handlers can be defined anywhere so long as they
end up being imported. Consider adding them to their own module that gets
imported by your urlconf, or even adding them to your views module since
they&amp;rsquo;re conceptually similar to views.&lt;/p&gt;

&lt;h4 id=&quot;binding-events-to-channels&quot;&gt;Binding Events to Channels&lt;/h4&gt;

&lt;p&gt;All events other than the &lt;code&gt;on_connect&lt;/code&gt; event can also be bound to particular
channels by passing a &lt;code&gt;channel&lt;/code&gt; argument to the event decorator. The channel
argument can contain a regular expression pattern used to match again multiple
channels of similar function.&lt;/p&gt;

&lt;p&gt;For example, suppose you implemented a chat site with multiple rooms.
WebSockets would be the basis for users communicating within each chat room,
however you may want to use them elsewhere throughout the site for different
purposes, perhaps for a real-time admin dashboard. In this case there would be
two distinct WebSocket uses, with the chat rooms each requiring their own
individual channels.&lt;/p&gt;

&lt;p&gt;Suppose each chat room user subscribes to a channel client-side using the
room&amp;rsquo;s ID:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;javascript&quot;&gt;&lt;span class=&quot;kd&quot;&gt;var&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;io&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;Socket&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;roomID&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;nx&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;p&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;nx&quot;&gt;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;subscribe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;room&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;roomID&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;/div&gt;

&lt;p&gt;Then server-side the different message handlers are bound to each type of
channel:&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;nd&quot;&gt;@on_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;dashboard&amp;quot;&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;my_dashboard_handler&lt;/span&gt;&lt;span class=&quot;p&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;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&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;nd&quot;&gt;@on_message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;channel&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;^room-&amp;quot;&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;my_chat_handler&lt;/span&gt;&lt;span class=&quot;p&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;socket&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;message&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;h4 id=&quot;chat-demo&quot;&gt;Chat Demo&lt;/h4&gt;

&lt;p&gt;The &amp;ldquo;hello world&amp;rdquo; of WebSocket applications is naturally the chat room. As
such django-socketio comes with a demo chat application that provides examples
of the different events and channel features available.&lt;/p&gt;
</content>
   
   <category term="django"></category>
   
   <category term="websockets"></category>
   
   <category term="socket.io"></category>
   
 </entry>
 
 <entry>
   <title>Rails Quick Start for Djangonauts</title>
   <link href="http://blog.jupo.org/2011/07/30/rails-quick-start-for-djangonauts/"/>
   <updated>2011-07-30T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2011/07/30/rails-quick-start-for-djangonauts</id>
   <content type="html">&lt;p&gt;I recently started a new role at a &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt; shop, which as a long time &lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; specialist was a really interesting opportunity. There&amp;rsquo;s a lot of competition between the two frameworks&amp;rsquo; communities, ranging from friendly rivalry and respectful admiration at the mature end of the scale, to all out fanboy fuelled flame-wars at the other.&lt;/p&gt;

&lt;p&gt;After you wade through the rivalry, the common wisdom voiced is that they&amp;rsquo;re conceptually the same. If you know &lt;a href=&quot;http://python.org/&quot;&gt;Python&lt;/a&gt;, go with Django and if you know &lt;a href=&quot;http://www.ruby-lang.org/&quot;&gt;Ruby&lt;/a&gt;, go with Rails. After spending several months with Rails I can attest to this being true. At a bird&amp;rsquo;s-eye view both frameworks contain almost identical concepts, implemented with different philosophies stemming from the ideals expressed by the languages they&amp;rsquo;re written in. Both frameworks provide a vastly superior approach to security, modularity and rapid development than &lt;a href=&quot;/2010/09/28/on-modern-web-development/&quot;&gt;their predecessors do&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An interesting question to ask would be: &lt;em&gt;which would be the best framework to choose, not knowing either language?&lt;/em&gt; It would be naive of me to believe I am unbiased, but I would certainly recommend Django over Rails. The relative strictness of Python and explicitness of each component in Django, compared to the implicit magic in Rails, is simply much more geared towards creating large-scale systems in a sane and transparent way. To Ruby&amp;rsquo;s credit though, I have developed a real admiration for the language itself, and have continued using it in my own projects - but that&amp;rsquo;s a topic for another post.&lt;/p&gt;

&lt;p&gt;Considering how similar the two frameworks are component-wise, one thing I did miss was a side-by-side cheat sheet for working out what each of the concepts were in Rails that I already knew well in Django. I&amp;rsquo;ve put one together below to help out anyone who might be picking up either framework while already knowing the other. For clarity, I&amp;rsquo;ve also included descriptions of each type of component, for those who haven&amp;rsquo;t used either framework.&lt;/p&gt;

&lt;table class=&quot;zebra-striped&quot;&gt;
&lt;tr&gt;
    &lt;th&gt;Django&lt;/th&gt;
    &lt;th&gt;Ruby on Rails&lt;/th&gt;
    &lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/http/urls/&quot;&gt;URL patterns&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/routing.html&quot;&gt;Routes&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;Regular expression definitions for each type of URL and what part of the web site they map to.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/http/views/&quot;&gt;Views&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/action_controller_overview.html&quot;&gt;Controllers&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The units of code that the above regular expressions map to, that perform application logic and pass data to a rendering layer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/templates/&quot;&gt;Templates&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/layouts_and_rendering.html&quot;&gt;Views&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The rendering layer that is given data from the code described above, and performs display logic typically wrapped around HTML code.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/ref/templates/builtins/&quot;&gt;Template tags&lt;/a&gt; &lt;em&gt;(built-in)&lt;/em&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://www.ruby-doc.org/stdlib-1.9.3/libdoc/erb/rdoc/ERB.html&quot;&gt;Embedded Ruby&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The flow-control language that can be used in the rendering layer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/howto/custom-template-tags/&quot;&gt;Template tags&lt;/a&gt; &lt;em&gt;(custom)&lt;/em&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/getting_started.html#view-helpers&quot;&gt;Helpers&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The system for defining custom functions that can be used in the rendering layer.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/db/models/&quot;&gt;Models&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/getting_started.html#getting-up-and-running-quickly-with-scaffolding&quot;&gt;Models&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The data-definition layer that maps classes to database tables - the &lt;a href=&quot;http://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORM&lt;/a&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/topics/db/managers/&quot;&gt;Managers&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/active_record_querying.html#scopes&quot;&gt;Scopes&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The way to extend the ORM to define custom database queries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/howto/custom-management-commands/&quot;&gt;Management commands&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/command_line.html&quot;&gt;Rake tasks&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;Scripts for performing administrative tasks via the command line.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/glossary/#term-project&quot;&gt;Project&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/getting_started.html#creating-the-blog-application&quot;&gt;App&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;An entire application built with the framework.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/intro/tutorial01/#creating-models&quot;&gt;App&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/plugins.html&quot;&gt;Plugin&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The way in which all components in the framework can grouped together in separate areas of functionality.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;http://south.aeracode.org/&quot;&gt;South&lt;/a&gt; &lt;em&gt;(third-party)&lt;/em&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://guides.rubyonrails.org/migrations.html&quot;&gt;Migrations&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The system used for automatically applying changes in the ORM definition to the underlying database tables, such as adding and removing columns.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;https://docs.djangoproject.com/en/dev/ref/contrib/admin/&quot;&gt;Admin&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;https://github.com/sferik/rails_admin&quot;&gt;RailsAdmin&lt;/a&gt; &lt;em&gt;(third-party)&lt;/em&gt;&lt;/td&gt;
    &lt;td&gt;A web-based interface for authenticating administrative users and providing CRUD tools for managing data.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td colspan=&quot;3&quot;&gt;
    &lt;p&gt;The following table lists software that aren't part of Django or Rails, but are core parts of the Python and Ruby eco-systems, and go hand-in-hand with using either framework.&lt;/p&gt;
    &lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;th&gt;Python&lt;/th&gt;
    &lt;th&gt;Ruby&lt;/th&gt;
    &lt;th&gt;&amp;nbsp;&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;http://www.virtualenv.org/&quot;&gt;Virtualenv&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://beginrescueend.com/&quot;&gt;RVM&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The system used for running isolated environments bound to a particular language version, combined with a set of install libraries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;http://www.pip-installer.org/&quot;&gt;PIP&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://gembundler.com/&quot;&gt;Bundler&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;The package manager for installing libraries.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;http://www.wsgi.org/&quot;&gt;WSGI&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Rack_(web_server_interface)&quot;&gt;Rack&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;A standard specification for applications to interface with HTTP, allowing for a single application entry point and middleware to be implemented.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
    &lt;td&gt;&lt;a href=&quot;http://fabfile.org/&quot;&gt;Fabric&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;&lt;a href=&quot;http://en.wikipedia.org/wiki/Capistrano&quot;&gt;Capistrano&lt;/a&gt;&lt;/td&gt;
    &lt;td&gt;A system for automating tasks on remote servers from a local machine, typically as part of a deployment process.&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;

&lt;p&gt;Not all of these pairings are a perfect one-to-one match conceptually, but should be good enough to get an overall view of what each concept is within both frameworks.&lt;/p&gt;

</content>
   
   <category term="python"></category>
   
   <category term="ruby"></category>
   
   <category term="ruby on rails"></category>
   
   <category term="django"></category>
   
 </entry>
 
 <entry>
   <title>Does Mezzanine Scale?</title>
   <link href="http://blog.jupo.org/2011/06/16/does-mezzanine-scale/"/>
   <updated>2011-06-16T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2011/06/16/does-mezzanine-scale</id>
   <content type="html">&lt;p&gt;The question of scalability with regard to
&lt;a href=&quot;http://mezzanine.jupo.org/&quot;&gt;Mezzanine&lt;/a&gt; and
&lt;a href=&quot;https://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; recently came on up on the
&lt;a href=&quot;http://groups.google.com/group/mezzanine-
users/topics&quot;&gt;mezzanine-users mailing list&lt;/a&gt;, to which I offered the following reply.&lt;/p&gt;

&lt;p&gt;Mezzanine and Django itself are fantastic choices for someone concerned with
scaling for high traffic.&lt;/p&gt;

&lt;p&gt;The &lt;a href=&quot;https://addons.mozilla.org&quot;&gt;Mozilla add-ons&lt;/a&gt; site that hosts all Firefox
plugins, and &lt;a href=&quot;http://disqus.com/&quot;&gt;Disqus&lt;/a&gt; which is currently the world&amp;rsquo;s
highest traffic commenting system, both run on Django. Each of these have been
quoted at &lt;a href=&quot;http://www.djangocon.eu/talks/18/&quot;&gt;500 million hits per day&lt;/a&gt; and &lt;a href=&quot;http://www.quora.com/Django/What-is-the-highest-traffic-
website-built-on-top-of-Django&quot;&gt;1
billion per month&lt;/a&gt; respectively.&lt;/p&gt;

&lt;p&gt;One of the keys to scaling sites like these is the wealth of options available
for caching in the Django ecosystem, and the ability to then scale out the
number of both application servers running Django, and caching servers
typically running &lt;a href=&quot;http://memcached.org/&quot;&gt;memcached&lt;/a&gt;, with very little
modification to your application code. Django comes out-of-the-box with the
ability to switch on &lt;a href=&quot;https://docs.djangoproject.com/en/1.3/topics/cache/#the-per-site-
cache&quot;&gt;site-wide
caching&lt;/a&gt;, &lt;a href=&quot;https://docs.djangoproject.com/en/1.3/topics/cache
/#the-per-view-cache&quot;&gt;per page caching&lt;/a&gt; and &lt;a href=&quot;https://docs.djangoproject.com/en/1.3/topics/cache/#template-
fragment-caching&quot;&gt;template fragment
caching&lt;/a&gt;. Beyond that there are also third-party Django applications
that implement object level caching such as &lt;a href=&quot;http://jbalogh.me/projects/cache-machine/&quot;&gt;Django Cache
Machine&lt;/a&gt; and &lt;a href=&quot;http://packages.python.org/johnny-cache/&quot;&gt;Johnny
Cache&lt;/a&gt;, for even finer-grained
control with little modification to your code.&lt;/p&gt;

&lt;p&gt;Both Mezzanine and &lt;a href=&quot;http://cartridge.jupo.org/&quot;&gt;Cartridge&lt;/a&gt; have been designed
from the ground up with scalability in mind. Particular care has been taken to
avoid any &lt;a href=&quot;http://www.pbell.com/index.cfm/2006/9/17
/Understanding-the-n1-query-problem&quot;&gt;n+1 queries&lt;/a&gt;, for example rendering out multiple
instances of Mezzanine&amp;rsquo;s navigation tree containing any number of nested
levels of navigation will only ever run a single database query. Same with
Cartridge&amp;rsquo;s products. What this means is that you can go very far traffic-
wise, using a single server without even thinking about caching. Once you do
then the ability to add application and cache servers is trivial, and will
take you incredibly far using a single database. Once you start needing
multiple database servers Django also comes with the built-in ability to
&lt;a href=&quot;https://docs.djangoproject.com/en/1.3/topics/db/multi-db/&quot;&gt;route models across different
databases&lt;/a&gt;, so
there are extra options there beyond your typical master/slave database
replication scenario.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve only touched on the topic to provide an overview of what&amp;rsquo;s available, and
you should certainly research it further for your particular scenario, but as
you can see scalability is a core concern baked into Django and Mezzanine, so
you can choose these with confidence.&lt;/p&gt;
</content>
   
   <category term="mezzanine"></category>
   
   <category term="cartridge"></category>
   
   <category term="django"></category>
   
   <category term="scalability"></category>
   
 </entry>
 
 <entry>
   <title>Mezzanine and Cartridge Hit the Mainstream</title>
   <link href="http://blog.jupo.org/2011/03/21/mezzanine-and-cartridge-hit-the-mainstream/"/>
   <updated>2011-03-21T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2011/03/21/mezzanine-and-cartridge-hit-the-mainstream</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.cio.com.au/article/380
246/melbourne_hacker_releases_open_source_django_apps/&quot;&gt;Mezzanine and Cartridge Hit the Mainstream&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This week I had the fantastic opportunity to be interviewed by &lt;a href=&quot;http://www.gedda.info/&quot;&gt;Rodney
Gedda&lt;/a&gt; from &lt;a href=&quot;http://idg.com/&quot;&gt;IDG&lt;/a&gt; for their
&lt;a href=&quot;http://www.techworld.com.au/&quot;&gt;TechWorld&lt;/a&gt; and &lt;a href=&quot;http://www.cio.com.au/&quot;&gt;CIO&lt;/a&gt;
magazines. The interview focused on my open source work with the
&lt;a href=&quot;http://mezzanine.jupo.org/&quot;&gt;Mezzanine&lt;/a&gt; and
&lt;a href=&quot;http://cartridge.jupo.org/&quot;&gt;Cartridge&lt;/a&gt; projects.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://www.cio.com.au/arti
cle/380246/melbourne_hacker_releases_open_source_django_apps/&quot;&gt;Melbourne hacker releases open source Django apps&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These projects have been gaining traction at an incredible pace, with a solid
community forming around them over the last year. All of this has happened in
spite of having no formal promotion outside of the community itself, which
makes this type of exposure all the more exciting.&lt;/p&gt;

&lt;p&gt;It&amp;rsquo;s also no secret that I think &lt;a href=&quot;http://blog.jupo.org/2010/09/28/on-
modern-web-development/&quot;&gt;the Australian web development market is
many years behind the United States&lt;/a&gt;, so it&amp;rsquo;s a great step forward to see modern
technology getting covered by the mainstream local tech media.&lt;/p&gt;
</content>
   
   <category term="mezzanine"></category>
   
   <category term="cartridge"></category>
   
   <category term="django"></category>
   
   <category term="australia"></category>
   
   <category term="techworld"></category>
   
   <category term="cio magazine"></category>
   
   <category term="open source"></category>
   
 </entry>
 
 <entry>
   <title>How I Now Know Node</title>
   <link href="http://blog.jupo.org/2011/02/01/how-i-now-know-node/"/>
   <updated>2011-02-01T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2011/02/01/how-i-now-know-node</id>
   <content type="html">&lt;p&gt;Having remained utterly faithful to &lt;a href=&quot;http://python.org/&quot;&gt;Python&lt;/a&gt; and
&lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; over the last year, and dedicating all
of my available time to open source projects like
&lt;a href=&quot;http://mezzanine.jupo.org&quot;&gt;Mezzanine&lt;/a&gt; and
&lt;a href=&quot;http://cartridge.jupo.org&quot;&gt;Cartridge&lt;/a&gt;, with the new year at hand I though it
was about time to take a break and add some new technology to my repertoire.
During his &lt;a href=&quot;http://djangoconeu.blip.tv/file/3674233/&quot;&gt;keynote speech at Djangocon.eu
2010&lt;/a&gt;, the creator of Django &lt;a href=&quot;http://jacobian.org/&quot;&gt;Jacob
Kaplan-Moss&lt;/a&gt; states “it will challenge what you think
you know about web/server architecture” when referring to
&lt;a href=&quot;http://nodejs.org&quot;&gt;Node.js&lt;/a&gt;. Since then it has been sitting in the back of my
mind as something I definitely needed to check out, so I decided to dive in
head first.&lt;/p&gt;

&lt;p&gt;Node.js is a general purpose JavaScript development environment geared towards
writing network servers. It uses an event-based, non-blocking architecture
which allows your web application to scale to thousands of concurrent
connections without needing a finite pool of threads or processes. JavaScript
is executed using &lt;a href=&quot;http://code.google.com/apis/v8/intro.html&quot;&gt;Google&amp;rsquo;s V8
engine&lt;/a&gt; which ranks very highly in
speed &lt;a href=&quot;http://shootout.alioth.debian.org/u32/which-programming-languages-
are-fastest.php&quot;&gt;compared to other dynamic
languages&lt;/a&gt;, so not only does Node.js scale elegantly, it&amp;rsquo;s damn fast.&lt;/p&gt;

&lt;p&gt;Having recently built &lt;a href=&quot;http://github.com/stephenmcd/grillo&quot;&gt;Grillo&lt;/a&gt;, a console
based chat server, I&amp;rsquo;d been considering what it would be like to put together
a web-based version. In fact I had achieved &lt;a href=&quot;http://code.google.com/p/cmdsvr/&quot;&gt;something
similar&lt;/a&gt; in the past using &lt;a href=&quot;http://docs.python.org/library/basehttpserver.html&quot;&gt;Python&amp;rsquo;s
BaseHttpServer module&lt;/a&gt;,
and while functional for a few hundred connections, my approach would never
scale, as either a separate thread or process would be required for each open
connection. The event driven architecture of scaling a web server for an
increasing number of open connections is mostly a solved problem, especially
in the Python community with projects like
&lt;a href=&quot;http://twistedmatrix.com/&quot;&gt;Twisted&lt;/a&gt; and
&lt;a href=&quot;http://www.tornadoweb.org/&quot;&gt;Tornado&lt;/a&gt;. However Node.js is different in that
its non-blocking evented model is a first class citizen by design.&lt;/p&gt;

&lt;p&gt;With a useful project at hand to try Node.js on, I set about creating what
I&amp;rsquo;ve named &lt;a href=&quot;http://chat.jupo.org/about&quot;&gt;Grillode&lt;/a&gt; (yes you guessed it: Grillo
+ Node). It&amp;rsquo;s a web-based chat server with a set of configuration options that
lets you run it in various modes, such as a customer support queue, or with
&lt;a href=&quot;http://en.wikipedia.org/wiki/Chatroulette&quot;&gt;Chatroulette&lt;/a&gt; style random match-
ups. I&amp;rsquo;ve released the source onto
&lt;a href=&quot;http://github.com/stephenmcd/grillode&quot;&gt;Github&lt;/a&gt; and
&lt;a href=&quot;http://bitbucket.org/stephenmcd/grillode&quot;&gt;Bitbucket&lt;/a&gt;, and also have &lt;a href=&quot;http://chat.jupo.org/&quot;&gt;a demo
up and running&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The process of putting Grillode together led me through many parts of the
ecosystem that has developed around Node.js - following is an overview of the
pieces I ended up working with.&lt;/p&gt;

&lt;h4 id=&quot;node-package-manager-npm&quot;&gt;Node Package Manager (NPM)&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://npmjs.org/&quot;&gt;NPM&lt;/a&gt; is a command line utility that gives you access to a
central online repository of packages built for Node.js. It works wonderfully
when installed correctly, but on my machine I encountered a handful of issues
where it ended up recreating various system directories all throughout my home
directory. After setting up various symlinks by hand, I did get it to work
after many failed attempts at installing it. This issue was definitely
specific to my machine as I was then able to go ahead and install NPM
seamlessly on several different servers.&lt;/p&gt;

&lt;p&gt;Once everything was working correctly it made deployment of Grillode a breeze.
By specifying all of its dependencies in a &lt;a href=&quot;https://github.com/s
tephenmcd/grillode/blob/master/package.json&quot;&gt;package.json&lt;/a&gt; file, NPM was able to installed
everything required in a single step.&lt;/p&gt;

&lt;h4 id=&quot;express&quot;&gt;Express&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://expressjs.com/&quot;&gt;Express&lt;/a&gt; provides basic URL routing to functions that
will typically perform some application logic and hand off data to a template
to be rendered. It contains integration points for a handful of different
templating libraries and it also contains a simple middleware system. I’d
definitely consider it to be a micro framework, but it’s a great start at
whipping your Node.js application into a well defined structure.&lt;/p&gt;

&lt;h4 id=&quot;socketio&quot;&gt;Socket.io&lt;/h4&gt;

&lt;p&gt;&lt;a href=&quot;http://socket.io/&quot;&gt;Socket.io&lt;/a&gt; takes all the leg work out of maintaining an
open connection to the browser. You start by attaching it to your Node.js
server which then automatically makes available the client-side JavaScript
required. This provides the communication channel between the client and the
server, which attempts to use &lt;a href=&quot;http://en.wikipedia.org/wiki/WebSockets&quot;&gt;web
sockets&lt;/a&gt; when available, and
transparently falls back to Flash sockets or even old-school AJAX polling if
the former options aren’t supported by the browser.&lt;/p&gt;

&lt;p&gt;It then provides all of the methods and event handlers for connecting and
sending data over the connection. The beauty behind how this is implemented is
that it exposes these methods and events almost identically to both the
Node.js server, and the browser client - instantly you have available two-way
communication between the browser and the server via an open connection,
without requiring any new requests to the server.&lt;/p&gt;

&lt;h4 id=&quot;coffeescript&quot;&gt;CoffeeScript&lt;/h4&gt;

&lt;p&gt;As many others have done, I’ve often compared JavaScript to Python in that
they both share an object model defined by a hash table of names and object
members, which can be introspected and dynamically modified. While this is a
very elegant model, JavaScript boasts syntax reminiscant of the turn of the
century, cluttered with semicolons and braces, and missing a handful of
features found in modern languages such as list comprehensions and much more.
Well &lt;a href=&quot;http://www.americanscientist.org/issues/i
d.3489,y.0,no.,content.true,page.1,css.print/issue.aspx&quot;&gt;the war on semicolons is over&lt;/a&gt; with the explosion in
popularity of languages such as Python and Ruby showing this to be true.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://coffeescript.org&quot;&gt;CoffeeScript&lt;/a&gt; is a language inspired by Python and
Ruby which gets compiled directly into JavaScript. It therefore retains all
the properties of JavaScript such as its data types, objects and methods, but
provides a much more modern and clean syntax with some fantastic sugar, such
as list comprehensions, class-based object construction, string interpolation
and more.&lt;/p&gt;

&lt;p&gt;Experimentally, CoffeeScript can be run directly in the browser in place of
JavaScript by including the compiler JavaScript file itself, but much more
interestingly it can be used as an execution environment for Node.js, which
will perform the compilation to JavaScript when the Node.js application is
first started. I found my experience to match reports of up to 30% in
reduction of the amount of code required.&lt;/p&gt;

&lt;h4 id=&quot;coffeekup&quot;&gt;Coffeekup&lt;/h4&gt;

&lt;p&gt;When looking into what was available for templating, I started out with
&lt;a href=&quot;http://github.com/visionmedia/ejs&quot;&gt;EJS&lt;/a&gt; which basically gives you executable
JavaScript within your template files. I found this to provide a poor
separation between display and application code, something that Django gets
right by providing a limited template language that has basic flow control,
but makes writing non-trivial logic difficult to do.&lt;/p&gt;

&lt;p&gt;I then discovered a Node.js template library called
&lt;a href=&quot;http://coffeekup.org/&quot;&gt;Coffeekup&lt;/a&gt;, that tied in very closely with the time I
had already spent with CoffeeScript. Coffeekup allows you to define your HTML
entirely in CoffeeScript. I’m still undecided on whether this is a thing of
beauty or horror. It’s very surreal to work with web page markup expressed
entirely in programming code. I guess there’s somewhat of an undeserved
feeling of the presentation being too closely tied to programming logic. There
is a magical feeling in having your server code, client code and presentation
code all in the exact same language, coupled with the given language being
CoffeeScript which is incredibly clean.&lt;/p&gt;

&lt;h4 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h4&gt;

&lt;p&gt;I really enjoyed working with Node.js and the young ecosystem surrounding it.
At this point in time, I wouldn’t consider it for a typical project over a
full stack framework like Django with the elegance of Python, however it
definitely serves as a fantastic choice for a very specific criteria -
scalable, real time web applications.&lt;/p&gt;
</content>
   
   <category term="coffeescript"></category>
   
   <category term="javascript"></category>
   
   <category term="node.js"></category>
   
   <category term="sockets"></category>
   
 </entry>
 
 <entry>
   <title>Grillo, a Terminal Based Chat Server and Client</title>
   <link href="http://blog.jupo.org/2011/01/03/grillo-a-terminal-based-chat-server-and-client/"/>
   <updated>2011-01-03T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2011/01/03/grillo-a-terminal-based-chat-server-and-client</id>
   <content type="html">&lt;p&gt;I recently came across the &lt;a href=&quot;http://rosettacode.org/&quot;&gt;Rosetta Code Project&lt;/a&gt;.
It&amp;rsquo;s a community contributed wiki that contains &lt;a href=&quot;http://rosettacode.org/wiki/Category:Programming_Tasks&quot;&gt;hundreds of solutions to
programming problems&lt;/a&gt;,
implemented in &lt;a href=&quot;http://rosettacode.org/wiki/Category:Programming_Languages&quot;&gt;hundreds of different programming
languages&lt;/a&gt;, which
is great source of entertainment for a programming languages enthusiast such
as myself. The main focus of the project isn&amp;rsquo;t to demonstrate individual
solutions on their own, but to provide comparisons between different
programming languages and how they approach the same task.&lt;/p&gt;

&lt;p&gt;The wiki also contains various dynamic reports, such as &lt;a href=&quot;http://rosettacode
.org/wiki/Category:Unimplemented_tasks_by_language&quot;&gt;which tasks have yet
to be implemented for each particular programming language&lt;/a&gt;. I took a look at &lt;a href=&quot;http://rosettacode.org/wiki/Reports:Tasks_not_implemented_in_Python&quot;&gt;the
Python
page&lt;/a&gt; to
see if there were any interesting tasks remaining that I could potentially
provide a solution to, and as I expected there were only a few relatively
obscure tasks that were yet to have solutions provided.&lt;/p&gt;

&lt;p&gt;One task remaining for Python that did catch my eye was to &lt;a href=&quot;http://rosettacode.org/wiki/Chat_server&quot;&gt;demonstrate a
simple chat server using sockets&lt;/a&gt;.
I&amp;rsquo;ve always been especially fond of network programming, from web crawlers to
XMLRPC to lower level sockets, I seem to really enjoy writing code that runs
over the Internet without necessarily being related to web development, so I
went ahead and added a &lt;a href=&quot;http://rosettacode.org/wiki/Chat_server#Python&quot;&gt;Python solution for the chat server
task&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I honestly had so much fun working on this that I decided to extend it even
further. Firstly I refactored it into a more object oriented approach which
allowed for creating both server and client tools which could be run
concurrently using separate threads of control. I then added several commands
that could be run directly in chat by any user, for example listing the
current users who were logged in.&lt;/p&gt;

&lt;p&gt;I then decided to publish the code for my chat server and client onto both
&lt;a href=&quot;https://github.com/stephenmcd/grillo&quot;&gt;GitHub&lt;/a&gt; and
&lt;a href=&quot;https://bitbucket.org/stephenmcd/grillo&quot;&gt;Bitbucket&lt;/a&gt;, after giving it the name
&amp;ldquo;Grillo&amp;rdquo;. It&amp;rsquo;s named after the &lt;a href=&quot;http://en.wikipedia.org/wiki/Grillo_telephone&quot;&gt;Italian
phone&lt;/a&gt; of the same name,
developed in 1965. They both share the common theme of being a very small
communications device for their class, while being implemented with relatively
basic technology.&lt;/p&gt;

&lt;p&gt;Given that there are many richer and more powerful applications available for
implementing the features Grillo provides, at the least it serves as a good
example of how to do basic socket programming in Python, as well as a
demonstrating some simple tricks for controlling threads. At the best case
someone will pick up the code base and extend it further in ways I haven&amp;rsquo;t
anticipated - here&amp;rsquo;s hoping!&lt;/p&gt;
</content>
   
   <category term="rosetta code"></category>
   
   <category term="python"></category>
   
   <category term="sockets"></category>
   
   <category term="open source"></category>
   
 </entry>
 
 <entry>
   <title>2010: My Year in Review</title>
   <link href="http://blog.jupo.org/2011/01/01/2010-my-year-in-review/"/>
   <updated>2011-01-01T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2011/01/01/2010-my-year-in-review</id>
   <content type="html">&lt;p&gt;2010 was an amazing year for me professionally. I learnt and achieved much
more than I&amp;rsquo;ve historically packed into a single year, so like many other I&amp;rsquo;ve
decided to jump on the &lt;em&gt;year in review&lt;/em&gt; bandwagon and put this post
together.&lt;/p&gt;

&lt;h4 id=&quot;open-source&quot;&gt;Open Source&lt;/h4&gt;

&lt;p&gt;Prior to 2010 I&amp;rsquo;d certainly used open source quite heavily, in fact I&amp;rsquo;d based
my entire career as a developer around it, focusing on
&lt;a href=&quot;http://python.org/&quot;&gt;Python&lt;/a&gt;, &lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; and
&lt;a href=&quot;http://en.wikipedia.org/wiki/Linux&quot;&gt;Linux&lt;/a&gt; and the open source ecosystem
surrounding this amazing platform. However as far as giving back to the
community went, I had only ever contributed small bug fixes and enhancements
to a handful of open source projects over the years, and had never been
heavily involved in any single project. So in 2010 I dived in head first
launching a range of open source projects. Firstly some smaller utilities such
as &lt;a href=&quot;https://github.com/stephenmcd/django-forms-builder&quot;&gt;django-forms-builder&lt;/a&gt;
and &lt;a href=&quot;https://github.com/stephenmcd/gunicorn-console&quot;&gt;gunicorn-console&lt;/a&gt;, and
then the &lt;a href=&quot;http://mezzanine.jupo.org/&quot;&gt;Django content management platform
Mezzanine&lt;/a&gt;, along with its &lt;a href=&quot;http://cartridge.jupo.org/&quot;&gt;shopping cart plug-in,
Cartridge.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The reception Mezzanine has received has been nothing short of amazing and
well beyond anything I had anticipated. It&amp;rsquo;s been an incredibly rewarding
learning experience, managing product development and working closely with its
contributors towards growing its community. Here are a few stats for the
project only 6 months since it was launched:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;120 followers and 30 forks across &lt;a href=&quot;https://github.com/stephenmcd/mezzanine&quot;&gt;GitHub&lt;/a&gt; and &lt;a href=&quot;https://bitbucket.org/stephenmcd/mezzanine&quot;&gt;Bitbucket&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Code contributions from &lt;a href=&quot;http://mezzanine.jupo.org/docs/colophon.html#authors&quot;&gt;10 developers&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;A &lt;a href=&quot;http://groups.google.com/group/mezzanine-users/topics&quot;&gt;mailing list&lt;/a&gt; with over 60 members and over 300 messages&lt;/li&gt;
  &lt;li&gt;Over 4,000 downloads from the &lt;a href=&quot;http://pypi.python.org/pypi&quot;&gt;Python Package Index&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Almost 9,000 visitors to the &lt;a href=&quot;http://mezzanine.jupo.org/&quot;&gt;website&lt;/a&gt; with over a 50% return rate&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;team-leadership&quot;&gt;Team Leadership&lt;/h4&gt;

&lt;p&gt;Shortly prior to 2010 I moved into the role of development team lead and 2010
provided me with a wealth of new experience in this regard. Being responsible
for a development team producing rock solid work has always been an aspiration
of mine and it was incredibly fulfilling to have that come to fruition. It&amp;rsquo;s
been a fairly painless experience due to working with some of the best
developers I&amp;rsquo;ve met in over a decade, and it&amp;rsquo;s been nothing short of amazing.&lt;/p&gt;

&lt;h4 id=&quot;tooling&quot;&gt;Tooling&lt;/h4&gt;

&lt;p&gt;I added a ton of new software tools to my arsenal, being largely responsible
for building out our development and deployment processes. I spent a lot of
time getting up to speed with some amazing software such as
&lt;a href=&quot;http://south.aeracode.org/&quot;&gt;South&lt;/a&gt;, &lt;a href=&quot;http://fabfile.org/&quot;&gt;Fabric&lt;/a&gt;,
&lt;a href=&quot;http://nginx.org/en/&quot;&gt;NGINX&lt;/a&gt; and &lt;a href=&quot;http://gunicorn.org/&quot;&gt;gunicorn&lt;/a&gt;. I&amp;rsquo;ve
recently been putting together a development and deployment guide that covers
combining all of these and other parts of our stack which I&amp;rsquo;ll also publish in
the near future, so stay tuned for that.&lt;/p&gt;

&lt;h4 id=&quot;django-dash&quot;&gt;Django Dash&lt;/h4&gt;

&lt;p&gt;This was the first year I decided to put my Django skills to the test by
entering &lt;a href=&quot;http://djangodash.com/&quot;&gt;Django Dash&lt;/a&gt; - the 48 hour hackathon where
teams of 3 build a Django project from scratch. Teaming up with &lt;a href=&quot;http://www.joshdeblank.com/&quot;&gt;Josh de
Blank&lt;/a&gt; and &lt;a href=&quot;http://ajfisher.me/&quot;&gt;Andrew Fisher&lt;/a&gt;,
the final result saw us launch &lt;a href=&quot;http://ratemyflight.org&quot;&gt;Rate My Flight&lt;/a&gt; which
earned us 8th place out of 40 teams internationally. It was a ton of fun
getting to interact with some of the wider Django community and I even picked
up a few new Django tricks along the way.&lt;/p&gt;

&lt;p&gt;So that was 2010 - what a blast! I can&amp;rsquo;t imagine what 2011 will bring if it
involves as much change and initiative as the previous year has. Bring it on!&lt;/p&gt;
</content>
   
   <category term="python"></category>
   
   <category term="django"></category>
   
   <category term="open source"></category>
   
   <category term="mezzanine"></category>
   
   <category term="django dash"></category>
   
 </entry>
 
 <entry>
   <title>On Modern Web Development</title>
   <link href="http://blog.jupo.org/2010/09/28/on-modern-web-development/"/>
   <updated>2010-09-28T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2010/09/28/on-modern-web-development</id>
   <content type="html">&lt;p&gt;Web development technology has come an incredibly long way over the last
decade. Unfortunately in local markets like Australia where I reside, the tech
sector seems to languish years behind the United States and Europe. We saw
this happen with broadband adoption at the turn of the century where most
Australians were still on dial-up for years after the rest of the world
enjoyed widely available cable and ADSL.&lt;/p&gt;

&lt;p&gt;Fast forward to 2010; and the same situation has occurred with web
development. Anyone who follows the startup scene focused around San Francisco
and NYC will be familiar with the most popular development technologies
available - &lt;a href=&quot;http://www.ruby-lang.org/en/&quot;&gt;Ruby&lt;/a&gt;,
&lt;a href=&quot;http://python.org/&quot;&gt;Python&lt;/a&gt;, &lt;a href=&quot;http://www.scala-lang.org/&quot;&gt;Scala&lt;/a&gt; and many
more. Contrast this to the Australian space where the majority of development
houses have been sitting for many years on technologies such as ASP.NET and
PHP, which reached their peak in popularity over half a decade ago.&lt;/p&gt;

&lt;p&gt;Why are the majority of shops in markets like mine so complacent when it comes
to their technology stacks? Is it CTOs that haven&amp;rsquo;t written a line of code in
years and their fear of the unknown? Is it a lack of willingness to invest in
keeping their developers&amp;rsquo; skill-sets up to date and marketable? On these
things I can only speculate. What I can speak about knowledgeably however are
some of the reasons these latest technologies far outshine their predecessors,
and if only a single technology manager reads this post and decides to act on
it then it was well worth the time spent writing it.&lt;/p&gt;

&lt;p&gt;We chose &lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt; several years ago so that&amp;rsquo;s
what I&amp;rsquo;ll be making reference to in the following comparisons, however you
could just as easily swap it out with &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Ruby on Rails&lt;/a&gt;
or any other modern platform and the points would be more or less equivalent.&lt;/p&gt;

&lt;table class=&quot;compare zebra-striped&quot; cellpadding=&quot;8&quot; cellspacing=&quot;0&quot; align=&quot;center&quot;&gt;&lt;tr class=&quot;row1 top&quot;&gt;&lt;th&gt;
&lt;/th&gt;&lt;th&gt;ASP.NET&lt;/th&gt;&lt;th&gt;PHP&lt;/th&gt;&lt;th&gt;Java&lt;/th&gt;&lt;th&gt;Django&lt;/th&gt;&lt;/tr&gt;&lt;tr class=&quot;row2&quot;&gt;&lt;th&gt;Efficiency&lt;/th&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;row1&quot;&gt;&lt;th&gt;Security&lt;/th&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;row2&quot;&gt;&lt;th&gt;Freedom&lt;/th&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;row1&quot;&gt;&lt;th&gt;Developers&lt;/th&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;/tr&gt;&lt;tr class=&quot;row2&quot;&gt;&lt;th&gt;Mature Applications&lt;/th&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;td class=&quot;y&quot;&gt;✔&lt;/td&gt;&lt;td class=&quot;n&quot;&gt;✘&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;

&lt;h4 id=&quot;efficiency&quot;&gt;Efficiency&lt;/h4&gt;

&lt;p&gt;I recently came across this fantastic quote:&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;&amp;ldquo;IDEs: a form of automation needed when the environment in question erects
artificial barriers.&amp;rdquo;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Have you ever tried writing C# or Java in a plain text editor? It is an
exercise in futility as these languages sport incredibly verbose syntax and
deeply nested libraries which require specialised tools simply to write code.
What about the developer that needs to work with all of these different
technologies each day, can they be expected to be experts in several different
IDEs and switch between them freely? If only these different programming
environments could be used from the same editor - what a joy it would be as a
practicing polyglot!&lt;/p&gt;

&lt;p&gt;On the plus side languages like C# and Java are relatively clean and
consistant when compared to abominations such as PHP, which truly is a
disorganized mess - functions named using both verb_noun and noun_verb, lots
of similar functions with no apparent naming convention (Eg: &lt;code&gt;sort()&lt;/code&gt;,
&lt;code&gt;arsort()&lt;/code&gt;, &lt;code&gt;asort()&lt;/code&gt;, &lt;code&gt;ksort()&lt;/code&gt;, &lt;code&gt;natsort()&lt;/code&gt;, &lt;code&gt;natcasesort()&lt;/code&gt;, &lt;code&gt;rsort()&lt;/code&gt;,
&lt;code&gt;usort()&lt;/code&gt;, &lt;code&gt;array_multisort()&lt;/code&gt;, &lt;code&gt;uksort()&lt;/code&gt;) and a weak type system that can
lead to bugs which are difficult to discover.&lt;/p&gt;

&lt;p&gt;These languages stand in great contrast to modern dynamic languages such as
Python and Ruby. Ask any Python or Ruby developer which IDE they use and the
majority of them will tell you they don&amp;rsquo;t need one. These languages are terse,
use flat heirarchies in their libraries and are incredibly expressive.&lt;/p&gt;

&lt;h4 id=&quot;security&quot;&gt;Security&lt;/h4&gt;

&lt;p&gt;Aside from the recent &lt;a href=&quot;http://securitytracker.com/alerts/2010/Sep/1024459.html&quot;&gt;Padding Oracle
Exploit&lt;/a&gt;, ASP.NET has
remained fairly secure over the years. Unfortunately the other parts of the
stack, IIS and SQL Server, that it&amp;rsquo;s exclusively tied to have been the
punching bag of the network security world throughout the last decade with
viruses such as &lt;a href=&quot;http://en.wikipedia.org/wiki/Code_Red_(computer_worm)&quot;&gt;Code
Red,&lt;/a&gt; &lt;a href=&quot;http://en.wikipedia.org/wiki/SQL_Slammer&quot;&gt;SQL
Slammer&lt;/a&gt; and more, leaving countless
websites either defaced or knocked entirely offline. With a track record like
this it truly is a wonder how anyone would knowingly choose to build public
facing Internet services based on a Windows stack.&lt;/p&gt;

&lt;p&gt;Inversely we have PHP that while typically deployed on a LAMP stack built with
security in mind, the language itself makes writing secure applications an
extemely disciplined task. One need look no further than the &lt;a href=&quot;http://secunia.com/advisories/product/6745/?task=advisories&quot;&gt;ongoing range of
security issues&lt;/a&gt;
that have plagued applications such as Wordpress over the years: SQL
injection, cross site scripting (XSS), remote code execution - it&amp;rsquo;s like an
all-you-can-eat smorgasbord of web application exploits.&lt;/p&gt;

&lt;p&gt;Django in contrast runs on top of a secure LAMP stack and is designed from the
ground up with security in mind. It&amp;rsquo;s protected by default against SQL
injection, XSS and cross site request forgeries. A developer would actually
have to make a concerted effort to create an exploitable Django application.
Also like many open source projects a security issue in Django isn&amp;rsquo;t dealt
with because a corporation deems it to be the most cost effective decision. In
the very few and far between occasions when security issues have inevitably
been discovered, turn-around time for resolving these has been within a 24
hour period - not weeks or even months as is often the case with corporate
vendors that lack the agility and motivation to act responsibly.&lt;/p&gt;

&lt;h4 id=&quot;freedom&quot;&gt;Freedom&lt;/h4&gt;

&lt;p&gt;A common misconception about open source software is that it lacks the
reliability of support that comes with choosing a commercial vendor. This is a
short sighted view now plaguing many businesses. When Microsoft introduced
.NET it made the skill-sets of thousands of VB developers redundant. What
happens when Microsoft announces that .NET is to be deprecated in their next
technical adventure? The problem here is that a public company with an
obligation to generate as much profit as it can controls the technology path
of billions of dollars of software. Sometimes it&amp;rsquo;s in their best financial
interest to create fantastic technology, and sometimes it&amp;rsquo;s in their best
financial interest to tear it all down again.&lt;/p&gt;

&lt;p&gt;There is then the issue of acquisition. Companies like Microsoft and Oracle
have a long and successful history of acquiring their competitors simply to
discontinue their competing technology - let&amp;rsquo;s hope the vendor you&amp;rsquo;re in bed
with isn&amp;rsquo;t &lt;em&gt;too&lt;/em&gt; good.&lt;/p&gt;

&lt;p&gt;Even Java, which for all intents and purposes is an open source technology has
recently shown that it isn&amp;rsquo;t immune to the flaw of corporate ownership with
&lt;a href=&quot;http://news.cnet.com/8301-30684_3-20013546-265.html&quot;&gt;Oracle suing Google&lt;/a&gt;
over its use on Android phones. Will Android developers find that their time
and effort invested in this platform will all be for naught?&lt;/p&gt;

&lt;p&gt;Python and Django are both licensed under &lt;a href=&quot;http://en.wikipedia.org/wiki/Permissive_free_software_license&quot;&gt;Permissive Free Software
Licenses&lt;/a&gt;,
which allow anyone to go ahead and do whatever the hell they like with them.
They are owned by the &lt;a href=&quot;http://www.python.org/psf/&quot;&gt;Python Software Foundation&lt;/a&gt;
and the &lt;a href=&quot;http://www.djangoproject.com/foundation/&quot;&gt;Django Software Foundation&lt;/a&gt;
respectively. These are non-profit bodies that for the most part exist to
enforce the IP rights of each technology. These technologies are community
driven with one goal in mind: to create best of breed technology. There is no
financial motivation here and so we thus find ultimate reliability with this
software stack being impervious to the risks described above - it cannot be
made redundant by any financially driven corporate strategy as the licensing
and foundations have been specifically put in place to prevent this from being
possible. An interesting corollary to this is that these technologies go
relatively unheard of without the backing of large corporations promoting
them. Next time you&amp;rsquo;re the target of a technology sales pitch consider the
high likelihood that you&amp;rsquo;re not looking at the best technology in its given
application domain - best of breed doesn&amp;rsquo;t need to be sold at all, it sells
itself.&lt;/p&gt;

&lt;h4 id=&quot;developers&quot;&gt;Developers&lt;/h4&gt;

&lt;p&gt;ASP.NET, Java and PHP developers outnumber Python and Ruby developers by the
hundreds if not thousands. This is a great selling point to technical decision
makers - the ability to quickly and easily hire developers when the need
arises is critical. But what of the quality of these developers? An
interesting question to pose is why a developer who specializes in .NET or PHP
chose their particular technology. Answer: because that&amp;rsquo;s what everyone else
uses. You certainly won&amp;rsquo;t find a developer who has gone out and thoroughly
investigated a broad range of different languages coming back and choosing
.NET over everything else. Those who have done so have chosen their languages
on its technical merits. These are the passionate developers with a love for
their craft, not those who are merely in it for a paycheck and whose
workmanship will reflect as much. Paul Graham referred to this in 2004 in his
incredibly insightful essay &lt;a href=&quot;http://www.paulgraham.com/pypar.html&quot;&gt;The Python
Paradox&lt;/a&gt;.&lt;/p&gt;

&lt;h4 id=&quot;mature-applications&quot;&gt;Mature Applications&lt;/h4&gt;

&lt;p&gt;The final point I&amp;rsquo;d like to cover is the maturity of applications developed on
top of any given platform. This is where modern languages fall short as by
definition they simply haven&amp;rsquo;t gained enough penetration for mature
applications to have been developed yet. This is where there is opportunity.
This is where the next generation of mature web applications will be built
using web application frameworks like &lt;a href=&quot;http://www.djangoproject.com/&quot;&gt;Django&lt;/a&gt;
and &lt;a href=&quot;http://rubyonrails.org/&quot;&gt;Rails&lt;/a&gt; that are designed from the ground up for
rapid customization over long software life-cycles while maintaining the
original design integrity of your application - something only a very
disciplined developer can maintain with something like PHP, which in almost
all cases will eventually end up as spaghetti code.&lt;/p&gt;

&lt;p&gt;Applications like &lt;a href=&quot;http://wordpress.org/&quot;&gt;Wordpress&lt;/a&gt; and
&lt;a href=&quot;http://www.magentocommerce.com/&quot;&gt;Magento&lt;/a&gt; may work fine off the shelf for an
end user, but what type of path have you created for your customer by
implementing technology that gets closer and closer to its end of life the
more it&amp;rsquo;s customized?&lt;/p&gt;

&lt;p&gt;In conclusion - developers and CEOs, challenge your technical decision makers
to overcome their complacency and invest in technology of the future. Push
your employer, your peers and most importantly yourself forward. Invest your
time in efficient, secure and unencumbered technology.&lt;/p&gt;
</content>
   
   <category term="asp.net"></category>
   
   <category term="australia"></category>
   
   <category term="django"></category>
   
   <category term="java"></category>
   
   <category term="open source"></category>
   
   <category term="php"></category>
   
   <category term="security"></category>
   
 </entry>
 
 <entry>
   <title>Plugging In Cartridge</title>
   <link href="http://blog.jupo.org/2010/09/21/plugging-in-cartridge/"/>
   <updated>2010-09-21T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2010/09/21/plugging-in-cartridge</id>
   <content type="html">&lt;p&gt;I&amp;rsquo;m happy to announce the first release of
&lt;a href=&quot;http://cartridge.jupo.org/&quot;&gt;Cartridge&lt;/a&gt; - a
&lt;a href=&quot;http://djangoproject.com/&quot;&gt;Django&lt;/a&gt; shopping cart application I started
working on back in 2009. The development path that Cartridge has taken has
been a strange one. I stopped working on it throughout 2010 in order to get
the ball rolling with a project called &lt;a href=&quot;http://mezzanine.jupo.org/&quot;&gt;Mezzanine&lt;/a&gt;
that I&amp;rsquo;ve &lt;a href=&quot;http://blog.jupo.org/2010/06/11
/mezzanine-just-another-django-cms/&quot;&gt;blogged about previously&lt;/a&gt;. Many parts of Mezzanine actually
originated in Cartridge and once development of Mezzanine was well under way
it made the most sense for continued development of Cartridge to occur as a
plug-in for Mezzanine which has now come to fruition.&lt;/p&gt;

&lt;p&gt;Beyond creating a kick-ass shopping cart application for Django, the main
design goal I originally had for Cartridge was to address some of the mistakes
I felt existed in other offerings available both within and outside of the
Django community. These areas I&amp;rsquo;ve aimed to address are as follows:&lt;/p&gt;

&lt;h4 id=&quot;performance&quot;&gt;Performance&lt;/h4&gt;

&lt;p&gt;The &lt;a href=&quot;http://docs.djangoproject.com/en/dev/topics/db/models/&quot;&gt;Django ORM&lt;/a&gt; is a
double-edged sword that while saving you a lot of time can also do a lot of
damage when used without regard to the underlying SQL queries being performed.
I&amp;rsquo;ve come across examples in templates where queries are being performed
inside loops nested inside more loops resulting in abysmal performance. Fixing
these problems wasn&amp;rsquo;t simply a case of refactoring template logic as these
issues were core to the design of how prices and variations were modelled. The
only solution was to throw a ton of fine-grained
&lt;a href=&quot;http://memcached.org&quot;&gt;memcached&lt;/a&gt; usage at the problem, but this should be a
optional approach to scalability - not a minimum requirement for keeping the
site from falling offline. Cartridge has been designed with performance in
mind from the start with a range of denormalized data structures providing
O(n) performance as the number of products and categories grow.&lt;/p&gt;

&lt;h4 id=&quot;intuitive-interfaces&quot;&gt;Intuitive Interfaces&lt;/h4&gt;

&lt;p&gt;An end user should be able to use an admin system for the first time and
discover an interface that is logical and intuitive. Having to go through a
handful of screens to set up a single product requires users to mentally train
themselves to remember a work-flow that isn&amp;rsquo;t entirely natural. If the former
is achievable then the latter is definitely unacceptable. The number of forms
and fields in an interface can be described as a conversation between the
system and its user, and this conversation should be as quick and painless as
possible. Cartridge provides single interfaces for creating products,
categories, discounts and other types of shop data, with only applicable
fields making up these forms.&lt;/p&gt;

&lt;h4 id=&quot;bloated-code&quot;&gt;Bloated Code&lt;/h4&gt;

&lt;p&gt;Having a system that implements every single feature that might ever be
required in a shopping cart implementation certainly makes for an easy sell,
however as this list grows these features become more obscure and less likely
to be required in an average implementation. This can result in a convoluted
code base that is very difficult to apply customizations to - an inevitable
requirement given the unique nature of shopping cart implementations.
Cartridge addresses this issue by implementing only the features typically
required by all shopping cart implementations, leaving custom features up to
the developer who will find that due to a tight feature list that the code
base and data models remain very clean and easy to work with.&lt;/p&gt;

&lt;p&gt;Aside from these features that distinguish it from other shopping cart
applications, Cartridge comes with a standard set of features that you&amp;rsquo;d
expect to find:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Hierarchical categories (via Mezzanine pages)&lt;/li&gt;
  &lt;li&gt;Easily configurable product options (colours, sizes, etc)&lt;/li&gt;
  &lt;li&gt;Hooks for shipping calculations and payment gateway&lt;/li&gt;
  &lt;li&gt;Sale pricing and discount codes&lt;/li&gt;
  &lt;li&gt;Stock management&lt;/li&gt;
  &lt;li&gt;Product popularity&lt;/li&gt;
  &lt;li&gt;Thumbnail generation&lt;/li&gt;
  &lt;li&gt;Built-in test suite&lt;/li&gt;
  &lt;li&gt;Separation of presentation (no embedded markup)&lt;/li&gt;
  &lt;li&gt;Smart categories (by price range, colour, etc)&lt;/li&gt;
  &lt;li&gt;Configurable number of checkout steps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href=&quot;http://mezzanine.jupo.org/&quot;&gt;live demo of Mezzanine&lt;/a&gt; now includes
Cartridge so go ahead and try it out! If you&amp;rsquo;re interested in hacking on
Cartridge then the source code is freely available under a &lt;a href=&quot;http://www.linfo.org/bsdlicense.html&quot;&gt;BSD
license&lt;/a&gt; at both
&lt;a href=&quot;http://github.com/stephenmcd/cartridge&quot;&gt;github&lt;/a&gt; and
&lt;a href=&quot;http://bitbucket.org/stephenmcd/cartridge&quot;&gt;bitbucket&lt;/a&gt;.&lt;/p&gt;
</content>
   
   <category term="django"></category>
   
   <category term="python"></category>
   
   <category term="mezzanine"></category>
   
   <category term="cartridge"></category>
   
   <category term="ecommerce"></category>
   
   <category term="n+1 queries"></category>
   
 </entry>
 
 <entry>
   <title>An Open Source Brief</title>
   <link href="http://blog.jupo.org/2010/07/22/an-open-source-brief/"/>
   <updated>2010-07-22T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2010/07/22/an-open-source-brief</id>
   <content type="html">&lt;p&gt;You&amp;rsquo;d be forgiven for reading the title of this post and thinking it&amp;rsquo;s about a
crazy approach to project briefing that somehow mimics open source development
- as interesting as that sounds, it isn&amp;rsquo;t the case and my motives are much
more simplistic and sinister. What I&amp;rsquo;d like to do here is put a brief together
for an open source project called &lt;a href=&quot;http://mezzanine.jupo.org&quot;&gt;Mezzanine&lt;/a&gt;. This
brief isn&amp;rsquo;t specifically geared towards programmers so if you think this isn&amp;rsquo;t
for you then please continue reading and let me prove you wrong.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What is Mezzanine?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Anyone who &lt;a href=&quot;http://twitter.com/stephen_mcd&quot;&gt;follows my updates&lt;/a&gt; will know it&amp;rsquo;s
an open source CMS framework I&amp;rsquo;ve been working on over the last couple of
months. It now has a &lt;a href=&quot;http://mezzanine.jupo.org/docs/overview.html#features&quot;&gt;concrete feature
set&lt;/a&gt; having come
remarkably far in a very short amount of time. This might lead you to believe
an entire team of people have been working on it but in fact it&amp;rsquo;s mostly been
myself alone - it&amp;rsquo;s thanks to the incredibly rapid development that using
&lt;a href=&quot;http://djangoproject.com&quot;&gt;Django&lt;/a&gt; brings you that so much has been done so
quickly. For those readers who aren&amp;rsquo;t familiar with it please go ahead and
check out the &lt;a href=&quot;http://mezzanine.jupo.org/docs/overview.html&quot;&gt;overview&lt;/a&gt; in the
&lt;a href=&quot;http://mezzanine.jupo.org/docs/&quot;&gt;documentation&lt;/a&gt;, play around with the &lt;a href=&quot;http://mezzanine.jupo.org/blog/&quot;&gt;live
demo&lt;/a&gt; and have a read of my &lt;a href=&quot;http://blog.jupo.org/2010/06/11/mezzanine-just-another-django-cms/&quot;&gt;previous blog
post&lt;/a&gt; that
talks about why I started Mezzanine and what I hope to achieve.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Why would I want to help?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Perhaps you&amp;rsquo;re an end user of a poorly designed CMS and you&amp;rsquo;ve often wished
you could do something about it. Perhaps you&amp;rsquo;re a developer that&amp;rsquo;s had the
unfortunate experience of trying to extend a seemingly user-friendly CMS
that&amp;rsquo;s built using archaic technology, and wished you could be working with
something that&amp;rsquo;s much more cutting edge and elegantly designed. Perhaps you&amp;rsquo;re
someone who &amp;ldquo;gets&amp;rdquo; open source at a deeper level but always felt as someone
who isn&amp;rsquo;t a coder that you couldn&amp;rsquo;t contribute. Perhaps you&amp;rsquo;re in business
development and you&amp;rsquo;re tired of trying to sell &amp;ldquo;enterprise&amp;rdquo; crap with
completely absurd price tags. If you have anything to do with web development
then there&amp;rsquo;s something in this for you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What do I get out of it?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;As much as you put in of course. The experience of contributing to open source
software on paper can often be a competitive advantage over other candidates
for a job interview or even development contracts for your business. There&amp;rsquo;s
also the chance of notoriety - imagine being responsible for the user
interface or branding of the next &lt;a href=&quot;http://wordpress.org/&quot;&gt;Wordpress&lt;/a&gt;. Imagine
your staff are core contributors to one of the web&amp;rsquo;s leading development
tools. Again the success of the project will only match its contributions so
it&amp;rsquo;s ultimately up to you.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What can I do to help?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A common misconception about open source software is that it&amp;rsquo;s something that
only coders can participate in. Unfortunately the result of this is that the
majority of open source software ends up being only contributed to by coders
and is incredibly lacking in a variety of areas. I&amp;rsquo;m talking about visual
branding, copy-writing, UI development - all these areas that fall outside of
coding but are equally crucial in successfully shipping a professional piece
of software. Mezzanine has now reached a point where it can only continue to
move forward at a consistent pace by bringing in these skills that I don&amp;rsquo;t
specialise in. So without further ado, here are the specific roles I think
need filling and what the focus of each would be.&lt;/p&gt;

&lt;h4 id=&quot;graphic-designer&quot;&gt;Graphic Designer&lt;/h4&gt;

&lt;p&gt;The entire project is desperately in need of some visual love. At the simplest
level it could really use some basic branding such as a logo and &amp;ldquo;powered by&amp;rdquo;
buttons. Then there&amp;rsquo;s the &lt;a href=&quot;http://mezzanine.jupo.org&quot;&gt;Mezzanine website&lt;/a&gt;,
&lt;a href=&quot;http://mezzanine.jupo.org/docs/&quot;&gt;documentation&lt;/a&gt; and &lt;a href=&quot;http://mezzanine.jupo.org/blog/&quot;&gt;default
site&lt;/a&gt; that are all currently quite spartan
looking.&lt;/p&gt;

&lt;h4 id=&quot;interface-developer&quot;&gt;Interface Developer&lt;/h4&gt;

&lt;p&gt;So far the template mark-up for the default site is as minimal as can be.
While this is intentional to some extent in order to best serve those that
would come along and customise it for their projects, I think this idea could
be improved upon with a greater level of modularity. I&amp;rsquo;m also keen to
introduce a CSS framework like &lt;a href=&quot;http://blueprintcss.org/&quot;&gt;Blueprint&lt;/a&gt; into the
default site. Once that&amp;rsquo;s all in order then I&amp;rsquo;d like to address what theming
would look like. Is this simply a matter of packaging up copies of the
templates as separate themes? A great milestone for Mezzanine would be to have
a handful of built-in themes created, as well as documenting the process for
creating your own.&lt;/p&gt;

&lt;h4 id=&quot;ux-designer&quot;&gt;UX Designer&lt;/h4&gt;

&lt;p&gt;I&amp;rsquo;ve introduced a handful of user interface elements into Mezzanine that could
definitely use some ironing out from a usability and accessibility
perspective. The main contender is the navigation tree in the admin that&amp;rsquo;s
used for managing the hierarchy of the entire site as well as being the entry
point for accessing most of the content management. There&amp;rsquo;s the dashboard
interface for the admin area which is in a very early stage. There&amp;rsquo;s the
overall layout for both the &lt;a href=&quot;http://mezzanine.jupo.org&quot;&gt;project&amp;rsquo;s own site&lt;/a&gt;
and the &lt;a href=&quot;http://mezzanine.jupo.org/blog/&quot;&gt;default site&lt;/a&gt;. Lastly and of great
importance, there&amp;rsquo;s the &lt;a href=&quot;http://mezzanine.jupo.org/docs/inline-editing.html&quot;&gt;entire system for in-line
editing&lt;/a&gt; which is featured
in the default site - making this feature as user-friendly as possible is
critical.&lt;/p&gt;

&lt;h4 id=&quot;technical-writer&quot;&gt;Technical Writer&lt;/h4&gt;

&lt;p&gt;Mezzanine currently has a good start on
&lt;a href=&quot;http://mezzanine.jupo.org/docs/&quot;&gt;documentation&lt;/a&gt; but at the moment it&amp;rsquo;s mostly
focused on developers. I&amp;rsquo;d eventually like to have a lot more material aimed
at both end users of Mezzanine as well as marketing material geared towards
business decision makers.&lt;/p&gt;

&lt;h4 id=&quot;product-evangelist&quot;&gt;Product Evangelist&lt;/h4&gt;

&lt;p&gt;This is probably the easiest task of all. We simply need the word to be
spread. Learn about Mezzanine and use whatever medium you like to let the
world know how great it is, be it &lt;a href=&quot;http://twitter.com&quot;&gt;Twitter&lt;/a&gt;, mailing lists
or blog posts.&lt;/p&gt;

&lt;p&gt;This list isn&amp;rsquo;t entirely complete and some of the tasks certainly overlap. If
you think you fit the bill or know anyone else who would get a kick out of
working on Mezzanine then there&amp;rsquo;s no time like the present to get started.
There aren&amp;rsquo;t any obligations with this so contributions of any size are
welcome. If you&amp;rsquo;d like to get involved but don&amp;rsquo;t know where to start just
&lt;a href=&quot;http://groups.google.com/group/mezzanine-
users&quot;&gt;post a message to the mailing list&lt;/a&gt; and let&amp;rsquo;s talk!&lt;/p&gt;
</content>
   
   <category term="python"></category>
   
   <category term="django"></category>
   
   <category term="open source"></category>
   
   <category term="mezzanine"></category>
   
 </entry>
 
 <entry>
   <title>Mezzanine: Just Another Django CMS?</title>
   <link href="http://blog.jupo.org/2010/06/11/mezzanine-just-another-django-cms/"/>
   <updated>2010-06-11T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2010/06/11/mezzanine-just-another-django-cms</id>
   <content type="html">&lt;p&gt;Ask any developer that has put together a &lt;a href=&quot;http://docs.djangoproject.com/en/dev/intro/tutorial02/&quot;&gt;Django admin
interface&lt;/a&gt; and they&amp;rsquo;ll
tell you that it&amp;rsquo;s an amazing piece of technology that allows you to whip up
an admin system for your web application in a number of minutes rather than
days. Unfortunately this power can be a double-edged sword as without enough
time and thought up front, a developer can easily end up creating an interface
that&amp;rsquo;s almost impossible for its intended audience to work with.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve seen this issue manifest itself in two ways. The first is the bare-bones
case where the options available for creating the admin interface are simply
left out, resulting in a spartan admin that does little to guide the user on
how to use it. The second could be described as the opposite end of the scale
where there is actually far too much going on in the admin interface at the
cost of simplicity and intuitiveness. This can often be the result of gluing
together a range of resuable apps that each have their own approach to
providing admin interfaces with the end result looking like a &lt;a href=&quot;http://en.wikipedia.org/wiki/Rube_Goldberg_machine&quot;&gt;Rube Goldberg
machine&lt;/a&gt;. These scenarios
are bad for customers and bad for Django. When end-users think of Django, they
think of the admin interface - that&amp;rsquo;s what Django is to them so it&amp;rsquo;s critical
to get this component right.&lt;/p&gt;

&lt;p&gt;I recently had &lt;a href=&quot;http://wordpress.org/&quot;&gt;Wordpress&lt;/a&gt; suggested to me as a
solution to this problem and it&amp;rsquo;s easy to see why. The Wordpress install base
alone speaks huge volumes while its admin interface is incredibly user-
friendly. It also benefits from not requiring technical expertise to get a
simple website with pages and a blog up and running. However I felt this idea
overlooked the underlying issue of poorly configured Django admin interfaces,
while taking a step backwards by investing in &lt;a href=&quot;http://php.net/&quot;&gt;PHP&lt;/a&gt; - a
relatively inelegant technology with a very limited application scope.&lt;/p&gt;

&lt;p&gt;My solution to the problem was to tackle the underlying issue more directly by
creating a Django application which I&amp;rsquo;ve called
&lt;a href=&quot;http://github.com/stephenmcd/mezzanine&quot;&gt;Mezzanine&lt;/a&gt;. The approach I&amp;rsquo;ve taken
is to have functionality on par with Wordpress that can be used as a starting
point when developing basic websites. This meant putting a lot of thought into
the admin options used, as well as including a custom version of the &lt;a href=&quot;http://code.google.com/p/django-grappelli/&quot;&gt;django-
grappelli admin skin&lt;/a&gt; to come up
with a modern looking and intuitive admin interface. The other key approach
I&amp;rsquo;ve taken is to include as much functionality as possible directly in the
application itself for the sake of a consistent and lightweight code base that
can easily be hacked on. It&amp;rsquo;s worth noting that this is in total contrast to
other Django website applications such as
&lt;a href=&quot;http://github.com/montylounge/django-mingus&quot;&gt;Mingus&lt;/a&gt; and
&lt;a href=&quot;http://pinaxproject.com/&quot;&gt;Pinax&lt;/a&gt;, and that this difference really comes down
to a question of scope. Pinax for example is capable of a much wider range of
functionality than what I&amp;rsquo;m aiming for with Mezzanine out of the box which is
to cater for basic websites with the following features:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Hierarchical page navigation&lt;/li&gt;
  &lt;li&gt;Save as draft and preview on site&lt;/li&gt;
  &lt;li&gt;Drag-n-drop page ordering&lt;/li&gt;
  &lt;li&gt;WYSIWYG editing&lt;/li&gt;
  &lt;li&gt;SEO friendly URLs and meta data&lt;/li&gt;
  &lt;li&gt;Mobile device detection and templates&lt;/li&gt;
  &lt;li&gt;Blogging engine&lt;/li&gt;
  &lt;li&gt;Tagging&lt;/li&gt;
  &lt;li&gt;Custom templates per page or blog post&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://gravatar.com/&quot;&gt;Gravatar&lt;/a&gt; integration&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.google.com/analytics/&quot;&gt;Google Analytics&lt;/a&gt; integration&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://twitter.com&quot;&gt;Twitter&lt;/a&gt; feed integration&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://bit.ly/&quot;&gt;bit.ly&lt;/a&gt; integration&lt;/li&gt;
  &lt;li&gt;Sharing via Facebook or Twitter&lt;/li&gt;
  &lt;li&gt;Built-in threaded comments, or:&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://disqus.com/&quot;&gt;Disqus&lt;/a&gt; integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em class=&quot;center&quot;&gt;&lt;img src=&quot;/static/img/mezzanine-original-dashboard.png&quot; /&gt;&lt;br /&gt;The Mezzanine admin dashboard&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve open sourced the initial version of Mezzanine with a BSD license on both
&lt;a href=&quot;http://github.com/stephenmcd/mezzanine&quot;&gt;github&lt;/a&gt; and
&lt;a href=&quot;http://bitbucket.org/stephenmcd/mezzanine&quot;&gt;bitbucket&lt;/a&gt; - it still has a long
way to go so jump right in and fork away.&lt;/p&gt;
</content>
   
   <category term="django"></category>
   
   <category term="python"></category>
   
   <category term="php"></category>
   
   <category term="cms"></category>
   
   <category term="mezzanine"></category>
   
 </entry>
 
 <entry>
   <title>Announcing gunicorn-console</title>
   <link href="http://blog.jupo.org/2010/05/29/announcing-gunicorn-console/"/>
   <updated>2010-05-29T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2010/05/29/announcing-gunicorn-console</id>
   <content type="html">&lt;p&gt;Like a lot of Django shops our software stack consists of two layers up front:
a public facing web/proxy server and an application server sitting behind it.
For a long time we&amp;rsquo;ve enjoyed success using &lt;a href=&quot;http://nginx.org/en/&quot;&gt;nginx&lt;/a&gt; and
&lt;a href=&quot;http://httpd.apache.org/&quot;&gt;Apache&lt;/a&gt; to fill these roles respectively, but as an
application server the 800 pound gorilla that is Apache can really be
overkill, which over time we&amp;rsquo;ve found can have quite a cost around lack of
granular control. So we recently decided to try out the up and coming
&lt;a href=&quot;http://gunicorn.org/&quot;&gt;gunicorn&lt;/a&gt; which is currently gaining in popularity
throughout the Django community and so far it&amp;rsquo;s been very smooth.&lt;/p&gt;

&lt;p&gt;One of the interesting features it provides is the ability to handle various
kill signals which map to functions such as adding and removing worker
processes as well as reloading the master process, all on the fly without
dropping a single client connection. So after a brief honeymoon period I then
came up with the following list of questions that mightn&amp;rsquo;t be apparent when
serving a single application, but really come into play when serving dozens of
applications this way on a single server:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;How can we deal with the signals interface without knowledge of process IDs?&lt;/li&gt;
  &lt;li&gt;How can we gain visiblity around the ports being used?&lt;/li&gt;
  &lt;li&gt;How can we gain visiblity around the number of worker procesess being used?&lt;/li&gt;
  &lt;li&gt;How can we gain visiblity around the amount of memory being used per application?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these can be answered with a small amount of command-line-fu, however I
wanted this process to be ridiculously easy for our entire team. For quite
some time I&amp;rsquo;ve wanted to put together a console application using the &lt;a href=&quot;http://docs.python.org/howto/curses.html&quot;&gt;curses
library&lt;/a&gt; so a simple management
console for gunicorn seemed like the perfect opportunity to do so and as such,
&lt;a href=&quot;http://github.com/stephenmcd/gunicorn-console&quot;&gt;gunicorn-console&lt;/a&gt; was born.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://media.tumblr.com/tumblr_l35p9x2tmU1qa0qji.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;As pictured above, after firing up a few gunicorn instances with varying
parameters gunicorn-console gives you the following interface in all its 8bit
glory:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://media.tumblr.com/tumblr_l35pgbDlII1qa0qji.gif&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If you&amp;rsquo;re hosting multiple applications served up via gunicorn then gunicorn-
console should make managing them easier. I&amp;rsquo;ve released it with a BSD license
on both &lt;a href=&quot;http://github.com/stephenmcd/gunicorn-console&quot;&gt;github&lt;/a&gt; and
&lt;a href=&quot;http://bitbucket.org/stephenmcd/gunicorn-console&quot;&gt;bitbucket&lt;/a&gt; using the
amazing &lt;a href=&quot;http://hg-git.github.com/&quot;&gt;hg-git extension&lt;/a&gt;, so go ahead and make it
better!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update, May 30:&lt;/strong&gt; I ended this post with a request for others to contribute and after only a day someone already has. &lt;a href=&quot;http://adamv.com/&quot;&gt;Adam Vandenberg&lt;/a&gt; went ahead and forked the project with &lt;a href=&quot;http://github.com/adamv/gunicorn-console/commit/0f9bc2672f4cc0b1d560b353304d374d5c927120&quot;&gt;some patches&lt;/a&gt; to get it running on OSX, so a big thanks goes to him.&lt;/p&gt;
</content>
   
   <category term="django"></category>
   
   <category term="gunicorn"></category>
   
   <category term="python"></category>
   
   <category term="curses"></category>
   
 </entry>
 
 <entry>
   <title>Optional Django Apps</title>
   <link href="http://blog.jupo.org/2010/05/10/optional-django-apps/"/>
   <updated>2010-05-10T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2010/05/10/optional-django-apps</id>
   <content type="html">&lt;p&gt;A project of mine contains a number of third-party apps that are development
related and potentially not available on every machine the project will run
on. My general approach to dealing with these was to try and import the app in
my &lt;code&gt;settings&lt;/code&gt; module and if successful, add it to the &lt;code&gt;INSTALLED_APPS&lt;/code&gt;
setting. However as the number of these apps grew it became a wart within the
settings module so I put together this snippet for managing them.&lt;/p&gt;

&lt;p&gt;We first create a sequence of dictionaries, each containing information about
an installed app such as the module to try and import, an extra potential
condition for checking and then the sequences of names to add to
&lt;code&gt;INSTALLED_APPS&lt;/code&gt;, &lt;code&gt;MIDDLEWARE_CLASSES&lt;/code&gt; and &lt;code&gt;TEMPLATE_CONTEXT_PROCESSORS&lt;/code&gt;.
Let&amp;rsquo;s start with the settings for optionally including the apps &lt;a href=&quot;http://github.com/django-extensions/django-extensions&quot;&gt;django-
command-extensions&lt;/a&gt;,
&lt;a href=&quot;http://github.com/robhudson/django-debug-toolbar&quot;&gt;django-debug-toolbar&lt;/a&gt; and
&lt;a href=&quot;http://south.aeracode.org&quot;&gt;south&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Define any settings specific to each of the optional apps.&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;sys&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;USE_SOUTH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;and&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sys&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&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;s&quot;&gt;&amp;quot;test&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;DEBUG_TOOLBAR_CONFIG&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;&amp;quot;INTERCEPT_REDIRECTS&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Sequence for each optional app as a dict containing info about the app.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;OPTIONAL_APPS&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;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;import&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;django_extensions&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;apps&amp;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;s&quot;&gt;&amp;quot;django_extensions&amp;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;s&quot;&gt;&amp;quot;import&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;debug_toolbar&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;apps&amp;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;s&quot;&gt;&amp;quot;debug_toolbar&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,),&lt;/span&gt; 
        &lt;span class=&quot;s&quot;&gt;&amp;quot;middleware&amp;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;s&quot;&gt;&amp;quot;debug_toolbar.middleware.DebugToolbarMiddleware&amp;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;s&quot;&gt;&amp;quot;import&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;south&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;apps&amp;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;s&quot;&gt;&amp;quot;south&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,),&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;condition&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;USE_SOUTH&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;/div&gt;

&lt;p&gt;Next we simply iterate through the sequence of optional apps and set them up.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;c&quot;&gt;# Set up each optional app if available.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;app&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;OPTIONAL_APPS&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;app&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;condition&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&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;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;nb&quot;&gt;__import__&lt;/span&gt;&lt;span class=&quot;p&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;s&quot;&gt;&amp;quot;import&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;INSTALLED_APPS&lt;/span&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;apps&amp;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;n&quot;&gt;MIDDLEWARE_CLASSES&lt;/span&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;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;middleware&amp;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;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

</content>
   
   <category term="python"></category>
   
   <category term="django"></category>
   
 </entry>
 
 <entry>
   <title>Where is PEP 8 for Technical Documentation?</title>
   <link href="http://blog.jupo.org/2010/03/18/where-is-pep-8-for-technical-documentation/"/>
   <updated>2010-03-18T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2010/03/18/where-is-pep-8-for-technical-documentation</id>
   <content type="html">&lt;p&gt;Anyone who has programmed in Python for a considerable length of time will at
least have some passing familiarity with &lt;a href=&quot;http://www.python.org/dev/peps/pep-0008/&quot;&gt;PEP
8&lt;/a&gt; - the document that goes into an
incredible level of detail in dictating precisely how code should be written.
While its primary goal is to ensure that Python code is written in a
consistant fashion throughout the community, therefore making it as easy as
possible to read, it also provides one of many aspects that makes programming
in Python an incredibly efficient process - it negates the need for a lot of
decision making around any of the choices one might come across that are
already covered in PEP 8.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;ve recently been spending a lot of time writing technical documentation.
While it&amp;rsquo;s been interesting doing something different for a change, the
perfectionist in me is constantly frustated with finding myself using
inconsistant language across different sections when faced with the exact same
context, for example:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;contains / has / includes / provides&lt;/li&gt;
  &lt;li&gt;discussed / referred to / described / mentioned&lt;/li&gt;
  &lt;li&gt;above / earlier / previously&lt;/li&gt;
  &lt;li&gt;below / later / next&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Are these types of ambiguities in technical writing something that
professional editors typically deal with? What I&amp;rsquo;d love to see is something
like PEP 8 for technical documentation.&lt;/p&gt;
</content>
   
   <category term="python"></category>
   
   <category term="documentation"></category>
   
 </entry>
 
 <entry>
   <title>Hasta la Vista, Windows</title>
   <link href="http://blog.jupo.org/2010/02/13/hasta-la-vista-windows/"/>
   <updated>2010-02-13T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2010/02/13/hasta-la-vista-windows</id>
   <content type="html">&lt;p&gt;Earlier this week I had the pleasure of removing my final Windows install
after wiping my machine at work and installing Ubuntu on it. It was during the
late 90s that I first tried out Linux after getting my hands on a Redhat 6.1
CD from the cover of a magazine I&amp;rsquo;d bought. I didn&amp;rsquo;t keep it installed for
very long and after a few more tries over the years with Mandrake (now
Mandrivia) and Damn Small Linux, it wasn&amp;rsquo;t until 2005 when I installed
Slackware 10.2 as my primary operating system at home and really cut my teeth
on it in order to test how cross-platform my Python projects were. It was a
great experience learning about all the various sub-systems, compiling
software and libraries from source, embracing the command line and modifying
some of the internal scripts to get things working the way I wanted.&lt;/p&gt;

&lt;p&gt;Fast forward to 2010 and in my workplace the migration from a Microsoft
development shop to a Linux/Python shop after several years is finally
complete, paving the way for this latest install. I did experience a couple of
hiccups that hadn&amp;rsquo;t happened before. Firstly I have dual wide-screen monitors
at work and I rotate one of them 90 degrees in order to maximize the amount of
visible code on my screen. The display properties in Ubuntu only gave me the
ability to flip the display 180 degrees which seemed quite odd so I then tried
to manually rotate the display with the
&lt;a href=&quot;http://en.wikipedia.org/wiki/RandR&quot;&gt;xrandr&lt;/a&gt; command which reported my overall
virtual screen size as being too small for the rotation. I resolved this with
the update below to my x.org configuration to use a virtual screen size large
enough to handle the rotation while including the second monitor.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;css&quot;&gt;&lt;span class=&quot;nt&quot;&gt;Section&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Screen&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;Identifier&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Configured Screen Device&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;Device&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Configured Video Device&amp;quot;&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;SubSection&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Display&amp;quot;&lt;/span&gt;
        &lt;span class=&quot;nt&quot;&gt;Virtual&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;2880&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;1440&lt;/span&gt;
    &lt;span class=&quot;nt&quot;&gt;EndSubSection&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;EndSection&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;The second issue was more a lack of foresight on my part than a problem with
the new install itself. After a vanilla install of any modern operating system
you&amp;rsquo;ll undoubtedly be required to download a series of updates that have
occurred since the version you&amp;rsquo;ve installed was initially released. The
difference with most Linux distributions is that almost all of your software
is managed in this way from installing to updating, it all goes through the
same service known as a package manager - one of the many things with Linux
that once you&amp;rsquo;re used to using you won&amp;rsquo;t know how you ever worked without it.
So away I went with the initial round of updates which left the package
manager busy for several hours, during which time some issues arose with a
project that immediately required my attention. Unfortunately I needed to
install a handful of libraries to get up and running and with the package
manager busy I was left in a real bind. Fortunately I was able to use one of
our test servers remotely to resolve the issue but the lesson learnt here is
that for a new development system it&amp;rsquo;s best to leave the initial system update
until after your development environment is completely set up.&lt;/p&gt;
</content>
   
   <category term="linux"></category>
   
   <category term="x.org"></category>
   
 </entry>
 
 <entry>
   <title>Linear Traversal of Adjacency List Trees</title>
   <link href="http://blog.jupo.org/2010/01/26/linear-traversal-of-adjacency-list-trees/"/>
   <updated>2010-01-26T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2010/01/26/linear-traversal-of-adjacency-list-trees</id>
   <content type="html">&lt;p&gt;My current project has the common requirement of storing and rendering a
hierarchical tree of categories. This project is geared towards potentially
junior developers with the expectation of it being hacked at every time it&amp;rsquo;s
used - a set of scaffolding where simplicity isn&amp;rsquo;t just a loose goal but a
fundamental requirement.&lt;/p&gt;

&lt;p&gt;Two popular approaches to the hierarchical tree are the &lt;a href=&quot;http://articles.sitepoint.com/print/hierarchical-data-database&quot;&gt;Adjacency List (AL)
and Modified Preorder Tree Traversal (MPTT)
models&lt;/a&gt;. The
advantage of AL is that it only stores the exact data required for
representing the tree while MPTT stores extraneous data for assisting in
traversing the tree in an optimal fashion. The simplicity of the AL model
makes it much better suited to the requirements I mentioned, however the
problem with AL is the recursive nature in which you traverse it.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;show_branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# iterating the entire tree for each branch gives quadratic performance&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodes&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;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&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;node&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;show_branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;Worst case here is &lt;em&gt;O&lt;/em&gt;(n²) performance but thanks to &lt;a href=&quot;http://wiki.python.org/moin/DictionaryKeys&quot;&gt;Python&amp;rsquo;s lightning fast
hashtable implementation&lt;/a&gt; we can
create a copy of the tree as a dictionary of branches giving us &lt;em&gt;O&lt;/em&gt;(n) overall
performance when traversing the entire tree.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;c&quot;&gt;# copy the tree into a dict of branches&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;branches&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;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;nodes&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&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;p&quot;&gt;[]&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&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;append&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&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;show_branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;c&quot;&gt;# iterating only the nodes in the branch gives linear performance&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;parent&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot; &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&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;node&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;branches&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;show_branch&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;node&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;depth&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;When rendering the entire tree, using this technique will greatly increase
performance as the tree grows in size. Be aware though that if your
application only ever deals with a single branch in any given view, this
technique won&amp;rsquo;t perform as well as directly querying the database for the
nodes in a single branch.&lt;/p&gt;
</content>
   
   <category term="python"></category>
   
   <category term="algorithms"></category>
   
   <category term="data structures"></category>
   
   <category term="n+1 queries"></category>
   
 </entry>
 
 <entry>
   <title>Technology Shows Style of Things to Come</title>
   <link href="http://blog.jupo.org/2009/11/30/technology-shows-style-of-things-to-come/"/>
   <updated>2009-11-30T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2009/11/30/technology-shows-style-of-things-to-come</id>
   <content type="html">&lt;p&gt;&lt;a href=&quot;http://www.smh.com.au/business
/technology-shows-style-of-things-to-come-20091129-jyxp.html&quot;&gt;Technology shows style of things to come&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Sydney Morning Herald has an article that talks about some of the work I
did for Sportsgirl including their community portal running on Django and a
Facebook app written in C# - nice to get recognized!&lt;/p&gt;
</content>
   
   <category term="django"></category>
   
   <category term="facebook"></category>
   
 </entry>
 
 <entry>
   <title>FTP Browser for gedit</title>
   <link href="http://blog.jupo.org/2009/11/03/ftp-browser-for-gedit/"/>
   <updated>2009-11-03T00:00:00-08:00</updated>
   <id>http://blog.jupo.org/2009/11/03/ftp-browser-for-gedit</id>
   <content type="html">&lt;p&gt;Many major Linux distributions such as Ubuntu ship with
&lt;a href=&quot;http://projects.gnome.org/gedit/&quot;&gt;gedit&lt;/a&gt; as the default text editor. It has
all the standard features you&amp;rsquo;d expect in an editor such as syntax
highlighting, a tabbed interface and the ability to integrate external tools.
Most importantly it&amp;rsquo;s highly extensible with the ability to &lt;a href=&quot;http://live.gnome.org/Gedit/Plugins&quot;&gt;create plug-
ins&lt;/a&gt; for it written in Python or C.&lt;/p&gt;

&lt;p&gt;One great plug-in that&amp;rsquo;s been written is &lt;a href=&quot;http://code.google.com/p/gedit-ftp-browser/&quot;&gt;gedit-ftp-
browser&lt;/a&gt;, an FTP client that
embeds itself into the editor giving you the ability to remotely edit files
over FTP. I&amp;rsquo;ve just been accepted as a contributor to the project which I&amp;rsquo;m
really excited about. &lt;a href=&quot;http://code.google.com/p/gedit-ftp-
browser/source/diff?spec=svn5&amp;amp;r=5&amp;amp;format=side&amp;amp;path=/trunk/FTP.py&quot;&gt;I&amp;rsquo;ve implemented a &amp;ldquo;Save As&amp;rdquo;
feature&lt;/a&gt; allowing the
user to create and upload new files over FTP. Next up I&amp;rsquo;ll be working on the
ability to manage multiple FTP servers via profiles.&lt;/p&gt;
</content>
   
   <category term="gedit"></category>
   
   <category term="linux"></category>
   
   <category term="python"></category>
   
   <category term="gnome"></category>
   
 </entry>
 
 <entry>
   <title>Dynamic Django Deployment</title>
   <link href="http://blog.jupo.org/2009/10/20/dynamic-django-deployment/"/>
   <updated>2009-10-20T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2009/10/20/dynamic-django-deployment</id>
   <content type="html">&lt;p&gt;Lately I&amp;rsquo;ve noticed people posting various different takes on making the
default Django settings a lot more dynamic. The development and deployment
requirements for the projects I work on tend to be far from straight-forward
and over time I&amp;rsquo;ve come up with my own approach to Django settings, so here it
is.&lt;/p&gt;

&lt;p&gt;The simplest approach typically involves importing all the names from a custom
settings module at the end of the project&amp;rsquo;s standard settings module,
providing the ability to override settings on a per machine basis.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;local_settings&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This still requires modifying &lt;code&gt;local_settings.py&lt;/code&gt; on a per machine basis.
Another approach that builds on this is to import a custom settings module
from a &lt;code&gt;host_settings&lt;/code&gt; package that has a unique name derived from the current
machine the site is running on - this gives the advantage of being able to
specify custom settings per machine and have each of these settings modules
reside in the version control system, without the same file having to be
modified on a per machine basis.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gethostname&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;from host_settings.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; import *&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; 
        &lt;span class=&quot;n&quot;&gt;gethostname&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;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;-&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;_&amp;quot;&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;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;_&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;This simple version of the &lt;code&gt;host_settings&lt;/code&gt; approach I&amp;rsquo;ve seen attempts to deal
with the differences between a valid hostname and a valid module name with the
two calls to replace, but ignores the fact a hostname could begin with a
number which would be an invalid module name. Other versions of this approach
handle this correctly and involve calling the &lt;code&gt;__import__&lt;/code&gt; built-in, iterating
over and updating each name in the settings module individually, but once we
look at some further requirements below and how to deal with them we&amp;rsquo;ll find
this isn&amp;rsquo;t necessary.&lt;/p&gt;

&lt;p&gt;Let&amp;rsquo;s take a step back for a moment and talk about some of the requirements I
mentioned before. Where I work a project can end up deployed in a dozen
different locations - a handful of developer machines and various different
servers managing the project life cycle. Due to a variety of non-technical
reasons it&amp;rsquo;s often required that various versions of a project run side by
side in the same location, so with a project called &lt;code&gt;project_x&lt;/code&gt; we end up with
&lt;code&gt;project_x_feature_a&lt;/code&gt; and &lt;code&gt;project_x_feature_b&lt;/code&gt; sitting in the same location -
all of a sudden all of our references to &lt;code&gt;project_x&lt;/code&gt; are broken. So we end up
taking the approach in our code that the actual name of a project&amp;rsquo;s directory
is a moving target and should never be referenced - we never import from
&lt;code&gt;package_x&lt;/code&gt; and anything in our settings module that would typically reference
this we set dynamically.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;project_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;dirname&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;abspath&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;__file__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;project_dir&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project_path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;split&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sep&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;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MEDIA_URL&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;/site_media/&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;MEDIA_ROOT&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MEDIA_URL&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;/&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TEMPLATE_DIRS&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;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;templates&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ADMIN_MEDIA_PREFIX&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;/media/&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;ROOT_URLCONF&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.urls&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;project_dir&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;So that removes any hard-coded reference to the project&amp;rsquo;s directory name,
however we sometimes have to go as far as having host specific settings that
vary across these different project versions residing on the same machine,
such as a different database for example. The ultimate goal here is to not
have any files in the project&amp;rsquo;s version control repository that are manually
edited for a specific instance of the project. So using the &lt;code&gt;host_settings&lt;/code&gt;
approach from earlier on, we continue on from the code above by using the
&lt;code&gt;project_dir&lt;/code&gt; variable when referencing the machine specific &lt;code&gt;host_settings&lt;/code&gt;
module so that each of the &lt;code&gt;host_settings&lt;/code&gt; modules are named not only after
the machine they exist for, but the project directory as well.&lt;/p&gt;

&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;python&quot;&gt;&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;socket&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gethostname&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;host_settings_module&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;&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;n&quot;&gt;project_dir&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;gethostname&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;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;-&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;_&amp;quot;&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;replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&amp;quot;.&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;_&amp;quot;&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;lower&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;host_settings_path&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;project_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;host_settings&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; 
    &lt;span class=&quot;s&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;.py&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host_settings_module&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;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;exists&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host_settings_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;open&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;host_settings_path&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;w&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;IOError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;print&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;couldnt create host_settings module: &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; &amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host_settings_path&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;try&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;exec&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&amp;quot;from host_settings.&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;%s&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; import *&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;host_settings_module&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;except&lt;/span&gt; &lt;span class=&quot;ne&quot;&gt;ImportError&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;pass&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;TEMPLATE_DEBUG&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;DEBUG&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;

&lt;p&gt;As an added bonus, we try to create the &lt;code&gt;host_settings&lt;/code&gt; module if it&amp;rsquo;s missing
and warn if we&amp;rsquo;re unable to create it.&lt;/p&gt;
</content>
   
   <category term="django"></category>
   
   <category term="python"></category>
   
 </entry>
 
 <entry>
   <title>Nokia E63</title>
   <link href="http://blog.jupo.org/2009/09/26/nokia-e63/"/>
   <updated>2009-09-26T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2009/09/26/nokia-e63</id>
   <content type="html">&lt;p&gt;After living the last few years without a mobile phone it finally became a
problem for me so recently I decided to get a new one. I only needed something
simple for receiving calls, not making them so any kind of plan or contract
was out of the question since I could spend a few bucks on pre-paid and
theoretically not pay anything after that. I didn&amp;rsquo;t consider any of the latest
smart-phones either like the HTC or iPhone (I wouldn&amp;rsquo;t buy a crippled device
anyway) since they&amp;rsquo;re so ridiculously priced compared to lower end models that
aren&amp;rsquo;t even a year old.&lt;/p&gt;

&lt;p&gt;I ended up getting a &lt;a href=&quot;http://www.nokia.com.au/find-products/all-
phones/nokia-e63&quot;&gt;Nokia E63&lt;/a&gt; that has a full qwerty keyboard and wireless LAN access
which were the main selling points for me. It cost $299 when it was being sold
for over $500 at other places around town so it felt like a smart purchase.
Apart from those features I looked at, I really knew nothing about the phone
and &lt;a href=&quot;http://symbian.org/&quot;&gt;Symbian OS&lt;/a&gt; which powers it and after taking a
closer look at the software available for it I&amp;rsquo;ve been really surprised.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;http://media.tumblr.com/tumblr_ksm6cyOpF11qa0qji.png&quot; alt=&quot;Google Apps for Symbian
OS&quot; /&gt; Google Apps for
Symbian OS &lt;img src=&quot;http://media.tumblr.com/tumblr_ksm6dyg1OZ1qa0qji.png&quot; alt=&quot;Putty for Symbian
OS&quot; /&gt; Putty for Symbian OS
&lt;img src=&quot;http://media.tumblr.com/tumblr_ksm6eqDj7h1qa0qji.png&quot; alt=&quot;Python (PyS60) on Symbian
OS&quot; /&gt; Python (PyS60) on
Symbian OS&lt;/p&gt;

&lt;p&gt;Google has created a &lt;a href=&quot;http://www.google.com/mobile/products.html#p=default&quot;&gt;handful of Symbian
apps&lt;/a&gt; for things like
gmail, reader, youtube and maps which all work great. The other day I found an
app called &lt;a href=&quot;http://www.fring.com&quot;&gt;fring&lt;/a&gt; which is very similar to
&lt;a href=&quot;http://pidgin.im/&quot;&gt;Pidgin&lt;/a&gt; in that it wraps up all the various IM clients
into one, even including Skype with voice working! So it has essentially
turned my phone into a Skype handset which is amazing. I&amp;rsquo;ve also found that
there&amp;rsquo;s a version of &lt;a href=&quot;http://s2putty.sourceforge.net/&quot;&gt;putty for Symbian&lt;/a&gt; so I
can actually SSH onto any of our Linux servers or desktops and access the
shell from my phone!&lt;/p&gt;

&lt;p&gt;The most incredible thing I&amp;rsquo;ve found for Symbian however has been a project
called &lt;a href=&quot;https://garage.maemo.org/projects/pys60/&quot;&gt;PyS60&lt;/a&gt; - Nokia has actually
ported the Python run-time to the Symbian OS. I was amazed once I had this
installed and was sitting there typing out Python code into an interactive
console directly on my phone. The standard library is even included and it&amp;rsquo;s
very interesting - certain pieces aren&amp;rsquo;t fully ported but it comes with a
handful of modules specifically for PyS60 which handle things like locating
wireless networks and working with the user interface. It even includes
&lt;a href=&quot;http://www.opengl.org/&quot;&gt;OpenGL&lt;/a&gt; bindings which is unbelievable - that&amp;rsquo;s
right, you can develop 3D games in Python for your phone.&lt;/p&gt;

&lt;p&gt;I&amp;rsquo;m well into developing my first app which is a small RPC server for the
phone controlled by a &lt;a href=&quot;http://wxwidgets.org/&quot;&gt;wxWidgets&lt;/a&gt; client. The idea is
to be able to traverse the phone&amp;rsquo;s file system and create, edit and execute
Python apps on the phone from a remote machine. The SimpleXMLRPCServer module
isn&amp;rsquo;t included with PyS60 and broke when I tried to copy it onto the phone and
import it manually. I&amp;rsquo;ve since been able to patch it and get it working which
I&amp;rsquo;ve &lt;a href=&quot;https://garage.maemo.org/tracker/index.php?func=deta
il&amp;amp;aid=4576&amp;amp;group_id=854&amp;amp;atid=3201&quot;&gt;submitted to Nokia&lt;/a&gt;, hopefully they&amp;rsquo;ll include it in their
next release.&lt;/p&gt;
</content>
   
   <category term="nokia"></category>
   
   <category term="symbian"></category>
   
   <category term="python"></category>
   
 </entry>
 
 <entry>
   <title>CHM Files on Linux</title>
   <link href="http://blog.jupo.org/2009/09/21/chm-files-on-linux/"/>
   <updated>2009-09-21T00:00:00-07:00</updated>
   <id>http://blog.jupo.org/2009/09/21/chm-files-on-linux</id>
   <content type="html">&lt;p&gt;I think CHM files are great and my main Python reference is the CHM version of
the main documentation. I’ve used &lt;a href=&quot;http://gnochm.sourceforge.net/&quot;&gt;GnoCHM&lt;/a&gt;
which is the default CHM viewer for Gnome for quite a while and it’s really
poor - slow as hell on startup and segfaults half the time you click a link. I
finally gave up and looked for an alternative which I found in
&lt;a href=&quot;http://www.kchmviewer.net/&quot;&gt;KchmViewer&lt;/a&gt; which appears to be the default CHM
viewer for KDE. As usual the KDE counterpart of a given Gnome app is much
easier on the eyes and in this case the problems I had are solved - lightning
fast and stable.&lt;/p&gt;
</content>
   
   <category term="chm"></category>
   
   <category term="linux"></category>
   
   <category term="gnome"></category>
   
   <category term="kde"></category>
   
 </entry>
 

</feed>
