<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3762802375429612564</id><updated>2011-11-27T14:19:38.962-10:00</updated><category term='ruby'/><category term='south'/><category term='javascript'/><category term='django-admin'/><category term='github'/><category term='template'/><category term='ASP.NET'/><category term='jslint'/><category term='inheritance'/><category term='grappelli'/><category term='css'/><category term='git'/><category term='settings.py'/><category term='python'/><category term='wsgi'/><category term='haskell'/><category term='komodo-themes'/><category term='tdd'/><category term='review'/><category term='komodo-edit'/><category term='django-models'/><category term='mit'/><category term='laptop'/><category term='apache'/><category term='RWH'/><category term='neenjah'/><category term='wmd-editor'/><category term='scala'/><category term='dcvs'/><category term='baretail'/><category term='ajax'/><category term='random'/><category term='lisp'/><category term='web-frameworks'/><category term='markedit'/><category term='django'/><category term='data-analysis'/><category term='fork'/><category term='lonerangeselector'/><category term='jquery-ui'/><category term='netbeans'/><category term='kindle'/><category term='jquery'/><category term='django-testing'/><category term='django-template'/><category term='sql'/><category term='ipod'/><category term='netbook'/><category term='html'/><category term='internet-explorer-fail'/><category term='marketing'/><category term='microsoft'/><category term='fizzbuzz'/><category term='moved'/><category term='mercurial'/><category term='widget'/><category term='functional-programming'/><category term='svn'/><title type='text'>url::Encode | (andstuff)</title><subtitle type='html'>Blog of Titus Stone; Part programmer, part... wait... what?</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>58</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5892392906246804884</id><published>2010-09-09T19:08:00.002-10:00</published><updated>2010-09-09T19:08:08.871-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='moved'/><title type='text'>Tumblr</title><content type='html'>Moved to tumblr:&amp;nbsp;&lt;a href="http://urlencode.tumblr.com/"&gt;http://urlencode.tumblr.com/&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5892392906246804884?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5892392906246804884/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5892392906246804884' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5892392906246804884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5892392906246804884'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/09/tumblr.html' title='Tumblr'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-1183817246464595974</id><published>2010-08-20T06:44:00.003-10:00</published><updated>2010-08-27T04:40:26.239-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Restoring Browser Functions to Clickable DIVs Without Javascript</title><content type='html'>Occasionally the situation comes up in web development where you have a highly styled tag such as &lt;br /&gt;&amp;lt;div&amp;gt;&amp;nbsp;or &amp;lt;li&amp;gt; that you want to make clickable. &amp;nbsp;This often happens when you have a list of items in which you want to make the entire item clickble&amp;nbsp;instead&amp;nbsp;of just the title for&amp;nbsp;example. &amp;nbsp;The standard fare for doing so is to set { cursor: pointer; } with CSS then to bind the .click event to the tag (in jQuery as so)....&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;$(&lt;span style="color: #4070a0;"&gt;'#myDivTag'&lt;/span&gt;).click(&lt;span style="color: #007020; font-weight: bold;"&gt;function&lt;/span&gt;() {&lt;br /&gt;    &lt;span style="color: #60a0b0; font-style: italic;"&gt;// your code here&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #60a0b0; font-style: italic;"&gt;&lt;/span&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Don't get me wrong -- this does work. &amp;nbsp;However, in doing this what you loose is the standard functionality that most viewers are used to; things like middle clicking to open in a new tab, right-click to copy URL address, and so on. &amp;nbsp;There is also the lag between when the page renders visibly to the user but the javascript hasn't downloaded and $(document).ready hasn't yet fired. &amp;nbsp;In this case the tag will not be clickable but the mouse cursor will indicate that it should be.&lt;br /&gt;&lt;br /&gt;I've been dealing with this more so lately and there are a couple of ways to solve this problem.&lt;br /&gt;&lt;br /&gt;The first is to wrap the &amp;lt;div&amp;gt; (or li or whatever else) tag in an anchor tag &amp;lt;a&amp;gt; and set that anchor tag to { display: block; }.&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;style &lt;/span&gt;&lt;span style="color: #4070a0;"&gt;type="text/css"&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;   &lt;span style="color: #06287e;"&gt;#myDivWrapper&lt;/span&gt; { &lt;span style="color: #007020; font-weight: bold;"&gt;display&lt;/span&gt;&lt;span style="color: #666666;"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold;"&gt;block&lt;/span&gt;; }&lt;br /&gt;   &lt;span style="color: #06287e;"&gt;#myDiv&lt;/span&gt; {  }&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;a&lt;/span&gt; &lt;span style="color: #4070a0;"&gt;id="myDivWrapper"&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;div&lt;/span&gt; &lt;span style="color: #4070a0;"&gt;id="myDiv"&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;       ... content ...&lt;br /&gt;    &lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By wrapping the div tag in an anchor you've restored the default functionality viewers are used to with a clickable item. &amp;nbsp;This works in most browsers but for the most part things like &amp;lt;div&amp;gt; and &amp;lt;li&amp;gt; are not allowed inside of an anchor tag per HTML language specifications. &amp;nbsp;In a sense in doing something like this you are rolling the dice that the next version of IE9 strict mode won't render when it hits this.&lt;br /&gt;&lt;br /&gt;It also feels kind of &amp;nbsp;weird, because all of the styling that was on #myDiv, things like height, width, float, etc., now need to be applied to the anchor tag instead. &amp;nbsp;And no one likes wrappers. They just clutter things up.&lt;br /&gt;&lt;br /&gt;The 2nd way to solve this problem which I've started using lately is to have an&amp;nbsp;absolutely&amp;nbsp;positioned anchor overlay the area of the div. &amp;nbsp;Consider the following...&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;style &lt;/span&gt;&lt;span style="color: #4070a0;"&gt;type="text/css"&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    &lt;span style="color: #06287e;"&gt;#myDiv&lt;/span&gt; { &lt;span style="color: #007020; font-weight: bold;"&gt;position&lt;/span&gt;&lt;span style="color: #666666;"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold;"&gt;relative&lt;/span&gt;; }&lt;br /&gt;    &lt;span style="color: #062873; font-weight: bold;"&gt;a&lt;/span&gt;&lt;span style="color: #0e84b5; font-weight: bold;"&gt;.clickable-overlay&lt;/span&gt; {&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold;"&gt;position&lt;/span&gt;&lt;span style="color: #666666;"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold;"&gt;absolute&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold;"&gt;top&lt;/span&gt;&lt;span style="color: #666666;"&gt;:&lt;/span&gt; &lt;span style="color: #40a070;"&gt;0&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold;"&gt;left&lt;/span&gt;&lt;span style="color: #666666;"&gt;:&lt;/span&gt; &lt;span style="color: #40a070;"&gt;0&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold;"&gt;height&lt;/span&gt;&lt;span style="color: #666666;"&gt;:&lt;/span&gt; &lt;span style="color: #40a070;"&gt;100%&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold;"&gt;width&lt;/span&gt;&lt;span style="color: #666666;"&gt;:&lt;/span&gt; &lt;span style="color: #40a070;"&gt;100%&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold;"&gt;z-index&lt;/span&gt;&lt;span style="color: &lt;br /&gt;#666666;"&gt;:&lt;/span&gt; &lt;span style="color: #40a070;"&gt;1&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold;"&gt;display&lt;/span&gt;&lt;span style="color: &lt;br /&gt;#666666;"&gt;:&lt;/span&gt; &lt;span style="color: #40a070;"&gt;block&lt;/span&gt;;&lt;br /&gt;        &lt;span style="color: #007020; font-weight: bold;"&gt;background-color&lt;/span&gt;&lt;span style="color: #666666;"&gt;:&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold;"&gt;transparent&lt;/span&gt;;&lt;br /&gt;    }&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;div&lt;/span&gt; &lt;span style="color: #4070a0;"&gt;id="myDiv"&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;    ... content ...&lt;br /&gt;    &lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;a&lt;/span&gt; &lt;span style="color: #4070a0;"&gt;class="clickable-overlay"&lt;/span&gt; &lt;span style="color: #4070a0;"&gt;href="#wherever"&lt;/span&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #062873; font-weight: bold;"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's what's happening: &amp;nbsp;At the end of the &amp;lt;div&amp;gt; (or li or whatever) we're adding an anchor tag. &amp;nbsp;The parent div is set to { position: relative }, and the anchor tag to { position: absolute; }. &amp;nbsp;If you're not familiar with position absolute you're missing out. &amp;nbsp;The way it is designed to work is that any element positioned&amp;nbsp;absolutely&amp;nbsp;is done so in relation to it's parent (or any parent's parent) that is positioned either&amp;nbsp;relatively&amp;nbsp;or&amp;nbsp;absolutely. &amp;nbsp;What this means is that in setting our &amp;lt;div&amp;gt; tag to position: relative the anchor tag with position absolute will be positioned relative to the div. &amp;nbsp;Top 0 and left 0 will be the top left of the div, &amp;nbsp;and height 100%, width 100% will cause the anchor to fill out the area of the div.&lt;br /&gt;&lt;br /&gt;The result is that we are left with a transparent anchor tag that completely overlays the area of the div, providing the default clickable behavior users expect. &amp;nbsp;The last CSS trick to making this work is to set z-index to 1, forcing the browser to always render the anchor tag &lt;i&gt;over&lt;/i&gt;&amp;nbsp;whatever content is in the div.&lt;br /&gt;&lt;br /&gt;The&amp;nbsp;positives&amp;nbsp;to this method are that 1. we restore the expected clickable behavior without javascript (meaning it also works as soon as that markup is rendered and not when $(document).ready is fired) and 2. we're using correct HTML syntax.&lt;br /&gt;&lt;br /&gt;I should point out there is a downside to this method, in that because the anchor overlays the div, the contents of the div are no longer selectable or clickable. &amp;nbsp;Depending on your application this may be negligible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-1183817246464595974?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/1183817246464595974/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=1183817246464595974' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1183817246464595974'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1183817246464595974'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/08/restoring-browser-functions-to.html' title='Restoring Browser Functions to Clickable DIVs Without Javascript'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-8065588934929862505</id><published>2010-08-12T17:27:00.000-10:00</published><updated>2010-08-12T17:27:47.693-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='random'/><title type='text'>Top 10 Programming T-Shirts That Don't Make Sense</title><content type='html'>&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;mind.location = unknown&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;a href="http://www.zazzle.com/javascript_mind_tshirt-235892961444457742"&gt;http://www.zazzle.com/javascript_mind_tshirt-235892961444457742&lt;/a&gt;&lt;br /&gt;I wonder what the value of the variable unknown is?&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;import_soul&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;a href="http://www.zazzle.com/import_soul_tshirt-235715645595867505"&gt;http://www.zazzle.com/import_soul_tshirt-235715645595867505&lt;/a&gt;&lt;br /&gt;&lt;div&gt;What is import_soul? &amp;nbsp;It's written as a keyword. &amp;nbsp;If it were a function it should be import_soul(), however the keywords for this shirt include "python" which makes me thinking they &lt;i&gt;meant&lt;/i&gt; &lt;b&gt;&lt;i&gt;import soul&lt;/i&gt;&lt;/b&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Function Check_Drunk()&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zazzle.com/function_check_drunk_tshirt-235954739548677347"&gt;http://www.zazzle.com/function_check_drunk_tshirt-235954739548677347&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Where do I begin with this one... drink is treated as a global variable but never declared globally. &amp;nbsp;Then there's the awesome conditional if "bra = true" which will always return true because a single = is assignment not evaluation. &amp;nbsp;Lastly, none of the functions are ever called, they're just declarations of.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Programmers Cool Club&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zazzle.com/cool_programmers_club_tshirt-235068746346060145"&gt;http://www.zazzle.com/cool_programmers_club_tshirt-235068746346060145&lt;/a&gt;&lt;/div&gt;&lt;div&gt;No comment needed.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;.ninja { color: black; }&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zazzle.com/css_ninja_style_class_tshirt-235091799113607617"&gt;http://www.zazzle.com/css_ninja_style_class_tshirt-235091799113607617&lt;/a&gt;&lt;/div&gt;&lt;div&gt;This one's amusing and almost worth getting, but the behavior of &lt;i&gt;visibility: hidden;&lt;/i&gt;&amp;nbsp;is such that it keeps screen space allocated for the element and simply does not render it. &amp;nbsp;I personally believe a true ninja would &lt;i&gt;{ display: none; }&lt;/i&gt;.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&lt;b&gt;Geek Power&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zazzle.com/geek_power_tshirt-235232870665206831"&gt;http://www.zazzle.com/geek_power_tshirt-235232870665206831&lt;/a&gt;&lt;/div&gt;&lt;div&gt;No geek would ever intentionally rip the cord to their keyboard like that&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Computers are Only Human&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zazzle.com/computers_are_only_human_tshirt-235670270460766159"&gt;http://www.zazzle.com/computers_are_only_human_tshirt-235670270460766159&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Apparently&amp;nbsp;I didn't get that memo.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&lt;b&gt;SELECT TOP 25&lt;/b&gt;&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zazzle.com/the_best_sql_query_ever_tshirt-235031072069340726"&gt;http://www.zazzle.com/the_best_sql_query_ever_tshirt-235031072069340726&lt;/a&gt;&lt;/div&gt;&lt;div&gt;Aside from using the vendor-specific "TOP 25" (MS SQL), the comment at the bottom brings to light the poor execution of this SQL: &amp;nbsp;It should be sorted DESCENDING&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Do You Speak Code&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zazzle.com/do_you_speak_code_tshirt-235290525576102199"&gt;http://www.zazzle.com/do_you_speak_code_tshirt-235290525576102199&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;There's No Crying in Programming&lt;/span&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;a href="http://www.zazzle.com/no_crying_in_programming_card-137314447034339159"&gt;http://www.zazzle.com/no_crying_in_programming_card-137314447034339159&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-8065588934929862505?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/8065588934929862505/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=8065588934929862505' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/8065588934929862505'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/8065588934929862505'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/08/top-10-programming-t-shirts-that-dont.html' title='Top 10 Programming T-Shirts That Don&apos;t Make Sense'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6740000498734877368</id><published>2010-08-06T20:20:00.001-10:00</published><updated>2010-08-06T20:29:59.875-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web-frameworks'/><category scheme='http://www.blogger.com/atom/ns#' term='template'/><title type='text'>Python Templating For the Web (Random Ideas)</title><content type='html'>Well it's past midnight and I'm musing about web templating in Python for Tornado or AppEngine. &amp;nbsp; The thing about most Python templating engines that I notice is...&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;There's kind of an obsession with being agnostic about everything. &amp;nbsp;You can render JSON and XML and whatever else you want with our engine! &amp;nbsp;When is someone just going to make a really awesome templating engine &lt;i&gt;for the web&lt;/i&gt;&amp;nbsp;and not try to be everything to everyone?&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The value in a template engine to me is how easy it is to allow the template to include logic that's independent of the controller that's generating it. &amp;nbsp;For example, you might want a server-side ad manager on every page, but you don't necessarily want to put the code for that in every controller. &amp;nbsp;Solution? &amp;nbsp;Some engines call it modules, some call it template tags, but whatever the name there is some type of logic that's independent of the controller. &amp;nbsp;The problem is that most of these pieces of independent logic are needlessly complicated. &amp;nbsp;(side note: Django has gotten a bit better about this since I first used it two years ago).&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Maybe some better support for lazy-loaded values which are available for all templates... things like static URL, etc.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;So it's late and I'm thinking, what if there was a template engine, and I mean one specifically for the web, which represented an HTML document more so as a Python class. &amp;nbsp;Every root element or element with an ID is a property of the document. &amp;nbsp;Tags can interact with each other... Maybe this is turning out to be too much of a DOM&amp;nbsp;implementation&amp;nbsp;in Python. &amp;nbsp;Maybe this is a bad idea. &amp;nbsp;Or maybe I should try coding up a prototype just to see what it's like.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But it's not quite a DOM. &amp;nbsp;The DOM is concerned with specific structure, where as we're only concerned with a loose abstract structure. &amp;nbsp;It doesn't matter of the area where scripts are printed too is in the  or right above , what's important is that there is a scripts area which sub-modules can access.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Instead of {% extends "some_html_file.html %}, it could be {% extends templates.master %} where templates.master is a python module.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;(15 minutes later)... you know it kind of sounds like I'm trying to re-invent ASP.NET WebForms...&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6740000498734877368?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6740000498734877368/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6740000498734877368' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6740000498734877368'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6740000498734877368'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/08/python-templating-for-web-random-ideas.html' title='Python Templating For the Web (Random Ideas)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-4002927791081896461</id><published>2010-07-27T19:34:00.002-10:00</published><updated>2010-07-27T19:35:39.842-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='komodo-themes'/><category scheme='http://www.blogger.com/atom/ns#' term='komodo-edit'/><title type='text'>Komodo Edit</title><content type='html'>I like Komodo Edit.  It's simple but at the same time functional enough to be useful.  It also does code completion, supports Python, and is cross-platform.  (Side note: If only it supported Scala, then it would be like the &lt;i&gt;&lt;span class="Apple-style-span" style="font-size: x-small;"&gt;**aaaaahhh**&lt;/span&gt;&lt;/i&gt; perfect editor).&lt;br /&gt;&lt;br /&gt;I've had this idea kicking around for a while of starting a Komodo color themes/schemes website.  The idea is simple.  You create a theme, then share it on the site.  Other people can download it or Vote/"Like" it.  Most-liked themes bubble to the top.  I'm thinking of a simple architecture using Google App Engine (python) + Tornado.  We'll see.&lt;br /&gt;&lt;br /&gt;In the mean time I sure did waste like 2 hours of what should have been productive programming time putting together a bunch of themes for Komodo Edit.  Here are some previews of them...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;"Heart Dry"&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_epp24QszXwI/TE_AzvvB1JI/AAAAAAAAAHs/sn28simtnlY/s1600/HeartDry.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="115" src="http://3.bp.blogspot.com/_epp24QszXwI/TE_AzvvB1JI/AAAAAAAAAHs/sn28simtnlY/s400/HeartDry.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;"Fancy Terminal"&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_epp24QszXwI/TE_AzHxyZiI/AAAAAAAAAHk/nwaMiGv5tbU/s1600/FancyTerminal.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="117" src="http://3.bp.blogspot.com/_epp24QszXwI/TE_AzHxyZiI/AAAAAAAAAHk/nwaMiGv5tbU/s400/FancyTerminal.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&amp;nbsp;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;"Happy Day"&lt;/span&gt; &lt;span class="Apple-style-span" style="font-size: small;"&gt;(currently used)&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://2.bp.blogspot.com/_epp24QszXwI/TE_AzYRWlXI/AAAAAAAAAHo/gqHChyuWQnI/s1600/HappyDay.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="115" src="http://2.bp.blogspot.com/_epp24QszXwI/TE_AzYRWlXI/AAAAAAAAAHo/gqHChyuWQnI/s400/HappyDay.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;&amp;nbsp;"Monokai"&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_epp24QszXwI/TE_Az60a1oI/AAAAAAAAAHw/dbVbqxy-oRw/s1600/Monokai.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="115" src="http://4.bp.blogspot.com/_epp24QszXwI/TE_Az60a1oI/AAAAAAAAAHw/dbVbqxy-oRw/s400/Monokai.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;"Phoenix"&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://3.bp.blogspot.com/_epp24QszXwI/TE_A0Bgq-RI/AAAAAAAAAH0/7e-STo6SdW8/s1600/Phoenix.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="115" src="http://3.bp.blogspot.com/_epp24QszXwI/TE_A0Bgq-RI/AAAAAAAAAH0/7e-STo6SdW8/s400/Phoenix.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;"Transformers"&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_epp24QszXwI/TE_A0SYyiVI/AAAAAAAAAH4/ue1vbu5jJwY/s1600/Transformers.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="113" src="http://4.bp.blogspot.com/_epp24QszXwI/TE_A0SYyiVI/AAAAAAAAAH4/ue1vbu5jJwY/s400/Transformers.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;"Work Scheme"&lt;/span&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://4.bp.blogspot.com/_epp24QszXwI/TE_A0t8TTlI/AAAAAAAAAH8/l4Ss2X9hdKE/s1600/WorkScheme.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" height="115" src="http://4.bp.blogspot.com/_epp24QszXwI/TE_A0t8TTlI/AAAAAAAAAH8/l4Ss2X9hdKE/s400/WorkScheme.png" width="400" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-4002927791081896461?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/4002927791081896461/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=4002927791081896461' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4002927791081896461'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4002927791081896461'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/07/komodo-edit.html' title='Komodo Edit'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_epp24QszXwI/TE_AzvvB1JI/AAAAAAAAAHs/sn28simtnlY/s72-c/HeartDry.png' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7990564276129329309</id><published>2010-06-04T23:58:00.001-10:00</published><updated>2010-06-04T23:59:45.008-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='functional-programming'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Formatting Console Output in Scala</title><content type='html'>So it's midnight and I'm programming Scala (heh). &amp;nbsp;I'm actually starting to be able to write little scripts in Scala. &amp;nbsp;Here's a quick one that I put together, inspired by Chapter 3 of "Programming Scala" which reads a source code file and formats the file to the screen with line numbers.&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/426501.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;I tried to be as functional as possible. &amp;nbsp;The "formatSourceLine" I felt was getting close to the concept, using recursion, immutable values, and always returning a value (avoiding the side effect of printing from within the function).&lt;br /&gt;&lt;br /&gt;The actual output from this script is:&lt;br /&gt;&lt;br /&gt;&lt;pre class="code"&gt;D:\Development\Scala&amp;gt;scala file.scala file.scala&lt;br /&gt;&lt;br /&gt;    1 | import scala.io.Source&lt;br /&gt;    2 |&lt;br /&gt;    3 | if (args.length &amp;gt; 0) {&lt;br /&gt;    4 |&lt;br /&gt;    5 |     def formatSourcePrefix(line: Int, suffix: String, max: Int = 5):&lt;br /&gt;    6 |         var prefix: String = ""&lt;br /&gt;    7 |         for (i &amp;lt;- 1 to (max - line.toString.length))&lt;br /&gt;    8 |             prefix += " "&lt;br /&gt;    9 |         prefix + line.toString + suffix&lt;br /&gt;   10 |     }&lt;br /&gt;   11 |&lt;br /&gt;   12 |     def formatSourceLine(lines: List[String], line: Int = 1, &lt;br /&gt;   13 |         val out = output + formatSourcePrefix(line, " | ")&lt;br /&gt;   14 |         if (lines.length &amp;gt; 1)&lt;br /&gt;   15 |             formatSourceLine(lines.tail, line + 1, out)&lt;br /&gt;   16 |         else&lt;br /&gt;   17 |             out&lt;br /&gt;   18 |     }&lt;br /&gt;   19 |&lt;br /&gt;   20 |     // Read file from disk&lt;br /&gt;   21 |     val lines = Source.fromPath(args(0)).getLines().toList&lt;br /&gt;   22 |&lt;br /&gt;   23 |     // Print results to screen&lt;br /&gt;   24 |     println&lt;br /&gt;   25 |     print(formatSourceLine(lines))&lt;br /&gt;   26 |&lt;br /&gt;   27 | }&lt;br /&gt;   28 | else {&lt;br /&gt;   29 |     // Print out proper syntax&lt;br /&gt;   30 |     Console.err.println&lt;br /&gt;   31 |     Console.err.println("scala file.scala [filename]")&lt;br /&gt;   32 | }&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7990564276129329309?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7990564276129329309/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7990564276129329309' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7990564276129329309'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7990564276129329309'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/06/formatting-console-output-in-scala.html' title='Formatting Console Output in Scala'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7384242535632568963</id><published>2010-05-29T11:05:00.002-10:00</published><updated>2010-05-29T11:09:53.769-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>The Difference a Space Can Make</title><content type='html'>How can you print the string value of a floating point value of 5 in Scala?&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; 5.toString  // Wrong!&lt;br /&gt;res16: java.lang.String = 5&lt;br /&gt;&lt;br /&gt;scala&amp;gt; 5. toString&lt;br /&gt;res17: java.lang.String = 5.0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In the first line scala counts the "." as syntax for "toString method of Integer 5". &amp;nbsp;The second line uses the "toString" method in infix notation, applying the toString method against the floating point value of 5.0.&lt;br /&gt;&lt;br /&gt;Here's another example where a space can make a big difference in type:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;scala&amp;gt; 4.+(1)&lt;br /&gt;res20: Double = 5.0&lt;br /&gt;&lt;br /&gt;scala&amp;gt; 4 .+(1)&lt;br /&gt;res21: Int = 5&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7384242535632568963?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7384242535632568963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7384242535632568963' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7384242535632568963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7384242535632568963'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/05/difference-space-can-make.html' title='The Difference a Space Can Make'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5308502644684591251</id><published>2010-05-29T09:07:00.000-10:00</published><updated>2010-05-29T09:07:50.629-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Abstract Types in Scala</title><content type='html'>Whenever I learn new programming languages, there is invariably features of the language that will surprise me. &amp;nbsp;One of those in Scala (one of many actually) is abstract types. &amp;nbsp;When I first read the textual description of an abstract type, I wondered what use it would ever entail. &amp;nbsp;However, the authors of &lt;a href="http://programming-scala.labs.oreilly.com/"&gt;&lt;i&gt;Programming Scala&lt;/i&gt;&lt;/a&gt; gave a very clear example of proper use.&lt;br /&gt;&lt;br /&gt;This code snippet comes from Chapter 2 of the book.&lt;br /&gt;&lt;br /&gt;Starting with an abstract class, we can declare an abstract type. &amp;nbsp;In this case, type "In" on our class "BulkReader" is abstract. &amp;nbsp;It's there, but it does not have a concrete type associated with it. &amp;nbsp;Yet, our value "Source" is typed as type "In". &amp;nbsp;How can this be?&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: sans-serif;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class="code" style="background-attachment: initial; background-clip: initial; background-color: #f2f2f2; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; font-family: 'andale mono', 'lucida console', monaco, monospace; font-size: 0.875em; margin-bottom: 1em; margin-left: 1em; margin-right: 1em; margin-top: 1em; padding-bottom: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.5em;"&gt;abstract class BulkReader {&lt;br /&gt;  type In&lt;br /&gt;  val source: In&lt;br /&gt;  def read: String&lt;br /&gt;}&lt;/pre&gt;The magic comes when we declare a concrete version of "BulkReader" and can assign a concrete type to "In":&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: sans-serif;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;pre class="code" style="background-attachment: initial; background-clip: initial; background-color: #f2f2f2; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; font-family: 'andale mono', 'lucida console', monaco, monospace; font-size: 0.875em; margin-bottom: 1em; margin-left: 1em; margin-right: 1em; margin-top: 1em; padding-bottom: 0.5em; padding-left: 0.5em; padding-right: 0.5em; padding-top: 0.5em;"&gt;class StringBulkReader(val source: String) extends BulkReader {&lt;br /&gt;  type In = String&lt;br /&gt;  def read = source&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;class FileBulkReader(val source: File) extends BulkReader {&lt;br /&gt;  type In = File&lt;br /&gt;  def read = {&lt;br /&gt;    val in = new BufferedInputStream(new FileInputStream(source))&lt;br /&gt;    val numBytes = in.available()&lt;br /&gt;    val bytes = new Array[Byte](numBytes)&lt;br /&gt;    in.read(bytes, 0, numBytes)&lt;br /&gt;    new String(bytes)&lt;br /&gt;  }&lt;br /&gt;}&lt;/pre&gt;Very cool! &amp;nbsp;In our two concrete instances of "BulkReader" we assigned two different types to our abstract type "In". &amp;nbsp;Note that the value typed as In, "source" is actually part of the concrete class's constructor which is, at this point for me, completely mind bending in terms of flexibility towards class design.&lt;br /&gt;&lt;br /&gt;I have a feeling it will take me some time to get used to this notion and power before I really start to design code that makes use of it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5308502644684591251?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5308502644684591251/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5308502644684591251' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5308502644684591251'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5308502644684591251'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/05/abstract-types-in-scala.html' title='Abstract Types in Scala'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-4094293482313770832</id><published>2010-05-26T08:40:00.000-10:00</published><updated>2010-05-26T08:40:50.312-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='functional-programming'/><category scheme='http://www.blogger.com/atom/ns#' term='scala'/><title type='text'>Language of the Summer: Scala?</title><content type='html'>I've been quite fascinated with Haskell as a language.&amp;nbsp; It's purely functional, ridiculously strict typing, and algebraic syntax were completely foreign to me before I began learning about it.&amp;nbsp; I wouldn't say that I &lt;i&gt;learned&lt;/i&gt; Haskell, but I did learn about it.&amp;nbsp; I learned enough to know that it's a bit over my head at this point and that I need a better theoretical foundation before I attempt to tackle it again.&lt;br /&gt;&lt;br /&gt;So in the mean time I wanted to take on something more transitional.&amp;nbsp; I had considered F#.&amp;nbsp; It seems like a great choice, especially since I already know C# and am familiar with the .NET platform.&amp;nbsp; But it's a Microsoft language and I was hoping for something more portable (actually, I'm harboring a secret desire to use a functional programming language to one day code for Lego Mindstorms NXT with).&lt;br /&gt;&lt;br /&gt;I happened to stumble upon &lt;a href="http://liftweb.net/"&gt;Lift&lt;/a&gt; again yesterday.&amp;nbsp; I had heard about it before when I was looking at other web frameworks after watching a keynote from the author of &lt;a href="http://www.seaside.st/"&gt;Seaside&lt;/a&gt; at &lt;a href="http://djangocon.blip.tv/"&gt;Djangocon&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;However, I realized something important about Lift and Scala which I had never caught before: Scala compiles to java bytecode.&amp;nbsp; Java can be run on Google App Engine.&amp;nbsp; I can use Scala/Lift to write for App Engine.&lt;br /&gt;&lt;br /&gt;Suddenly I'm hooked.&amp;nbsp; Oh, awesome, and the book "Programming Scala" is &lt;a href="http://programming-scala.labs.oreilly.com/"&gt;available freely online&lt;/a&gt;.&amp;nbsp; Mmmm... looks like I have my reading for my traveling this summer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-4094293482313770832?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/4094293482313770832/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=4094293482313770832' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4094293482313770832'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4094293482313770832'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/05/language-of-summer-scala.html' title='Language of the Summer: Scala?'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7799716275548490949</id><published>2010-05-25T16:05:00.000-10:00</published><updated>2010-05-25T16:05:36.425-10:00</updated><title type='text'>Getting a Python/Django Job</title><content type='html'>Getting a job doesn't seem like a fun activity to many people.&amp;nbsp; I'm in the thick of it right now.&amp;nbsp; The problem is when you have a skill set that you really like and that you feel confident with that &lt;i&gt;no company wants&lt;/i&gt;.&amp;nbsp; I feel like I'm in that place with Python/Django.&lt;br /&gt;&lt;br /&gt;Sure, I've worked on the "corporaty" systems, ASP.NET, MVC, C#, etc., but what I'd really prefer to work on is python/Django on the web.&amp;nbsp; It doesn't seem like there are &lt;i&gt;any&lt;/i&gt; companies hiring for that in Denver (or maybe I need to come up with a better way to find them?).&amp;nbsp; So I end up in an odd place where I wonder if it would be worth a.) going back to the corporaty things that get jobs (MSSQL, ASP, etc.) or b.) learning a newer but similar technology (RoR), and gamble that someone will hire me because I have previous experience in Django.&lt;br /&gt;&lt;br /&gt;No one in Colorado needs Django work done?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7799716275548490949?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7799716275548490949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7799716275548490949' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7799716275548490949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7799716275548490949'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/05/getting-pythondjango-job.html' title='Getting a Python/Django Job'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6027508720477021175</id><published>2010-05-11T12:57:00.001-10:00</published><updated>2010-05-11T12:59:01.806-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='laptop'/><title type='text'>I Bought It at Ross!</title><content type='html'>I was waiting on the wife at Ross Dress for Less the other day, when I happened to notice something:&amp;nbsp; They sell laptop bags!&amp;nbsp; Since when did Ross start carrying those?&amp;nbsp; Surprisingly they had a selection of name brands at, you guessed it, discount prices.&lt;br /&gt;&lt;br /&gt;I ended up buying a &lt;a href="http://www.google.com/products/catalog?q=Targus+Messenger+Fusion&amp;amp;oe=utf-8&amp;amp;client=firefox-a&amp;amp;cid=15912641570362461506&amp;amp;ei=FuDpS_OBPZ7ijASK6bnUDg&amp;amp;sa=title&amp;amp;ved=0CAcQ8wIwADgA#p"&gt;Targus Messenger Fusion&lt;/a&gt; in dark gray/green for the Toshiba 13.3" I got a few months ago.&amp;nbsp; It was marked down to $18.99 (compared to $26.50 online).&amp;nbsp; It's a pretty nice looking laptop bag with built-in padding and tons of pockets/storage.&amp;nbsp; It has an interesting feature in that it comes with multiple straps so you can keep the green one out for a young/hip look, or put the dark one one for a more serious professional look.&lt;br /&gt;&lt;a href="http://www.google.com/products/catalog?q=Targus+Messenger+Fusion&amp;amp;oe=utf-8&amp;amp;client=firefox-a&amp;amp;cid=15912641570362461506&amp;amp;ei=FuDpS_OBPZ7ijASK6bnUDg&amp;amp;sa=title&amp;amp;ved=0CAcQ8wIwADgA#p"&gt;&lt;/a&gt;&lt;br /&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="http://www.google.com/products/catalog?q=Targus+Messenger+Fusion&amp;amp;oe=utf-8&amp;amp;client=firefox-a&amp;amp;cid=15912641570362461506&amp;amp;ei=FuDpS_OBPZ7ijASK6bnUDg&amp;amp;sa=title&amp;amp;ved=0CAcQ8wIwADgA#p"&gt;&lt;img border="0" src="http://base0.googlehosted.com/base_media?q=FroogleCatalog_CNETI657907.jpg&amp;amp;size=20&amp;amp;dhm=f9fbfcfd&amp;amp;hl=en" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6027508720477021175?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6027508720477021175/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6027508720477021175' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6027508720477021175'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6027508720477021175'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/05/i-bought-it-at-ross.html' title='I Bought It at Ross!'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-4823031205811031391</id><published>2010-01-14T15:15:00.009-10:00</published><updated>2010-01-14T15:44:32.243-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='inheritance'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='django-models'/><title type='text'>Accessing Inherited Models from the Parent in Django</title><content type='html'>One of the neat features of Django's ORM is Model inheritance (table-level).  It allows several neat data design patterns to occur.  Here's an example.  Let's say we're developing a website for a game company.  The company sells two types of products:  board games and video games.  All of the products will share some data in common, name and product_id for example, but we also need to store specific details about each.  Using model inheritance we can do something as follows.&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;Product&lt;/span&gt;(models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;Model):&lt;br /&gt; name &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;75&lt;/span&gt;)&lt;br /&gt; product_id &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;SmallIntegerField&lt;/span&gt;()&lt;br /&gt; price &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;DecimalField()&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;BoardGame&lt;/span&gt;(Product):&lt;br /&gt; num_of_players &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;SmallIntegerField&lt;/span&gt;()&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;    game_type&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;50&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;VideoGame&lt;/span&gt;(Product):&lt;br /&gt; PLATFORM_CHOICES &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; (&lt;br /&gt;   (&lt;span style="color: rgb(64, 112, 160);"&gt;'wii'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'Wii),&lt;/span&gt;&lt;br /&gt;   (&lt;span style="color: rgb(64, 112, 160);"&gt;'xb3'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'Xbox 360'&lt;/span&gt;),&lt;br /&gt;   (&lt;span style="color: rgb(64, 112, 160);"&gt;'ps3'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'Playstation 3'&lt;/span&gt;),&lt;br /&gt; )&lt;br /&gt; platform &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;3&lt;/span&gt;, choices&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;PLATFORM_CHOICES)&lt;br /&gt;&lt;/pre&gt;In a real use-case scenario you'd most likely have more than 1 field per, but for this example I wanted to keep things simple.&lt;br /&gt;&lt;br /&gt;The way Django implements this, if you were to query one of the child models, you'd be able to access the methods from the parent models...&lt;br /&gt;&lt;pre class="code"&gt;b &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; BoardGame&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;objects&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;all()[&lt;span style="color: rgb(64, 160, 112);"&gt;1&lt;/span&gt;]&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;print&lt;/span&gt; b&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;name&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'Djangopoly'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Another thing that's cool is child instances have a parent instance record.  Using the "Djangopoloy" game from above, which is technically type BoardGame, one could still query Product and retrieve it.&lt;br /&gt;&lt;pre class="code"&gt;p &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; Product&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;objects&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;get(name&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'Djangopoly'&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;This is really useful, but sometimes you need to go the opposite direction, and this is where Django's implementation stops.  The link can't go from a Product model instance to a BoardGame.  It can't retrieve state as if it was of type BoardGame.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;print&lt;/span&gt; p&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;platform&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; CAN&lt;span style="color: rgb(64, 112, 160);"&gt;'T DO THAT!&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Because the need for this seems to be arising more often than not lately for me, I put together a re-usbale bit of code to overcome this limitation.  I'll post the code below (a GitHub gist), but using it is actually quite simple.&lt;br /&gt;&lt;br /&gt;It works by providing an abstract model that the parent model inherits from instead of models.Model:&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;inheritance.models&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; ChildAwareModel&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;Product&lt;/span&gt;(ChildAwareModel):&lt;br /&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;  pass&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Then, an inner class "Inheritance" is supplied to describe children of the model.&lt;br /&gt;&lt;pre class="code"&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;Product&lt;/span&gt;(ChildAwareModel):&lt;br /&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;Inheritance&lt;/span&gt;:&lt;br /&gt;     children &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; (&lt;br /&gt;         &lt;span style="color: rgb(64, 112, 160);"&gt;'myapp.models.BoardGame'&lt;/span&gt;,&lt;br /&gt;         &lt;span style="color: rgb(64, 112, 160);"&gt;'myapp.mdoels.VideoGame'&lt;/span&gt;,&lt;br /&gt;     )&lt;br /&gt;&lt;/pre&gt;Only children that need to be reversed to should be set.  Once that is configured, a method "get_child_model()" will become available, and can be used like so:&lt;br /&gt;&lt;pre class="code"&gt;p &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; Product&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;objects&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;get(name&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'Djangopoly'&lt;/span&gt;)&lt;br /&gt;b &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; p&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;get_child_model()&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;print&lt;/span&gt; b&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;num_of_players&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;4&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;I'm finding this particularly useful when I've created an aggregate type page -- that is a page that shows a summary of all the generic types (Product) -- but need to on user-click show them some type of product-specific detail.&lt;br /&gt;&lt;br /&gt;The implementation for ChildAwareModel is below.  Save it somewhere on your python path and enjoy.  :)&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/277703.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;a href="http://gist.github.com/277703"&gt;ChildAwareModel Gist&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-4823031205811031391?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/4823031205811031391/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=4823031205811031391' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4823031205811031391'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4823031205811031391'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/01/accessing-inherited-models-from-parent.html' title='Accessing Inherited Models from the Parent in Django'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6741048131880768488</id><published>2010-01-12T08:11:00.002-10:00</published><updated>2010-01-12T08:13:46.712-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mercurial'/><category scheme='http://www.blogger.com/atom/ns#' term='git'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><category scheme='http://www.blogger.com/atom/ns#' term='dcvs'/><title type='text'>Version Control Commit Messages</title><content type='html'>Soooooo... I think I'm going to put in a proposal at work.  All commit messages for our Mercurial DVCS need to be in 16-year-old girl language....&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;"Like, the JSON api is totally updated"&lt;/li&gt;&lt;li&gt;"fixed the buggies kk thx bai &lt;3"&lt;/li&gt;&lt;li&gt;"this import module is sooooo cuties!!!!!11`1"&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6741048131880768488?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6741048131880768488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6741048131880768488' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6741048131880768488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6741048131880768488'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/01/version-control-commit-messages.html' title='Version Control Commit Messages'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6970422911802075712</id><published>2010-01-10T23:55:00.005-10:00</published><updated>2010-01-11T00:04:48.668-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='RWH'/><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='haskell'/><category scheme='http://www.blogger.com/atom/ns#' term='kindle'/><category scheme='http://www.blogger.com/atom/ns#' term='ipod'/><title type='text'>Meh @reading (but Yay to reading on the iPod)</title><content type='html'>I can't be the only programmer who finds reading a chore?  And what about those 5-billion page programming tomes?  Am I the only one who never finishes those?&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It happens to be that I just found something about reading that totally rocks:  Reading on the iPhone/iPod Touch is totally different (better different).  HA!  I bet you didn't know I was going to say that (well if you read the title of this post you probably did.. that was totally a spoiler).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now it seems that I am finding I actually enjoy reading again (did I just write that?).  I ended up landing a very fetching iPod touch for Christmas which has been having it's battery drained near non-stop since the package was opened.  As a curiosity of sorts, I threw a lot of the big name apps onto it, including Kindle for Ipod, and purchased a book I had been eying for a while, &lt;a href="http://www.amazon.com/JavaScript-The-Good-Parts-ebook/dp/B0026OR2ZY"&gt;Javascript: The Good Parts&lt;/a&gt; (Douglas Crockford), to try out the experience of reading on the iPod.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;It seems so wrong but I found I actually &lt;i&gt;prefer&lt;/i&gt; reading on the iPod.  I've read a number of paper books, and I've tried reading eBook/PDFs on the computer, but neither of them really jump out at me.  But the iPod... the iPod I like.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The reason for this, I suspect, is that reading on the iPod is not intimidating.  The screen only shows 2 paragraphs at a time.  I don't need to be worried about the rest of the book, I'm only looking at these 2 paragraphs.  What I think this does for me is it allows me to read the book without feeling like I have to rush to get to the end.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My wife thinks it's silly, but &lt;b&gt;&lt;i&gt;whatever&lt;/i&gt;&lt;/b&gt; -- I'm already on my 2nd book.... &lt;a href="http://www.amazon.com/Real-World-Haskell-ebook/dp/B0026OR2FY"&gt;Real World Haskell&lt;/a&gt; (Donald Stewart).  Oooooh yes, it's &lt;a href="http://www.haskell.org/"&gt;Haskell&lt;/a&gt; time!  Expect some really weirded out blog posts from me in the future about functional programming.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6970422911802075712?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6970422911802075712/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6970422911802075712' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6970422911802075712'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6970422911802075712'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2010/01/meh-reading-but-yay-to-reading-on-ipod.html' title='Meh @reading (but Yay to reading on the iPod)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5319389338887974857</id><published>2009-12-18T16:16:00.010-10:00</published><updated>2009-12-18T16:36:16.619-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='lonerangeselector'/><category scheme='http://www.blogger.com/atom/ns#' term='widget'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery-ui'/><title type='text'>Where Have All The Good DatePickers Gone?</title><content type='html'>Is it just me, or is it hard to find a good jQuery-based date picker?  Or maybe I'm just picky.&lt;br /&gt;&lt;br /&gt;The widget needs to support selecting a date range (there goes most of them).  And it needs to be able to support configuring a starting and ending time.  And it has to support Themeroller (there goes whatever few remained).&lt;br /&gt;&lt;br /&gt;So as it seems is starting to become a norm for me, I'm working on a custom jQuery widget.  It's been provisionally named "LoneRangeSelector".  It's for a custom web-app-calendar sort of thing.&lt;br /&gt;&lt;br /&gt;Provisionally, here's my progress thus far...&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_epp24QszXwI/Syw6ZylcVsI/AAAAAAAAADs/688zpkwhqhw/s1600-h/LoneRangeSelector.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 177px;" src="http://2.bp.blogspot.com/_epp24QszXwI/Syw6ZylcVsI/AAAAAAAAADs/688zpkwhqhw/s400/LoneRangeSelector.png" alt="" id="BLOGGER_PHOTO_ID_5416768666569823938" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The main goal is to make an interface that's quick and easy to setup the date-related parameters of an event, without having to enter 4 different fields (start date, start time, end date, end time).&lt;br /&gt;&lt;br /&gt;The secondary goal is that as a byproduct of having a single widget, there is quite a bit less error checking that needs to be done (ie. it's impossible to type in an ending date that occurs before the starting date).&lt;br /&gt;&lt;br /&gt;I actually re-created the calendar code instead of trying to modify jQuery UI's datepicker, though it does implement identical css classes so the Themeroller appearance is the same.&lt;br /&gt;&lt;br /&gt;The lower portion has been one of the more challenging.  I think the time slider needs to have some type of time indication on it, because just at first glance there's no way you konw that it spans midnight to midnight.&lt;br /&gt;&lt;br /&gt;Concept:&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_epp24QszXwI/Syw8FruO0MI/AAAAAAAAAD0/QDnB_ASjwc8/s1600-h/LoneRangeSelector-ticks.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 203px;" src="http://3.bp.blogspot.com/_epp24QszXwI/Syw8FruO0MI/AAAAAAAAAD0/QDnB_ASjwc8/s400/LoneRangeSelector-ticks.png" alt="" id="BLOGGER_PHOTO_ID_5416770520153510082" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5319389338887974857?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5319389338887974857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5319389338887974857' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5319389338887974857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5319389338887974857'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/12/where-have-all-good-datepickers-gone.html' title='Where Have All The Good DatePickers Gone?'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_epp24QszXwI/Syw6ZylcVsI/AAAAAAAAADs/688zpkwhqhw/s72-c/LoneRangeSelector.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5375702125028838583</id><published>2009-12-11T09:31:00.003-10:00</published><updated>2009-12-11T09:45:34.411-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='settings.py'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>Using Google's jQuery CDN with Django when not in development</title><content type='html'>I wanted to transition the Django site I work with primarily to use &lt;a href="http://code.google.com/apis/ajaxlibs/documentation/"&gt;Google's jQuery CDN&lt;/a&gt;.  However, when developing locally, it's often faster to just use a local copy.  What I wanted was a way to toggle which copy of jQuery was being used based on the environment.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Environment Detection&lt;/span&gt;&lt;br /&gt;Before we can toggle the jQuery location, we need to have a way to detect which environment we're running in.I &lt;a href="http://urlencode.blogspot.com/2009/10/notating-environment-in-djangos.html"&gt;previously blogged about&lt;/a&gt; how I have my settings.py configured.  There are different ways to do this.  One is to use local_settings.py, the other is to have conditional code in your main settings.py which determines values.  Either way works.&lt;br /&gt;&lt;br /&gt;As for my setting, on the webserver is a folder structure that resembles...&lt;br /&gt;&lt;pre&gt;    \webroot&lt;br /&gt;       \django_devel&lt;br /&gt;       \django_prod&lt;br /&gt;&lt;/pre&gt;My webserver pulls double duty, hosting both a production version ("prod") and a staging/testing version ("devel").  In my settings.py file I'm using this to determine which environment Django is running in...&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;os&lt;/span&gt;&lt;br /&gt;DJANGO_ROOT &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; os&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;path&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;abspath(os&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;path&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;dirname(__file__))&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;#&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# Globals for determining settings&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;#&lt;/span&gt;&lt;br /&gt;STAGING &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; PRODUCTION &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; DEVELOPMENT &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;False&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'django_devel'&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;in&lt;/span&gt; DJANGO_ROOT:&lt;br /&gt;STAGING &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;elif&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'django_prod'&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;in&lt;/span&gt; DJANGO_ROOT:&lt;br /&gt;PRODUCTION &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;else&lt;/span&gt;:&lt;br /&gt;DEVELOPMENT &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Most likely, you'll have some other what you're determining your environment.  In that case just substitute that.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;{% jquery template tag %}&lt;/span&gt;&lt;br /&gt;Now that we have the required support code to detect what environment we're running in, putting together the actual implementation is simple.&lt;br /&gt;&lt;br /&gt;I decided to go with a template tag.  It's simple and easy.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;settings&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;django&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; template&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;django.conf&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; settings&lt;br /&gt;&lt;br /&gt;register &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; template&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;Library()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;#   jQuery&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# -----------------------------------------------------------------------------&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(85, 85, 85); font-weight: bold;"&gt;@register&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;tag&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt; &lt;span style="color: rgb(6, 40, 126);"&gt;jquery&lt;/span&gt;(parser, token):&lt;br /&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; JQueryNode()&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;JQueryNode&lt;/span&gt;(template&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;Node):&lt;br /&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt; &lt;span style="color: rgb(6, 40, 126);"&gt;render&lt;/span&gt;(&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;, context):&lt;br /&gt;     jquery &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js'&lt;/span&gt;&lt;br /&gt;     jquery_ui &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js'&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;getattr&lt;/span&gt;(settings, &lt;span style="color: rgb(64, 112, 160);"&gt;'DEVELOPMENT'&lt;/span&gt;, &lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;):&lt;br /&gt;         media_url &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;getattr&lt;/span&gt;(settings, &lt;span style="color: rgb(64, 112, 160);"&gt;'MEDIA_URL'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'/media/'&lt;/span&gt;)&lt;br /&gt;         jquery &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'&lt;/span&gt;&lt;span style="color: rgb(112, 160, 208); font-style: italic;"&gt;%s&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;js/jquery-1.3.2.min.js'&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;%&lt;/span&gt; media_url&lt;br /&gt;         jquery_ui &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'&lt;/span&gt;&lt;span style="color: rgb(112, 160, 208); font-style: italic;"&gt;%s&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;js/jquery-ui-1.7.2.custom.min.js'&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;%&lt;/span&gt; media_url&lt;br /&gt;     &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'&amp;lt;script type="text/javascript" src="&lt;/span&gt;&lt;span style="color: rgb(112, 160, 208); font-style: italic;"&gt;%s&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;"&amp;gt;&amp;lt;/script&amp;gt;&amp;lt;script type="text/javascript" src="&lt;/span&gt;&lt;span style="color: rgb(112, 160, 208); font-style: italic;"&gt;%s&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;"&amp;gt;&amp;lt;/script&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;%&lt;/span&gt; (jquery, jquery_ui)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There are a couple of things to note here.&lt;br /&gt;&lt;br /&gt;First, there's a hard-coded path in this code.  Because I plan to always use this template tag every time I need jQuery, I felt confident doing so as it leaves only one place to edit.  However, if you won't be following that rigid of an implementation pulling the locations of jQuery out and into a settings.py file would probably be a smart idea.&lt;br /&gt;&lt;br /&gt;Second, I'm implementing my type of detection for the environment.  This probably differs from the majority of Django users.&lt;br /&gt;&lt;br /&gt;When all is said and done, all that needs to remain is to simply drop the tag into base.html and we're good to go.&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;{%&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;load&lt;/span&gt; &lt;span style="color: rgb(187, 96, 213);"&gt;jquery&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;%}{%&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;jquery&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;%}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5375702125028838583?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5375702125028838583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5375702125028838583' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5375702125028838583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5375702125028838583'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/12/using-googles-jquery-cdn-with-django.html' title='Using Google&apos;s jQuery CDN with Django when not in development'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7231413978825988334</id><published>2009-12-10T09:20:00.007-10:00</published><updated>2009-12-10T09:32:42.299-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><title type='text'>How can I create a page that regularly updates itself with Django?</title><content type='html'>It seems this question has been coming up a lot on Stack Overflow, and I wrote a pretty lengthy response which I'll include here.  The original question is &lt;a href="http://stackoverflow.com/questions/1879872/django-update-div-with-ajax/1883266"&gt;#1883266&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Q:  I'm building a web application that needs to have content on the page updated in real time without a page refresh (like a chat or messaging application).  How do I do this with Django? In other words, how do I use AJAX with Django?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;A: There's a lot going on in order to make this process work...  &lt;ul&gt;&lt;li&gt;The client regularly polls the server for new chat entries&lt;/li&gt;&lt;li&gt;The server checks for and only replies with the newest &lt;/li&gt;&lt;li&gt;The client receives the newest entries and appends them to the DOM&lt;/li&gt;&lt;/ul&gt;  &lt;p&gt;This can be confusing when you're first starting because it's not always clear what the client does and what the server does, but if the large problem is broken down I think you'll find it's a simple process.&lt;/p&gt;  &lt;p&gt;If the client is going to regularly poll the server for new chat entries, then the server (django) needs to have some type of API to do so. Your biggest decision will be what data type the server returns. You can choose from: rendered HTML, XML, YAML, or JSON. The lightest weight is JSON, and it's supported by most of the major javascript frameworks (and django includes a JSON serializer since it's that awesome).&lt;br /&gt;&lt;/p&gt;&lt;pre&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic; font-weight: bold;"&gt;# (models.py)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# Your model I'm assuming is something to the effect of...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;ChatLine&lt;/span&gt;(models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;Model):&lt;br /&gt;  screenname &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; model&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;40&lt;/span&gt;)&lt;br /&gt;  value &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;CharField&lt;/span&gt;(max_length&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;100&lt;/span&gt;)&lt;br /&gt;  created &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;DateTimeField&lt;/span&gt;(default&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;datetime&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;now())&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic; font-weight: bold;"&gt;# (urls.py)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# A url pattern to match our API...&lt;/span&gt;&lt;br /&gt;url(&lt;span style="color: rgb(64, 112, 160);"&gt;r'^api/latest-chat/(?P&amp;lt;seconds_old&amp;gt;\d+)/$'&lt;/span&gt;,get_latest_chat),&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic; font-weight: bold;"&gt;# (views.py)&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# A view to answer that URL&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt; &lt;span style="color: rgb(6, 40, 126);"&gt;get_latest_chat&lt;/span&gt;(request, seconds_old):&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;    # Query comments since the past X seconds&lt;/span&gt;&lt;br /&gt;  chat_since &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; datetime&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;datetime&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;now() &lt;span style="color: rgb(102, 102, 102);"&gt;-&lt;/span&gt; datetime&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;timedelta(seconds&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;seconds_old)&lt;br /&gt;  chat &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; Chat&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;objects&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;filter(created__gte&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;comments_since)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;   # Return serialized data or whatever you're doing with it&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;   return&lt;/span&gt; HttpResponse(simplejson&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;dumps(chat),mimetype&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'application/json'&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So whenever we poll our API, we should get back something like this (JSON format)...&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;[&lt;/span&gt;&lt;br /&gt; {&lt;br /&gt;     &lt;span style="color: rgb(64, 112, 160);"&gt;'value'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'Hello World'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: rgb(64, 112, 160);"&gt;'created'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'2009-12-10 14:56:11'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: rgb(64, 112, 160);"&gt;'screenname'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'tstone'&lt;/span&gt;&lt;br /&gt; }&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt; {&lt;br /&gt;     &lt;span style="color: rgb(64, 112, 160);"&gt;'value'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'And more cool Django-ness'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: rgb(64, 112, 160);"&gt;'created'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'2009-12-10 14:58:49'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;     &lt;span style="color: rgb(64, 112, 160);"&gt;'screenname'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'leethax0r1337'&lt;/span&gt;&lt;br /&gt; }&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;]&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;On our actual page, we have a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; tag which we'll call &lt;code&gt;&amp;lt;div id="chatbox"&amp;gt;&lt;/code&gt; which will hold whatever the incoming chat messages are. Our javascript simple needs to poll the server API that we created, check if there is a response, and then if there are items, append them to the chat box.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&amp;lt;!-- I'm assuming you're using jQuery --&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;&amp;lt;script &lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;type="text/javascript"&lt;/span&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: rgb(102, 102, 102);"&gt;LATEST&lt;/span&gt;_CHAT_URL &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;{%&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;url&lt;/span&gt; &lt;span style="color: rgb(187, 96, 213);"&gt;get_latest_chat&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;5&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;%}&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// On page start...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;    $(&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;() {&lt;br /&gt;      &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// Start a timer that will call our API at regular intervals&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;        &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// The 2nd value is the time in milliseconds, so 5000 = 5 seconds&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;        setTimeout(updateChat&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;5000&lt;/span&gt;)&lt;br /&gt;  });&lt;br /&gt;&lt;br /&gt;  &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt; updateChat() {&lt;br /&gt;      $.getJSON(&lt;span style="color: rgb(102, 102, 102);"&gt;LATEST&lt;/span&gt;_CHAT_URL&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;(data){&lt;br /&gt;          &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// Enumerate JSON objects&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;            $.each(data.items&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;(i&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;item){&lt;br /&gt;              &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; newChatLine &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; $(&lt;span style="color: rgb(64, 112, 160);"&gt;'&amp;lt;span class="chat"&amp;gt;&amp;lt;/span&amp;gt;'&lt;/span&gt;);&lt;br /&gt;              newChatLine.append(&lt;span style="color: rgb(64, 112, 160);"&gt;'&amp;lt;span class="user"&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;+&lt;/span&gt; item.screenname &lt;span style="color: rgb(102, 102, 102);"&gt;+&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'&amp;lt;/span&amp;gt;'&lt;/span&gt;);&lt;br /&gt;              newChatLine.append(&lt;span style="color: rgb(64, 112, 160);"&gt;'&amp;lt;span class="text"&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;+&lt;/span&gt; item.text &lt;span style="color: rgb(102, 102, 102);"&gt;+&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'&amp;lt;/span&amp;gt;'&lt;/span&gt;);&lt;br /&gt;              $(&lt;span style="color: rgb(64, 112, 160);"&gt;'#chatbox'&lt;/span&gt;).append(newChatLine);&lt;br /&gt;          });&lt;br /&gt;      });&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;&amp;lt;div&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;id="chatbox"&lt;/span&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7231413978825988334?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7231413978825988334/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7231413978825988334' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7231413978825988334'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7231413978825988334'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/12/how-can-i-create-page-that-regularly.html' title='How can I create a page that regularly updates itself with Django?'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5909918367462924946</id><published>2009-12-08T11:02:00.005-10:00</published><updated>2009-12-08T11:39:40.213-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='baretail'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Efficiently View Apache Log Files on Windows</title><content type='html'>One of the annoying things about using multiple platforms is when one platform has a useful utility (no matter how small) and the other platform doesn't.  Have you ever needed to regularly check an Apache log file on your Windows development machine?  The shell user inside me says "just tail it"... but this is Windows.&lt;br /&gt;&lt;br /&gt;However, I just found a really amazing tool called &lt;a href="http://www.baremetalsoft.com/baretail/"&gt;BareTail&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_epp24QszXwI/Sx7HAWv95HI/AAAAAAAAADM/PAdPBUBsluA/s1600-h/untitled.JPG"&gt;&lt;img style="cursor: pointer; width: 400px; height: 165px;" src="http://3.bp.blogspot.com/_epp24QszXwI/Sx7HAWv95HI/AAAAAAAAADM/PAdPBUBsluA/s400/untitled.JPG" alt="" id="BLOGGER_PHOTO_ID_5412982611066610802" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;BareTail "connects" to a log file, and shows you an automatically updated (live) tail of that file.  It basically allows this situation to happen:&lt;br /&gt;&lt;br /&gt;I have a window up on my developer machine (in the 2nd monitor) which is the tail of the Apache error log.  Every time something is written to that log, BareTail pushes it into the window on my screen.  I see log entries in real time.&lt;br /&gt;&lt;br /&gt;Let me tell you, it makes debugging Apache error log issues much more efficient.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5909918367462924946?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5909918367462924946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5909918367462924946' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5909918367462924946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5909918367462924946'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/12/developing-with-apache-on-windows.html' title='Efficiently View Apache Log Files on Windows'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_epp24QszXwI/Sx7HAWv95HI/AAAAAAAAADM/PAdPBUBsluA/s72-c/untitled.JPG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-4249099547986732207</id><published>2009-12-08T08:42:00.003-10:00</published><updated>2009-12-08T08:53:22.275-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='neenjah'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Time for Python Neenjah!</title><content type='html'>I happened upon an administrative assistant today who was renaming files using Windows Explorer.  She had a folder with about 50 or so sub-folders, many of which contained sub-folders of their own.  Every file needed to be renamed with the company name prefixing it.  So for example, the file "january-charts.pdf" needed to be renamed to "Company - january-charts.pdf".&lt;br /&gt;&lt;br /&gt;"You know", I said, "I could help that go a little faster if you're interested."  I happened to know that these files needed to be sent as of yesterday, so I figured she wouldn't mind me helping to trim an hour or two off them getting out.  That must mean it's time for &lt;span style="font-weight: bold;"&gt;Python Neenjah&lt;/span&gt;!  (In case you missed it, that was a somewhat veiled reference to &lt;a href="http://xkcd.com/208/"&gt;xkcd 'Regular Expressions'&lt;/a&gt;)&lt;br /&gt;&lt;br /&gt;A month or two ago I had put together a re-usable python module to allow easy recursively searching a directory.  I know, I know, python already includes similar functionality.  But it was weird to me, and I wanted a simpler and more flexible format.  The module I built, inspire by some ideas I found around the internet, allows a callback to be specified whenever a file is found.  It means you can do virtually anything from that directory search, and never really be concerned about how it does it.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;dirsearch&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; DirSearch&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt; &lt;span style="color: rgb(6, 40, 126);"&gt;search_callback&lt;/span&gt;(&lt;span style="color: rgb(0, 112, 32);"&gt;file&lt;/span&gt;):&lt;br /&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;print&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;file&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;dir&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; DirSearch(&lt;span style="color: rgb(64, 112, 160);"&gt;'C:\Path\Whatever'&lt;/span&gt;, show_output&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;)&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;dir&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;search(search_callback)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The source for dirsearch.py is included below.&lt;br /&gt;&lt;br /&gt;Well with my module it only took a few lines of code to put together a command to complete the task at hand.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gist.github.com/251883"&gt;DirSearch.py&lt;/a&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/251883.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://gist.github.com/251885"&gt;rrename.py&lt;/a&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/251885.js"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-4249099547986732207?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/4249099547986732207/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=4249099547986732207' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4249099547986732207'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4249099547986732207'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/12/time-for-python-neenjah.html' title='Time for Python Neenjah!'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-8607950063276031583</id><published>2009-12-04T09:10:00.002-10:00</published><updated>2009-12-04T09:14:06.308-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='markedit'/><category scheme='http://www.blogger.com/atom/ns#' term='grappelli'/><title type='text'>MarkEdit in Django</title><content type='html'>Being primarily a Django developer, it was only a matter of time until MarkEdit came with Django integration.&lt;br /&gt;&lt;br /&gt;I added to the GitHub repo today a simple app which provides a widget that renders MarkEdit through Django.  It's probably a bit more difficult to use than the jQuery plugin because it makes some assumptions about your Django configuration and usage (as do many Django apps).  In any case, an initiated Django developer should be able to figure it out just fine.&lt;br /&gt;&lt;br /&gt;In addition to the source update, the wiki has also been updated with documentation for the &lt;a href="http://wiki.github.com/tstone/jquery-markedit/django-integration"&gt;Django integration&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;An of course, here's a screenshot of MarkEdit in the admin (running under &lt;a href="http://code.google.com/p/django-grappelli/"&gt;Grappelli&lt;/a&gt;):&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_epp24QszXwI/SxlfbddwXOI/AAAAAAAAADE/oXWUxXLSAF0/s1600-h/markedit-admin.jpg"&gt;&lt;img style="cursor: pointer; width: 400px; height: 363px;" src="http://1.bp.blogspot.com/_epp24QszXwI/SxlfbddwXOI/AAAAAAAAADE/oXWUxXLSAF0/s400/markedit-admin.jpg" alt="" id="BLOGGER_PHOTO_ID_5411461352632900834" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-8607950063276031583?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/8607950063276031583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=8607950063276031583' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/8607950063276031583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/8607950063276031583'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/12/markedit-in-django.html' title='MarkEdit in Django'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_epp24QszXwI/SxlfbddwXOI/AAAAAAAAADE/oXWUxXLSAF0/s72-c/markedit-admin.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7159927510681761012</id><published>2009-12-03T13:19:00.004-10:00</published><updated>2009-12-04T00:11:40.259-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='markedit'/><category scheme='http://www.blogger.com/atom/ns#' term='github'/><title type='text'>MarkEdit Moved to GitHub</title><content type='html'>I have relocated the &lt;a href="http://github.com/tstone/jquery-markedit/"&gt;MarkEdit repository to GitHub&lt;/a&gt; to give it more community accessibility.  The GitHub repo now also includes an &lt;a href="http://wiki.github.com/tstone/jquery-markedit"&gt;improved wiki&lt;/a&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;MarkEdit now has it's own project page, also hosted by GitHub:  &lt;a href="http://tstone.github.com/jquery-markedit/"&gt;http://tstone.github.com/jquery-markedit/&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The project page &lt;i&gt;finally&lt;/i&gt; puts a &lt;b&gt;working demo&lt;/b&gt; online (along with 4 other advanced demos, showing some of MarkEdit's flexibility).&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7159927510681761012?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7159927510681761012/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7159927510681761012' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7159927510681761012'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7159927510681761012'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/12/markedit-moved-to-github.html' title='MarkEdit Moved to GitHub'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6809684181779221768</id><published>2009-12-02T23:02:00.003-10:00</published><updated>2009-12-02T23:32:48.832-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='internet-explorer-fail'/><title type='text'>IE TextRanges, Selections, and Carriage Returns</title><content type='html'>I spent several hours this week trying to track down a bug with programmatically selecting text via javascript.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;pre class="code"&gt;&lt;span style=" font-weight: bold;color:#007020;"&gt;if&lt;/span&gt; (textarea.setSelectionRange) {&lt;br /&gt; &lt;span style=" font-style: italic;color:#60a0b0;"&gt;// Set selection for Mozilla-ish browsers&lt;/span&gt;&lt;br /&gt;&lt;span style=" font-style: italic;color:#60a0b0;"&gt;&lt;/span&gt;    textarea.setSelectionRange(start&lt;span style="color:#666666;"&gt;,&lt;/span&gt; end);&lt;br /&gt; }&lt;br /&gt; &lt;span style=" font-weight: bold;color:#007020;"&gt;else&lt;/span&gt; {&lt;br /&gt; &lt;span style=" font-style: italic;color:#60a0b0;"&gt;// Set selection for IE&lt;/span&gt;&lt;br /&gt;&lt;span style=" font-style: italic;color:#60a0b0;"&gt;&lt;/span&gt;    &lt;span style=" font-weight: bold;color:#007020;"&gt;var&lt;/span&gt; range &lt;span style="color:#666666;"&gt;=&lt;/span&gt; textarea.createTextRange();&lt;br /&gt; range.collapse(&lt;span style=" font-weight: bold;color:#007020;"&gt;true&lt;/span&gt;);&lt;br /&gt; range.moveEnd(&lt;span style="color:#4070a0;"&gt;'character'&lt;/span&gt;&lt;span style="color:#666666;"&gt;,&lt;/span&gt; start);&lt;br /&gt; range.moveStart(&lt;span style="color:#4070a0;"&gt;'character'&lt;/span&gt;&lt;span style="color:#666666;"&gt;,&lt;/span&gt; end);&lt;br /&gt; range.select();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div&gt;Assuming that 'textarea' is the element of the textarea, and 'start' and 'end' are Number values that indicate the start and end of the selection, respectively.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I was managing this in MarkEdit by keeping a state object that recorded the "Before Select" (everything before the selection), the selection text, and the "After Select" (everything after the selection).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So it seems easy to implement right?&lt;/div&gt;&lt;div&gt;&lt;pre&gt;start &lt;span style="color:#666666;"&gt;=&lt;/span&gt; beforeSelect.length&lt;span style="color:#666666;"&gt;;&lt;/span&gt;&lt;br /&gt;end &lt;span style="color:#666666;"&gt;=&lt;/span&gt; start &lt;span style="color:#666666;"&gt;+&lt;/span&gt; select.length&lt;span style="color:#666666;"&gt;;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;b&gt;That doesn't always work!&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;It works sometimes, but occasionally the selection will be the proper length, but randomly offset by 3-7  characters.  It took quite a bit to figure out (that's an understatement).  Here's what a lot of debugging and experimentation came up with:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;When Internet Explorer creates a Range or a TextRange object, it converts all line returns to Carriage Return/Line Feed format; basically [0A] -&gt; [0D 0A].  Easy to counter?  Just determine all the instances of [0D] and [0A], subtract the two, then you have the offset?  That's what I thought too, but even with the offset, the discrepancy persisted.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So it was time for a new approach.  What if we converted all of the [0A] characters in the textarea to [0D 0A]'s ?  Again, the offset of the selection persisted.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's what I finally narrowed it down to be:  When you create a TextRange, IE doesn't actually re-render the text internally, converting all [0A]'s.  However, when you request the text (TextRange.text) it converts it at that time.  This means if you were to get the length of the selected text, TextRange.text.length, it would be longer than what the TextRange is seeing internally!  When using .moveEnd on the TextRange, the value counts the position from the internal text, not from the text it gives you.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;That is reeeeeeediculously wrong.  The final solution is to "sanitize" the text TextRange gives you by removing all [0D] characters from it.  From there you can accurately calculate the selection position and give the TextRange the proper coordinates.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Whew... that was several hours needlessly wasted by IE (again).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6809684181779221768?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6809684181779221768/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6809684181779221768' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6809684181779221768'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6809684181779221768'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/12/ie-textranges-selections-and-carriage.html' title='IE TextRanges, Selections, and Carriage Returns'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6342691960128369126</id><published>2009-11-30T08:57:00.002-10:00</published><updated>2009-11-30T08:58:38.503-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='markedit'/><title type='text'>MarkEdit Documentation</title><content type='html'>For those interested, I finished the preliminary documentation and sample code for &lt;a href="http://bitbucket.org/tstone/jquery-markedit/"&gt;MarkEdit&lt;/a&gt;.  It can be viewed via the &lt;a href="http://bitbucket.org/tstone/jquery-markedit/wiki/Home"&gt;Bitbucket Wiki&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The documentation includes...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Installation&lt;/li&gt;&lt;li&gt;Sample code&lt;/li&gt;&lt;li&gt;Complete API reference&lt;/li&gt;&lt;li&gt;Complete configuration option reference&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6342691960128369126?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6342691960128369126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6342691960128369126' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6342691960128369126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6342691960128369126'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/markedit-documentation.html' title='MarkEdit Documentation'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-3298736961621702446</id><published>2009-11-28T09:38:00.002-10:00</published><updated>2009-11-28T10:08:27.557-10:00</updated><title type='text'>Developer Blog Entries</title><content type='html'>I haven't done any development in the past few days thanks to this holiday weekend, however I did have a chance to catch up on my blog subscriptions (which were really beginning to pile up).  &lt;i&gt;My wife just peeked over my should and said I was also spending quality time with her.&lt;/i&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://tomayko.com/writings/unicorn-is-unix"&gt;Ryan Tomayko on Unicorn&lt;/a&gt; (ruby), "because it's Unix."  A neat post on using some of the unixy functionalities of ruby to develop a network service.  I'd really like to try it, but I don't have a POSIX computer up and running.  Hmmm... I should fix that.&lt;br /&gt; &lt;/li&gt;&lt;li&gt;&lt;a href="http://simonwillison.net/2009/Nov/23/node/"&gt;Simon Willison talk&lt;/a&gt;s about &lt;a href="http://nodejs.org/"&gt;Node.js&lt;/a&gt;, a server-side javascript framework which looks ridiculously cool.  Having been spending a lot of time working on MarkEdit, I can certainly appreciate some of the non-blocking/callback ideas that it implements.  However, again with the lack of POSIX.  It doesn't yet run on Windows.  I really need to remedy that.&lt;br /&gt; &lt;/li&gt;&lt;li&gt;Again from Simon Willison he mentions &lt;a href="http://flxhr.flensed.com/"&gt;flXHR&lt;/a&gt; -- A replacement for XmlHttpRequest, but implemented by an invisible flash (.swf) wrapper layer.  It's a really interesting concept, though completely hacky.  Why bother with this?  1.) get around cross-browser issues, and 2.) get around browser security issues (like making an AJAX request to API on another domain for example).&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-3298736961621702446?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/3298736961621702446/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=3298736961621702446' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/3298736961621702446'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/3298736961621702446'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/developer-blog-entries.html' title='Developer Blog Entries'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7448702334267763721</id><published>2009-11-23T16:55:00.008-10:00</published><updated>2009-11-24T09:49:48.784-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='markedit'/><category scheme='http://www.blogger.com/atom/ns#' term='wmd-editor'/><title type='text'>WMD Editor &amp; jQuery</title><content type='html'>I've been using Dana Robinson's WMD Editor fork for a couple of months now.  It was a great place to start.  The problem is, it's becoming a real pain to customize.  Small things were taking huge amounts of time to accomplish so I had a tough decision to make:  Continue to use WMD Editor and have to spend hours for customization that should only take a minute or two -OR- re-write the UI myself on top of the existing Markdown rendering component?&lt;br /&gt;&lt;br /&gt;Introducing:  &lt;a style="font-weight: bold;" href="http://bitbucket.org/tstone/jquery-markedit"&gt;MarkEdit&lt;/a&gt; -- the Javascript MarkDown editor built on jQuery (to replace WMD Editor).  It's currently being hosted on &lt;a href="http://bitbucket.org/tstone/jquery-markedit/src/"&gt;BitBucket&lt;/a&gt;.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Built On jQuery/UI -- Lighter weight code and less cross-browser issues &lt;/li&gt;&lt;li&gt;Themeroller Support -- Use existing or custom jQuery UI Themeroller themes &lt;/li&gt;&lt;li&gt;International -- Supports the loading of an alternate language &lt;/li&gt;&lt;li&gt;Sensible Defaults -- You should be able to get up and running with 1 line of javascript &lt;/li&gt;&lt;li&gt;Configurable -- Almost all defaults can be overridden very easily upon initialization &lt;/li&gt;&lt;li&gt;Public API -- Interact with whatever part you're interested in &lt;/li&gt;&lt;li&gt;Documented &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Since everybody loves screenshots, here's the editor running under a jQuery theme and using the Chinese language translation.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_epp24QszXwI/SwtNIsM71FI/AAAAAAAAACs/ktUxymI-FXM/s1600/preview1.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 338px;" src="http://3.bp.blogspot.com/_epp24QszXwI/SwtNIsM71FI/AAAAAAAAACs/ktUxymI-FXM/s400/preview1.png" alt="" id="BLOGGER_PHOTO_ID_5407500589288117330" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And again here's a translated popup asking for the URL to insert an image...&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_epp24QszXwI/SwtNdzAHEcI/AAAAAAAAAC0/xaSAQEqcvCo/s1600/preview2.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 338px;" src="http://4.bp.blogspot.com/_epp24QszXwI/SwtNdzAHEcI/AAAAAAAAAC0/xaSAQEqcvCo/s400/preview2.png" alt="" id="BLOGGER_PHOTO_ID_5407500951890629058" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;And here's preview mode using a different theme but still in Chinese...&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_epp24QszXwI/SwtOBWs0KMI/AAAAAAAAAC8/WejDs_dRw1w/s1600/preview3.png"&gt;&lt;img style="cursor: pointer; width: 400px; height: 338px;" src="http://3.bp.blogspot.com/_epp24QszXwI/SwtOBWs0KMI/AAAAAAAAAC8/WejDs_dRw1w/s400/preview3.png" alt="" id="BLOGGER_PHOTO_ID_5407501562768795842" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So far I've invested about 15 hours into the project with it being probably around 95% complete for initial coding.  There is still a TON of testing to be done.  I haven't even tried to fire it up in any of the fussy browsers yet.&lt;br /&gt;&lt;br /&gt;I actually wrote a whole lot about it so far -- it's all up on the &lt;a href="http://bitbucket.org/tstone/jquery-markedit/wiki/Home"&gt;Wiki&lt;/a&gt;.  There is still lots more documentation to come.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7448702334267763721?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7448702334267763721/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7448702334267763721' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7448702334267763721'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7448702334267763721'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/wmd-editor-jquery.html' title='WMD Editor &amp; jQuery'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_epp24QszXwI/SwtNIsM71FI/AAAAAAAAACs/ktUxymI-FXM/s72-c/preview1.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-2224340237388818952</id><published>2009-11-20T11:12:00.012-10:00</published><updated>2009-11-20T13:55:00.242-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>Using jQuery's Dialog Function to Get User Input</title><content type='html'>The&lt;a href="http://www.jqueryui.com/"&gt; jQuery UI&lt;/a&gt; library provides a really useful method:  &lt;a href="http://docs.jquery.com/UI/Dialog"&gt;dialog&lt;/a&gt;.  It allows any tag to be rendered as a fake "window" in the browser in either a modal or non-modal way.  It's easily a natural choice for any time we need a dialog to fetch input from a user.&lt;br /&gt;&lt;br /&gt;What makes it somewhat difficult for some programmers to use is that it's an asynchronous function.  It opens the dialog then keeps on executing code.  Here's why this can be confusing.&lt;br /&gt;&lt;br /&gt;Let's say for example we're building a web-based instant messaging client.  A feature of this client would be the ability for the user to change their nickname, even after they've already logged in (similar to how MSN works).  We've decided to implement this functionality by adding a button on the IM toolbar "Change Nickname".  When the user clicks it, the dialog should popup, ask them for their new nickname, and then perform whatever action should be taken to update the nickname.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;The Natural-But-Doesn't-Work Method&lt;/span&gt;&lt;br /&gt;The natural way (using synchronous code) to achieve this would be something like the following:&lt;br /&gt;&lt;script src="http://gist.github.com/239795.js?file=gistfile1.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;"&gt;(Javascript code is included above.  I'm not sure if this renders via RSS)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;But as you can see from the code, this won't work.  Why?  Dialog is an asynchronous method.  It's easy to get comfortable with synchronous dialogs, after all, Javascript has a built in one: input();  However in this case we need to develop a new strategy for working with an asynchronous dialog.&lt;br /&gt;&lt;br /&gt;Now lest you be tempted to do something like &lt;code&gt;while(!closed) { // do nothing };&lt;/code&gt; let me just ask you now:  stop!.  That's fighting the system.  That will most likely lock up the browser, and it's an absolute waste of resources.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Using Callbacks to Trigger Behavior&lt;/span&gt;&lt;br /&gt;Stepping back from the problem, what we really want is that we have some code, and we want it to run only when the user has finished using the dialog boxes.  Being asynchronous, the dialog function actually provides a measure for doing so:  Callbacks.  When we defined the OK and Cancel buttons, we specified an anonymous function() that would be called whenever that button was clicked.  This is a call back.  We can use this exact same mechanic to signal our other code when it should run.&lt;br /&gt;&lt;br /&gt;But how?  Here's some updated code...&lt;br /&gt;&lt;script src="http://gist.github.com/239877.js?file=gistfile1.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-style: italic;"&gt;(Javascript code is included above.  I'm not sure if this renders via RSS)&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To accomplish this would could create a "gating" function -- a function which be responsible for dispatching either a call to our dialog, or a call to the code which should be run when we have a value.  This is implemented in the example code as &lt;code&gt;getAndSetNewNickname&lt;/code&gt;.  In addition to the new method we've also added a parameter to the &lt;code&gt;getNewNick&lt;/code&gt; method: callback.  This parameter will allow us to pass a function that will be called whenever the "Ok" button is clicked.  In our implementation, this function happens to be the same function that's calling it.  The situation we end up with is a recursive callback which will call our function a 2nd time when it has a value.&lt;br /&gt;&lt;br /&gt;The end result?  Our setNewNickname method only gets called when we actually have a value.  You've successfully gotten user input, and didn't have to restore to timeouts or a weird while loop solution.&lt;br /&gt;&lt;br /&gt;Happy jQuery'ing!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-2224340237388818952?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/2224340237388818952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=2224340237388818952' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2224340237388818952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2224340237388818952'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/using-jquerys-dialog-function-to-get.html' title='Using jQuery&apos;s Dialog Function to Get User Input'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-1547054071704342055</id><published>2009-11-17T18:22:00.007-10:00</published><updated>2009-11-17T19:59:32.172-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web-frameworks'/><category scheme='http://www.blogger.com/atom/ns#' term='template'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>A Truly Modular Web Framework</title><content type='html'>Web development is weird.  It's weird because the rendering of the UI (in HTML) feels so incredibly disconnected from the logic and objects that are represented by the UI.  It's weird because the UI is usually rendered by a server-side template facility, but is occasionally rendered by cilent-side javascript.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's an example.  I find the &lt;a href="http://en.wikipedia.org/wiki/Decorator_pattern"&gt;decorator pattern&lt;/a&gt; really cool.  Let's say I'm writing a blog.  Actually, everyone writes blogs.  Let's say I'm writing a &lt;a href="http://www.stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; clone.  In Stack Overflow users can ask questions.  Other users can answer them.  Users can also comment on a question (different from an answer), or comment on an answer made by another user.  The shared functionality is placing comments on an object.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The mechanics of a comment aren't particularly unique that they need to be bound to a specific object.  A UI is presented for a user to type in a message.  The server should store the messages and associate them with a specific object.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So let's say in this example the comment functionality is implemented as a decorator generically which can be applied to any object.  So we have the question functionality, implemented perhaps as a class, and we can decorate it with the comment functionality.  If later we decided to implement a "News" section, and need each news post to also have comments, we could also decorate the news posts using the same decorator as well.  The comment functionality at this point is a self-contained decorator.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Programatically this doesn't seem like a difficult suggestion (maybe), but what happens when we need to render this out to HTML?  Even worse, let's say the comment functionality requires javascript to fetch comments via AJAX -- then what?&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I think the problem here is that whatever is doing the rendering doesn't know what the object Question or Answer looks like.  It also doesn't know what the decorator Comments looks like either.  If it did, it could render them intelligently.  I'm thinking of decorated rendering here.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Functionality similar to what I'm describing can be achieved in current web frameworks, but usually through things like template tags, framework widgets, or partially rendered views.  But that's not quite what I'm talking about.  It still requires a lot of repetition and isn't really modular.  Template tags still need to be coded very specifically.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Part of the issue is that in web development frameworks we are using HTML to describe too many things.  Take Django's template system, which despite many people's complaints, is quite functional and I actually kind of like.  Django templates provide the facilities of context (passing variable data to the template), template tags (custom bits of code that can be arbitrarily executed) and filters (helpers to modify data to conform to a particular output).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But for each page template that is created, the following things are being handled in that one template:&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;How a given object should be rendered&lt;/li&gt;&lt;li&gt;How the page is layed out&lt;/li&gt;&lt;li&gt;What media files need to be linked/included in order for this page to rendered properly (js/css/etc.)'&lt;/li&gt;&lt;/ol&gt;&lt;div&gt;I'm thinking out loud here, but what would a rendering system (and supporting framework) look like which separated what the template system did into separate pieces?&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Each object has it's own multiple HTML "interpretations", outfits if you will for the class.  Decorators can call the decorated class's outfits if needed.&lt;/li&gt;&lt;li&gt;Page templates become more semantic and less about display -- they're interested in where pieces go on the page, not necessarily the markup to render it to appear a certain way.  More of an outline than a sea of div tags.  It seems very often the task of laying out what goes on a page, and implementing the UI are two completely separate tasks, and yet most template systems I've used force you to do them together.&lt;/li&gt;&lt;li&gt;Media files are handled programatically in code, somewhere, somehow.  An object outfit can for example say "I need this javascript file" and the template engine will react accordingly.  This prevents &amp;lt;script&amp;gt; tags and @import statements being strewn all over, and allows the framework to pass those media files off to a handler that decide if something should be done with them (ie automatically put &amp;lt;script&amp;gt; at bottom and &amp;lt;link&amp;gt; at the top, compress the JS, etc.).  Disclaimer:  This is probably a harder problem than I'm thinking it is, especially when AJAX gets involved.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;More to come on this.  I might have to do some experimental code.  Should template engines help you by handling some pieces of the Javascript on the client?  Hmm...&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-1547054071704342055?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/1547054071704342055/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=1547054071704342055' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1547054071704342055'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1547054071704342055'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/truly-modular-web-framework.html' title='A Truly Modular Web Framework'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-2699090366307013807</id><published>2009-11-06T13:41:00.006-10:00</published><updated>2009-11-06T13:55:28.100-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='south'/><category scheme='http://www.blogger.com/atom/ns#' term='fork'/><title type='text'>Django South Fork</title><content type='html'>I really like &lt;a href="http://south.aeracode.org/"&gt;South&lt;/a&gt; (the database migration app for &lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt;).  I even wrote about it a little bit on &lt;a href="http://stackoverflow.com/questions/1590944/currently-using-django-evolution-is-south-better-and-worth-switching/1591183#1591183"&gt;Stack Overflow (#1590944)&lt;/a&gt;.  In short, South lets you create python-based migrations to change the database schema.  The translation is that you can have a Django model, make a change to it, and then have south change the database schema for you.  It's generally awesome.&lt;br /&gt;&lt;br /&gt;But there's this one catch to it:  It doesn't seem to be able to order migrations on a project-wide basis.  This is a biggie for me.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Consider this scenario:&lt;/span&gt;&lt;br /&gt;app1.model has a foreign key to app2.model.  App2 is a 3rd party app and you decide to extend it.  So you create app3.model which inherits from app2.model.  Then, you update the foreign key on app1.model to point to app3.model.  In order for the app1 migration to run successfully, the app3 migration that creates the table app3_model needs to have run first.  How do you control this?&lt;br /&gt;&lt;br /&gt;So I &lt;a href="http://bitbucket.org/tstone/south/overview/"&gt;forked South&lt;/a&gt;.  Yeah, bitbucket and all.&lt;br /&gt;&lt;br /&gt;My fork works by creating a manifest file (in JSON format for easy human and VCS editing).  Each time a 'startmigration' is created, a new entry is added to the manifest.  The manifest entries can be re-ordered whenever without any real consequence.  When a 'manage.py migrate' is run, if no app or migration number are given, the migrate command will default to running the migrations in the order of the manifest.  If an app or a migration number are given, 'migrate' will run as it normally does.&lt;br /&gt;&lt;br /&gt;I also added another command 'manage.py create_manifest' if, for whatever reason, you need to manually create a new manifest file.&lt;br /&gt;&lt;br /&gt;For the most part my fork is a drop-in replacement.  You can use it, gaining the manifest functionality, and not have to modify any of the way you work.&lt;br /&gt;&lt;br /&gt;The only caveat I've found so far is in creating a new manifest file.  When a few manifest is created, it generates its list from the applied manifests in the south_migrationhistory table.  What it doesn't do is add manifests that are waiting to be applied to the new list.  It's a limitation.  At some point I'll probably develop a fix for it, but in the mean time you can add entries to the manifest file manually if they need to be applied (but have already been generated by 'startmigration').&lt;br /&gt;&lt;br /&gt;Happy migrating.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-2699090366307013807?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/2699090366307013807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=2699090366307013807' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2699090366307013807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2699090366307013807'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/django-south-fork.html' title='Django South Fork'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-4056425859910733793</id><published>2009-11-05T16:39:00.007-10:00</published><updated>2009-11-05T16:47:45.056-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='komodo-themes'/><category scheme='http://www.blogger.com/atom/ns#' term='komodo-edit'/><title type='text'>Komodo Edit Theme:  Candybar Ver 1</title><content type='html'>Well what's the fun of a &lt;a href="http://urlencode.blogspot.com/2009/11/komodo-edit.html"&gt;new editor&lt;/a&gt; without a new color scheme to go along with it?  Here's my latest Komodo Edit color scheme.  The colors were inspired by Mozilla &lt;a href="https://bespin.mozilla.com/"&gt;Bespin&lt;/a&gt;, but I've certainly taken things in my own direction.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;Python&lt;br /&gt;&lt;/span&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_epp24QszXwI/SvONE9kTJiI/AAAAAAAAACU/qerG_VW3kyo/s1600-h/candybar-python.jpg"&gt;&lt;img style="cursor: pointer; width: 400px; height: 270px;" src="http://4.bp.blogspot.com/_epp24QszXwI/SvONE9kTJiI/AAAAAAAAACU/qerG_VW3kyo/s400/candybar-python.jpg" alt="" id="BLOGGER_PHOTO_ID_5400815494533162530" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:180%;" &gt;&lt;br /&gt;HTML&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_epp24QszXwI/SvONJXrFR0I/AAAAAAAAACc/vhtey0qpg6E/s1600-h/candybar-html.jpg"&gt;&lt;img style="cursor: pointer; width: 400px; height: 189px;" src="http://4.bp.blogspot.com/_epp24QszXwI/SvONJXrFR0I/AAAAAAAAACc/vhtey0qpg6E/s400/candybar-html.jpg" alt="" id="BLOGGER_PHOTO_ID_5400815570260412226" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Instead of uploading the file somewhere (which would create a broken link a year from now), I'm going to insert the text right here.&lt;br /&gt;&lt;br /&gt;To use this theme copy all of the text and save it as "Candybar ver 1.ksf".  Save the file to &lt;code&gt;C:\Documents and Settings\[username]\Application Data\ActiveState\KomodoEdit\[version]\schemes&lt;/code&gt;.  Under Windows Vista and 7 Save to the "Users" folder instead of "Documents and Settings".&lt;br /&gt;&lt;span style="text-decoration: underline;"&gt;&lt;br /&gt;&lt;/span&gt;This is my first release of this theme.  Expect minor tweaks in the future.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Version = 4&lt;br /&gt;&lt;br /&gt;Booleans = {'caretLineVisible': False, 'preferFixed': True, 'useSelFore': False}&lt;br /&gt;&lt;br /&gt;CommonStyles = {'attribute name': {'fore': 8904824},&lt;br /&gt;'attribute value': {'fore': 13676752},&lt;br /&gt;'bracebad': {'back': 2501686, 'bold': 1, 'fore': 128},&lt;br /&gt;'bracehighlight': {'back': 2501686, 'bold': 1, 'fore': 2721475},&lt;br /&gt;'classes': {'back': 2501686, 'bold': True, 'fore': 5546676},&lt;br /&gt;'comments': {'back': 2501686, 'fore': 9411235, 'italic': 1},&lt;br /&gt;'control characters': {'back': 2501686, 'fore': 7897997},&lt;br /&gt;'default_fixed': {'back': 2501686,&lt;br /&gt;                'bold': False,&lt;br /&gt;                'eolfilled': 0,&lt;br /&gt;                'face': 'Monaco',&lt;br /&gt;                'fore': 11581373,&lt;br /&gt;                'hotspot': 0,&lt;br /&gt;                'italic': 0,&lt;br /&gt;                'size': 10,&lt;br /&gt;                'useFixed': 1},&lt;br /&gt;'default_proportional': {'back': 2501686,&lt;br /&gt;                       'bold': 0,&lt;br /&gt;                       'eolfilled': 0,&lt;br /&gt;                       'face': 'Verdana',&lt;br /&gt;                       'fore': 11581373,&lt;br /&gt;                       'hotspot': 0,&lt;br /&gt;                       'italic': 0,&lt;br /&gt;                       'size': 10,&lt;br /&gt;                       'useFixed': 0},&lt;br /&gt;'fold markers': {'back': 2501686, 'fore': 7897997},&lt;br /&gt;'functions': {'fore': 7897997},&lt;br /&gt;'identifiers': {'fore': 7897997},&lt;br /&gt;'indent guides': {'back': 6710886, 'fore': 7897997},&lt;br /&gt;'keywords': {'fore': 5546676},&lt;br /&gt;'keywords2': {'fore': 8904824},&lt;br /&gt;'linenumbers': {'fore': 7897997},&lt;br /&gt;'numbers': {'fore': 12688752},&lt;br /&gt;'operators': {'fore': 4222857},&lt;br /&gt;'preprocessor': {'fore': 7897997},&lt;br /&gt;'regex': {'fore': 16751415},&lt;br /&gt;'stderr': {'fore': 7897997, 'italic': 1},&lt;br /&gt;'stdin': {'fore': 7897997},&lt;br /&gt;'stdout': {'fore': 7897997},&lt;br /&gt;'stringeol': {'back': 6697728, 'eolfilled': 1, 'fore': 7897997},&lt;br /&gt;'strings': {'fore': 12688752},&lt;br /&gt;'tags': {'fore': 52735},&lt;br /&gt;'variables': {'fore': 0}}&lt;br /&gt;&lt;br /&gt;LanguageStyles = {'C#': {'UUIDs': {'fore': 0},&lt;br /&gt;     'commentdockeyword': {'fore': 0},&lt;br /&gt;     'commentdockeyworderror': {'fore': 2293759},&lt;br /&gt;     'globalclass': {'fore': 7632127},&lt;br /&gt;     'verbatim': {'fore': 0}},&lt;br /&gt;'C++': {'UUIDs': {'fore': 0},&lt;br /&gt;      'commentdockeyword': {'fore': 0},&lt;br /&gt;      'commentdockeyworderror': {'fore': 2293759},&lt;br /&gt;      'globalclass': {'fore': 7632127},&lt;br /&gt;      'verbatim': {'fore': 0}},&lt;br /&gt;'CSS': {'classes': {'fore': 12629703},&lt;br /&gt;      'identifiers': {'fore': 16634024},&lt;br /&gt;      'ids': {'fore': 7986943},&lt;br /&gt;      'operators': {'fore': 11599871},&lt;br /&gt;      'tags': {'fore': 3977471},&lt;br /&gt;      'values': {'fore': 14155775}},&lt;br /&gt;'Diff': {'additionline': {'fore': 7667711},&lt;br /&gt;       'chunkheader': {'fore': 7632127},&lt;br /&gt;       'deletionline': {'fore': 16777076},&lt;br /&gt;       'diffline': {'fore': 9868950, 'italic': 1},&lt;br /&gt;       'fileline': {'fore': 8904824, 'italic': 1}},&lt;br /&gt;'Errors': {'Error lines': {'fore': 16777113, 'hotspot': 1, 'italic': 1}},&lt;br /&gt;'HTML': {'attribute name': {'fore': 16428914},&lt;br /&gt;       'attributes': {'fore': 13369361},&lt;br /&gt;       'cdata': {'fore': 7667711},&lt;br /&gt;       'default': {'fore': 11581373},&lt;br /&gt;       'entity references': {'fore': 11581373},&lt;br /&gt;       'keywords': {'fore': 10091996},&lt;br /&gt;       'numbers': {'fore': 13625852},&lt;br /&gt;       'operators': {'fore': 7897997},&lt;br /&gt;       'strings': {'fore': 5546676},&lt;br /&gt;       'tags': {'fore': 7897997},&lt;br /&gt;       'variables': {'fore': 2721475}},&lt;br /&gt;'IDL': {'UUIDs': {'fore': 0},&lt;br /&gt;      'commentdockeyword': {'fore': 0},&lt;br /&gt;      'commentdockeyworderror': {'fore': 2293759},&lt;br /&gt;      'globalclass': {'fore': 7632127},&lt;br /&gt;      'verbatim': {'fore': 0}},&lt;br /&gt;'Java': {'UUIDs': {'fore': 0},&lt;br /&gt;       'commentdockeyword': {'fore': 0},&lt;br /&gt;       'commentdockeyworderror': {'fore': 2293759},&lt;br /&gt;       'globalclass': {'fore': 7632127},&lt;br /&gt;       'verbatim': {'fore': 0}},&lt;br /&gt;'JavaScript': {'UUIDs': {'fore': 0},&lt;br /&gt;             'commentdockeyword': {'fore': 0},&lt;br /&gt;             'commentdockeyworderror': {'fore': 2293759},&lt;br /&gt;             'globalclass': {'fore': 7632127},&lt;br /&gt;             'verbatim': {'fore': 0}},&lt;br /&gt;'PHP': {'attribute name': {'fore': 16428914},&lt;br /&gt;      'attribute value': {'fore': 13676752},&lt;br /&gt;      'bracehighlight': {'back': 5197568},&lt;br /&gt;      'identifiers': {'fore': 10091996},&lt;br /&gt;      'keywords': {'fore': 11140834},&lt;br /&gt;      'operators': {'fore': 39423},&lt;br /&gt;      'strings': {'fore': 15523820},&lt;br /&gt;      'tags': {'fore': 39423},&lt;br /&gt;      'variables': {'fore': 15838576}},&lt;br /&gt;'Perl': {'here documents': {'bold': 1, 'fore': 8183004}},&lt;br /&gt;'Python': {'identifiers': {'fore': 7897997}, 'keywords2': {'fore': 11581373}},&lt;br /&gt;'Regex': {'charclass': {'fore': 10092543, 'italic': 1},&lt;br /&gt;        'charescape': {'fore': 12303104, 'italic': 1},&lt;br /&gt;        'charset_operator': {'fore': 8372175, 'size': 12},&lt;br /&gt;        'comment': {'fore': 12303291, 'italic': 1},&lt;br /&gt;        'default': {},&lt;br /&gt;        'eol': {},&lt;br /&gt;        'groupref': {'fore': 10092543, 'italic': 1},&lt;br /&gt;        'grouptag': {'fore': 8372175, 'size': 8},&lt;br /&gt;        'match_highlight': {'back': 6908265},&lt;br /&gt;        'operator': {'fore': 8372175, 'size': 12},&lt;br /&gt;        'quantifier': {'bold': 1, 'fore': 10092543, 'size': 12},&lt;br /&gt;        'special': {'bold': 1, 'fore': 10092543},&lt;br /&gt;        'text': {}},&lt;br /&gt;'XML': {'cdata': {'fore': 7667711},&lt;br /&gt;      'cdata content': {'fore': 7632127},&lt;br /&gt;      'cdata tags': {'fore': 7667711},&lt;br /&gt;      'data': {'fore': 14474353},&lt;br /&gt;      'declarations': {'fore': 13418403},&lt;br /&gt;      'entity references': {'fore': 14474353},&lt;br /&gt;      'pi content': {'fore': 7632127},&lt;br /&gt;      'pi tags': {'fore': 7667711},&lt;br /&gt;      'prolog': {'fore': 0},&lt;br /&gt;      'xpath attributes': {'fore': 7667711},&lt;br /&gt;      'xpath content': {'fore': 16741120},&lt;br /&gt;      'xpath tags': {'fore': 16777204}}}&lt;br /&gt;&lt;br /&gt;MiscLanguageSettings = {}&lt;br /&gt;&lt;br /&gt;Colors = {'activeBreakpointColor': 526573,&lt;br /&gt;'bookmarkColor': 13434879,&lt;br /&gt;'callingLineColor': 16776960,&lt;br /&gt;'caretFore': 16777215,&lt;br /&gt;'caretLineBack': 3355443,&lt;br /&gt;'currentLineColor': 16777011,&lt;br /&gt;'edgeColor': 3355443,&lt;br /&gt;'foldMarginColor': None,&lt;br /&gt;'pendingBreakpointColor': 569096,&lt;br /&gt;'selBack': 6238471,&lt;br /&gt;'selFore': 65535,&lt;br /&gt;'whitespaceColor': 16777215}&lt;br /&gt;&lt;br /&gt;Indicators = {'find_highlighting': {'alpha': 100,&lt;br /&gt;                    'color': 1110271,&lt;br /&gt;                    'draw_underneath': True,&lt;br /&gt;                    'style': 7},&lt;br /&gt;'linter_error': {'alpha': 100,&lt;br /&gt;               'color': 255,&lt;br /&gt;               'draw_underneath': True,&lt;br /&gt;               'style': 1},&lt;br /&gt;'linter_warning': {'alpha': 100,&lt;br /&gt;                 'color': 32768,&lt;br /&gt;                 'draw_underneath': True,&lt;br /&gt;                 'style': 1},&lt;br /&gt;'soft_characters': {'alpha': 100,&lt;br /&gt;                  'color': 13209,&lt;br /&gt;                  'draw_underneath': False,&lt;br /&gt;                  'style': 6},&lt;br /&gt;'tabstop_current': {'alpha': 100,&lt;br /&gt;                  'color': 3355647,&lt;br /&gt;                  'draw_underneath': True,&lt;br /&gt;                  'style': 7},&lt;br /&gt;'tabstop_pending': {'alpha': 100,&lt;br /&gt;                  'color': 16751001,&lt;br /&gt;                  'draw_underneath': True,&lt;br /&gt;                  'style': 6},&lt;br /&gt;'tag_matching': {'alpha': 100,&lt;br /&gt;               'color': 33023,&lt;br /&gt;               'draw_underneath': True,&lt;br /&gt;               'style': 7}}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-4056425859910733793?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/4056425859910733793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=4056425859910733793' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4056425859910733793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4056425859910733793'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/komodo-edit-theme-candybar-ver-1.html' title='Komodo Edit Theme:  Candybar Ver 1'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_epp24QszXwI/SvONE9kTJiI/AAAAAAAAACU/qerG_VW3kyo/s72-c/candybar-python.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6554036714275299935</id><published>2009-11-05T16:22:00.011-10:00</published><updated>2009-11-05T16:54:28.255-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='komodo-edit'/><title type='text'>Komodo Edit</title><content type='html'>I've been using &lt;a href="http://www.netbeans.org/features/python/"&gt;Netbeans for Python&lt;/a&gt; for several releases now.  Don't get me wrong, it's not a bad editor.  The problem is that I've used &lt;a href="http://en.wikipedia.org/wiki/Microsoft_Visual_Studio"&gt;Visual Studio&lt;/a&gt; which includes &lt;a href="http://en.wikipedia.org/wiki/IntelliSense"&gt;Intellisense&lt;/a&gt;.  In &lt;a href="http://blog.stackoverflow.com/2009/04/podcast-50/"&gt;Episode 50 of the Stack Overflow podcast&lt;/a&gt; &lt;a href="http://www.joelonsoftware.com/"&gt;Joel&lt;/a&gt; refereed to Intellisense as "crack" -- once developers try it they're hooked.  After coding in Visual Basic, VB.NET, and then C# I can say it's a &lt;span style="font-weight: bold; font-style: italic;"&gt;very&lt;/span&gt; nice feature.&lt;br /&gt;&lt;br /&gt;But I'm writing python now (and playing house with Ruby).  Python is a dynamic language.  I threw all hope of code completion and automatically displaying method tooltips away.&lt;br /&gt;&lt;br /&gt;Then I found &lt;a href="http://www.activestate.com/komodo_edit/"&gt;Komodo Edit&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;It isn't perfect, but it's good enough™, perhaps even the best there is for Python at the moment (though what's the rumor I hear about &lt;a href="http://www.google.com/search?q=visual+studio+2010+python&amp;amp;ie=utf-8&amp;amp;oe=utf-8&amp;amp;aq=t&amp;amp;rls=org.mozilla:en-US:official&amp;amp;client=firefox-a"&gt;Visual Studio 2010 supporting python&lt;/a&gt;?).&lt;br /&gt;&lt;br /&gt;Aside from the code completion support, Komodo Edit has some neat features...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;The Toolbox&lt;/span&gt;&lt;br /&gt;That's cool!  Komodo Edit (KE from this point on) offers a Toolbox running vertically on the right part of the interface.  IT allows you to create items in a tree-view format which can do things.  Lots of things.  Execute a shell script: Check!  Open a URL: Check!  Insert a custom code snippet: Check!  I guarantee you'll find like 10 things to stick there the first time you use it.&lt;br /&gt;&lt;br /&gt;For Django development for example I've added items to open...&lt;br /&gt;http://localhost:8000&lt;br /&gt;http://localhost:8000/admin&lt;br /&gt;The project issue tracker&lt;br /&gt;The mercurial repository page&lt;br /&gt;&lt;br /&gt;And code snippets to...&lt;br /&gt;Skeleton Admin model&lt;br /&gt;mode_save method&lt;br /&gt;Skeleton Model&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Ctrl+R&lt;/span&gt;&lt;br /&gt;I know this is available in other places, but it's amazing to see it as a default in KE.  Ctrl+R will launch the command line.  I probably find this particularly notable since back in 2001 I released a Windows utility "CmdHotKey" which did pretty much the exact same thing (except it was Windows-wide).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Project File Tree View&lt;/span&gt;&lt;br /&gt;I was really surprised to see that KE shows a tree view of the folder structure of your project.  I assumed that would only be available in the paid-for version (Komodo &lt;span style="font-style: italic;"&gt;IDE&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Color Schemes&lt;/span&gt;&lt;br /&gt;What's an editor that forces you to look at a white background and black text?&lt;br /&gt;&lt;br /&gt;KE also supports other neat stuff that I didn't mention like recordable macros.&lt;br /&gt;&lt;br /&gt;All-in-all it's a very feature-packed editor and for free.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;But it has some quirks...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Sadly, it's not perfect.&lt;br /&gt;&lt;br /&gt;If one is using the tree view showing the project folders, right-clicking and choosing "Add &gt;" gives several options including:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;New Folder...&lt;/li&gt;&lt;li&gt;New Live Folder...&lt;/li&gt;&lt;/ul&gt;This is really stupid.  New Folder creates a project-only organizational unit which from what I can tell serves no actual purpose.  New Live Folder creates an actual disk directory like you'd expect.&lt;br /&gt;&lt;br /&gt;Another thing -- If you move or delete a file, perhaps with mercurial so the move changes are in the repository, KE will show the old and new file, but the old file will be marked read and you'll need to remove it form the project manually.  It would be nicer if since it detected the change it would just remove it from the project.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6554036714275299935?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6554036714275299935/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6554036714275299935' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6554036714275299935'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6554036714275299935'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/komodo-edit.html' title='Komodo Edit'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-1784189810790503648</id><published>2009-11-02T14:29:00.029-10:00</published><updated>2009-11-03T08:14:00.960-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mercurial'/><category scheme='http://www.blogger.com/atom/ns#' term='wsgi'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Mercurial hgwebdir under Apache using WSGI</title><content type='html'>The &lt;a href="http://mercurial.selenic.com/wiki/HgWebDirStepByStep"&gt;doc for setting up HgWebDirStepByStep&lt;/a&gt; sure make it seem harder than it really is.  Maybe it is hard.  Maybe it's just that I spent so much time trying to understand the doc, that when I actually just tried it myself it wasn't that bad.&lt;br /&gt;&lt;br /&gt;I've been thinking of moving over from SVN to Mercurial (hg) but wasn't sure if it was worth the fuss.  I moved.  It seems nice.  Here's a guide, including tutorials for both the server and client, to switch to hgwebdir (server) /TortoiseHg (client).&lt;br /&gt;&lt;br /&gt;Let's get started.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;Setting up Mercurial&lt;/span&gt; (Part 1)&lt;br /&gt;Before we configure the server, the following must already be installed and running:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.google.com/url?q=http://www.python.org/download/&amp;amp;ei=qnrvSrXZOYyCswOJr-TwBQ&amp;amp;sa=X&amp;amp;oi=smap&amp;amp;resnum=1&amp;amp;ct=result&amp;amp;cd=1&amp;amp;ved=0CAoQqwMoAA&amp;amp;usg=AFQjCNEI4nBza0bIdJkjxBQz7594bslU_w"&gt;Python 2.x (not ActivePython)&lt;br /&gt;&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://httpd.apache.org/"&gt;Apache 2&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://code.google.com/p/modwsgi/"&gt;mod_wsgi&lt;/a&gt; under Apache&lt;/li&gt;&lt;/ul&gt;I'm going to be demonstrating the setup on Windows 2000 Server as the host for hgwebdir/apache, and Windows Xp as the developer machine.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1. Setup the Directory Structure&lt;/span&gt;&lt;br /&gt;Create the following structure:&lt;pre class="syntax"&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;C&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt;&lt;span style="border: 1px solid rgb(255, 0, 0);"&gt;\&lt;/span&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;Mercurial&lt;/span&gt;&lt;br /&gt;&lt;span style="border: 1px solid rgb(255, 0, 0);"&gt;\&lt;/span&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;src&lt;/span&gt;&lt;br /&gt;&lt;span style="border: 1px solid rgb(255, 0, 0);"&gt;\&lt;/span&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;hgwebdir&lt;/span&gt;&lt;br /&gt;&lt;span style="border: 1px solid rgb(255, 0, 0);"&gt;\&lt;/span&gt;&lt;span style="color: rgb(6, 40, 115); font-weight: bold;"&gt;repositories&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We'll be putting files into these folders shortly.&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;&lt;br /&gt;2.  Download mercurial source&lt;/span&gt;&lt;br /&gt;Whatever you do,&lt;span style="font-style: italic;"&gt; don't install TortoiseHg on the server&lt;/span&gt;.  To run hgwebdir you're going to want to have compiled mercurial yourself.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://mercurial.selenic.com/"&gt;Download Mercuria sourcel&lt;/a&gt;  (Be sure to not get the binary)&lt;br /&gt;&lt;br /&gt;After download, extract the contents to C:\Mercurial\src, or wherever you created your 'src' folder in the directory structure from above.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3.  Build Mercurial&lt;/span&gt;&lt;br /&gt;(Instructions taken mostly from &lt;a href="http://mercurial.selenic.com/wiki/WindowsInstall"&gt;WindowsInstall wiki page&lt;/a&gt;)&lt;br /&gt;Open up the command prompt...&lt;pre class="syntax"&gt;C:\&amp;gt;&lt;br /&gt;C:\&amp;gt; cd mercurial\src&lt;br /&gt;C:\Mercurial\src&amp;gt; python setup.py --pure build_py -c -d . build_ext -i build_mo --force&lt;br /&gt;C:\Mercurial\src&amp;gt; python setup.py --pure install --force--force&lt;br /&gt;&lt;/pre&gt;Mercurial will now be built and installed into your python site-packages folder.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4.  Setup a 3-way Diff tool&lt;/span&gt;&lt;br /&gt;&lt;a href="http://sourceforge.net/projects/kdiff3/"&gt;Download KDiff3&lt;/a&gt; from SourceForge.  Install using the included windows setup binary.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;5.  Configure Environment Variables&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Add "C:\Python26\Scripts" and  "C:\Program Files\KDiff3" to your PATH environment variable.&lt;br /&gt;&lt;br /&gt;You will need to be logged into an account that has admin right on the machine to setup this next part.  Right-click on "My Computer" and choose "Properties".  Choose "Advanced", then click the button "Environment Variables."  In the list at the bottom, "System variables", scroll down and find PATH.  Click "Edit".  At the end of the textbox add a semi-colon, then type in the path from above.  Your PATH variable should then resemble...&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-style: italic;"&gt;...;C:\Program Files\KDiff3;C:\Program Files\KDiff3;&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;span style="font-weight: bold;"&gt;6.  Create a local Mercurial configuration file&lt;/span&gt;&lt;br /&gt;Windows Explorer has a limitation that won't let you create a new file that starts with a period.  The way around this is to use notepad on the command line to type in the name of a file with a period.&lt;br /&gt;&lt;br /&gt;Mercurial uses a local file named ".hgrc" to configure itself about which 3-way merge tool it should use and similar things to that nature.&lt;br /&gt;&lt;br /&gt;Follow these commands to create the .hgrc file for the server:&lt;br /&gt;&lt;pre class="syntax"&gt;C:\&amp;gt;&lt;br /&gt;C:\&amp;gt; cd Python26\Scripts&lt;br /&gt;C:\Python26\Scripts&amp;gt; notepad .hgrc&lt;br /&gt;&lt;/pre&gt;This will cause notepad to pop up and ask if you want to create a new file named .hgrc.  You do.  Here's the basic configuration file for mercurial.  Edit these settings to whatever you need to be.&lt;br /&gt;&lt;pre class="syntax"&gt;[ui]&lt;br /&gt;editor = Notepad&lt;br /&gt;username = hgadmin &amp;lt;webmaster@yourdomain.com&amp;gt;&lt;br /&gt;&lt;br /&gt;[merge-tools]&lt;br /&gt;kdiff3.priority=-1&lt;br /&gt;kdiff3.args=-L1 base --L2 local --L3 other $base $local $other -o $output&lt;br /&gt;kdiff3.regkey=Software\KDiff3&lt;br /&gt;kdiff3.regappend=\kdiff3.exe&lt;br /&gt;kdiff3.fixeol=True&lt;br /&gt;kdiff3.gui=True&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;You've got Mercurial installed now!  If you open a command prompt and type 'hg' it should print out the command's help.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:180%;"&gt;Setting up Apache/hgwebdir&lt;/span&gt;&lt;br /&gt;Hmmm... ok maybe that is a lot of work.  It didn't seem like it at the time.&lt;br /&gt;&lt;br /&gt;Going into this, I'm assuming you've already got Apache with mod_wsgi up and running.  I should point out that you can technically run hgwebdir under CGI or mod_python, but as WSGI is the hotness now days probably a lot of people have already started using that.&lt;br /&gt;&lt;br /&gt;Good news is, I think this part is a lot easier.&lt;br /&gt;&lt;br /&gt;On my setup I've only got 1 server, and so I tend to run things as Virtual Hosts.  If you're already running Apache I'm assuming you've got the smarts to change this config if you like a different setup, but for the example httpd.conf below, I'll be doing a virtual host.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;1.  Create our WSGI handler file&lt;/span&gt;&lt;br /&gt;The WSGI file is really simple.  We basically need to 1.) import the necessary modules, then 2.) pass a new instance of hgwebdir with the configuration file path back to WSGI handler.&lt;br /&gt;&lt;script src="http://gist.github.com/225263.js"&gt;&lt;/script&gt;&lt;br /&gt;Create this script and save it as "hgwebdir.wsgi" into the C:\Mercurial\hgwebdir folder.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;2.  WSGI app config file&lt;/span&gt;&lt;br /&gt;You probably noticed in the code above that we're referencing a config file that doesn't yet exist.  Let's go ahead and create that.&lt;br /&gt;&lt;pre class="syntax"&gt;[web]&lt;br /&gt;style = coal&lt;br /&gt;&lt;br /&gt;[paths]&lt;br /&gt;/ = C:/Mercurial/repositories/**&lt;br /&gt;&lt;/pre&gt;Save this file as "hgweb.config" into the C:\Mercurial\hgwebdir folder.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;3.  Configure Apache&lt;/span&gt;&lt;br /&gt;So we've got our WSGI script and config file setup.  All that's left now is to configure Apache.&lt;br /&gt;&lt;br /&gt;We need to have a few things in our Apache conf in order to get everything working:  1.) A new virtual host to hold all of our Mercurial settings, 2.) a WSGIScriptAlias to make sure our hgwebdir.wsgi file handles web request, 3.) authentication (skip this if you want a public repository), and 4.) SSL.&lt;br /&gt;&lt;script src="http://gist.github.com/225279.js"&gt;&lt;/script&gt;&lt;br /&gt;All of the above goes in httpd.conf.&lt;br /&gt;&lt;br /&gt;There's lots of stuff going on in there.  Let's look at a bit of it.&lt;br /&gt;&lt;br /&gt;Even though we've configured hg.yourdomain.com, unless you have actually setup an A record of "hg" for yourdomain.com, that address won't actually resolve (ie. it won't work).&lt;br /&gt;&lt;br /&gt;The WSGIScriptAliasMatch is actually what routes our incoming Mercurial requests to the hgwebdir.wsgi file for handling.  &lt;span style="font-style: italic; font-weight: bold;"&gt;In order for this directive to work, you'll need to have mod_wsgi enabled in your httpd.conf file&lt;/span&gt;.  (this is outside the scope of this post)&lt;br /&gt;&lt;br /&gt;Just copy and paste the SSL configuration from elsewhere in your config file if you're already using it.  If you don't have a cert or don't want to use SSL then delete all of that section and change the ports at the top back to 80.&lt;br /&gt;&lt;br /&gt;Lastly, did you notice the auth part of the configuration?  We're using AuthType Basic and we have a user file "C:\Mercurial\accounts".  We haven't created that yet, so let's go ahead and do so.&lt;br /&gt;&lt;br /&gt;In Part 2 when we look at setting up the client, I'll show you how to configure TortoiseHg to save the user/pass that will be authenticated here by Apache.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;4.  Setting up Mercurial Users&lt;/span&gt;&lt;br /&gt;It's important to note at this point that the authentication is being handled by Apache and not Mercurial.  In my setup we're just using HTTP Basic, however if your Apache install is already using something better (LDAP, DBM, whatever), then by all means use that instead.&lt;br /&gt;&lt;br /&gt;If you've never made an Apache passwd file, it's an easy process.  Apache gives you a tool "htpasswd" which allows you to create a new passwd file or to add new users to an existing file.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;* Important:  I don't know where your Apache is installed, so you'll need to interpret some of these paths for your specific machine.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;htpasswd is located in the Apache bin folder.  We're talking something to the &lt;span style="font-style: italic;"&gt;effect of&lt;/span&gt; C:\Apache2.2\bin\htpasswd.exe.  Figure that out, and then use the command line as indicated below to create our accounts file for Mercurial.&lt;pre&gt;C:\&amp;gt;&lt;br /&gt;C:\&amp;gt; cd Apache2.2\bin&lt;br /&gt;C:\Apache2.2\bin&amp;gt; htpasswd "C:\Mercurial\accounts" testuser&lt;br /&gt;&lt;/pre&gt;This will create the file "accounts" in C:\Mercurial, add a new user "testuser" and then prompt you for a password for the new user.  You can repeat this command later on to add new users.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;5.  Bask in how fashionable you are using Mercurial&lt;/span&gt;&lt;br /&gt;If everything went well up to this point, you should be able to start Apache and try out your new hgwebdir install.&lt;br /&gt;&lt;br /&gt;But upon arrival you'll probably notice that... it's empty.  Let's create a new repository.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;6.  Adding repositories&lt;/span&gt;&lt;br /&gt;Remember the folder we made C:\Mercurial\repositories?  That's where all the repos will be living.  Create a new repository like you would locally...&lt;pre class="syntax"&gt;C:\&amp;gt;&lt;br /&gt;C:\&amp;gt; cd Mercurial\repositories&lt;br /&gt;C:\Mercurial\repositories\&amp;gt; mkdir testrepo&lt;br /&gt;C:\Mercurial\repositories\&amp;gt; cd testrepo&lt;br /&gt;C:\Mercurial\repositories\testrepo\&amp;gt; hg init&lt;br /&gt;&lt;/pre&gt;Refreshing your the URL of your hgwebdir install should now show a new read-only repository.  &lt;span style="font-weight: bold;"&gt;By default new repositories are not push-able&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;7.  Create a repository configuration file&lt;/span&gt;&lt;br /&gt;We can enable features and also fill out the details about repository by create a configuration file.  Assuming the repository you created was "testrepo", create a new file named "hgrc" in C:\Mecurial\repositories\testrepo\.hg\ folder.&lt;pre class="syntax"&gt;[web]&lt;br /&gt;contact = Your Name&lt;br /&gt;name  = Test Repository&lt;br /&gt;description = A repository that I can show how cool I am using hg&lt;br /&gt;allow_push = *&lt;br /&gt;allow_archive = gz zip&lt;br /&gt;&lt;/pre&gt;Most of this is self-explanatory.  The "allow_push" directive enables files to be synchronized from the client's repository up to the repository on the server.  The "allow_archives" will enable a snapshot of a given revision to be downloaded in those formats.&lt;br /&gt;&lt;br /&gt;Save this file, refresh and there you go.&lt;br /&gt;&lt;br /&gt;Next, we'll look at setting up TortoiseHg on a Windows XP developer box to connect to our new hgwebdir instance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-1784189810790503648?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/1784189810790503648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=1784189810790503648' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1784189810790503648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1784189810790503648'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/mercurial-hgwebdir-under-apache-using.html' title='Mercurial hgwebdir under Apache using WSGI'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5337132745691827311</id><published>2009-11-01T20:11:00.003-10:00</published><updated>2009-11-01T20:15:36.755-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='jslint'/><title type='text'>Javascript: The Good Parts (the Video)</title><content type='html'>Kudos to Google and MIT for uploading such great quality stuff to YouTube (if you've been scared off by the drones of webcam-yourself-complaining-about-current-events then give it another try).&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I stumbled upon Doug Crockford at google speaking on "Javascript: The Good Parts".  He has a book by the same title as well.  In his presentation he also mentions JSLint, a tool he wrote to parse Javascript and act as a syntax checker.  I'm wondering if it could be rolled into my Django workflow or automated testing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here's the video:&lt;/div&gt;&lt;div&gt;&lt;object width="425" height="344"&gt;&lt;param name="movie" value="http://www.youtube.com/v/hQVTIJBZook&amp;amp;hl=en&amp;amp;fs=1&amp;amp;"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/hQVTIJBZook&amp;amp;hl=en&amp;amp;fs=1&amp;amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="344"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5337132745691827311?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5337132745691827311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5337132745691827311' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5337132745691827311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5337132745691827311'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/javascript-good-parts-video.html' title='Javascript: The Good Parts (the Video)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5042250843367757009</id><published>2009-11-01T15:17:00.003-10:00</published><updated>2009-11-01T15:20:13.389-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='fizzbuzz'/><title type='text'>FizzBuzz Ruby</title><content type='html'>Well since I was in the mood writing FizzBuzz and since I've been working on learning Ruby, I figured, why not do recursive fizzbuzz in ruby? &lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;script src="http://gist.github.com/223841.js"&gt;&lt;/script&gt;&lt;div&gt;Ruby is quite an expressive language.  It feels like it comes with more built-in keywords and default methods than python, so I'm sensing it is a bit more complex to learn.  However, it has some neat stuff.  I'm thinking of writing FizzBuzz using some of the different methods it has, like .net and the '..' range.  I've also seen a built in function something to the effect of '.upto' which might be fun to play with as well.&lt;span class="Apple-tab-span" style="white-space:pre"&gt; &lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5042250843367757009?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5042250843367757009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5042250843367757009' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5042250843367757009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5042250843367757009'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/fizzbuzz-ruby.html' title='FizzBuzz Ruby'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6443523388930793649</id><published>2009-11-01T14:59:00.003-10:00</published><updated>2009-11-01T15:16:12.169-10:00</updated><title type='text'>Toshiba T135-1309 13.3" Laptop Review</title><content type='html'>It's funny how times changes.  9 years ago I purchased a Toshiba laptop for college.  It set me back $1800 and came "loaded" with a Pentium 3 @ 600Mhz and 64MB RAM.  I promptly maxed out the RAM to 192MB.  I ended up &lt;i&gt;hating&lt;/i&gt; it and vowed never to buy a laptop again.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Fast forward now a decade into the future.  Things have changed.  I'm married, have a full time job, and laptop hardware doesn't suck now.  Neither does battery life.  Windows 7 comes out.  A netbook looks tempting.  But do I really want a machine with such a tiny keyboard and screen?  The shopping begins.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;My search end when I decided on a &lt;a href="http://laptops.toshiba.com/laptops/satellite/T100/T135-S1309"&gt;Toshiba T135-1309 laptop&lt;/a&gt;.  For the price, the specs are quite impressive:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_epp24QszXwI/Su4yyN0uIII/AAAAAAAAAB8/Bgh40u8RSME/s1600-h/satellite-t135-s1309-laptop.png"&gt;&lt;img src="http://3.bp.blogspot.com/_epp24QszXwI/Su4yyN0uIII/AAAAAAAAAB8/Bgh40u8RSME/s200/satellite-t135-s1309-laptop.png" border="0" alt="" id="BLOGGER_PHOTO_ID_5399308841549439106" style="cursor: pointer; width: 200px; height: 138px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Intel Pentium SU4100&lt;/li&gt;&lt;li&gt;3GB RAM (DDR3 1066)&lt;/li&gt;&lt;li&gt;13.3" LCD (16:9)&lt;/li&gt;&lt;li&gt;3.88 lbs.&lt;/li&gt;&lt;li&gt;Windows 7, 32-bit Premium&lt;/li&gt;&lt;li&gt;320 GB Hard Drive&lt;/li&gt;&lt;li&gt;~8hrs battery life&lt;/li&gt;&lt;li&gt;Built in ethernet/wifi-N&lt;/li&gt;&lt;li&gt;Built in webcam&lt;/li&gt;&lt;li&gt;Built in memory card reader&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Cost at Best Buy:  $549&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So far there are some things I really like about this laptop:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Affordable&lt;/li&gt;&lt;li&gt;Small and lightweight&lt;/li&gt;&lt;li&gt;Seems to get really good battery life&lt;/li&gt;&lt;li&gt;Comes with windows 7 premium&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;Overall I've been rather happy with this machine.  There are 2 small things I'm not so pleased with, but they're very minor and defiantly not deal breakers.&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;The speakers are quiet; a lot more quiet than you'd expect.  There's no external sound control either.&lt;/li&gt;&lt;li&gt;The whole laptop is rather glossy.  I wish they had toned that down a bit.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;At the time of purchase, I also picked up a Logitech M305 wireless mouse.  Let me tell you -- it's worth the money.  The mouse comes with a dongle that is ridiculously small.  You plug it in and basically leave it there.  There isn't a huge thumb-drive sized thing hanging out the side of your laptop.  It's smaller in size, but not uncomfortably so.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The M305 actually comes in 3 'fashionable' designs and of course boring gray.  I went with a designed model that was a bit more subtle than the other 2.  It fits with the laptop.'&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_epp24QszXwI/Su4ynZ3xc7I/AAAAAAAAAB0/M3gcbmZU-xs/s1600-h/9253416_sb.jpg"&gt;&lt;img src="http://3.bp.blogspot.com/_epp24QszXwI/Su4ynZ3xc7I/AAAAAAAAAB0/M3gcbmZU-xs/s200/9253416_sb.jpg" border="0" alt="" id="BLOGGER_PHOTO_ID_5399308655804904370" style="cursor: pointer; width: 125px; height: 200px; " /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6443523388930793649?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6443523388930793649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6443523388930793649' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6443523388930793649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6443523388930793649'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/11/toshiba-t135-1309-133-laptop-review.html' title='Toshiba T135-1309 13.3&quot; Laptop Review'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_epp24QszXwI/Su4yyN0uIII/AAAAAAAAAB8/Bgh40u8RSME/s72-c/satellite-t135-s1309-laptop.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-1394202956437446599</id><published>2009-10-28T21:06:00.015-10:00</published><updated>2009-10-28T21:30:15.952-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lisp'/><category scheme='http://www.blogger.com/atom/ns#' term='mit'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Structure and Interpretation, LISP and Python</title><content type='html'>I'm a "city boy" when it comes to programming languages. 90% of my experience in programming is in ridiculously high level languages like VB.NET, C#, and Python.  I haven't "roughed it out" much in the wilderness of some low level language.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I had heard talk about MIT's Open Courseware, but wasn't sure what the hoopla was all about.  Based on a reference in &lt;a href="http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html"&gt;someone's blog post&lt;/a&gt;, I happened upon a 1986 recording of &lt;a href="http://www.youtube.com/watch?v=2Op3QLzMgSY"&gt;Structure and Interpretation of Computer Programs (YouTube)&lt;/a&gt;.  I watched the whole thing.  I am thoroughly &lt;i&gt;blown away&lt;/i&gt;.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'll admit, the first 20 minutes were dizzyingly abstract, but once some code samples started to show up, it began to make sense.  What I was floored the most about was seeing how much an influence LISP had over the language I've been working in the most lately -- Python.  All of the "funky things" that I wasn't used too from C#, inner methods, using "def" instead of "function", etc. etc. were all elements of LISP.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So having &lt;a href="http://www.codinghorror.com/blog/archives/000781.html"&gt;FizzBuzz&lt;/a&gt; on the mind, I decided to give it a shot.... in LISP (and then after recursively in Python).  The course professor noted that LISP had no for loops.  "A challenge" I thought to myself.  (Long Side Tangent:  I feel at the moment as if I'm creating a programmer's Fight Club where I mentally abuse the comfortable high-level language life I once knew to get down and dirty fighting with the bare essentials of computational logic.  [end sensationalistic, metaphoric movie reference])&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In case you're not familiar with FizzBuzz, here's the problem:&lt;/div&gt;&lt;div&gt;&lt;blockquote&gt;Write a program that prints the numbers from 1 to 100. But for multiples of three print "Fizz" instead of the number and for the multiples of five print "Buzz". For numbers which are multiples of both three and five print "FizzBuzz".&lt;/blockquote&gt;&lt;/div&gt;&lt;div&gt;I "made up" a function definition for print since the details of printing to the  screen weren't really my concern.&lt;/div&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/221239.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;div&gt;The odd part for me was after working through the mental process of how the solution would work recursively in LISP, writing it again in Python felt ridiculously easy...&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;&lt;script src="http://gist.github.com/221242.js"&gt;&lt;/script&gt;&lt;br /&gt;&lt;b&gt;Look mom, no loops!&lt;/b&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-1394202956437446599?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/1394202956437446599/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=1394202956437446599' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1394202956437446599'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1394202956437446599'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/10/structure-and-interpretation-lisp-and.html' title='Structure and Interpretation, LISP and Python'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6491263308299135242</id><published>2009-10-26T09:03:00.007-10:00</published><updated>2009-10-26T09:24:54.661-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='settings.py'/><title type='text'>Notating Environment in Django's settings.py</title><content type='html'>I answered a &lt;a href="http://stackoverflow.com/questions/1626326/how-to-manage-local-vs-production-settings-in-django/1626529#1626529"&gt;question (#1626326)&lt;/a&gt; on &lt;a href="http://www.stackoverflow.com/"&gt;Stack Overflow&lt;/a&gt; recently about Django setttings.py files which reminded me I'm still undecided on what the best way to handle this issue.&lt;br /&gt;&lt;br /&gt;The issue is -- How do you handle Django settings which change based on which environment the web app is running in?  The Django documentation recommends using something to this effect.&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; DEBUG:&lt;br /&gt;VALUE &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'something'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;else&lt;/span&gt;:&lt;br /&gt;VALUE &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'something else'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;I used this approach for a while, but found that it was becoming limiting.  The reason why is that it doesn't actually address the issue of environment.  Where the "if DEBUG" trick is used, we're really only looking to see if we're in DEBUG mode, &lt;span style="font-style: italic;"&gt;&lt;span style="font-weight: bold;"&gt;not&lt;/span&gt;&lt;/span&gt; if we're running in a development or production environment.&lt;br /&gt;&lt;br /&gt;When I was first setting up &lt;a href="http://github.com/mintchaos/django_compressor"&gt;django-compressor&lt;/a&gt; (which by the way is &lt;span style="font-style: italic;"&gt;teh hotness&lt;/span&gt;) I wanted to sets the setup in a development environment with DEBUG=False.  The "if DEBUG" failed miserably here.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;settings.py Version 2&lt;/span&gt;&lt;br /&gt;So my next attempt was to attempt to determine the machine the settings.py was being evaluated on, and from there set what the environment was.&lt;br /&gt;&lt;pre class="syntax"&gt;PRODUCTION_SERVERS &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; [&lt;span style="color: rgb(64, 112, 160);"&gt;'WEBSERVER1'&lt;/span&gt;,&lt;span style="color: rgb(64, 112, 160);"&gt;'WEBSERVER2'&lt;/span&gt;,]&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; os&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;environ[&lt;span style="color: rgb(64, 112, 160);"&gt;'COMPUTERNAME'&lt;/span&gt;] &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;in&lt;/span&gt; PRODUCTION_SERVERS:&lt;br /&gt; PRODUCTION &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;else&lt;/span&gt;:&lt;br /&gt; PRODUCTION &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;False&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;DEBUG &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;not&lt;/span&gt; PRODUCTION&lt;br /&gt;TEMPLATE_DEBUG &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; DEBUG&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# ...&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; PRODUCTION:&lt;br /&gt; VALUE &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'something'&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;else&lt;/span&gt;:&lt;br /&gt; VALUE &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'something else'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;This is easily an improvement over "if DEBUG".  At least now we have some control over values based on environment.  I used this for a while and then realized...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;What if I have two installations of Django running on the same physical machine but which should be running in different environments?&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;This is a conceivable situation.  Let's say in a smaller company like the one I work in we only have 1 webserver.  Departments request features from the web team, which then get implemented by developer(s).  However the features need to be tested by the web team and evaluated by the department before deployed to the production site.&lt;br /&gt;&lt;br /&gt;So, the webserver gets configured to run 2 virtual hosts:  One for "testing.domain.com" and another for "www.domain.com."  BAM!  The "if PRODUCTION" method just failed.  Why?  Because we technically now have 3 environments (development, testing, production), 2 of which run on the same physical server.&lt;br /&gt;&lt;br /&gt;Imagine this bit of settings.py...&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; PRODUCTION:&lt;br /&gt;  DATABASE_HOST &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'192.168.1.1'&lt;/span&gt; &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# Production MySQL&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;else&lt;/span&gt;:&lt;br /&gt;  DATABASE_HOST &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'localhost'&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Bad things would follow.  The testing copy of the web app (testing.domain.com) would load up, and mark PRODUCTION=True as it is technically on the production server.  It then uses the production MySQL database.  Fail.&lt;br /&gt;&lt;br /&gt;To overcome this, our settings.py file really needs a way to distinguish what environment it is regardless of which physical machine it's located on.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;settings.py Version 3&lt;/span&gt;&lt;br /&gt;Some ideas I've had to address this would to set the environment based on...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;The folder path the app is currently located in&lt;/span&gt;&lt;br /&gt;The code for this could get ugly easily.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;The complete hostname the server is running under&lt;/span&gt;&lt;br /&gt;This might be win.&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-weight: bold;"&gt;Something else?&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;I'll post some code when I find something that works well that I like.&lt;br /&gt;&lt;br /&gt;On a related note, I was reading the &lt;a href="http://www.sinatrarb.com/configuration.html"&gt;Sinatra (ruby) documentation&lt;/a&gt; this week, and I noticed that it automatically sets the environment based on the values set by the RACK_ENV variable.  This would be the equivalent of adjusting the settings.py based on the presence of a WSGI or FCGI (or whatever) variable.  It seems like a really neat idea, however it's somewhat dependent on the way Django is being loaded, and given that there are so many possibilities, I'm not sure if it'd work out as well for Django as it does for Sinatra.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6491263308299135242?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6491263308299135242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6491263308299135242' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6491263308299135242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6491263308299135242'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/10/notating-environment-in-djangos.html' title='Notating Environment in Django&apos;s settings.py'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-4947353454050403887</id><published>2009-10-16T16:16:00.012-10:00</published><updated>2009-10-16T16:51:22.431-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django-admin'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><title type='text'>Neat Django Admin Tricks (Part 1)</title><content type='html'>About django admin in a sec.  First a bit of setup:&lt;br /&gt;&lt;br /&gt;As I've been organizing our office into using an issue tracker for keeping track of our Django app, I haven't really found one that 'fit' yet.  The issue trackers I looked at had 1 of 2 problems:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;They were massive, huge, enterprise-level monsters.  I need something for 3-4 people.&lt;/li&gt;&lt;li&gt;They required a bunch of extra setup.  I already have a Django app running, why load the server with extra software?&lt;/li&gt;&lt;/ol&gt;So I spent a few hours after work putting a simple issue tracker together.  To really improve the usability and get what I wanted out of the app, I needed to control how the django admin was presenting things.  Some neat django admin tricks were in order.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Making An Admin list_display Field Show In Color&lt;/span&gt;&lt;br /&gt;By default, all values in the Django admin model list view are in black.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://s524.photobucket.com/albums/cc327/wastingtape/blog_images/?action=view&amp;amp;current=boring-black.jpg" target="_blank"&gt;&lt;img src="http://i524.photobucket.com/albums/cc327/wastingtape/blog_images/boring-black.jpg" alt="Photobucket" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Boring black.  If you have an issue tracker it should show in color, right?  Here's what my issue admin was looking like:&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;IssueAdmin&lt;/span&gt;(admin&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;ModelAdmin):&lt;br /&gt;list_display &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; (&lt;span style="color: rgb(64, 112, 160);"&gt;'title'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'priority'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'completed'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'assigned_to'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'last_modified'&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;The easiest way to get the 'priority' column in color would be to render it as HTML.  If you were to try this, you'd find that the Django admin does not escape that value, and you'd end up with &lt; &gt;'s showing.  However, &lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display"&gt;there is an API &lt;/a&gt;to get around that.  In Python all functions are objects.  Because of this, we can add a property '.allow_tags' at runtime to signal to Django that the value contains tags.&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# models.py&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt; &lt;span style="color: rgb(6, 40, 126);"&gt;priority_html&lt;/span&gt;(&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;):&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; u&lt;span style="color: rgb(64, 112, 160);"&gt;'&amp;lt;span &amp;gt;&lt;/span&gt;&lt;span style="color: rgb(112, 160, 208); font-style: italic;"&gt;%s&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;&amp;lt;/span&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;%&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;get_priority_display()&lt;br /&gt;priority_html&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;allow_tags &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;Our value now shows in color, but the column header renders as "Priority html".  We can add one more property 'short_description' which would allow us to specify the equivalent of 'verbose_name' for a field.&lt;br /&gt;&lt;br /&gt;Here's the (almost) complete code to achieve a custom HTML value in a list_display entry:&lt;pre class="syntax"&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# models.py&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;Issue&lt;/span&gt;(models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;Model):&lt;br /&gt;&lt;br /&gt;PRIORITY_CHOICES &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; (&lt;br /&gt; (&lt;span style="color: rgb(64, 160, 112);"&gt;1&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'Low'&lt;/span&gt;),&lt;br /&gt; (&lt;span style="color: rgb(64, 160, 112);"&gt;2&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'Normal'&lt;/span&gt;),&lt;br /&gt; (&lt;span style="color: rgb(64, 160, 112);"&gt;3&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'High'&lt;/span&gt;),&lt;br /&gt;)&lt;br /&gt;&lt;br /&gt;priority &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; models&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;&lt;span style="color: rgb(0, 112, 32);"&gt;IntegerField&lt;/span&gt;(choices&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;PRIORITY_CHOICES, help_text&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;'Default:  Normal Priority'&lt;/span&gt;, default&lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;2&lt;/span&gt;)&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt; &lt;span style="color: rgb(6, 40, 126);"&gt;priority_html&lt;/span&gt;(&lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;):&lt;br /&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;priority &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;3&lt;/span&gt;:&lt;br /&gt;     color &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;"652D90"&lt;/span&gt;&lt;br /&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;elif&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;priority &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;2&lt;/span&gt;:&lt;br /&gt;     color &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;"37B34A"&lt;/span&gt;&lt;br /&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;else&lt;/span&gt;:&lt;br /&gt;     color &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;"26A9E0"&lt;/span&gt;&lt;br /&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;return&lt;/span&gt; u&lt;span style="color: rgb(64, 112, 160);"&gt;'&amp;lt;span style="color:#&lt;/span&gt;&lt;span style="color: rgb(112, 160, 208); font-style: italic;"&gt;%s&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;"&amp;gt;&lt;/span&gt;&lt;span style="color: rgb(112, 160, 208); font-style: italic;"&gt;%s&lt;/span&gt;&lt;span style="color: rgb(64, 112, 160);"&gt;&amp;lt;/span&amp;gt;'&lt;/span&gt; &lt;span style="color: rgb(102, 102, 102);"&gt;%&lt;/span&gt; (color, &lt;span style="color: rgb(0, 112, 32);"&gt;self&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;get_priority_display())&lt;br /&gt;priority_html&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;allow_tags &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32);"&gt;True&lt;/span&gt;&lt;br /&gt;priority_html&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;short_description &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'priority'&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# admin.py&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;class&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;IssueAdmin&lt;/span&gt;(admin&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;ModelAdmin):&lt;br /&gt;list_display &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; (&lt;span style="color: rgb(64, 112, 160);"&gt;'title'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'priority_html'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'completed'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'assigned_to'&lt;/span&gt;, &lt;span style="color: rgb(64, 112, 160);"&gt;'last_modified'&lt;/span&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://s524.photobucket.com/albums/cc327/wastingtape/blog_images/?action=view&amp;amp;current=now-in-color.jpg" target="_blank"&gt;&lt;img src="http://i524.photobucket.com/albums/cc327/wastingtape/blog_images/now-in-color.jpg" alt="Photobucket" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Given the code above, I bet you can guess how the (Email) link was achieved.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-4947353454050403887?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/4947353454050403887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=4947353454050403887' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4947353454050403887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/4947353454050403887'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/10/neat-django-admin-tricks-part-1.html' title='Neat Django Admin Tricks (Part 1)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://i524.photobucket.com/albums/cc327/wastingtape/blog_images/th_boring-black.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7052778372728407157</id><published>2009-10-02T18:22:00.009-10:00</published><updated>2009-10-24T10:29:20.865-10:00</updated><title type='text'>Managing Django Dependencies via SVN</title><content type='html'>One of the things that's been somewhat of a small hassle in working with Django/Python is managing dependencies across multiple developer machines, especially through an SVN managed project.  Whenever a new dependency is needed, we've ended up doing 1 of 2 things:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Adding the name of the package to a text file which can be "easy_install &lt;package&gt;"&lt;/package&gt;&lt;/li&gt;&lt;li&gt;Copying the zip/bz/exe to a folder "dependencies"&lt;/li&gt;&lt;/ul&gt;In both cases, what normally happens is Developer X will add a dependency to the repo.  Developer Y at some point does an SVN update, and pulls down the code Developer X committed.  Developer Y runs his runserver and BAM! &lt;module&gt; not found!  So then Developer Y has to go and look in the "dependencies" folder and try and figure out if he needs to run an easy_install or run an exe or whatever.&lt;br /&gt;&lt;br /&gt;Obviously a rather inefficient task.  I had heard about systems that will do automatic dependency installing, but the problem I had with that is that sometimes you have dependencies that are just a single file, and other times a whole package, others are in Pypi, etc..  The packages come in a huge crazy mess of ways.  I wanted something &lt;span style="font-weight: bold; font-style: italic;"&gt;consistent&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;My (somewhat hacky) Soultion&lt;/span&gt;&lt;br /&gt;Create a 'dependencies' folder that lives inside the project root (mine is named "_dependencies").  This folder will hold everything needed by Django (except Django itself) that would normally go in 'site-packages'.&lt;br /&gt;&lt;br /&gt;When the Django app starts, have this path added to the Python path, so that libraries can be installed to it, instead of ..\Python26\Lib\site-packages.  This allows dependencies to be setup in a folder that everyone pulls down via SVN.  &lt;span style="font-style: italic;"&gt;Bingo!&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;There are mainly 2 times when we need to make sure that dependency path is available:&lt;br /&gt;&lt;/module&gt;&lt;ol&gt;&lt;li&gt;When we're doing a management task (including runserver)&lt;/li&gt;&lt;li&gt;When the production site actually handles a task via Apache or whatever&lt;/li&gt;&lt;/ol&gt;&lt;module&gt;&lt;/module&gt;To accomplish both, I came up with the following code:&lt;br /&gt;&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# manage.py (before Django loads itself)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;os&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;sys&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;def&lt;/span&gt; &lt;span style="color: rgb(6, 40, 126);"&gt;set_dependency_path&lt;/span&gt;():&lt;br /&gt;dep_root &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; os&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;path&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;join(os&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;path&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;abspath(os&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;path&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;dirname(__file__)), &lt;span style="color: rgb(64, 112, 160);"&gt;'_dependencies'&lt;/span&gt;)&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;not&lt;/span&gt; dep_root &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;in&lt;/span&gt; sys&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;path:&lt;br /&gt; sys&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;path&lt;span style="color: rgb(102, 102, 102);"&gt;.&lt;/span&gt;append(dep_root)&lt;br /&gt;&lt;br /&gt;set_dependency_path()&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;# __init__.py (the main, root init)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;from&lt;/span&gt; &lt;span style="color: rgb(14, 132, 181); font-weight: bold;"&gt;manage&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;import&lt;/span&gt; set_dependency_path&lt;br /&gt;set_dependency_path()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7052778372728407157?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7052778372728407157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7052778372728407157' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7052778372728407157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7052778372728407157'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/10/managing-django-dependencies-via-svn.html' title='Managing Django Dependencies via SVN'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5753959380900182081</id><published>2009-10-01T16:27:00.017-10:00</published><updated>2009-10-01T17:22:17.749-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='wmd-editor'/><title type='text'>Hacking wmd-editor into using a custom image gallery</title><content type='html'>Oh man, I feel accomplished.  I just finished hacking &lt;a href="http://github.com/cky/wmd"&gt;wmd editor&lt;/a&gt; to use a custom image gallery being generated server-side.&lt;br /&gt;&lt;br /&gt;If you don't know, WMD editor is a WYSIWYG editor written in Javascript.&lt;br /&gt;&lt;a href="http://s524.photobucket.com/albums/cc327/wastingtape/blog_images/?action=view&amp;amp;current=editor.jpg" target="_blank"&gt;&lt;img src="http://i524.photobucket.com/albums/cc327/wastingtape/blog_images/editor.jpg" alt="Photobucket" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;By default, when you click on the 'Insert Image' button you get a prompt allowing you to enter a URL.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Insert Image Screen&lt;/span&gt;&lt;br /&gt;&lt;a href="http://s524.photobucket.com/albums/cc327/wastingtape/blog_images/?action=view&amp;amp;current=default-image-insert.jpg" target="_blank"&gt;&lt;img src="http://i524.photobucket.com/albums/cc327/wastingtape/blog_images/default-image-insert.jpg" alt="Photobucket" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The functionality I was looking for was instead of showing you a text box where you could paste in the URL, I wanted to see more of a gallery type interface, that showed media that both you and others had uploaded.  I also wanted to have a system where multiple files could be uploaded at once, and multiple files could be inserted at once.&lt;br /&gt;&lt;br /&gt;There were primarily two challenges in attempting to accomplish that plan:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The WMD editor code doesn't really come with documentation.  There are a handful of in-line comments and that's all you get.  For the most part, I ignored them and read the code instead.&lt;/li&gt;&lt;li&gt;The WMD editor code is a little strange in areas.&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Modifying WMD Editor&lt;/span&gt;&lt;br /&gt;For the most part, I ended up not needing to change really all that much code.  WMD editor is odd, yes, but workable.&lt;br /&gt;&lt;br /&gt;The first task was to replace the default prompt function, with my own....&lt;br /&gt;&lt;div class="highlight"&gt;&lt;pre class="syntax"&gt;command.doLinkOrImage &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;(chunk&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; postProcessing&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; isImage){&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; (isImage) {&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// OLD: util.prompt(imageDialogText, imageDefaultText, makeLinkMarkdown);&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;        &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// WMD_IMAGE_GALLERY_URL loaded from a global settings elsewhere&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;        util.imageGallery(&lt;span style="color: rgb(102, 102, 102);"&gt;WMD&lt;/span&gt;_IMAGE_GALLERY_URL&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; makeLinkMarkdown);&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;else&lt;/span&gt; {&lt;br /&gt;util.prompt(linkDialogText&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; linkDefaultText&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; makeLinkMarkdown);&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;This was an easy change, and allowed to to write a pretty much drop-in replacement for util.promt().&lt;br /&gt;&lt;br /&gt;This particular bit of code is jQuery-specific.  It uses a div already existing on the page, #wmd-media-gallery, and applies jQuery UI's $.dialog() method to it, converted it into a popup modal dialog.  The contents of the dialog is then loaded by AJAX.  The URL that is loads is being specified in a global variable that is passed to it (WMD_IMAGE_GALLERY_URL).&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight"&gt;&lt;pre class="syntax"&gt;util.imageGallery &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;(gallery_url&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; makeLinkFunction){&lt;br /&gt;gallery &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; $(&lt;span style="color: rgb(64, 112, 160);"&gt;'#wmd-media-gallery'&lt;/span&gt;);&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; (gallery.html()) {&lt;br /&gt;gallery.dialog(&lt;span style="color: rgb(64, 112, 160);"&gt;'open'&lt;/span&gt;);&lt;br /&gt;}&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;else&lt;/span&gt; {&lt;br /&gt;gallery.css(&lt;span style="color: rgb(64, 112, 160);"&gt;'visibility'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'visible'&lt;/span&gt;);&lt;br /&gt;gallery.load(gallery_url&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;(){&lt;br /&gt;  gallery.dialog({&lt;br /&gt;      title&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;'Media Gallery'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;      bgiframe&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;true&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;      height&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;550&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;      width&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;900&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;      modal&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;true&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;      buttons&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; {&lt;br /&gt;          &lt;span style="color: rgb(64, 112, 160);"&gt;'Insert'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;(){&lt;br /&gt;              &lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// Enumerate images; insert those checked.&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;                        $(&lt;span style="color: rgb(64, 112, 160);"&gt;'#wmd-media-gallery .media-insert'&lt;/span&gt;).each(&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;(){&lt;br /&gt;                  &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; ($(&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;).is(&lt;span style="color: rgb(64, 112, 160);"&gt;':checked'&lt;/span&gt;)) {                              &lt;br /&gt;                      img_path &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; $(&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;).attr(&lt;span style="color: rgb(64, 112, 160);"&gt;'rel'&lt;/span&gt;);&lt;br /&gt;                      console.log(img_path);&lt;br /&gt;                      makeLinkFunction(img_path);&lt;br /&gt;                  }&lt;br /&gt;                });&lt;br /&gt;              $(&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;).dialog(&lt;span style="color: rgb(64, 112, 160);"&gt;'close'&lt;/span&gt;);&lt;br /&gt;          }&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;br /&gt;          &lt;span style="color: rgb(64, 112, 160);"&gt;'Cancel'&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;:&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;() {$(&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;this&lt;/span&gt;).dialog(&lt;span style="color: rgb(64, 112, 160);"&gt;'close'&lt;/span&gt;); }&lt;br /&gt;      }&lt;br /&gt;  });&lt;br /&gt;});&lt;br /&gt;}&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;Pretty simple so far, right?&lt;br /&gt;&lt;br /&gt;Well, here's the tricky part.  If you notice what happens when the 'Insert' button is clicked, the code will scan the HTML of the dialog, and look for any input:checked element that has class "media-insert".  For those elements which are found, it searches for ones which are checked.  If a checked item is found, it will grab the Image URL out of the rel="" attribute, and pass that back to WMD editor for insertion into the edit box.&lt;br /&gt;&lt;br /&gt;Ah, but not so fast. The problem comes in that WMD editor was not originally written to insert multiple images in such a way.  With the stock code, what happens is it will inset one image inside another, thus only rendering 1 image properly, and leaving a mish-mash of code around it.&lt;br /&gt;&lt;br /&gt;It should render this:&lt;pre class="syntax"&gt;![alt text][1]&lt;br /&gt;![alt text][2]&lt;br /&gt;&lt;br /&gt;   [1]: /some/url.jpg&lt;br /&gt;   [2]: /some/image.jpg&lt;/pre&gt;What it was rendering was this:&lt;pre class="syntax"&gt;![alt text![alt text][1]][1]&lt;/pre&gt;The particular code in question lies int he command.addLinkDef() function.  What needs to happen is that before that function processes the 'chunk', it must first clear the settings from the previous image out of it.&lt;br /&gt;&lt;br /&gt;&lt;div class="highlight"&gt;&lt;pre class="syntax"&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; getLink &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;function&lt;/span&gt;(wholeMatch&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; link&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; id&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; end){&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// ...&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;};&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// This goes after the getLink function&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;if&lt;/span&gt; (chunk.after.substring(&lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;2&lt;/span&gt;) &lt;span style="color: rgb(102, 102, 102);"&gt;==&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;"]["&lt;/span&gt;) {&lt;br /&gt;chunk.before &lt;span style="color: rgb(102, 102, 102);"&gt;+=&lt;/span&gt; chunk.selection &lt;span style="color: rgb(102, 102, 102);"&gt;+&lt;/span&gt; chunk.after.substring(&lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt;&lt;span style="color: rgb(64, 160, 112);"&gt;2&lt;/span&gt;);&lt;br /&gt;chunk.after &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; chunk.after.substring(&lt;span style="color: rgb(64, 160, 112);"&gt;2&lt;/span&gt;);&lt;br /&gt;&lt;span style="color: rgb(0, 112, 32); font-weight: bold;"&gt;var&lt;/span&gt; linkEnd &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; chunk.after.indexOf(&lt;span style="color: rgb(64, 112, 160);"&gt;']'&lt;/span&gt;);&lt;br /&gt;linkEnd&lt;span style="color: rgb(102, 102, 102);"&gt;++;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;chunk.before &lt;span style="color: rgb(102, 102, 102);"&gt;+=&lt;/span&gt; chunk.after.substring(&lt;span style="color: rgb(64, 160, 112);"&gt;0&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; linkEnd);&lt;br /&gt;chunk.selection &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; &lt;span style="color: rgb(64, 112, 160);"&gt;""&lt;/span&gt;&lt;span style="color: rgb(102, 102, 102);"&gt;;&lt;/span&gt;&lt;br /&gt;chunk.after &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; chunk.after.substring(linkEnd &lt;span style="color: rgb(102, 102, 102);"&gt;+&lt;/span&gt; &lt;span style="color: rgb(64, 160, 112);"&gt;1&lt;/span&gt;);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;// ...and before the chunk.before [...] code&lt;/span&gt;&lt;br /&gt;&lt;span style="color: rgb(96, 160, 176); font-style: italic;"&gt;&lt;/span&gt;&lt;br /&gt;chunk.before &lt;span style="color: rgb(102, 102, 102);"&gt;=&lt;/span&gt; chunk.before.replace(regex&lt;span style="color: rgb(102, 102, 102);"&gt;,&lt;/span&gt; getLink);&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;And there you have it, you can now run custom image galleries from WMD editor.  I'll leave it up to your imagination as to what you can do with that, but here's what mine ended up looking like:&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;Editor Window&lt;/span&gt;&lt;br /&gt;&lt;a href="http://s524.photobucket.com/albums/cc327/wastingtape/blog_images/?action=view&amp;amp;current=finishedgallery.jpg" target="_blank"&gt;&lt;img src="http://i524.photobucket.com/albums/cc327/wastingtape/blog_images/finishedgallery.jpg" alt="Photobucket" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Inserted Image&lt;/span&gt;&lt;br /&gt;&lt;a href="http://s524.photobucket.com/albums/cc327/wastingtape/blog_images/?action=view&amp;amp;current=insertedmedia.jpg" target="_blank"&gt;&lt;img src="http://i524.photobucket.com/albums/cc327/wastingtape/blog_images/insertedmedia.jpg" alt="Photobucket" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If you noticed, the &lt;a href="http://urlencode.blogspot.com/2009/09/django-uploadify-released.html"&gt;django-uploadify app&lt;/a&gt; is being used in the gallery (hence why I began working on it).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5753959380900182081?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5753959380900182081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5753959380900182081' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5753959380900182081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5753959380900182081'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/10/hacking-wmd-editor-into-using-custom.html' title='Hacking wmd-editor into using a custom image gallery'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://i524.photobucket.com/albums/cc327/wastingtape/blog_images/th_editor.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7002903180460292332</id><published>2009-09-30T21:52:00.002-10:00</published><updated>2009-09-30T21:55:27.472-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='data-analysis'/><title type='text'>MS SQL Data Analysis Server</title><content type='html'>I was doing some investigation on the Stack Overflow data dump, and ran across a rather interesting blog posts:  &lt;a href="http://sqlserverpedia.com/blog/sql-server-tutorial/data-mining-the-stackoverflow-database/"&gt;Data Mining the StackOverflow Database&lt;/a&gt;.  He uses a tool called 'SQL Analysis Server' (or something like that) to automatically create fuzzy comparisons between records in a database.  The software ends up spitting out trend categories.  Neat stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7002903180460292332?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7002903180460292332/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7002903180460292332' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7002903180460292332'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7002903180460292332'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/09/ms-sql-data-analysis-server.html' title='MS SQL Data Analysis Server'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6507572991339940771</id><published>2009-09-30T14:44:00.004-10:00</published><updated>2009-09-30T14:48:05.071-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><title type='text'>django-uploadify Released</title><content type='html'>So I finally joined the rest of the Django community in releasing a re-usable app on &lt;a href="http://www.github.com/"&gt;GitHub&lt;/a&gt;.  Announcing... &lt;a href="http://github.com/tstone/django-uploadify"&gt;django-uploadify&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.uploadify.com/"&gt;Uploadify&lt;/a&gt; is a great flash-based &lt;a href="http://www.jquery.com/"&gt;jQuery&lt;/a&gt; plugin which allows users to upload an un-fixed quantity of files through a single interface.  It looks and works great.&lt;br /&gt;&lt;br /&gt;Django-uploadify is a django app that serves as a wrapper for the functionality, allowing it to interface with Django.  The app works by firing a signal whenever a file is received from Uploadify, allowing developers to tie uploadify into their existing apps rather easily.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6507572991339940771?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6507572991339940771/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6507572991339940771' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6507572991339940771'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6507572991339940771'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/09/django-uploadify-released.html' title='django-uploadify Released'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-6955683852487693470</id><published>2009-09-29T17:28:00.004-10:00</published><updated>2009-09-29T17:42:55.805-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='marketing'/><category scheme='http://www.blogger.com/atom/ns#' term='microsoft'/><title type='text'>Microsoft Marketing and Kylie age 4.5</title><content type='html'>I recall reading 6-8 months ago that Microsoft had recently hired a marketing firm to re-make the company's image after continuous negativity surrounding Windows Vista (the same firm incidentally which was behind 'The King' in Burger King's marketing strategy).  Their work began to show up a few months ago, first in the form of the "we'll give you $800 to buy any computer you want" type of ads.&lt;br /&gt;&lt;br /&gt;However, with Winodws 7 on the horizon, it seems that a new campaign has been launched:  Kylie, age 4.5&lt;br /&gt;&lt;br /&gt;When I saw the first of this ad series, I didn't think much of it.  It's a 4 year old plugging in a camera to USB and downloading the pictures.  Ok.  Exciting.  As with most of my TV watching experience, half of the fun is analyzing the commercials for intent.  I couldn't figure out who Microsoft's target was at that point.  The message seemed to be "Windows 7: so easy a preschooler can use it."  But at the same time, I wondered why they would be targeting the sub-section of society that was looking for an OS that was that easy to use.&lt;br /&gt;&lt;br /&gt;And then came the "happy words" commercial...&lt;br /&gt;&lt;object height="340" width="560"&gt;&lt;param name="movie" value="http://www.youtube.com/v/ssOq02DTTMU&amp;amp;hl=en&amp;amp;fs=1&amp;amp;"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/ssOq02DTTMU&amp;amp;hl=en&amp;amp;fs=1&amp;amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="340" width="560"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;I saw that and went "&lt;span style="font-style: italic;"&gt;wow that was awesome!&lt;/span&gt;"  Odd thing is, all of my computer tech/programmer friends that I sent that video too also thought it was amazing.  So I told myself, "Hey, Microsoft is doing their homework and trying to make cool commercials to target to techies who have long since stopped believing in them."  It was exciting.  Momentarily.  (Ok, I'll admit to looking up what song that was a buying it on Amazon MP3 downloads, which if you're still buying stuff from iTunes quit messing with the DRM already and get it from amazon!)&lt;br /&gt;&lt;br /&gt;I felt on several levels that this commercial worked because it...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The music.  The epic conquer the world music makes the commercial.  Hands down.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The juxtaposition of elements works -- ponies, and pink, and rats with hats have nothing to do with Windows, but what it does is push the concept of Windows out of the "boring" category -- effective!&lt;/li&gt;&lt;li&gt;The very last words on the ad are perfect:  "I'm a PC!! ...and more happy is coming!"  They're short and choppy like a kid would actually say it (well an ornery kid, but it fits rather well with not being in the "boring" category)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;And then just a  day or so ago I saw what is apparently the next 'episode' in the campaign:&lt;br /&gt;&lt;object height="340" width="560"&gt;&lt;param name="movie" value="http://www.youtube.com/v/U6adgIJE8YQ&amp;amp;hl=en&amp;amp;fs=1&amp;amp;"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowscriptaccess" value="always"&gt;&lt;embed src="http://www.youtube.com/v/U6adgIJE8YQ&amp;amp;hl=en&amp;amp;fs=1&amp;amp;" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="340" width="560"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;So they're playing A-Team music... meh.  I'm wondering if the "happy words" ad was just by luck that it was cool, and we won't be seeing much like that for the rest of the campaign.&lt;br /&gt;&lt;br /&gt;By the way, if you didn't catch it, there is a pig in a bonnet in the first commercial.  Watch it again!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-6955683852487693470?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/6955683852487693470/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=6955683852487693470' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6955683852487693470'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/6955683852487693470'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/09/microsoft-marketing-and-kylie-age-45.html' title='Microsoft Marketing and Kylie age 4.5'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-2088082938004608088</id><published>2009-09-20T23:07:00.007-10:00</published><updated>2009-09-20T23:16:11.776-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='tdd'/><category scheme='http://www.blogger.com/atom/ns#' term='django-testing'/><title type='text'>Token  Testing Slides</title><content type='html'>OH-MAN.  This has to be some of the coolest TDD stuff I've ever seen, &lt;i&gt;&lt;b&gt;and it's in the web platform I'm already using&lt;/b&gt;&lt;/i&gt;.  Django 1.1 introduces some super-awesome new features for testing.  I'll probably spend some time this week trying them out.  So far the coolest is easily the...&lt;div&gt;&lt;/div&gt;&lt;pre class="syntax"&gt;django-admin.py test --coverage --report&lt;/pre&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;...to generate a %-based coverage report of all your web app's code.  Man that's sweet.&lt;br /&gt;&lt;br /&gt;Here's where I'm seeing all these new features:&lt;/div&gt;&lt;div&gt;&lt;div style="width:425px;text-align:left" id="__ss_1969262"&gt;&lt;a style="font:14px Helvetica,Arial,Sans-serif;display:block;margin:12px 0 3px 0;text-decoration:underline;" href="http://www.slideshare.net/ericholscher/token-testing-slides" title="Token  Testing Slides"&gt;Token  Testing Slides&lt;/a&gt;&lt;object style="margin:0px" width="425" height="355"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=tokentestingslides-090908145239-phpapp02&amp;amp;stripped_title=token-testing-slides"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=tokentestingslides-090908145239-phpapp02&amp;amp;stripped_title=token-testing-slides" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" width="425" height="355"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;"&gt;View more &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/"&gt;documents&lt;/a&gt; from &lt;a style="text-decoration:underline;" href="http://www.slideshare.net/ericholscher"&gt;ericholscher&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-2088082938004608088?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/2088082938004608088/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=2088082938004608088' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2088082938004608088'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2088082938004608088'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/09/token-testing-slides.html' title='Token  Testing Slides'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-315419939824830403</id><published>2009-09-14T09:53:00.011-10:00</published><updated>2009-09-20T23:15:51.479-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='django-template'/><title type='text'>Extending Django's Built-In Template Filters</title><content type='html'>Django provides quite a few &lt;a href="http://docs.djangoproject.com/en/dev/ref/templates/builtins/"&gt;built-in template filters&lt;/a&gt; right out of the box.  However, eventually you might need them to work in a specific way that isn't quite the default behavior.  In my case, the django date filter worked great, but I needed something specific:&lt;br /&gt;&lt;br /&gt;By default, the date filter takes a value, along with a formatting argument, and returns a "rendered" string:&lt;br /&gt;&lt;pre class="syntax"&gt;{{ value|date:"F d, Y P" }}&lt;/pre&gt;This would render as:&lt;br /&gt;&lt;pre class="syntax"&gt;January 18, 2009 1:30 p.m.&lt;/pre&gt;However, what happens if time information is entered for some datetime's and not for others?  We end up with stuff like "January 18, 2009 midnight".  Not what I wanted.&lt;br /&gt;&lt;br /&gt;Turns out it's quite easy to not only implement your own custom filters, but also to tap the power of the built-in filters as well.  To achieve the functionality I was looking for, my custom filter needed to...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Work just like the default date filter, taking an argument of format&lt;/li&gt;&lt;li&gt;Check if there was no time information on the datetime&lt;/li&gt;&lt;li&gt;Remove the time formatting if so&lt;/li&gt;&lt;/ul&gt;Starting out, here's the base custom filter, along with the code to remove time arguments from the formatting argument:&lt;br /&gt;&lt;pre class="syntax"&gt;import re&lt;br /&gt;&lt;br /&gt;@register.filter&lt;br /&gt;def smart_date(value, arg):&lt;br /&gt;   if value.hour == 0 and value.minute == 0 and value.second == 0:&lt;br /&gt;       arg = re.sub(r'[aAfgGhHijlLPOsTZ]', '', arg)&lt;br /&gt;&lt;/pre&gt;What I did not want to do was re-implement the functionality of the date filter.  However, it's easy to bring that functionality into your custom filter by simply importing it (yeah, it's really that easy).&lt;br /&gt;&lt;br /&gt;Here's my completed "smart_date" tag:&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;re&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;from&lt;/span&gt; &lt;span style="color: #0e84b5; font-weight: bold"&gt;django.template.defaultfilters&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;import&lt;/span&gt; date &lt;span style="color: #007020; font-weight: bold"&gt;as&lt;/span&gt; date_filter&lt;br /&gt;&lt;br /&gt;&lt;span style="color: #555555; font-weight: bold"&gt;@register&lt;/span&gt;&lt;span style="color: #666666"&gt;.&lt;/span&gt;filter&lt;br /&gt;&lt;span style="color: #007020; font-weight: bold"&gt;def&lt;/span&gt; &lt;span style="color: #06287e"&gt;smart_date&lt;/span&gt;(value, arg):&lt;br /&gt;   &lt;span style="color: #007020; font-weight: bold"&gt;if&lt;/span&gt; &lt;span style="color: #007020; font-weight: bold"&gt;not&lt;/span&gt; value&lt;span style="color: #666666"&gt;.&lt;/span&gt;time():&lt;br /&gt;       &lt;span style="color: #60a0b0; font-style: italic"&gt;# remove all time-related formatting&lt;/span&gt;&lt;br /&gt;       arg &lt;span style="color: #666666"&gt;=&lt;/span&gt; re&lt;span style="color: #666666"&gt;.&lt;/span&gt;sub(&lt;span style="color: #4070a0"&gt;r&amp;#39;[aAfgGhHijlLPOsTZ]&amp;#39;&lt;/span&gt;, &lt;span style="color: #4070a0"&gt;&amp;#39;&amp;#39;&lt;/span&gt;, arg)&lt;br /&gt;&lt;br /&gt;   &lt;span style="color: #007020; font-weight: bold"&gt;return&lt;/span&gt; date_filter(value, arg)&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-315419939824830403?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/315419939824830403/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=315419939824830403' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/315419939824830403'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/315419939824830403'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/09/extending-djangos-built-in-template.html' title='Extending Django&apos;s Built-In Template Filters'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-2779445558906376644</id><published>2009-07-10T10:18:00.003-10:00</published><updated>2009-07-10T10:22:16.071-10:00</updated><title type='text'>Jquery and Effect.ScrollTo</title><content type='html'>It was beginning to become a bane: missing scriptaculou's Effect.ScrollTo.  And then I found this gem.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.learningjquery.com/2007/10/improved-animated-scrolling-script-for-same-page-links"&gt;Improved Animated Scroll Script For Same-Page Links&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And all is well again.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-2779445558906376644?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/2779445558906376644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=2779445558906376644' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2779445558906376644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2779445558906376644'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/07/jquery-and-effectscrollto.html' title='Jquery and Effect.ScrollTo'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-1798182804319158990</id><published>2009-06-26T12:55:00.000-10:00</published><updated>2009-06-26T13:05:30.381-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='netbook'/><category scheme='http://www.blogger.com/atom/ns#' term='review'/><title type='text'>Lenovo S10e Netbook Review (vs. Asus Wind vs. Acer Aspire One)</title><content type='html'>Before I continue on with the Apache/Django series, I thought I'd post a review of the &lt;a href="http://www.amazon.com/Lenovo-41874KU-Netbook-Dark-Gray/dp/B00274AGQK/ref=sr_1_1?ie=UTF8&amp;amp;s=electronics&amp;amp;qid=1246056887&amp;amp;sr=8-1"&gt;Lenovo S10e netbook&lt;/a&gt;.  Netbooks still seem in their infancy that it's hard to pick the right one without wondering what smashing new feature is just around the corner.&lt;br /&gt;&lt;br /&gt;Just to be clear this entire review is being typed on the netbook.&lt;br /&gt;&lt;br /&gt;Initially upon seeing the Lenovo S10e I dismissed it.  At a glance it didn't seem special, and compared to all the netbooks around it, it wasn't shiny and eye-catching.  However, there were a few things that brought me back for a second look, and why I decided on this book for my Philly '09 trip:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It was the lightest (compared against an Asus Wind-120U and Acer Aspire One)&lt;/li&gt;&lt;li&gt;It had a 10.2" screen, again the largest of the comparasion models&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It was black/dark so I didn't need to worry about the dirty-factor&lt;/li&gt;&lt;/ul&gt;In addition to that, I have discovered that...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Initially I was worried the right-shift key (which is moved to the right of the up arrow) would be problematic for typing.  It's not.  I haven't even noticed it.&lt;/li&gt;&lt;li&gt;I was also worried the keyboard would be way too small and would get in the way.  Again, it doesn't.  It's a comfortable size.&lt;/li&gt;&lt;/ul&gt;I've been using it now for a week, and there are definatly some factors that I do not care for...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The fan.  Oh my gosh.  It sounds like this "brrrr... (wait 2 seconds) brrrr... (wait 2 seconds) brrrr.... (wait 2 seconds) brrr...."  On-off-on-off-on-off.  It gets annoying.&lt;/li&gt;&lt;li&gt;When you close the lid the laptop just kinda, I'm not sure what it does.  But when you re-open the lid hours later tapping the power button or hitting the space bar does... nothing!  Maybe it's Windows or maybe I just don't use it right, but I end up having to hold the power button down, let it go completely off, and then reboot again.  I wish it was more mac-like where I can just close the lid when I'm done, the netbook turns off in power-save, and then when I re-open it it powers up again.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The battery has two rear rubber feet, which when on a desk are nice, but when on your lap while sitting tend to jab into your thighs.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I miss having a "home" and "end" key, but I suppose that's the price you pay for a netbook.  To be fair, there are home and end, but you have to press Fn+PgUp or Down to get them.  It kinda defeats the quick home/end purpose of those keys.&lt;/li&gt;&lt;/ul&gt;Overall this netbook is a viable contender in the $400 market.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-1798182804319158990?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/1798182804319158990/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=1798182804319158990' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1798182804319158990'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1798182804319158990'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/lenovo-s10e-netbook-review-vs-asus-wind.html' title='Lenovo S10e Netbook Review (vs. Asus Wind vs. Acer Aspire One)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-635424222439237365</id><published>2009-06-18T07:54:00.000-10:00</published><updated>2009-06-18T11:07:46.673-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='wsgi'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Django, Apache 2.2, mod_ssl, and mod_wsgi (Part 3)</title><content type='html'>&lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-and-modwsgi.html"&gt;Part 1&lt;/a&gt;  |  &lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi.html"&gt;Part 2&lt;/a&gt;  |  &lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi_18.html"&gt;Part 3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Serving up Django&lt;/span&gt;&lt;br /&gt;Following the completion of &lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi.html"&gt;Part 2&lt;/a&gt;, we have an install of Django and Apache, and 2 virtual hosts running on it.  What we need to do is connect those virtual hosts up to the content we need to serve.&lt;br /&gt;&lt;br /&gt;Before we can do that, we need to add our Django app to the server.  Create a folder on your webserver (anywhere really) that isn't in your document root (don't use htdocs).  In my case I just made a folder off of the drive "Root_Django".   Copy your django app folder into there.  You should end up with something like...&lt;br /&gt;&lt;br /&gt;C:\Root_Django\{nameofyourwebapp}&lt;br /&gt;&lt;br /&gt;To connect Django up to Apache, we need to create a python file that will initialize WSGI and pass that information on to Django.  In your {nameofwebapp} folder, create a sub folder named "apache", and then create a new file in that folder named "django.wsgi".&lt;br /&gt;&lt;br /&gt;Paste this script into that file:&lt;br /&gt;&lt;pre class="syntax"&gt;import os, sys&lt;br /&gt;sys.path.append('C:\\Root_Django')&lt;br /&gt;sys.path.append('C:\\Root_Django\\{nameofwebapp}')&lt;br /&gt;os.environ['DJANGO_SETTINGS_MODULE'] = '{nameofwebapp}.settings'&lt;br /&gt;&lt;br /&gt;# Remap stdout to err&lt;br /&gt;sys.stdout = sys.stderr&lt;br /&gt;&lt;br /&gt;import django.core.handlers.wsgi&lt;br /&gt;application = django.core.handlers.wsgi.WSGIHandler()&lt;br /&gt;&lt;/pre&gt;Be sure of course to change "{nameofwebapp}" to the actual name of your django application.  This short script will take care of a few things...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;the sys.path.append portion needs to point to your actual Django root.  Adjust that if it's not C:\Root_Django&lt;/li&gt;&lt;li&gt;sys.stdout = sys.stderrd will redirect all "print" statements to the Apache error log.  If you don't have any print statements in your code you can drop this, but I prefer to keep it "just in case"&lt;/li&gt;&lt;/ul&gt;This script will take care of initializing Django.  All we need to do is connect up the requests to our apache virtual host to this script.  Edit httpd.confi, and inside of the virtual host for your "www" host, add these lines:&lt;br /&gt;&lt;pre class="syntax"&gt;WSGIScriptAlias / C:\Root_Django\{nameofwebapp}\apache\django.wsgi&lt;br /&gt;&lt;br /&gt;&amp;lt;Directory "C:\Root_Django\{nameofwebapp}"&amp;gt;&lt;br /&gt;Order deny,allow&lt;br /&gt;Allow from all&lt;br /&gt;&amp;lt;/Directory&amp;gt;&lt;br /&gt;&lt;/pre&gt;This tells Apache that all requests coming in from / and up should be handled through the django.wsgi script.  Save your httpd.conf file and restart Apache.  You should see your Django app being served up now, minus the media (images, css, etc.).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Serving up Static Media&lt;/span&gt;&lt;br /&gt;So you're serving python now, but your web app without images and css is pretty limp right?&lt;br /&gt;&lt;br /&gt;Here's what we need to do:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Configure an Apache virtual host to serve our static media&lt;/li&gt;&lt;li&gt;Configure Django to know the correct place for our media&lt;/li&gt;&lt;li&gt;Make sure we're also serving the grappelli media instead of the default admin media&lt;/li&gt;&lt;/ul&gt;Open up your httpd.conf file again, and find the Virtual host for your media.example.com (or whatever your static host name is).  Since this will just be basic serving without WSGI, we simply need to configur the document root to the proper location...&lt;br /&gt;&lt;pre class="syntax"&gt;DocumentRoot "C:\Root_Django\{nameofwebapp}\media"&lt;br /&gt;&amp;lt;Directory "C:\Root_Django\{nameofwebapp}\media"&amp;gt;&lt;br /&gt;Order deny,allow&lt;br /&gt;Allow from all&lt;br /&gt;&amp;lt;/Directory&amp;gt;&lt;br /&gt;&lt;/pre&gt;Tada!  If you were to save and restart apache, then navigation to media.example.com/some/css/file.css it should work.  What we still need is to serve up the grappelli media.  To do this we'll add another alias so that media.example.com&lt;span style="font-weight: bold;"&gt;/admin&lt;/span&gt; will serve the grappelli content.&lt;br /&gt;&lt;pre class="syntax"&gt;Alias /admin "C:\Root_Django\{nameofwebapp}\grappelli\media"&lt;br /&gt;&lt;br /&gt;&amp;lt;Directory "C:\Root_Django\{nameofwebapp}\grappelli\media"&amp;gt;&lt;br /&gt;Order deny,allow&lt;br /&gt;Allow from all&lt;br /&gt;&amp;lt;/Directory&amp;gt;&lt;br /&gt;&lt;/pre&gt;This will make sure that all requests to /admin on your media host will serve the grappelli media files.  If you installed grappelli into your Python site-packages folder, be sure to set the path to that folder instead of the django root like shown above.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Configure Django With Media Prefixes&lt;/span&gt;&lt;br /&gt;Lastly for this section, we need to do some configuration in Django.&lt;br /&gt;&lt;br /&gt;In &lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-and-modwsgi.html"&gt;Part 1&lt;/a&gt; we installed grappelli but we haven't configured it, so we'll do that now.  Also, we're using a static media virtual host, so we need to make sure that Django is prefixing our media files correctly.  All of this takes place in the settings.py file.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Configure grappelli&lt;/span&gt;&lt;br /&gt;If you'd rather see the officiall grappelli installation instructions, they are available on the &lt;a href="http://code.google.com/p/django-grappelli/wiki/Installation"&gt;grappelli google code wiki&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;First, add grappelli to the content processors:&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;br /&gt;TEMPLATE_CONTEXT_PROCESSORS = (&lt;br /&gt; "django.core.context_processors.auth",&lt;br /&gt; "django.core.context_processors.request",&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;    "grappelli.context_processors.admin_url",&lt;/span&gt;&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;Next, add the grappelli template directory:&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;br /&gt;import os&lt;br /&gt;here = lambda x: os.path.join(os.path.abspath(os.path.dirname(__file__)), x)&lt;br /&gt;TEMPLATE_DIRS = (&lt;br /&gt; # ...&lt;br /&gt; here('grappelli/templates/'),&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;Lastly, of course, add grappelli to your list of installed apps:&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;br /&gt;INSTALLED_APPS = (&lt;br /&gt; # ...&lt;br /&gt; 'grappelli',&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;span style="font-size:130%;"&gt;Configure Media Prefixes&lt;/span&gt;&lt;br /&gt;We want to configure Django to automatically insert "http://media.example.com/" in front of all of our media files.  The setting in the settings.py to do this is:&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;br /&gt;MEDIA_URL = 'http://media.example.com/'&lt;br /&gt;&lt;/pre&gt;But this could easily become problematic when we're testing django with runserver.  A better result would be to conditionally test if we're in DEBUG mode, and to change the value based on that.&lt;br /&gt;&lt;pre class="syntax"&gt;if DEBUG:&lt;br /&gt; MEDIA_URL = '/media/'&lt;br /&gt;else:&lt;br /&gt; MEDIA_URL = 'http://media.example.com/'&lt;br /&gt;&lt;/pre&gt;We'll also want to make sure the admin media files from grappelli are being served.  The code is almost identical, expect the setting is ADMIN_MEDIA_PREFIX:&lt;br /&gt;&lt;pre class="syntax"&gt;if DEBUG:&lt;br /&gt; ADMIN_MEDIA_PREFIX = '/grappelli/media/'&lt;br /&gt;else:&lt;br /&gt; ADMIN_MEDIA_PREFIX = 'http://media.example.com/admin/'&lt;br /&gt;&lt;/pre&gt;At this point Django should be running, and serving media files.&lt;br /&gt;&lt;br /&gt;Still to come:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Configuring HTTPS&lt;/li&gt;&lt;li&gt;Forcing HTTPS for django admin&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-635424222439237365?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/635424222439237365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=635424222439237365' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/635424222439237365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/635424222439237365'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi_18.html' title='Django, Apache 2.2, mod_ssl, and mod_wsgi (Part 3)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5196295811864496081</id><published>2009-06-17T16:05:00.000-10:00</published><updated>2009-06-18T08:39:17.530-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Django, Apache 2.2, mod_ssl, and mod_wsgi (Part 2)</title><content type='html'>&lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-and-modwsgi.html"&gt;Part 1&lt;/a&gt;  |  &lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi.html"&gt;Part 2&lt;/a&gt;  |  &lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi_18.html"&gt;Part 3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;DNS&lt;/span&gt;&lt;br /&gt;I'm going to make an assumption.  You have 1 domain and can create hosts under that domain.  Fr example, say you own example.com.  I'm assuming you have the ability to create "media.example.com" with whomever hosts your DNS.&lt;br /&gt;&lt;br /&gt;You'll want to setup 2 domains:&lt;br /&gt;www.example.com&lt;br /&gt;media.example.com&lt;br /&gt;&lt;br /&gt;Since this process is virtually different for everyone I won't cover it.  Contact your DNS provider for assistance here.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Setting up Apache&lt;/span&gt;&lt;br /&gt;If you've never used Apache, you can pretty much control the entire webserver from 1 file:  httpd.conf.  By default it's located in {apache install direcotory}\conf\httpd.conf.  I like to edit it in &lt;a href="http://en.wikipedia.org/wiki/WordPad"&gt;Wordpad&lt;/a&gt; becuase I'm a &lt;a href="http://en.wikipedia.org/wiki/Lightweight"&gt;lightweight&lt;/a&gt;, but you're welcome to edit it in whatever hacker-rific editor you prefer.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:100%;" &gt;Installing mod_wsgi&lt;/span&gt;&lt;br /&gt;mod_wsgi is a module addon for Apache which is responsible for accepting the incoming HTTP connection, turning that connection into a python object, and then passing it to Django.  In other words, it's &lt;span style="font-style: italic;"&gt;really&lt;/span&gt; important.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Copy the .so file you downloaded into {apache install directory}\modules.&lt;/li&gt;&lt;li&gt;Open the httpd.conf file and find the section that has all the "LoadModule"s in it&lt;/li&gt;&lt;li&gt;Add this line:&lt;br /&gt;LoadModule wsgi_module modules/mod_wsgi.so&lt;br /&gt;&lt;/li&gt;&lt;li&gt;This will load the wgi module whenever Apache runs&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;"&gt;Configuring Virtual Hosts&lt;/span&gt;&lt;br /&gt;Let's recall what our objectives for this project are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Serve up Django with mod_wsgi by default from the site root&lt;/li&gt;&lt;li&gt;Serve up static media (images, css, js, etc.) from a separate virtual host&lt;/li&gt;&lt;li&gt;Offer the Django app in both HTTP and HTTPS&lt;/li&gt;&lt;/ul&gt;To achieve this, we'll need to setup &lt;span style="font-weight: bold;"&gt;at least&lt;/span&gt; 2 virtual hosts:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;to hold the Django application&lt;/li&gt;&lt;li&gt;to hold all of our static media&lt;/li&gt;&lt;/ol&gt;We'll also need a way to configure Django to know where our static virtual host is.&lt;br /&gt;&lt;br /&gt;In case you're wondering, there are a few reasons why this is an ideal setup:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;We want our Django application to worry about 1 thing: serving up python&lt;/li&gt;&lt;li&gt;It would probably be a performance hit if python had to process all of our media files.  It makes more sense to have them directly served by apache&lt;/li&gt;&lt;li&gt;We can enable caching for everything on our static media virtual host, also improving performance&lt;/li&gt;&lt;li&gt;It's uber leet cool (no, not really)&lt;/li&gt;&lt;/ol&gt;You can create virtual hosts in apache with the &lt;virtualhost&gt; directive.  Here's a sample of 2 virtual hosts:&lt;br /&gt;&lt;br /&gt;&lt;pre class="syntax"&gt;NameVirtualHost *:80&lt;br /&gt;&lt;br /&gt;&amp;lt;VirtualHost *:80&amp;gt;&lt;br /&gt;ServerName www.example.com:80&lt;br /&gt;ServerAdmin you@example.com&lt;br /&gt;&amp;lt;/virtualhost&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;VirtualHost *:80&amp;gt;&lt;br /&gt;ServerName media.example.com:80&lt;br /&gt;ServerAdmin you@example.com&lt;br /&gt;&amp;lt;/VirtualHost&amp;gt;&lt;/pre&gt;&lt;br /&gt;This will give us 2 domains to serve from.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Still to come....&lt;br /&gt;&lt;/span&gt;&lt;/virtualhost&gt;&lt;ul&gt;&lt;li&gt;Configuring wsgi and django&lt;/li&gt;&lt;li&gt;Configuring django settings to use our virtual hosts&lt;/li&gt;&lt;li&gt;Adding https support&lt;/li&gt;&lt;li&gt;Forcing /admin to use https only&lt;/li&gt;&lt;/ul&gt;&lt;virtualhost&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/virtualhost&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5196295811864496081?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5196295811864496081/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5196295811864496081' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5196295811864496081'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5196295811864496081'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi.html' title='Django, Apache 2.2, mod_ssl, and mod_wsgi (Part 2)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-186609414667235062</id><published>2009-06-17T15:45:00.000-10:00</published><updated>2009-06-18T08:39:25.359-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Django, Apache 2.2, mod_ssl, and mod_wsgi (Part 1)</title><content type='html'>&lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-and-modwsgi.html"&gt;Part 1&lt;/a&gt;  |  &lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi.html"&gt;Part 2&lt;/a&gt;  |  &lt;a href="http://urlencode.blogspot.com/2009/06/django-apache-22-modssl-and-modwsgi_18.html"&gt;Part 3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It's funny how experience changes your perspective on things.  5 years ago I tried apache and thought it was too hard.  "Why can't I just have a GUI to click the settings I want?!?" I would think in frustration.  Now that I'm making the transition to Django, I thought I'd give Apache another try.  Wow -- What a luxury it is to be able to just write a config file to do exactly what you want to do and not have to monkey around with where some stupid settings is in an extensive maze of windows and tabs.  Hah... how times change indeed.&lt;br /&gt;&lt;br /&gt;Anyways, here's a bit about setting up Django with Apache.  Here's what will be achieved:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Serve up Django with mod_wsgi by default from the site root&lt;/li&gt;&lt;li&gt;Serve up static media (images, css, js, etc.) from a separate virtual host&lt;/li&gt;&lt;li&gt;Offer the Django app in both HTTP and HTTPS&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Force the Django admin to always use HTTPS&lt;/li&gt;&lt;li&gt;Use &lt;a href="http://code.google.com/p/django-grappelli/"&gt;django-grappelli&lt;/a&gt; for a slicker looking Django admin, as well as some other goodies&lt;/li&gt;&lt;li&gt;And lastly... do all this on a Windows Server (hah)&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-size:130%;"&gt;Installing Software&lt;/span&gt;&lt;br /&gt;If you're following along at home, here's the list of things you'll need to download for a complete deployment:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The latest &lt;a href="http://httpd.apache.org/download.cgi"&gt;Apache Web Server Win32 binary package&lt;/a&gt; (get the installer)&lt;/li&gt;&lt;li&gt;The lastest &lt;a href="http://code.google.com/p/modwsgi/downloads/list"&gt;mod_wsgi binary package&lt;/a&gt; (pre-compiled *.so)&lt;/li&gt;&lt;li&gt;&lt;a href="http://tortoisesvn.net/downloads"&gt;TortoiseSVN&lt;/a&gt; (to get Django and grappelli with)&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.python.org/download/"&gt;Python 2.6 Windows installer&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;Install all of the above, accepting the defaults (unless you otherwise know what you're doing).  For mod_wsgi I'll cover installation in the next part, so hang onto it for now.&lt;br /&gt;&lt;br /&gt;Now we'll need to do 2 SVN checkouts of code...&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Installing the latest Django trunk from SVN&lt;/span&gt;&lt;br /&gt;(note: this method will have the Django development trunk as your production copy of Django.  If you don't like that, then just use the installer or the tar/zip from the Django site).&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In your python install directory (default: C:\Python26) open Lib and then site-packages&lt;/li&gt;&lt;li&gt;Create a new folder named "django"&lt;/li&gt;&lt;li&gt;Right-click on that folder and choose "SVN Checkout" (this will launch TortoiseSVN)&lt;/li&gt;&lt;li&gt;Paste this URL into that window:&lt;code&gt;&lt;br /&gt;http://code.djangoproject.com/svn/django/trunk/django&lt;span style="font-family:Georgia,serif;"&gt;&lt;/span&gt;&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;You've now got a fresh copy of Django, but we need to configure Windows to be able to find Python and the Django tools.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Right-click "My Computer" and choose Properties (this might be slightly different for Vista+)&lt;/li&gt;&lt;li&gt;"Advanced" tab&lt;/li&gt;&lt;li&gt;"Environment Variables" at the bottom&lt;/li&gt;&lt;li&gt;In the "System variables" list, find "Path"&lt;/li&gt;&lt;li&gt;Highlight it, and hit "Edit"&lt;/li&gt;&lt;li&gt;Add this value to the end of it (change the path to your python path if it's not default):&lt;br /&gt;C:\Python26;C:\Python26\Lib\site-packages\django\bin;&lt;/li&gt;&lt;/ul&gt;Now you'll want to test to make sure this works.&lt;br /&gt;&lt;br /&gt;Open a command prompt and type "python" and hit enter.  Python should run.  Ctrl+Z out of that, and type "django-admin.py help" and hit enter.  It also should run.&lt;br /&gt;&lt;br /&gt;Django and python are good to go.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Installing grappelli&lt;/span&gt;&lt;br /&gt;This next part uses my preferences.  You can do this different if you want, but keep in mind you'll need to adjust paths later on to account for your differences.&lt;br /&gt;&lt;br /&gt;I like to install my django re-usable apps into my project directory.  This adds them to the SVN trunk and I don't have to worry about if others in the office have the app installed on their local machine or not.&lt;br /&gt;&lt;br /&gt;Anyways, open your Django project (the site you've made), and create a new folder called "grappelli".  Right-click that folder and choose "SVN Checkout".  Paste this URL and hit ok:&lt;br /&gt;&lt;br /&gt;&lt;tt id="checkoutcmd"&gt;&lt;strong&gt;&lt;em&gt;http&lt;/em&gt;&lt;/strong&gt;://django-grappelli.googlecode.com/svn/trunk/grappelli&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;It should download a copy of grappelli.   We'll look at adding it to Django in the next part.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-186609414667235062?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/186609414667235062/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=186609414667235062' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/186609414667235062'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/186609414667235062'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/django-apache-22-and-modwsgi.html' title='Django, Apache 2.2, mod_ssl, and mod_wsgi (Part 1)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5984276282009712295</id><published>2009-06-10T06:48:00.001-10:00</published><updated>2009-06-10T06:52:14.350-10:00</updated><title type='text'>Desktop  :: June</title><content type='html'>&lt;div style="padding: 3px; text-align: left;"&gt;&lt;a href="http://www.flickr.com/photos/24591966@N02/3612828853/" title="photo sharing"&gt;&lt;img src="http://farm4.static.flickr.com/3623/3612828853_a894ba22d4.jpg" style="border: 2px solid rgb(0, 0, 0);" alt="" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="margin-top: 0px;font-size:0;" &gt;&lt;a href="http://www.flickr.com/photos/24591966@N02/3612828853/"&gt;Vista Desktop, June 09&lt;/a&gt;, originally uploaded by &lt;a href="http://www.flickr.com/people/24591966@N02/"&gt;wastingtape&lt;/a&gt;.&lt;/span&gt;&lt;/div&gt;&lt;p&gt;My desktop for June&lt;br /&gt;&lt;br /&gt;OS: Windows XP&lt;br /&gt;Wallpaper: &lt;a href="http://thebestisaac.deviantart.com/art/Watercolor-Emico-Wallpaper-97156964"&gt;Watercolor Emico&lt;/a&gt;&lt;br /&gt;Dock:  &lt;a href="http://home.cogeco.ca/%7Erklauncher/"&gt;RKLauncher&lt;/a&gt;&lt;br /&gt;Dock Skin: &lt;a href="http://v1art-star.deviantart.com/art/AluCurved3D-RK-Launcher-mod-92755988"&gt;AluCurved3D&lt;/a&gt;&lt;br /&gt;Desktop Info: &lt;a href="http://www.google.com/url?q=http://www.samurize.com/&amp;amp;ei=oOQvSr6UEaaItAOcitW4CA&amp;amp;sa=X&amp;amp;oi=spellmeleon_result&amp;amp;resnum=1&amp;amp;ct=result&amp;amp;usg=AFQjCNHts46Jb_5lVOCc-7D6BCfAVRUVvg"&gt;Samurize&lt;/a&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5984276282009712295?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5984276282009712295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5984276282009712295' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5984276282009712295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5984276282009712295'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/vista-desktop-june-09.html' title='Desktop  :: June'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3623/3612828853_a894ba22d4_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-2049721575125077241</id><published>2009-06-06T10:11:00.001-10:00</published><updated>2009-06-06T10:17:28.051-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='django-models'/><title type='text'>Determine Model Change v2</title><content type='html'>Well I got around to doing a 2nd revision on my model change code (being the weekend I was wondering if it would come to pass).  Per a suggestion by "thepointer" (#django IRC on freenode), I switched the code from using Python's generic vars() to Django's interal _meta.  Using an internal API is probably not the ultimate best, but _meta has been stable and unchanged for quite a while.&lt;br /&gt;&lt;br /&gt;I also added a "human_friendly" mode, which will take the model change and attempt to turn it into an understandable statement (string) about what exactly has changed.  It still returns it in a dictionary with the field name as the key.&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;django.db&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;determine_model_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;human_friendly&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;False&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ignore_fields&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{}):&lt;/span&gt;&lt;br /&gt;    &lt;span class="sd"&gt;"""&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    Compares the two models against each other, returning a dictionary of&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    values that have changed (new value only).&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    Setting human_friendly=True will cause ignore internal fields like&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    SlugField.  It will also attempt to parse a meaningful statement for&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    the model change. ie 'Event date is now 5/7/2009'&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    """&lt;/span&gt;&lt;br /&gt;    &lt;span class="n"&gt;not_human_fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;SlugField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;        &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;FilePathField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;IPAddressField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;FileField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;ImageField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;        &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;XMLField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;    &lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="n"&gt;changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_meta&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;ignore_fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;                &lt;span class="n"&gt;new_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span class="n"&gt;old_value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;old_value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;human_friendly&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;                        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;not_human_fields&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;                            &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;__verbose_field_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;                    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;                        &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_value&lt;/span&gt;&lt;br /&gt;    &lt;br /&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__verbose_field_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;    &lt;span class="sd"&gt;"""&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    Returns the human-friendly text for a field change&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    """&lt;/span&gt;&lt;br /&gt;    &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;getattr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;DateField&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; \&lt;br /&gt;        &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;TimeField&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; \&lt;br /&gt;        &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;models&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="kp"&gt;DateTimeField&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;            &lt;br /&gt;        &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;strftime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'%b &lt;/span&gt;&lt;span class="si"&gt;%d&lt;/span&gt;&lt;span class="s"&gt;, %Y %I:%M %p'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;br /&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt; has changed to &lt;/span&gt;&lt;span class="si"&gt;%s&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;br /&gt;            &lt;span class="n"&gt;old_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;            &lt;span class="n"&gt;field&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;verbose_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;br /&gt;            &lt;span class="n"&gt;value&lt;/span&gt;&lt;br /&gt;        &lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="c"&gt;# -------------&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# Sample usage:&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# -------------&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;happenings.models&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;datetime&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;objects&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pk&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"My Birthday"&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;newevent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;newevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;determine_model_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newevent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'start_time'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;datetime&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2009&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;determine_model_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;newevent&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;human_friendly&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'start_time'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'My Birthday start time has changed to Jul 15, 2009 12:00 AM'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-2049721575125077241?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/2049721575125077241/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=2049721575125077241' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2049721575125077241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2049721575125077241'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/determine-model-change-v2.html' title='Determine Model Change v2'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-1663273685741163068</id><published>2009-06-06T08:43:00.000-10:00</published><updated>2009-06-06T08:53:37.124-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Easier, Faster Property Enumeration in Python</title><content type='html'>I just discovered a really neat trick in Python to take a collection of objects, and turn one of their properties into a list.  It's not terribly difficult to perform this the old way...&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span class="n"&gt;subscribers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;&lt;br /&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;subscription&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscriptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;&lt;br /&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;subscribers&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;This would return something like [(User:bob),(User:jerry),(User:tim)] and so on.  However, this can be done in just a single line...&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;s&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;subscriptions&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;all&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;&lt;/pre&gt;+1 for &lt;a href="http://www.python.org/"&gt;Python&lt;/a&gt; coolness.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-1663273685741163068?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/1663273685741163068/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=1663273685741163068' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1663273685741163068'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1663273685741163068'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/easier-faster-property-enumeration-in.html' title='Easier, Faster Property Enumeration in Python'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-8901116721322299043</id><published>2009-06-05T21:01:00.000-10:00</published><updated>2009-06-05T22:30:42.808-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='django-models'/><title type='text'>Django Model Comparison</title><content type='html'>I started working more with &lt;a href="http://github.com/jtauber/django-notification/"&gt;django-notification&lt;/a&gt; today.  However, my implementation (outside of Pynax) is to create a subscription app, particularly for subscribing to event or calendar updates.  Using &lt;a href="http://docs.djangoproject.com/en/dev/topics/signals/"&gt;Django's signals&lt;/a&gt;, I've got most of it setup.  What I needed was a way to tell the difference between the old and new event, so that the notification could actually tell you "The start time for Event X has changed to Y:ZX" instead of "An event you're watching has been updated!".&lt;br /&gt;&lt;br /&gt;So to do that I needed some model comparision code.  Here's version 1.&lt;br /&gt;&lt;br /&gt;&lt;pre class="syntax"&gt;&lt;br /&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;determine_model_change&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;  &lt;span class="sd"&gt;"""&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    Compares the two models against each other, returning a dictionary of&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="sd"&gt;    values that have changed (new value only).&lt;/span&gt;&lt;br /&gt;&lt;span class="sd"&gt;    """&lt;/span&gt;&lt;br /&gt;  &lt;span class="n"&gt;new_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;  &lt;span class="n"&gt;old_values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;vars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;old_model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="n"&gt;changed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;new_values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;      &lt;span class="c"&gt;# Skip internal and 'magical' properties&lt;/span&gt;&lt;br /&gt;      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;startswith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'_'&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;&lt;br /&gt;          &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;old_values&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;              &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;cmp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;old_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;                  &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;br /&gt;          &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;              &lt;span class="c"&gt;# Save the new value if it is not in the old values&lt;/span&gt;&lt;br /&gt;              &lt;span class="n"&gt;changed&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;new_values&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;br /&gt;  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;changed&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="c"&gt;# Sample run:&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt; class SomeModel(models.Model):&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt;    name = models.CharField()&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt;    age = models.IntegerField()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt; foo1 = SomeModel()&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt; foo1.name = "Some Model Name"&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt; foo1.age = 14&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt; foo2 = SomeModel()&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt; foo2.name = "Foo Model 2"&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt; foo2.age = 14&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# &amp;gt;&amp;gt;&amp;gt; determine_model_change(foo1, foo2)&lt;/span&gt;&lt;br /&gt;&lt;span class="c"&gt;# {'name': 'Foo Model 2'}&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-8901116721322299043?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/8901116721322299043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=8901116721322299043' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/8901116721322299043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/8901116721322299043'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/i-think-i-figured-out-how-to-format.html' title='Django Model Comparison'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-5318689078403179297</id><published>2009-06-05T10:25:00.000-10:00</published><updated>2009-06-05T22:24:20.201-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='netbeans'/><title type='text'>Ignoring .pyc files in NetBeans</title><content type='html'>I started using the &lt;a href="http://www.netbeans.org/features/python/"&gt;Netbeans 6.5 Python (Early Access) IDE&lt;/a&gt; a couple of weeks ago, and while all seemed to be going well, one thing that bugged me was seeing all the &lt;a href="http://www.fileinfo.com/extension/pyc"&gt;.pyc&lt;/a&gt; (python compiled file) in the treeviews.  Turns out NetBeans has a simple way to fix this:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;On the menu go to Tools &gt; Options&lt;/li&gt;&lt;li&gt;Then "Miscellaneous"&lt;/li&gt;&lt;li&gt;Then "Files" tab&lt;/li&gt;&lt;li&gt;And find the section "Files Ignored by the IDE"&lt;/li&gt;&lt;/ul&gt;This value is a &lt;a href="http://www.regular-expressions.info/"&gt;regular expression&lt;/a&gt; which makes it easy to add the functionality we're looking for.&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;Change this:&lt;br /&gt;^(CVS|SCCS|vssver.?\.scc|#.*#|%.*%|_svn)$|~$|^\.(?!htaccess$).*$&lt;br /&gt;&lt;br /&gt;To this:&lt;br /&gt;^(CVS|SCCS|vssver.?\.scc|#.*#|%.*%|_svn)$|~$|^\.(?!htaccess$)&lt;span style="font-weight: bold;"&gt;|pyc&lt;/span&gt;.*$&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;You could easily use this method for any other file type you'd like to ignore.  Just add "|extension" before the .*$.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-5318689078403179297?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/5318689078403179297/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=5318689078403179297' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5318689078403179297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/5318689078403179297'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/06/ignoring-pyc-files-in-netbeans.html' title='Ignoring .pyc files in NetBeans'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-3029540171669090925</id><published>2009-05-25T23:00:00.000-10:00</published><updated>2009-06-05T22:24:35.070-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><title type='text'>Django is awesomeness (for serious web apps)</title><content type='html'>&lt;a href="http://www.djangoproject.com/"&gt;Django&lt;/a&gt; is awesome.&lt;br /&gt;&lt;br /&gt;I shouldn't say that since I haven't built anything substantial in it.  But having created 3 or 4 test apps and having read half of the documentation, I'm rather (more or less) confident in that statement.&lt;br /&gt;&lt;br /&gt;And they have &lt;a href="http://www.djangopony.com/"&gt;ponies&lt;/a&gt;.  &lt;a href="http://www.flickr.com/photos/juliaelman/2868050230/"&gt;Serious ponies&lt;/a&gt;.  (but where are the pony shirts guys?)&lt;br /&gt;&lt;a href="http://djangopony.com/" class="ponybadge" title="Magic! Ponies! Django! Whee!"&gt;&lt;img src="http://media.djangopony.com/img/small/badge.png" alt="ponybadge" width="210" height="65" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Features I'm already all up in arms about:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://djangoplugables.com/"&gt;Self-contained, reusable apps&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Automatic &lt;a href="http://docs.djangoproject.com/en/dev/ref/contrib/admin/#ref-contrib-admin"&gt;admin interface&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://docs.djangoproject.com/en/dev/topics/forms/#topics-forms-index"&gt;"Classed" forms&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://docs.djangoproject.com/en/dev/ref/unicode/#ref-unicode"&gt;Unicode support&lt;/a&gt; through and through&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Multiple &lt;a href="http://docs.djangoproject.com/en/dev/topics/auth/#authentication-backends"&gt;authentication backends&lt;/a&gt; (including user-created)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A great &lt;a href="http://djangopeople.net/"&gt;community&lt;/a&gt;, with &lt;a href="http://docs.djangoproject.com/en/dev/"&gt;documentation&lt;/a&gt;, free &lt;a href="http://www.djangobook.com/en/2.0/"&gt;online books&lt;/a&gt;, and &lt;a href="http://www.youtube.com/view_play_list?p=D415FAF806EC47A1"&gt; DjangoCon&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;And of course the ponies.&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-3029540171669090925?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/3029540171669090925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=3029540171669090925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/3029540171669090925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/3029540171669090925'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/05/django-is-awesomeness-for-serious-web.html' title='Django is awesomeness (for serious web apps)'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-7878687369277573016</id><published>2009-05-15T10:33:00.001-10:00</published><updated>2009-06-05T22:24:44.984-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>Customizing the ASP.NET Issue Tracker</title><content type='html'>Microsoft's &lt;a href="http://www.asp.net/downloads/archived/starter-kits/issue-tracker/"&gt;Issue Tracker&lt;/a&gt; (part of the ASP.NET Starter Kit package) is old code.  But it's free, it works, it runs on IIS6, and it's open source.  I probably could have gone with something fancier.&lt;br /&gt;&lt;br /&gt;Anyways, right away I noticed that there were a number of usability tweaks that could be made to make the application quite a bit easier to use.  Here's a good example...&lt;br /&gt;&lt;br /&gt;When you create a new event, by default, the "Owned By" combo box is empty.  To me, that doesn't make sense.  The majority of the time the owner will also be the... creator!  So why not just set the value to the current user automatically when a new event is being created?&lt;br /&gt;&lt;br /&gt;If you look inside ~/Issues/IssueDetail.aspx you'll see that the type of control for choosing the Owner is &lt;it:picksingleuser&gt;.  All of the data bound controls within Issue Tracker are regular Web Forms controls, wrapped in a custom UserControl.  By default not all of the properties are exposed, so we'll need to first expose the list items collection by adding this code to ~/UserControls/PickSingleUser.aspx.cs&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;        public ListItemCollection Items&lt;br /&gt;       {&lt;br /&gt;           get { return dropUsers.Items; }&lt;br /&gt;       }&lt;/blockquote&gt;&lt;br /&gt;Now that we can access the list items, let's write the code to actually find and select the user.&lt;br /&gt;&lt;br /&gt;The most logical place to handle this is on the Page_Load event of ~/Issues/IssueDetail.aspx.cs  There's already an if/then handler for if the item is new or not.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;if (IssueId == 0)&lt;br /&gt;{&lt;br /&gt;   BindOptions();&lt;br /&gt;   ctlCustomFields.DataSource = CustomField.GetCustomFieldsByProjectId(ProjectId);&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;   BindValues();&lt;br /&gt;   ctlCustomFields.DataSource = CustomField.GetCustomFieldsByIssueId(IssueId);&lt;br /&gt;}&lt;/blockquote&gt;&lt;br /&gt;Here's what we need to do:  Get the current user's full name, find that name in the databound dropOwned list items, and then select that item.&lt;br /&gt;&lt;br /&gt;Issue Tracker already includes a user manager type object built in, ITUser.  We can pass it the current page's identity and get the user from the user database.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;ITUser user = ITUser.GetUserByUsername(Page.User.Identity.Name);&lt;/blockquote&gt;&lt;br /&gt;From there, all we need to do is identify that user in the list item collection, and select it.&lt;br /&gt;&lt;blockquote&gt;foreach (ListItem li in dropOwned.Items)&lt;br /&gt;   if (li.Text == user.DisplayName)&lt;br /&gt;   {&lt;br /&gt;       li.Selected = true;&lt;br /&gt;       break;&lt;br /&gt;   }&lt;/blockquote&gt;The current user should not automatically be selected as the Owner default when a new issue is created.&lt;br /&gt;&lt;br /&gt;The complete code listing should look like this:&lt;br /&gt;&lt;blockquote&gt;// Initialize for Adding or Editing&lt;br /&gt;if (IssueId == 0)&lt;br /&gt;{&lt;br /&gt;    BindOptions();&lt;br /&gt;    ctlCustomFields.DataSource = CustomField.GetCustomFieldsByProjectId(ProjectId);&lt;br /&gt;&lt;br /&gt;    // Choose defaults&lt;br /&gt;    ITUser user = ITUser.GetUserByUsername(Page.User.Identity.Name);&lt;br /&gt;    foreach (ListItem li in dropOwned.Items)&lt;br /&gt;        if (li.Text == user.DisplayName)&lt;br /&gt;        {&lt;br /&gt;            li.Selected = true;&lt;br /&gt;            break;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;}&lt;br /&gt;else&lt;br /&gt;{&lt;br /&gt;    BindValues();&lt;br /&gt;    ctlCustomFields.DataSource = CustomField.GetCustomFieldsByIssueId(IssueId);&lt;br /&gt;}&lt;/blockquote&gt;&lt;br /&gt;&lt;/it:picksingleuser&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-7878687369277573016?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/7878687369277573016/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=7878687369277573016' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7878687369277573016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/7878687369277573016'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/05/customizing-aspnet-issue-tracker.html' title='Customizing the ASP.NET Issue Tracker'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-1490325590585075866</id><published>2009-05-04T00:19:00.000-10:00</published><updated>2009-06-05T22:25:05.772-10:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ASP.NET'/><title type='text'>HttpModule for easier Master Pages</title><content type='html'>You know, I just don't like ASP.NET's Master Page feature.  The thing is, I've always felt they create more hassle in the long run.  Typically, I end up with way too many content place holders.  I also really dislike looking at HTML pages which are only a series of &lt;asp:content&gt;&amp;lt;asp:Content&amp;gt; tag blocks.&lt;br /&gt;&lt;br /&gt;So here's my workaround: a custom HttpModule that emulates the effect of Master Page without having to actually create and use a Master Page.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;How It Works&lt;/span&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A custom HttpModule is registered in the Web.config file&lt;/li&gt;&lt;li&gt;This module catches every request that is of Content-Type text/html (this includes aspx and such) and inserts pre-defined HTML into the outgoing HTML.&lt;/li&gt;&lt;/ul&gt;Sounds easy right?  Yeah it is.  I call my creation "Page Master"!  (Aren't I a clever one)&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Specifics of What It Makes Easier&lt;/span&gt;&lt;br /&gt;Generally, I found the only thing I was using master pages for was...&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Common CSS files&lt;/li&gt;&lt;li&gt;Common Javascript files&lt;/li&gt;&lt;li&gt;Page header&lt;/li&gt;&lt;li&gt;Page footer&lt;/li&gt;&lt;/ul&gt;Page Master supports all 3 of these, all user-definable in the Web.config file:&lt;pre&gt;&amp;lt;!-- Config for PageMaster Http Module --&amp;gt;&lt;br /&gt;&amp;lt;pagemaster&amp;gt;&lt;br /&gt; &amp;lt;headtag file="~/PageMaster/headtag.html" /&amp;gt;&lt;br /&gt; &amp;lt;header file="~/PageMaster/header.html" /&amp;gt;&lt;br /&gt; &amp;lt;footer file="~/PageMaster/footer.html" &amp;gt;&lt;br /&gt;&amp;lt;/pagemaster&amp;gt;&lt;/pre&gt;&lt;br /&gt;So, let's say you have a basic HTML page...&lt;br /&gt;&lt;pre&gt;&amp;lt;-- HTML page unmodified --&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;lt;title&amp;gt;My Page&amp;lt;/title&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt; &amp;lt;p&amp;gt;Body Text&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;&lt;br /&gt;The contents of your "headtag.html" is...&lt;br /&gt;&lt;pre&gt;&amp;lt;script src="/js/fake.js" /&amp;gt;&lt;/pre&gt;&lt;br /&gt;With Page Master, the resulting page will be returned to the client instead as...&lt;br /&gt;&lt;pre&gt;&amp;lt;-- HTML page after Page Master --&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;&amp;lt;script src="/js/fake.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&amp;lt;title&amp;gt;My Page&amp;lt;/title&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt; &amp;lt;p&amp;gt;Body Text&amp;lt;/p&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;/pre&gt;...without ever having to use Master Pages.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Limitations&lt;/span&gt;&lt;br /&gt;Since this was a quick project I whipped up in a couple hours, it's rather limited.&lt;br /&gt;&lt;br /&gt;Right now this module only supports static HTML files, but I'd like to expand it to also be able to execute ASPX files, or to fetch a file from another server with HTTP.  In case you're worried about performance, the data which is inserted is cached to the whole application, so it's only loaded once, and not every page request.&lt;br /&gt;&lt;br /&gt;Another limitation is that it will insert the values for all pages of Content-Type "text/html", and not just the ones you've specified.  Perhaps a feature that allows one to configure pages to ignore or not to do that on.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.2shared.com/file/5626166/4ed997a5/PageMaster-100.html"&gt;Download Page Master Http Module Source + Test Project&lt;/a&gt;&lt;br /&gt;(Hit the 'click here' link next to "Save file to your PC:")&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;References&lt;/span&gt;&lt;br /&gt;&lt;a href="http://aspnetresources.com/articles/HttpFilters.aspx"&gt;Producing XHTML Compliant Pages with Response Filters&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.codeproject.com/KB/aspnet/Managing_Webconfig.aspx"&gt;Customizing SectionGroups and Sections&lt;/a&gt;&lt;br /&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ms228256.aspx"&gt;configSection Element (MSDN)&lt;/a&gt;&lt;br /&gt;&lt;/asp:content&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-1490325590585075866?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/1490325590585075866/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=1490325590585075866' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1490325590585075866'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/1490325590585075866'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2009/05/httpmodule-for-easier-master-pages.html' title='HttpModule for easier Master Pages'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3762802375429612564.post-2596383676603564139</id><published>2008-03-19T23:31:00.000-10:00</published><updated>2008-03-19T23:54:41.620-10:00</updated><title type='text'>Webservices, MS Ajax, and Safari</title><content type='html'>&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;.NET Webservice&lt;/span&gt;&lt;/div&gt;I finally wrote a webservice that will go into production usage when the beta site launches: &lt;a href="http://beta.hanalani.org/addresslookup.asmx"&gt;AddressLookup&lt;/a&gt;.  It reads ZIP code/city/state information from an Access database and returns it.  This webservice will get implemented in various areas of the site to do things like auto-populate the city and state when you enter in your zip.  Expect to see it on the Application Packet Request page and the like.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-weight: bold;"&gt;ASP.NET AJAX&lt;/span&gt;&lt;/div&gt;&lt;div&gt;Embrace, extend, extinguish mmm?  Microsoft apparently released an update for .NET Framework 2.0 (included with 3.0 and above) which adds AJAX support (returning JSON for Javascript) for .NET Webservices.  Since I was mainly looking at utilizing my newly minted webservice on the client-side this was something I was all ears for.  After some fiddling with the web.config I had "MS AJAX" running on the webserver.  I don't know why Microsoft feels the need to make everything their own.  The oddest part is how it appears that MS attempted to set it up so that one could program an AJAX (client-side javascript partial page updates) completely from Visual Studio or Visual Web Developer without ever writing a line of Javascript.  :/ &lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I like:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;That the AJAX addition on the webservice takes care of serializing to JSON for me in the same way the normal webservice automatically serializes to XML&lt;/li&gt;&lt;li&gt;That the service auto-generates a client-side proxy class which handles the JSON and returns it in the custom formatted type that I specified in the .NET webservice&lt;/li&gt;&lt;li&gt;That MS isn't ignoring client-side development in their web development system&lt;/li&gt;&lt;li&gt;That it doesn't use vbscript&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;I don't like:&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Anything that starts with "asp:Script"&lt;asp:script&gt;&lt;asp:script&gt;&lt;script src=""&gt;&lt;/script&gt;&lt;/asp:script&gt;&lt;/asp:script&gt;&lt;/li&gt;&lt;li&gt;Why can't I just access the JSON webservice straight, using my own Javascript framework instead of important a redundant ".NET client side framework"?  I don't need Sys.Whatever on my pages.  Really.&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;[[ I had typed up a review of initial thoughts regarding Safari for Windows, but apparently when you use the less than or greater than signs in a blogger post, the auto-draft save totally drops out half of your post, thinking it's HTML of some sort... ]]&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3762802375429612564-2596383676603564139?l=urlencode.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://urlencode.blogspot.com/feeds/2596383676603564139/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3762802375429612564&amp;postID=2596383676603564139' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2596383676603564139'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3762802375429612564/posts/default/2596383676603564139'/><link rel='alternate' type='text/html' href='http://urlencode.blogspot.com/2008/03/webservices-ms-ajax-and-safari.html' title='Webservices, MS Ajax, and Safari'/><author><name>titus</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
