<?xml version="1.0" encoding="UTF-8"?>
<feed
  xmlns="http://www.w3.org/2005/Atom"
  xmlns:thr="http://purl.org/syndication/thread/1.0"
  xml:lang="en"
   >
  <title type="text">centripetal.ca</title>
  <subtitle type="text">Personal website of Dieter Limeback</subtitle>

  <updated>2012-01-30T16:41:54Z</updated>
  <generator uri="http://blogofile.com/">Blogofile</generator>

  <link rel="alternate" type="text/html" href="http://www.centripetal.ca/blog" />
  <id>http://www.centripetal.ca/blog/feed/atom/</id>
  <link rel="self" type="application/atom+xml" href="http://www.centripetal.ca/blog/feed/atom/" />
  <entry>
    <author>
      <name></name>
      <uri>http://www.centripetal.ca/blog</uri>
    </author>
    <title type="html"><![CDATA[Getting Started with Selenium and Jenkins]]></title>
    <link rel="alternate" type="text/html" href="http://www.centripetal.ca/blog/2011/02/07/getting-started-with-selenium-and-jenkins" />
    <id>http://www.centripetal.ca/blog/2011/02/07/getting-started-with-selenium-and-jenkins</id>
    <updated>2011-02-07T17:04:45Z</updated>
    <published>2011-02-07T17:04:45Z</published>
    <category scheme="http://www.centripetal.ca/blog" term="jenkins" />
    <category scheme="http://www.centripetal.ca/blog" term="ci" />
    <category scheme="http://www.centripetal.ca/blog" term="testing" />
    <category scheme="http://www.centripetal.ca/blog" term="selenium" />
    <summary type="html"><![CDATA[Getting Started with Selenium and Jenkins]]></summary>
    <content type="html" xml:base="http://www.centripetal.ca/blog/2011/02/07/getting-started-with-selenium-and-jenkins"><![CDATA[<p>These are notes on how to integrate <a href="http://seleniumhq.org/">Selenium</a> tests into a <a href="http://jenkins-ci.org/">Jenkins</a>-based continuous integration setup. Since I had next to no experience with either project, there are several moving parts, and there are many different ways to achive this goal, I decided to write up my notes. Note also that until very recently, <a href="http://jenkins-ci.org/content/jenkins">Jenkins was known as Hudson</a>; in the interests of being forward-thinking I'm going to refer to it by its new name.</p>
<h2>Preliminaries</h2>
<p>At <a href="http://www.waveaccounting.com/">work</a> we already had a CI server configured and running, so I'll skip the basic Jenkins configuration stuff here. But the upshot is, it was configured for a fairly standard procedure:</p>
<ol>
<li>Poll our git repository for changes.</li>
<li>When a change happens, make a new virtualenv for the project, install the necessary packages, and run our Django tests.</li>
<li>Email the devs if something's busted.</li>
</ol>
<p>We now want to add "run some Selenium test suites" somewhere in step 2, and "show me reports" somewhere in step 3. I'm going to assume that you can install the Selenium IDE Firefox plugin and author some simple tests, which is very easy: just open the plugin window, start recording, click around, and save the results somewhere. For getting-started purposes I made one test suite, consisting of two test cases: one to check that an unsuccessful login attempt keeps me on the same page and shows an error, and one to check that a successful login takes me to the page I expect. These are saved as "Selenese" HTML files for now. (<em>How</em> to write good test suites, and <em>what</em> specifically to test, seems like a topic in and of itself.)</p>
<h2>Selenium server setup</h2>
<p>First, a quick overview of the different bits of the Selenium project:</p>
<ul>
<li>Selenium IDE  ==&gt; A Firefox plugin used to record and playback tests. Note that you'll probably want to edit and tweak these tests after creating them with a regular text editor.</li>
<li>Selenium RC   ==&gt; Two parts: one, a server that handles taking the above test commands and sending them to a client (ie, a browser) for execution; and two, language drivers that allow you to do that from different programmming languages (which will allow for greater flexibility for your tests)</li>
<li>Seleniun Grid ==&gt; Allows you to do what RC does above, except on multiple client machines at once.</li>
</ul>
<p>So, we know we need to author tests (IDE) and we know we need to run them against a browser (RC). For ease of setup and because I don't have more than one machine to play with at the moment, I'm not going to set up Grid for now (although it definitely seems like a good idea, to cut down on the time it takes to get feedback on a build).</p>
<p>Additionally, we need to decide what machine to run the RC server on. I had a couple of choices:</p>
<ol>
<li>A regular desktop machine (eg Windows, Mac or Linux box) with a GUI and browser(s), separate from whatever Jenkins is running on. Many people use a spare machine in their office for this purpose, but note that you'll then need to deal with telling Jenkins how to talk to this machine.</li>
<li>A faked-out headless Linux box. For convenience, on the same machine as Jenkins. The browser stuff takes a bit of setting up but it's easier for Jenkins to talk to since all paths are local, etc.</li>
</ol>
<p>I chose the latter. Our CI server is actually an EC2 instance running Ubuntu, but Selenium needs an actual browser to do its stuff against and obviously we don't have (or want) that on a remote server. However, it's possible to run GUI apps headless in a virtual display. This was not difficult at all, thanks to <a href="http://shotgunsandpenguins.blogspot.com/2008/10/how-to-run-selenium-tests-with-hudson.html">these</a> <a href="http://shotgunsandpenguins.blogspot.com/2008/10/how-to-run-selenium-tests-with-hudson_14.html">three</a> <a href="http://shotgunsandpenguins.blogspot.com/2008/10/how-to-run-selenium-tests-with-hudson_15.html">posts</a>. Summarizing those, first we need to install some packages:</p>
<div class="pygments_monokai"><pre>sudo aptitude install xvfb          <span class="c"># virtual display</span>
sudo aptitude install x11-apps      <span class="c"># installs xclock (to test things are working), and xwd (for taking screenshots)</span>
sudo aptitude install imagemagick   <span class="c"># for converting screenshots</span>
sudo aptitude install firefox
</pre></div>

<p>Then, we start the virtual display, launch the GUI app <code>xclock</code> (so we have something to look at), take a screenshot of it, and (since <code>xwd</code> uses its own image output format) convert that into a regular image file:</p>
<div class="pygments_monokai"><pre>/usr/bin/Xvfb :5 -ac -screen 0 1024x768x8 &amp;
xclock -display :5.0 &amp;
xwd -root -display :5.0 -out outputfile
convert outputfile outputfile.png
</pre></div>

<p>I <code>scp</code>ed that image back to my desktop, and voila, there's a clock. Now we can copy up our HTML test files, and the Selenium server file (part of the Selenium RC download), and on the server, try running our tests:</p>
<div class="pygments_monokai"><pre><span class="nb">export </span><span class="nv">DISPLAY</span><span class="o">=</span>:5.0 <span class="c"># firefox needs this to know where to find a display to run on</span>
java -jar /path/to/selenium-server.jar -htmlSuite *firefox <span class="s2">&quot;http://www.example.com/&quot;</span> <span class="s2">&quot;/path/to/tests/suite.html&quot;</span> <span class="s2">&quot;/path/to/results/results.html&quot;</span>
w3m results.html
</pre></div>

<p>Sweet! Note that you'll want to set up your server to somehow launch xvfb on startup, so the virtual display is always available. Ok, onwards.</p>
<h2>Build a test site</h2>
<p>If you noticed above, our previous Jenkins build steps only configured the bare minimum Python environment to run our Django tests: just installthe necessary packages, make sure <code>PYTHONPATH</code> is correct, etc. We now need a complete setup of our site, including a database with known data to test against, and a way to reach it in a browser. Documenting this in a generic way is obviously not possible, since it's very project-specific. But Jenkins build steps are nice in that they're just shell commands, so just do whatever you need to do. I had to do stuff like set up a test domain name, configure Apache to point to the directory with the project code, dump in some known data into a test DB, copy a settings file to the right place, compile our JavaScript code into a production file, etc. And if you haven't already, be sure to add your Selenium test files to your code repository, since obviously we don't want to have to copy them up manually like we did above. You could potentially wrap all that up in a shell script, or, I guess people are using Ant or Maven for this kind of thing as well. Make sure you can visit your site and it looks normal before proceeding. </p>
<p>Also, be sure to first run your tests manually from the server (adjust the above command for any new paths) against the test site before attempting to get Jenkins to do it. Doing this I discovered that I needed to make a custom Firefox profile; since our site is using an SSL certificate and our test app is not using the correct one, my sample tests begain failing because the Firefox that Selenium launches was presenting the security warning page first (where locally I had confirmed an exception). This is easy to resolve, but yet another step. On the Mac, launch <code>/Applications/Firefox.app/Contents/MacOS/firefox-bin -ProfileManager -no-remote</code>, choose to create a new profile and name it Selenium-something, and then navigate to the test app. Get the warning, confirm and save the exception (or install the certificate), then quit Firefox. Then find that profile you just created, and copy it up to the server. Then you can launch your tests from the server like: </p>
<div class="pygments_monokai"><pre>java -jar /path/to/selenium-server.jar -htmlSuite *firefox <span class="s2">&quot;http://www.example.com/&quot;</span> <span class="s2">&quot;/path/to/tests/suite.html&quot;</span> <span class="s2">&quot;/path/to/results/results.html&quot;</span> -firefoxProfileTemplate <span class="s2">&quot;/path/to/profile/foo.Selenium&quot;</span>
</pre></div>

<p>Now when you view your results.html, they should be all nice and green!</p>
<h2>Tell Jenkins to report</h2>
<p>So now that the test site is available and Selenium is happy running tests, we can (finally) get to having Jenkins do all this for us. This simply involves adding another build step to kick off running the test suite and getting the results. We'll need the <a href="http://wiki.hudson-ci.org/display/HUDSON/seleniumhtmlreport+Plugin">Selenium HTML report plugin</a> (note there is also the <a href="http://wiki.hudson-ci.org/display/HUDSON/Seleniumhq+Plugin">Seleniumhq plugin</a>, but I couldn't get that to work). From within Jenkins, go to Manage Jenkins &gt; Manage Plugins, click the Available tab, find and select it, click Install, and restart Jenkins.</p>
<p>Now, add the additional shell build step to your project and simply enter the same command that launched your tests above (don't forget to set the <code>DISPLAY</code> env var as well). Finally, under "Post-build Actions" check off "Publish Selenium Html Report" and point it to the <em>directory</em> of where your results are -- that is, if the command is outputting to <code>/path/to/results/results.html</code>, you enter <code>/path/to/results</code>, since it can parse more than one HTML file of results (meaning you can add as many build steps to run other test suites as you want, as long as they output to the same place). You should get details in the Console Output for a job (for debugging) and can see the results by clicking "Selenium Html Report" in the left column.</p>
<p>That's it for now, and I hope it works for you -- happy testing!</p>]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://www.centripetal.ca/blog</uri>
    </author>
    <title type="html"><![CDATA[The Ubuntu Switch Experiment]]></title>
    <link rel="alternate" type="text/html" href="http://www.centripetal.ca/blog/2010/11/16/the-ubuntu-switch-experiment" />
    <id>http://www.centripetal.ca/blog/2010/11/16/the-ubuntu-switch-experiment</id>
    <updated>2010-11-16T22:02:33Z</updated>
    <published>2010-11-16T22:02:33Z</published>
    <category scheme="http://www.centripetal.ca/blog" term="linux" />
    <category scheme="http://www.centripetal.ca/blog" term="ubuntu" />
    <summary type="html"><![CDATA[The Ubuntu Switch Experiment]]></summary>
    <content type="html" xml:base="http://www.centripetal.ca/blog/2010/11/16/the-ubuntu-switch-experiment"><![CDATA[<p>A while back I read a great post by <a href="http://al3x.net/">Alex Payne</a>, which hit very close to home for me, about his periodic need to consider <a href="http://al3x.net/2009/08/10/switching-season.html">switching away from Apple</a>. I've had a similar storied history of using both OSes. I was first a long-time Mac user. Then after OSX came out and I used it for a while I realized the parts I really cared about were the UNIX-y bits, so I ran Ubuntu for quite some time (including a tiling window manager and trying to spend as much in the shell as possible). Then I had kids and I discovered I didn't have 14 hours try and get suspend and wake working or fiddle with <code>xorg.conf</code>, so I switched back. And now here we are again. This latest round I think has to do with my feelings about Apple as a company and where it's headed, but I don't think it really matters at this point. </p>
<p>However, I would like to document this round; that way I'll have something to refer back to, not only so I can say "What happened the last time I did this? Oh yeah" but also so I'll have something to gauge progress with. Perhaps by next time these issues will be resolved, and we can settle the matter.</p>
<p>Anyway, I recently nabbed a Thinkpad at work, installed Ubuntu 10.10 on it, and spent a week or so with it as my primary machine. The time was right for this experiment:</p>
<ul>
<li>I spend 90% of my day in vim, a terminal, and a browser.</li>
<li>I don't use a lot Apple software heavily, or I'm not a "Mac-like" user, or something. My usage of iTunes, Mail.app, etc is minimal.</li>
<li>The Finder bugs me. I shouldn't have to patch it with Launchbar, Divvy, Growl, etc. (as great as those apps are).</li>
<li>The OS matters much less to me now that stuff is moving to the cloud -- hello Gmail, Google Docs, Last.fm/Grooveshark, etc.</li>
<li>Webapps are getting so good that they merit using instead of having a dedicated desktop client. The OS integration of Tweetie is nice but again, #newtwitter is fine for my light needs. And OS integration can also mean easier distractability.</li>
<li>Finally, I reset the feeling of being held hostage, or of being dependent, because I'm too busy or lazy to switch. I want to aim for flexibility and (yes I said it) freedom.</li>
</ul>
<h2>Great parts</h2>
<p>OK, first the positive:</p>
<ul>
<li>Monitor, wifi, sleep/wake, keyboard special keys, external devices, printing -- all worked out of the box for me. This is amazing, as previously I felt like I was an Apple user to basically pay for supported hardware.</li>
<li>The appearance is stunning. Gone are the days of the fugly super-nerd desktops. (Although obviously you can have that too if you want.) I feel like it's on a par with the Mac; the typography is polished (another previous sticking point for me), and I like the customizability and choice.</li>
<li>Sane spaces management. This is a breath of fresh air, as I can't seem to make OSX's Spaces work the way I want.</li>
<li>It goes without saying but package management is wonderful, both for its similarity to deployment on the server side, and for more thoroughly integrated desktop software.</li>
</ul>
<h2>Not so great</h2>
<p>And, stuff that is still irksome:</p>
<ul>
<li>The hardware is ugly. I'm suprised this mattered to me, and it's not about rounded corners and fanatical attention to detail; I really like the feel of Apple keyboards/trackpads and the minimalist aspect. (Granted, I could run on Apple hardware, but then I'd have to play the "is my hardware supported?" game.) (Also, that would just feel weird.)</li>
<li>I could not get behind the keyboard shortcuts.<ul>
<li>I could <em>maybe</em> get used to ctrl as a primary modifier, but it feels twisty, and I'll never get "just remap your capslock!" under my fingers.</li>
<li>Using an external Apple keyboard means alt is option and command is meta, but on the Thinkpad meta is where option is. (If that makes any sense; basically keyboard shortcuts were different depending on where I was sitting.)</li>
<li>The use of ctrl-c/v for paste means in a Terminal I need to ctrl-<em>shift</em>-v, and in gVim I'd need to remember <code>"+p</code>. That is, I had to maintain <em>context-aware</em> copying and pasting, which was severely annoying.</li>
</ul>
</li>
<li>There were lots of tiny polish issues. The icon display when using alt-tab was terrible (pixellated, not cleanly scaled svgs), inconsistent icon states (the menubar Evolution icon looks like no mail, but when alt-tabbing it does), occasionally a window would not come to the foreground as expected, etc.</li>
<li>Don't like how switching is going to yank my phone along with it. I don't know enough about the current state of Android phones to consider using a non-iPhone (not to mention I'm pwned by my carrier).</li>
<li>I'm a frontend developer, so I still have to deal with Photoshop. Don't say the Gimp.</li>
</ul>
<h2>Results</h2>
<p>My overall impression was that Ubuntu is now in a kind of OS <a href="http://en.wikipedia.org/wiki/Uncanny_valley">uncanny valley</a>. The community has made some <em>amazing</em> achievements since I last was a regular user, and they deserve much admiration and appreciation. Unfortunately, that's not how it works. Like in the security industry, if you do your job really well, <em>nobody notices and nothing happens</em>. And that also means that any tiny missteps are magnified and come immediately to the forefront, where the experience is very jarring. So we're out of the era when using Linux meant "you're going to have to invest time learning this". Now the expectation is that users can jump right in -- which is simultaneously a major accomplishment and a major hurdle to overcome.</p>
<p>So, I'm typing this back on my Macbook. If I were solely a backend developer, I would probably officially make the switch. However, I'm not, and while I occasionally have multi-hat abilities -- indeed this is likely the source of my perennial switching investigation, ultimately I'm a frontend at heart, and there's something that still feels <em>appropriate</em> about the Mac, so here I am. Maybe next time, though. </p>]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://www.centripetal.ca/blog</uri>
    </author>
    <title type="html"><![CDATA[Talking to CouchDB with curl]]></title>
    <link rel="alternate" type="text/html" href="http://www.centripetal.ca/blog/2010/11/04/talking-to-couchdb-with-curl" />
    <id>http://www.centripetal.ca/blog/2010/11/04/talking-to-couchdb-with-curl</id>
    <updated>2010-11-04T21:08:33Z</updated>
    <published>2010-11-04T21:08:33Z</published>
    <category scheme="http://www.centripetal.ca/blog" term="couchdb" />
    <summary type="html"><![CDATA[Talking to CouchDB with curl]]></summary>
    <content type="html" xml:base="http://www.centripetal.ca/blog/2010/11/04/talking-to-couchdb-with-curl"><![CDATA[<p>I've recently started hacking on a small single-page app to try and get on top of the wonderful <a href="http://code.quirkey.com/sammy/">Sammy</a> JavaScript framework. A natural fit for storing data is <a href="http://couchdb.apache.org">CouchDB</a>, which I also have no prior experience with, so I needed to also jump into that. Since it talks HTTP and JSON it's very easy to get started with: I threw together this quick cheatsheet for making CRUD operations to CouchDB with <code>curl</code>. This is basically just my quick notes, the short version of the <a href="http://wiki.apache.org/couchdb/HTTP_Document_API">HTTP API docs</a>, which are obviously more thorough.</p>
<h2>Install</h2>
<p>Installation is easy: <code>sudo aptitude install couchdb</code> (or <code>brew install couchdb</code>). You can verify that worked by visiting Futon, the web-based interface that ships with Couch, at <a href="http://localhost:5984/_utils">http://localhost:5984/_utils</a>. But of course we want to use <code>curl</code> here, so:</p>
<div class="pygments_monokai"><pre>curl http://localhost:5984/
==&gt; {&quot;couchdb&quot;:&quot;Welcome&quot;,&quot;version&quot;:&quot;1.0.1&quot;}
</pre></div>

<h2>Simple CRUD operations</h2>
<p>Create a database:</p>
<div class="pygments_monokai"><pre>curl -X PUT http://localhost:5984/testdb
==&gt; {&quot;ok&quot;: true}
curl -X PUT http://localhost:5984/testdb
==&gt; {&quot;error&quot;:&quot;file_exists&quot;,&quot;reason&quot;:&quot;The database could not be created, the file already exists.&quot;}
</pre></div>

<p>Add a document with a POST request. Couchdb will automatically create a UUID for you:</p>
<div class="pygments_monokai"><pre>curl -X POST http://localhost:5984/testdb -d &#39;{&quot;name&quot;: &quot;Dieter&quot;}&#39; -H &quot;Content-Type: application/json&quot;
==&gt; {&quot;ok&quot;:true,&quot;id&quot;:&quot;4f761d4dc3b4d602ffdc2be02600140b&quot;,&quot;rev&quot;:&quot;1-40b62334f3dc02ca64532fbec03f3a35&quot;}
</pre></div>

<p>Or, to do a PUT request, you need to provide a UUID yourself (ie, make one in your application):</p>
<div class="pygments_monokai"><pre>curl -X PUT http://localhost:5984/testdb/youruuid -d &#39;{&quot;name&quot;: &quot;Joe&quot;}&#39; -H &quot;Content-Type: application/json&quot;
</pre></div>

<p>Either way, use that UUID to read the document:</p>
<div class="pygments_monokai"><pre>curl -X GET http://localhost:5984/testdb/4f761d4dc3b4d602ffdc2be02600140b
==&gt; {&quot;_id&quot;:&quot;4f761d4dc3b4d602ffdc2be02600140b&quot;,&quot;_rev&quot;:&quot;1-40b62334f3dc02ca64532fbec03f3a35&quot;,&quot;name&quot;:&quot;Dieter&quot;}
</pre></div>

<p>You can also read <em>all</em> documents with the special:</p>
<div class="pygments_monokai"><pre>curl -X GET http://localhost:5984/testdb/_all_docs
==&gt; {&quot;total_rows&quot;:1,&quot;offset&quot;:0,&quot;rows&quot;:[{&quot;id&quot;:&quot;4f761d4dc3b4d602ffdc2be02600140b&quot;,&quot;key&quot;:&quot;4f761d4dc3b4d602ffdc2be02600140b&quot;,&quot;value&quot;:{&quot;rev&quot;:&quot;1-40b62334f3dc02ca64532fbec03f3a35&quot;}}]}
</pre></div>

<p>Couchdb stores <em>versions</em> of documents, so to edit one, you need to provide the revision as well:</p>
<div class="pygments_monokai"><pre>curl -X PUT http://localhost/couchdb/testdb/4f761d4dc3b4d602ffdc2be02600140b -d &#39;{&quot;_id&quot;:&quot;4f761d4dc3b4d602ffdc2be02600140b&quot;,&quot;_rev&quot;:&quot;1-40b62334f3dc02ca64532fbec03f3a35&quot;,&quot;name&quot;:&quot;Dieter Limeback&quot;}&#39;
==&gt; {&quot;ok&quot;:true,&quot;id&quot;:&quot;4f761d4dc3b4d602ffdc2be02600140b&quot;,&quot;rev&quot;:&quot;2-5fc171bc10cb2c7cff75741dff04fa0d&quot;}
</pre></div>

<p>...unless what's currently in the db doesn't match what you're doing (ie, you're trying to edit an old revision) at which point you'll see:</p>
<div class="pygments_monokai"><pre>{&quot;error&quot;:&quot;conflict&quot;,&quot;reason&quot;:&quot;Document update conflict.&quot;}
</pre></div>

<p>To delete, you pass the rev as a parameter:</p>
<div class="pygments_monokai"><pre>curl -X DELETE http://localhost:5984/testdb/4f761d4dc3b4d602ffdc2be02600140b?rev=2-5fc171bc10cb2c7cff75741dff04fa0d
==&gt; {&quot;ok&quot;:true,&quot;id&quot;:&quot;4f761d4dc3b4d602ffdc2be02600140b&quot;,&quot;rev&quot;:&quot;3-7b5d2673af36e2fd42b7f356b9bc6c7b&quot;}
</pre></div>

<p>which looks weird, since it looks like it created a new revision, except if you try to GET it again:</p>
<div class="pygments_monokai"><pre>curl -X GET http://localhost:5984/testdb/4f761d4dc3b4d602ffdc2be0260014b0
==&gt; {&quot;error&quot;:&quot;not_found&quot;,&quot;reason&quot;:&quot;deleted&quot;}
</pre></div>

<p>Delete the entire db with:</p>
<div class="pygments_monokai"><pre>curl -X DELETE http://localhost:5984/testdb
==&gt; {&quot;ok&quot;:true}
</pre></div>

<p>That's it for now; nothing too crazy. If I end up making similar notes when learning about Couch <a href="http://wiki.apache.org/couchdb/Introduction_to_CouchDB_views">views</a> I'll be sure to post them as well.</p>]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://www.centripetal.ca/blog</uri>
    </author>
    <title type="html"><![CDATA[JavaScript templating with Django]]></title>
    <link rel="alternate" type="text/html" href="http://www.centripetal.ca/blog/2010/10/19/javascript-templating-with-django" />
    <id>http://www.centripetal.ca/blog/2010/10/19/javascript-templating-with-django</id>
    <updated>2010-10-19T22:01:54Z</updated>
    <published>2010-10-19T22:01:54Z</published>
    <category scheme="http://www.centripetal.ca/blog" term="javascript" />
    <category scheme="http://www.centripetal.ca/blog" term="django" />
    <summary type="html"><![CDATA[JavaScript templating with Django]]></summary>
    <content type="html" xml:base="http://www.centripetal.ca/blog/2010/10/19/javascript-templating-with-django"><![CDATA[<p>I've just spent some time finally getting around to investigating client-side JavaScript templating. Prior to this none of the projects I've done have managed to cross the web app event horizon -- that point at which a lot of page assembly is done in the browser, you have to maintain a lot of state, your code become complex enough that readability is becoming a concern, etc. and the traditional web "site" technique of building pages with plugins and lots of DOM parsing and reassembling becomes tedious and difficult.</p>
<p>The most common JS templating options seem to be <a href="http://github.com/janl/mustache.js">mustache.js</a>, jQuery's <a href="http://github.com/jquery/jquery-tmpl">Templates plugin</a> (which is destined to become part of jQuery core at some point) and <a href="http://documentcloud.github.com/underscore/">Underscore</a>'s <a href="http://documentcloud.github.com/underscore/#template">template</a> method. Unfortunately my backends are usually provided by Django, and as there are some implementation challenges with this I thought I'd note down my options, although I have no nice solutions. (Also note that the more you move template rendering out to the client with JavaScript, the more you are basically yanking page assembly out of Django's responsibility, and as one of the three <a href="http://docs.djangoproject.com/en/dev/faq/general/#django-appears-to-be-a-mvc-framework-but-you-call-the-controller-the-view-and-the-view-the-template-how-come-you-don-t-use-the-standard-names">MTV</a> pillars that Django provides, perhaps it begins to make less sense to use a large framework and you want to start investigating another tool. But that's a different argument for another time. For now I think it's still a viable option that Django handles drawing the initial page load or the non-JS version, and then everything else is done in the browser.)</p>
<h2>Templates in JavaScript</h2>
<p>So the first "hello world" you see with templates is that they get defined inside your JavaScript files:</p>
<div class="pygments_monokai"><pre><span class="kd">var</span> <span class="nx">artistTemplate</span> <span class="o">=</span> <span class="s1">&#39;&lt;div class=&quot;artist&quot;&gt;{{ artist-name }}&lt;/div&gt;&#39;</span><span class="p">,</span>
    <span class="nx">artistData</span> <span class="o">=</span> <span class="p">{</span>
        <span class="s1">&#39;artist-name&#39;</span><span class="o">:</span> <span class="s1">&#39;John Coltrane&#39;</span>
    <span class="p">},</span>
    <span class="nx">artistHtml</span> <span class="o">=</span> <span class="nx">Mustache</span><span class="p">.</span><span class="nx">to_html</span><span class="p">(</span><span class="nx">artistTemplate</span><span class="p">,</span> <span class="nx">artistData</span><span class="p">);</span>
<span class="nx">$</span><span class="p">(</span><span class="s2">&quot;#someDiv&quot;</span><span class="p">).</span><span class="nx">html</span><span class="p">(</span><span class="nx">artistHtml</span><span class="p">);</span>
</pre></div>

<p>The problem with this is obviously that you mix HTML into your JavaScript, which is ugly -- you lose nice linebreaks, syntax highlighting, it's harder for making markup changes, etc. You can of course pull in these templates via AJAX calls, but that seems a bit more heavy in that you have to deal with setting that up on the backend somehow.</p>
<h2>Templates in HTML</h2>
<p>The other place you can put JS templates is inside your HTML templates. As <a href="http://ejohn.org/blog/javascript-micro-templating/">John Resig first described</a> back in 2008, you can declare a script block with a nonsense <code>type</code> attribute. The browser doesn't know how to parse it and so ignores it, but it can then be grabbed and used like any other DOM element from within your jQuery code.</p>
<div class="pygments_monokai"><pre><span class="c">&lt;!-- in html source --&gt;</span>
<span class="nt">&lt;script </span><span class="na">id=</span><span class="s">&quot;artistTemplate&quot;</span> <span class="na">type=</span><span class="s">&quot;text/x-jquery-template&quot;</span><span class="nt">&gt;</span>
    <span class="o">&lt;</span><span class="nx">div</span> <span class="kr">class</span><span class="o">=</span><span class="s2">&quot;artist&quot;</span><span class="o">&gt;</span><span class="p">{{</span> <span class="nx">artist</span><span class="o">-</span><span class="nx">name</span> <span class="p">}}</span><span class="o">&lt;</span><span class="err">/div&gt;</span>
<span class="nt">&lt;/script&gt;</span>
<span class="c">&lt;!-- or {% include %} a reusable template to avoid DRY violations --&gt;</span>
</pre></div>

<div class="pygments_monokai"><pre><span class="c1">// in javascript</span>
<span class="kd">var</span> <span class="nx">artistTemplate</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="s2">&quot;#artistTemplate&quot;</span><span class="p">).</span><span class="nx">html</span><span class="p">();</span>
<span class="c1">// ... same rendering as before ...</span>
</pre></div>

<p>But here we run into trouble, because <code>{{</code> and <code>}}</code> are parsed by Django looking for template context variables, so this means both Mustache and jQuery templates won't work as expected. This is a <a href="http://github.com/nje/jquery-tmpl/issues#issue/17">known issue</a> with jQuery templates but it remains to be seen if/when it will be addressed.</p>
<p>Some workarounds:</p>
<p>1) Use Django's <a href="http://docs.djangoproject.com/en/1.1/ref/templates/builtins/#templatetag">templatetag</a> tag instead of <code>{{</code>. This seems quite unreadable for anything other than trivial snippets.</p>
<div class="pygments_monokai"><pre><span class="nt">&lt;script </span><span class="na">id=</span><span class="s">&quot;artistTemplate&quot;</span> <span class="na">type=</span><span class="s">&quot;text/x-jquery-template&quot;</span><span class="nt">&gt;</span>
    <span class="o">&lt;</span><span class="nx">div</span> <span class="kr">class</span><span class="o">=</span><span class="s2">&quot;artist&quot;</span><span class="o">&gt;</span><span class="p">{</span><span class="o">%</span> <span class="nx">templatetag</span> <span class="nx">openvariable</span> <span class="o">%</span><span class="p">}</span> <span class="nx">artist</span><span class="o">-</span><span class="nx">name</span> <span class="p">{</span><span class="o">%</span> <span class="nx">templatetag</span> <span class="nx">closevariable</span> <span class="o">%</span><span class="p">}</span><span class="o">&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
<span class="nt">&lt;/script&gt;</span>
</pre></div>

<p>2) Use a different set of characters to delimit template code (eg., <code>[[</code> and <code>]]</code>) in your script blocks, and use a regex to replace occurrences of them with the correct ones. This seems kind of ugly I guess.</p>
<div class="pygments_monokai"><pre><span class="nx">$</span><span class="p">(</span><span class="s2">&quot;script&quot;</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">(){</span>
    <span class="kd">var</span> <span class="nx">new_replaced_txt</span> <span class="o">=</span> <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">text</span><span class="p">().</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\[\[/g</span><span class="p">,</span> <span class="s2">&quot;{{&quot;</span><span class="p">).</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/\]\]/g</span><span class="p">,</span> <span class="s2">&quot;}}&quot;</span><span class="p">);</span>
    <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">text</span><span class="p">(</span><span class="nx">new_replaced_txt</span><span class="p">);</span>
<span class="p">});</span>
</pre></div>

<p>3) Use Underscore's templating, which uses ERB-style tags:</p>
<div class="pygments_monokai"><pre><span class="nt">&lt;script </span><span class="na">id=</span><span class="s">&quot;artistTemplate&quot;</span> <span class="na">type=</span><span class="s">&quot;text/x-underscore-template&quot;</span><span class="nt">&gt;</span>
    <span class="o">&lt;</span><span class="nx">div</span> <span class="kr">class</span><span class="o">=</span><span class="s2">&quot;artist&quot;</span><span class="o">&gt;&lt;%=</span> <span class="nx">artist</span><span class="o">-</span><span class="nx">name</span> <span class="o">%&gt;&lt;</span><span class="nx">div</span><span class="o">&gt;</span>
<span class="nt">&lt;/script&gt;</span>
</pre></div>

<p>If you can tolerate that it will work, but for me that markup will never get under my fingers (and is kind of heretical to a Django person ;). Note you can change what Underscore uses for delimiters, but that basically does the same regex behind the scenes.</p>
<p>So essentially, no quick easy elegant solution that I can see. If you have a better solution, please let me know!</p>]]></content>
  </entry>
  <entry>
    <author>
      <name></name>
      <uri>http://www.centripetal.ca/blog</uri>
    </author>
    <title type="html"><![CDATA[Preliminary thoughts on bootstrapping]]></title>
    <link rel="alternate" type="text/html" href="http://www.centripetal.ca/blog/2010/10/12/preliminary-thoughts-on-bootstrapping" />
    <id>http://www.centripetal.ca/blog/2010/10/12/preliminary-thoughts-on-bootstrapping</id>
    <updated>2010-10-12T22:25:37Z</updated>
    <published>2010-10-12T22:25:37Z</published>
    <category scheme="http://www.centripetal.ca/blog" term="startup" />
    <summary type="html"><![CDATA[Preliminary thoughts on bootstrapping]]></summary>
    <content type="html" xml:base="http://www.centripetal.ca/blog/2010/10/12/preliminary-thoughts-on-bootstrapping"><![CDATA[<p>Now that I've reached somewhat of a first milestone with my
sideproject-slash-startup <a href="http://www.dojominder.com/">Dojominder</a>, I thought
I'd jot down some notes on what I've learned about the bootstrapping process
thus far. These have mostly been stated first or reiterated
<a href="http://news.ycombinator.com/">elsewhere</a> so this is more of a reminder to myself
&mdash; I now know from <em>experience</em> that people that have said similar things are
both smart and right, and thus are worth listening to in the future.</p>
<h2>Fatigue and Motivation</h2>
<p>OK, most obvious things first. I am a single founder, working a full time job,
with a wife and kids. This is basically the holy trinity of what not to do, and
I understand why now. </p>
<p>I settled into a routine of working a full day, coming home to be with family,
and then working again after the kids went to sleep. This is actually
sustainable, but you have to be careful: (a) you must ensure you don't shirk
your other responsibilities &mdash; set aside time to do your share
of the housework and spend proper time with your family (and when you're doing
that your laptop is closed); and (b) it's easy to throw off your sleep
schedule, not sleep enough, or get annoyed by the need to sleep when you're on
a roll.</p>
<p>Working by yourself, you can also dry up on motivation &mdash; <em>cf</em> reason number 1
on Paul Graham's <a href="http://www.paulgraham.com/startupmistakes.html">list of mistakes that kill
startups</a>. So you need to do
<em>something</em>, even if it's small, every day. This seems deceptively doable &mdash;
after all, you'd break tasks down into small subtasks anyway &mdash; but in
reality it doesn't seem to work like that. And if you step away from it from a
small period (like a few days), it becomes harder to pick it up again, not only
because it's not loaded fresh in your head any more but also to summon the
motivation to get excited. This also means it's <em>catastrophic</em> if you take a
real break. I did this, and I think it was a mistake that might be
unrecoverable.</p>
<p>You also need inspiration. It can be envigorating to see the cool stuff that
other people are doing, but again you need to be careful not to let that
lead you into prioritizing the wrong things, or actually <em>demotivate</em> yourself
by being reminded how large your task is and making it look more
insurmountable.</p>
<h2>Know your reasons</h2>
<p>A lot of startups grow out of a need the founders have (the well-known
"scratching your own itch") or as a hobby/learning project to play with on
the side. When you're merely writing for yourself, you're not necessarily
focusing on building a business; you just have a problem you want gone. Which
is fine, but you need to understand how it's affecting your decisions, and how
they'd change if at some point you <em>do</em> want to start making money. For
example, if I want to learn more about frontend work, maybe I write all my own
HTML/CSS/JavaScript &mdash; but if I want to buld a business, maybe I just spend $35
on some template somewhere and slap it on. It can be tricky to figure
out where you are. </p>
<h2>Don't rush</h2>
<p>With so little time to dedicate, you're obviously going to be moving at a
slower pace. Thus you can't gauge your progress based on what you're used to;
you're going to work for two months, look at what you end up with, and say to
yourself "Pssh, I could build that in about two weeks". Well, you <em>did</em> build
it in two weeks, just broken into tiny pieces and spread out a lot.</p>
<p>So it's hard to avoid feeling like you're in a rush. Unfortunately it's
important that you do, because being rushed can lead to poor decisions. And
nothing is worse, when time is tight, than having to go back and redo something
or start over again.</p>
<p>Additionally, work is harder and slower when you're wearing more than one hat
at once. It's common to end up thinking about UX and frontend code at the same
time, for example, or backend and deployment. Break these into discrete
subtasks if possible, so it's always clear what you're working on.</p>
<h2>It's not free</h2>
<p>A common attraction about bootstrapping is that it appears to basically be
free: apart from trivial server and domain registration costs, there's nothing
preventing you from starting. And if it doesn't work out, "the only thing lost
is time". This is true, but I think it drastically underemphasizes the
opportunity costs involved. If you're going to spend most of your spare moments
working, you'll have less time to attend meetups, contribute to open source
projects, have any other time consuming hobbies, or for that matter do anything
that's extra stressful or occupies a lot of brain space, like take a new day
job. There's lots you can't do when you're trying to get the ball rolling.</p>
<h2>So, why do it?</h2>
<p>This all doesn't paint that rosy of a picture, agreed. But I think it's still
worth doing. You learn a lot and get to keep your skills sharp. It can
be fun and exciting, and it definitely beats 2 hours of TV every night.
And let's face it, as dedicated geeks we know we're going to be working on
something on the side anyway, so it might as well be something that might have
a future. I don't belive the difficulties mentioned above are insurmountable;
it's just that they're not necessarily obvious, so you don't know to look out
for them. But with experience you do, and you gain the meta-wisdom from putting
in the work learning how to work.</p>]]></content>
  </entry>
</feed>
