<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Karl on web development</title>
	<atom:link href="http://www.karlstanley.net/blog/?feed=rss2" rel="self" type="application/rss+xml" />
	<link>http://www.karlstanley.net/blog</link>
	<description>Discoveries, opinions and ideas about building applications for the web.</description>
	<lastBuildDate>Wed, 20 Apr 2011 01:28:55 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.4</generator>
		<item>
		<title>Smarter drawing for 3d wireframe models in javascript</title>
		<link>http://www.karlstanley.net/blog/?p=33</link>
		<comments>http://www.karlstanley.net/blog/?p=33#comments</comments>
		<pubDate>Wed, 20 Apr 2011 01:25:54 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[Web authoring]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=33</guid>
		<description><![CDATA[The last post had backface culling turned on, which makes the shapes look solid and has the nice side-effect of speeding up drawing (since it&#8217;s quicker to figure out if a face should be drawn than it is to draw the face). There were 2 problems though: The shape&#8217;s centre of rotation was wrong Each [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.karlstanley.net/blog/?p=32">last post</a> had backface culling turned on, which makes the shapes look solid and has the nice side-effect of speeding up drawing (since it&#8217;s quicker to figure out if a face should be drawn than it is to draw the face). There were 2 problems though:</p>
<ol>
<li>The shape&#8217;s centre of rotation was wrong</li>
<li>Each edge was being drawn twice, once for each face containing that edge</li>
</ol>
<p>Fixing the first was easy &#8211; just make sure the centroid of the shape gets calculated at load time. The centroid for a 3-dimensional shape is just the average of the X, Y and Z values respectively.</p>
<p>Fixing the second was a bit trickier. We want to keep a list of edges so we can mark off the ones that have been drawn as we go along. The shape data doesn&#8217;t contain edges (only faces and vertices), so we have to build the edge list when we load the shape. I decided to use a hash (or dictionary) to store a key for each edge in the shape, where each edge key is just a string built from the 2 vertex numbers. I made sure the smaller vertex number is first to make sure each edge is unique. Typical edge keys look like <code>10_17</code> or <code>7_21</code>. At the start of each frame we set each edge value for each edge to <code>false</code>. When we draw the edge we set the edge value to <code>true</code>. If we go to draw an edge that has already been marked <code>true</code>, we skip it. This means that every edge only gets drawn once. </p>
<p>It&#8217;s a bit expensive to do this key checking every time, but it seems to be quicker than actually drawing the edge. I haven&#8217;t benchamarked this, it&#8217;s just subjective from running the code on an ipad. Benchmarks to follow. In the meantime,<a href="http://karlstanley.net/examples/canvas/smarter_redraw.html"> check out the demo (now with added asp!)</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=33</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>3d vector graphics using canvas &#8211; back face culling</title>
		<link>http://www.karlstanley.net/blog/?p=32</link>
		<comments>http://www.karlstanley.net/blog/?p=32#comments</comments>
		<pubDate>Sun, 23 May 2010 18:37:07 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[Web authoring]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=32</guid>
		<description><![CDATA[The previous post shows how to display a wireframe model using canvas and javascript. One problem with the example is that the spaceship appears to be transparent. This is because we&#8217;re drawing all the faces that make up the shape, instead of just the faces that are pointing towards the viewer. We decide which faces [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://www.karlstanley.net/blog/?p=31">previous post</a> shows how to display a wireframe model using canvas and javascript. One problem with <a href="http://karlstanley.net/examples/canvas/cobra.html">the example</a> is that the spaceship appears to be transparent. This is because we&#8217;re drawing <em>all</em> the faces that make up the shape, instead of just the faces that are pointing towards the viewer.</p>
<p>We decide which faces are pointing towards the viewer using a <a href="http://en.wikipedia.org/wiki/Cross_product">vector cross-product</a>. The cross-product of 2 3-dimensional vectors is another vector whose direction is at right-angles to the first 2. So the cross-product of 2 vectors along a face points directly away from the face. If the cross-product vector has a negative Z-component, it is pointing towards the viewer (we define the positive direction of the Z axis as pointing into the screen) and thus we should be able to see the face.</p>
<p>The good news is that we&#8217;re only interested in the Z component of the cross-product, which simplifies matters considerably. Given a list of points <code>(p<sub>0</sub>, p<sub>1</sub>, p<sub>2</sub>, ....)</code> that define a face, we create 2 vectors <code>A = p<sub>0</sub> - p<sub>1</sub></code> and <code>B = p<sub>2</sub> - p<sub>1</sub></code>. If the components of A and B are <code>(a<sub>x</sub>, a<sub>y</sub>, a<sub>z</sub>)</code> and <code>(b<sub>x</sub>, b<sub>y</sub>, b<sub>z</sub>)</code> then the Z component of <code>A x B</code> is</p>
<p><code>(A x B)<sub>z</sub> = a<sub>x</sub> * b<sub>y</sub> - b<sub>x</sub> * a<sub>y</sub> </code></p>
<p>Substituting p<sub>0</sub> etc. back into the equation above, we get</p>
<p><code>z_component =<br />
          (p2[0] - p1[0]) * (p0[1] - p1[1]) - (p0[0] - p1[0]) * (p2[1] - p1[1])</code></p>
<p>This technique works well for <a href="http://en.wikipedia.org/wiki/Convex_and_concave_polygons">convex</a> shapes. A shape is said to be convex if a straight line can be drawn between any 2 points inside the shape without the line ever crossing the boundary of the shape. Because of this, I  commented out the gun/antenna part of the ship &#8211; the remaining part of the ship is convex. Have a look at the results <a href="http://karlstanley.net/examples/canvas/backface.html">here</a>.</p>
<p>There is still 1 important thing missing &#8211; perspective. At the moment, the ship is the same size no matter how far away from the viewer it is. We&#8217;ll have a look at this next time.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=32</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>3d vector graphics using canvas &#8211; cobra mk III</title>
		<link>http://www.karlstanley.net/blog/?p=31</link>
		<comments>http://www.karlstanley.net/blog/?p=31#comments</comments>
		<pubDate>Thu, 20 May 2010 22:07:44 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[Web authoring]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=31</guid>
		<description><![CDATA[Further to my previous post, I decided to upgrade my 3d wireframe code to use some more interesting shapes. I got the data for the ships from elite from Neil Wallis&#8217;s site. Check out the rotating cobra mk III canvas demo. The previous version of the code was very simple &#8211; just take 4 points [...]]]></description>
			<content:encoded><![CDATA[<p>Further to <a href="http://www.karlstanley.net/blog/?p=30">my previous post</a>, I decided to upgrade my 3d wireframe code to use some more interesting shapes. I got the data for the ships from elite from <a href="http://www.neilwallis.com/elitejava/geos/bbc/geosbbc.zip">Neil Wallis&#8217;s site</a>. Check out the <a href="http://karlstanley.net/examples/canvas/cobra.html">rotating cobra mk III canvas demo</a>.</p>
<p>The <a href="http://karlstanley.net/examples/canvas/">previous version</a> of the code was very simple &#8211; just take 4 points and join them all to each other. This gives you a <a href="http://en.wikipedia.org/wiki/Tetrahedron">tetrahedron</a> provided all the vertices aren&#8217;t on the same plane. For the case where not every vertex is joined to every other vertex, we need something a little more sophisticated. I pretty much followed the layout given in Neil Wallis&#8217;s data set, which defines a shape as follows:</p>
<ol>
<li>a list of vertices.</li>
<li>a list of lists of indices into the vertex list &#8211; each sublist defines one of the faces of the shape.</li>
</ol>
<p>If you view the source of <a href="http://karlstanley.net/examples/canvas/shape_simple.js">shape.js</a> you can see what&#8217;s going on. The <code>ALL_SHAPES</code> variable is a dictionary &#8211; each key refers to a different shape. In turn, each key refers to a dictionary with 2 elements &#8211; the vertex list and the face list. This is doubtless not very efficient but it&#8217;s simple (and simple is good!). The <code>draw()</code> method of the shape class simply loops through each face and draws lines from the first vertex through to the last (and back to the first to close the loop), in order.</p>
<p>A problem with this simple way of drawing is that the boundary between 2 adjacent faces will be drawn twice (once for each face), which isn&#8217;t very efficient. For the moment we&#8217;re going to ignore this issue &#8211; making the code fast is something we can do once it is working the way we want it to.</p>
<p>The next thing to do is back-face culling (or hidden line removal) &#8211; that is, removing the faces of the ship that are pointing away from the viewer. This will make the ship look more solid. There&#8217;s a tiny bit of <a href="http://en.wikipedia.org/wiki/Normal_vector">vector maths</a> involved, but not much. Next time!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=31</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>3d vector graphics using canvas</title>
		<link>http://www.karlstanley.net/blog/?p=30</link>
		<comments>http://www.karlstanley.net/blog/?p=30#comments</comments>
		<pubDate>Mon, 17 May 2010 22:03:37 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[Web authoring]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=30</guid>
		<description><![CDATA[I have been doing a bit of playing around with the &#60;canvas&#62; element lately. When I was a kid I was a big fan of elite on the spectrum. I remember being blown away by the wireframe 3d graphics. In homage to the old-skool world of 3d wireframe graphics (and in an effort to learn [...]]]></description>
			<content:encoded><![CDATA[<p>I have been doing a bit of playing around with the <code>&lt;canvas&gt;</code> element lately. When I was a kid I was a big fan of  <a href="http://en.wikipedia.org/wiki/Elite_(video_game)">elite</a> on the <a href="http://en.wikipedia.org/wiki/ZX_Spectrum">spectrum</a>. I remember being blown away by the wireframe 3d graphics.</p>
<p>In homage to the old-skool world of 3d wireframe graphics (and in an effort to learn some stuff about how <code>&lt;canvas&gt;</code>) works, I&#8217;ve written a super-simple <a href="http://karlstanley.net/examples/canvas/">rotating tetrahedron</a> script. I&#8217;m planning to build this up into something a bit more interesting over the coming days and weeks, including:</p>
<ul>
<li>hidden line removal</li>
<li>perspective</li>
<li>more interesting shapes (there&#8217;s only so long you can look at a tetrahedron)</li>
<li>rotation about other axes</li>
<li>keyboard control (fly the ship!)</li>
</ul>
<p>I&#8217;ll post my progress up here as I go along. I learned how to use canvas from the <a href="https://developer.mozilla.org/en/Drawing_Graphics_with_Canvas">mozilla page</a> and <a href="http://billmill.org/static/canvastutorial/">Bill Mill&#8217;s &#8220;how to build a breakout clone&#8221;</a> canvas tutorial.<br />
I&#8217;d recommend them both as places to start.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=30</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Big spikes in velocity should tell you something</title>
		<link>http://www.karlstanley.net/blog/?p=28</link>
		<comments>http://www.karlstanley.net/blog/?p=28#comments</comments>
		<pubDate>Thu, 20 Aug 2009 11:42:01 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[agile]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=28</guid>
		<description><![CDATA[I posted something here at the start of my last agile project. I meant to follow it up but didn&#8217;t get around to it. Bad me. Anyway, it was a 10-iteration project that had the following velocities: As you can see, there was a big spike around iterations 5 and 6 and a trough in [...]]]></description>
			<content:encoded><![CDATA[<p>I posted something here at the start of my last agile project. I meant to follow it up but didn&#8217;t get around to it. Bad me. Anyway, it was a 10-iteration project that had the following velocities:</p>
<p><img src="http://chart.apis.google.com/chart?cht=bvs&amp;chs=320x200&amp;chd=t:47,76,84,55,122,87,60,81,13,42&amp;chco=4D89F9&amp;chxt=x,y&amp;chxl=0:|1|2|3|4|5|6|7|8|9|10|1:|0|20|40|60|80|100|120" title="velocities for 10-iteration project" alt="velocities for 10-iteration project" height="200" width="320" /></p>
<p>As you can see, there was a big spike around iterations 5 and 6 and a trough in iteration 9. The dip in iteration 9 was caused by paying off technical debt and resolving some deployment issues we hadn&#8217;t originally budgeted for. I hadn&#8217;t thought too much about what caused the surge in velocity around the middle of the project. At the time the attitude was &#8220;we&#8217;re going really fast, let&#8217;s just keep doing what we&#8217;re doing&#8221;. In hindsight I think the spike in velocity should have raised an alarm bell. Here&#8217;s why&#8230;</p>
<p>I am now working on a new project that reuses some of the work we did previously. Alas I have discovered that some of the code requires extensive refactoring. The current implementation of some features is both fragile and incomprehensible. There are methods that are hundreds of lines long (!) with no comments. There is hand-rolled code  that duplicates the intent of library functions. Some functionality is poorly encapsulated and thus not reusable. All the usual suspects! SVN tells me that this code was committed when? That&#8217;s right, iterations 5 and 6 of the last project. It seems our gains in velocity came at the cost of maintainability.</p>
<p>All it not lost: the old code has pretty decentÂ  test coverage, so at least the refactoring effort isn&#8217;t working blind. That said, I think our iteration retrospectives will in future check our velocity against our historical performance.Â  Up till now we&#8217;ve been pretty good at identifying the causes of slow iterations &#8211; from now on we&#8217;ll be having a look at what a fast iteration velocity is masking beneath the surface.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=28</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>callables in django URLconfs and dispatch by HTTP verb</title>
		<link>http://www.karlstanley.net/blog/?p=26</link>
		<comments>http://www.karlstanley.net/blog/?p=26#comments</comments>
		<pubDate>Thu, 12 Mar 2009 10:57:22 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[python]]></category>
		<category><![CDATA[Web authoring]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=26</guid>
		<description><![CDATA[This information is lifted from RESTful Web Services, which is well worth the read if you build web applications. See chapter 12, page 354 onwards to find out about using django RESTfully. Django users will know that it&#8217;s possible to use callable objects instead of strings in URLconfs. One great use of this feature is [...]]]></description>
			<content:encoded><![CDATA[<p>This information is lifted from <a href="http://www.amazon.com/RESTful-Web-Services-Leonard-Richardson/dp/0596529260/ref=pd_bbs_sr_1?ie=UTF8&amp;s=books&amp;qid=1236853784&amp;sr=8-1" title="RESTful Web Services - Leonard Richardson, Sam Ruby">RESTful Web Services</a>, which is well worth the read if you build web applications. See chapter 12, page 354 onwards to find out about using django RESTfully.</p>
<p>Django users will know that it&#8217;s possible to use <a href="http://docs.djangoproject.com/en/dev/topics/http/urls/#passing-callable-objects-instead-of-strings" title="django - passing callable objects instead of strings">callable objects instead of strings</a> in URLconfs. One great use of this feature is to dispatch HTTP requests to different functions based on the HTTP verb. This turned out to be useful for me recently when I had to implement HTTP DELETE for some resources. Instead of having one big method like this</p>
<pre>if 'GET' == request.method:
    ... do something ...
elif 'POST' == request.method:
    ... do something else ...
elif 'DELETE' == request.method:
    ... do somethign else ...</pre>
<p>we can use python&#8217;s reflection capabilities and dispatch to different methods based on which HTTP verb was used. First we define a base class like this:</p>
<pre>class HTTPVerbDispatcher:
    def __call__(self, request, *args, **kwargs):
        try:
            fn = getattr(self, "do_%s" % request.method)
        except AttributeError: # this method is not supported
            allowed_methods = [m.lstrip("do_") for m in dir(self) if m.startswith("do_")]
            return HttpResponseNotAllowed(allowed_methods)
        return fn(request, *args, **kwargs)</pre>
<p>Any classes deriving from this will call a method called <code>do_GET</code>, <code>do_POST</code> <em>etc.</em> based on the value of request.method. We can create a class in <code>views.py</code> like this:</p>
<pre>
class BlogEntryView(HTTPVerbDispatcher):
    def do_GET(self, request, id):
        .... handle GET request - view object ...

    def do_POST(self, request, id):
        .... handle POST request - view object ...

    def do_DELETE(self, request, id):
        .... handle DELETE request - view object ...</pre>
<p>Finally, we set up our URLconf to use the <code>BlogEntryView</code> callable:</p>
<pre>
from django.conf.urls.defaults import *

from myapp.views import BlogEntryView

urlpatterns = patterns('',
    (r'^(?P&lt;id&gt;d+)$', BlogEntryView()),
)</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=26</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Changing DocumentRoot in Apache: files, directories and permissions</title>
		<link>http://www.karlstanley.net/blog/?p=25</link>
		<comments>http://www.karlstanley.net/blog/?p=25#comments</comments>
		<pubDate>Thu, 12 Feb 2009 17:48:21 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[linux]]></category>
		<category><![CDATA[Web authoring]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=25</guid>
		<description><![CDATA[While setting up a WSGI-hosted django app on Fedora 10, I could not get apache to serve any files that weren&#8217;t stored under /var/www/. I tried changing the DocumentRoot using the Alias Directive using symbolic links from /var/www to the required location chmod -R go=rx path/to/my/files chown -R apache path/to/my/files Changing the User and Group [...]]]></description>
			<content:encoded><![CDATA[<p>While setting up a WSGI-hosted django app on Fedora 10, I could not get apache to serve any files that weren&#8217;t stored under <code>/var/www/</code>. I tried</p>
<ul>
<li>changing the <code>DocumentRoot</code></li>
<li>using the <code>Alias</code> Directive</li>
<li>using symbolic links from <code>/var/www</code> to the required location</li>
<li><code>chmod -R go=rx path/to/my/files</code></li>
<li><code>chown -R apache path/to/my/files</code></li>
<li>Changing the User and Group apache runs under</li>
</ul>
<p>All to no avail. By symlinking to my directory I could get apache to list directory contents, but trying to access any files within gave a <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html" title="HTTP status codes">403 forbidden</a> error. It turns out the problem was to do with SELinux &#8211; by default the security policy is set to &#8216;enforcing&#8217;. I <a href="http://www.crypt.gen.nz/selinux/disable_selinux.html" title="How to disable SELinux">changed the policy to &#8216;permissive&#8217;</a> and all was well.</p>
<p>It took me an embarrassingly long time to figure this one out &#8211; I thought it was caused by a combination of apache configuration and filesystem permissions, but it turned out the security policy had a role to play too.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=25</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using javascript to change img src attribute, far-future Expires headers and cacheing</title>
		<link>http://www.karlstanley.net/blog/?p=24</link>
		<comments>http://www.karlstanley.net/blog/?p=24#comments</comments>
		<pubDate>Tue, 27 Jan 2009 22:36:35 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[Web authoring]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=24</guid>
		<description><![CDATA[The Leitrim Design House web site went down the other day because it had exceeded its bandwidth allocation. This caught me by surprise &#8211; it&#8217;s a relatively small site for a niche audience and I was quite sure I had put it on a hosting plan with enough available resources. The server statistics revealed that [...]]]></description>
			<content:encoded><![CDATA[<p>The <a href="http://leitrimdesignhouse.ie" title="The Leitrim Design House">Leitrim Design House web site</a> went down the other day because it had exceeded its bandwidth allocation. This caught me by surprise &#8211; it&#8217;s a relatively small site for a niche audience and I was quite sure I had put it on a hosting plan with enough available resources. The server statistics revealed that the same 6 images were requested over 80,000 times each on a single day. Huh?</p>
<p>The pictures in question appear in a slideshow on the site&#8217;s home page. There is a javascript component that fades the images into each other every 10 seconds or so. Using <a href="https://addons.mozilla.org/en-US/firefox/addon/3829" title="Live HTTP Headers addon for Firefox">Live HTTP Headers</a> I could see that every time the image changed a new HTTP request was being sent. On my machine I was getting a <a href="http://www.w3.org/Protocols/HTTP/HTRESP.html">status code 304</a> and the image wasn&#8217;t being requested every time but it seems this wasn&#8217;t the case everywhere (local cache disabled? Badly-behaved browser? It&#8217;s hard to tell.). So, what to do?</p>
<p>I decided to take a belt-and-braces approach on this to prevent a recurrence (and to avoid incurring the wrath of the nice people at <a href="http://blacknight.ie">blacknight</a>). The first thing was to have a look at the javascript and stop it requesting images unnecessarily. The <a href="http://intoleitrim.com/ldh/javascript/ImageRotator_old.js">original slideshow code</a> worked like this:</p>
<ol>
<li> clone the DOM node containing the first image in the slideshow</li>
<li>put the new image under the old one (using z-index and absolute positioning)</li>
<li>change the src attribute of the lower image to contain the URL of the next image in the slideshow</li>
<li>wait a few seconds</li>
<li>fade the upper image to 0 opacity</li>
<li>swap the Z position of the 2 images</li>
<li>set the lower image to full opacity</li>
<li>go to step 3</li>
</ol>
<p>I could see that a new HTTP request was going out at step 3 above, even when it was reloading an image that had previously been displayed. So I <a href="http://intoleitrim.com/ldh/javascript/ImageRotator.js">changed the code</a> to this:</p>
<ol>
<li>clone the original image node N-1 times (for a slideshow with N images)</li>
<li>hide all but 2 images</li>
<li>put the visible images 1 on top of the other (using z-index and absolute position)</li>
<li>wait a few seconds</li>
<li>fade the upper image to 0 opacity</li>
<li>hide the upper image and increase the z-index of the lower image</li>
<li>unhide the next image</li>
<li>go to step 3</li>
</ol>
<p>The new code loads all the images at once and makes no more requests once they are loaded. There are some obvious problems with this code</p>
<ul>
<li>what happens if there are hundreds of images?
<ul>
<li>there will be a stampede of HTTP requests which will inevitably lead to timeouts and missing images</li>
<li>potentially unlimited amounts of memory will be used to store all the images</li>
</ul>
</li>
<li>what if the wait period between displaying images is shorter than the time it takes to load them all?
<ul>
<li>once again there will be partially loaded/missing images</li>
</ul>
</li>
</ul>
<p>However, I&#8217;m a firm believer in the agile mantra &#8220;<a href="http://www.xprogramming.com/Practices/PracNotNeed.html">You&#8217;re not gonna need it</a>&#8220;. For this application there are no more than 6 images in any slideshow, the images are all under 50kB and the wait time is about 10 seconds so the above problems shouldn&#8217;t arise. If I need to refactor in future, so be it.</p>
<p>Finally, I realised that I hadn&#8217;t configured apache to <a href="http://developer.yahoo.com/performance/rules.html">set the Expires header</a> on images to a date far in the future. I added a couple of lines in the top-level .htaccess fileto make the images expire in 5 years. This means that on repeat visits to the site, if there is a copy of an image in your browser cache, the browser won&#8217;t even check to see if the image has been modified.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=24</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>starting a new project: weighing up agile methods</title>
		<link>http://www.karlstanley.net/blog/?p=22</link>
		<comments>http://www.karlstanley.net/blog/?p=22#comments</comments>
		<pubDate>Thu, 15 Jan 2009 10:54:15 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[agile]]></category>
		<category><![CDATA[process]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=22</guid>
		<description><![CDATA[Here at ticket-text we have just embarked on a new software project. I&#8217;m not going to get into the specifics of what it&#8217;s supposed to do*, but suffice to say it&#8217;s a large-ish django app. We&#8217;ve spent the last couple of months figuring out what it&#8217;s supposed to do and doing proof-of-concept work. Now we&#8217;re [...]]]></description>
			<content:encoded><![CDATA[<p>Here at <a href="http://www.ticket-text.com" title="ticket-text">ticket-text</a> we have just embarked on a new software project. I&#8217;m not going to get into the specifics of what it&#8217;s supposed to do*, but suffice to say it&#8217;s a large-ish <a href="http://www.djangoproject.com" title="django - the web framework for ponies with magical powers">django</a> app. We&#8217;ve spent the last couple of months figuring out what it&#8217;s supposed to do and doing proof-of-concept work. Now we&#8217;re starting to build production code.</p>
<p>At my previous employer, <a href="http://ammado.com" title="ammado">ammado</a>, we used an agile/XP hybrid approach to manage our work. It started off well but we ran into some pitfalls:</p>
<ul>
<li>the team got too big to manage effectively with a self-organising process.
<ul>
<li>the usual rule-of-thumb for agile is that it works for teams of up to about 8 or10 people. We had over 20.</li>
</ul>
</li>
<li><a href="http://www.dehora.net/journal/2009/01/07/large-stories-on-agile-projects/" title="Bill de hOra's blog">fetishisation of index cards.</a></li>
<li>short-termism of agile allowed us to ignore architectural issues. Over the course of several iterations we built up substantial technical debt which caused progress to slow significantly.</li>
<li>cultural resistance to pair programming led to less refactoring and more code duplication than we would have liked (pair programming keeps you honest!).</li>
</ul>
<p>There were some positive points too:</p>
<ul>
<li>constant emphasis on shipping working code. Before we adopted the agile approach we spent a year making exactly zero releases. Afterwards we were releasing once a month.</li>
<li>retrospectives after every iteration helped change my mindset (I can&#8217;t speak for the whole team, but I would like to think I&#8217;m not the only one who got this)Â  from ticking-off-items-on-a-requirements-list to how-can-I-make-this-team-work-better.</li>
</ul>
<p>We are adopting a similar process here in ticket-text. We would like to maximise the positives and avoid the pitfalls outlined above. I&#8217;m going to keep track of how we are progressing here &#8211; hopefully we&#8217;ll all learn something.</p>
<p>* in case one of my millions of readers leaks it to the competition. Lolz!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=22</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>django&#8217;s O/R mapper, list comprehensions and lambda functions</title>
		<link>http://www.karlstanley.net/blog/?p=21</link>
		<comments>http://www.karlstanley.net/blog/?p=21#comments</comments>
		<pubDate>Fri, 12 Dec 2008 10:35:47 +0000</pubDate>
		<dc:creator>karl</dc:creator>
				<category><![CDATA[python]]></category>

		<guid isPermaLink="false">http://www.karlstanley.net/blog/?p=21</guid>
		<description><![CDATA[I have been using django of late and am very impressed with most of it. Here&#8217;s just 1 little gem. Suppose you have 3 models like this: class Owner: &#160;&#160;&#160;&#160;name = models.CharField("name", max_length=100) class Pet: &#160;&#160;&#160;&#160;name = models.CharField("name", max_length=100) &#160;&#160;&#160;&#160;owner = models.ForeignKey(Owner, verbose_name=_(u"owner")) class Toy: &#160;&#160;&#160;&#160;name = models.CharField("name", max_length=100) &#160;&#160;&#160;&#160;owner = models.ForeignKey(Pet, verbose_name=_(u"owner")) This represents [...]]]></description>
			<content:encoded><![CDATA[<p>I have been using <a href="http://www.djangoproject.com" title="django - the web framework for ponies with magical powers">django</a> of late and am very impressed with most of it. Here&#8217;s just 1 little gem. Suppose you have 3 models like this:<br />
<code><br />
class Owner:<br />
&nbsp;&nbsp;&nbsp;&nbsp;name = models.CharField("name", max_length=100)</code></p>
<p><code>class Pet:<br />
&nbsp;&nbsp;&nbsp;&nbsp;name = models.CharField("name", max_length=100)<br />
&nbsp;&nbsp;&nbsp;&nbsp;owner = models.ForeignKey(Owner, verbose_name=_(u"owner"))</code></p>
<p><code>class Toy:<br />
&nbsp;&nbsp;&nbsp;&nbsp;name = models.CharField("name", max_length=100)<br />
&nbsp;&nbsp;&nbsp;&nbsp;owner = models.ForeignKey(Pet, verbose_name=_(u"owner"))</code></p>
<p>This represents pet owners, each of whom may have zero or more pets. Each pet in turn may have zero or more toys. So far, so simple. Now suppose we want the owner to have a property saying how many toys in total she owns (the pets don&#8217;t <em>really</em> own the toys!). There are a whole bunch of ways to do this, but one neat way python/django makes possible is to change the definition of <code>Owner </code>to this:</p>
<p><code>class Owner:<br />
&nbsp;&nbsp;&nbsp;&nbsp;name = models.CharField("name", max_length=100)<br />
&nbsp;&nbsp;&nbsp;&nbsp;toy_count = property(fget=lambda self:sum([pet.toy_set.count() for pet in self.pet_set.all()]))</code></p>
<p>There are a couple of things going on here: <a href="http://www.python.org/download/releases/2.2/descrintro/#property">properties</a>, <a href="http://docs.python.org/tutorial/controlflow.html#lambda-forms">lambdas</a>, <a href="http://docs.python.org/tutorial/datastructures.html#list-comprehensions">list comprehensions</a> and <a href="http://www.djangoproject.com/documentation/models/many_to_one/">many-to-one relationships as expressed by django&#8217;s O/R mapper</a>. Together they allow for some concise yet powerful constructs like the one above.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.karlstanley.net/blog/?feed=rss2&#038;p=21</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

