<?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"
	>

<channel>
	<title>Graceful Exits &#187; layers</title>
	<atom:link href="http://www.jpstacey.info/blog/category/layers/www.jpstacey.info/blog/category/layers/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.jpstacey.info/blog</link>
	<description>Garbage collection, in a very real sense</description>
	<pubDate>Tue, 30 Sep 2008 20:10:32 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.6.2</generator>
	<language>en</language>
			<item>
		<title>The TimeToLead.eu technical stack: Django and Flex</title>
		<link>http://www.jpstacey.info/blog/2008/09/11/the-timetoleadeu-technical-stack-django-and-flex/</link>
		<comments>http://www.jpstacey.info/blog/2008/09/11/the-timetoleadeu-technical-stack-django-and-flex/#comments</comments>
		<pubDate>Thu, 11 Sep 2008 18:57:43 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[configuration]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[beautifulsoup]]></category>

		<category><![CDATA[django]]></category>

		<category><![CDATA[flex]]></category>

		<category><![CDATA[fouc]]></category>

		<category><![CDATA[hosting]]></category>

		<category><![CDATA[i18n]]></category>

		<category><![CDATA[l10n]]></category>

		<category><![CDATA[lapd]]></category>

		<category><![CDATA[multilingual]]></category>

		<category><![CDATA[swfobject]]></category>

		<category><![CDATA[technical]]></category>

		<category><![CDATA[timetolead.eu]]></category>

		<category><![CDATA[unit test]]></category>

		<category><![CDATA[webfactional]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=203</guid>
		<description><![CDATA[Move over LAMP: here comes LAPD.]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.jpstacey.info/blog/2008/09/05/they-work-for-eu/" >As discussed previously</a>, at <a href="http://torchbox.com/" >Torchbox</a> we recently built <a href="http://timetolead.eu/" >TimeToLead.eu</a>, an advocacy site set up by the four major environmental NGOs to prompt MEPs into passing the sort of legislation the world&#8217;s climate desperately needs. The project itself needed a fast turnaround time, and its pan-European audience demanded strong <abbr title="internationalization" >i18n</abbr> and <abbr title="localization">l10n</abbr>. This had to be in place at all layers, including an embedded Flex application.</p>
<p>In a parallel with the acronym LAMP, the overall stack is probably best described as &#8220;LAPD&#8221;:</p>
<ul>
<li><strong>L</strong>inux</li>
<li><strong>A</strong>pache</li>
<li><strong>P</strong>ostgreSQL</li>
<li><strong>D</strong>jango</li>
</ul>
<p>although there are refinements at almost every level, which I&#8217;ll go into below.</p>
<p>I18n with <strong>Django</strong> was a joy to implement. We had to have the site translated in six languages, but this was practically a doddle with <a href="http://www.djangoproject.com/documentation/i18n/" >Django&#8217;s core internationalization behaviour</a>. Before we&#8217;d put the translations in, I clicked on &#8220;POLSKI&#8221; and was sure the l10n wasn&#8217;t working, until I spotted that the &#8220;ENGLISH&#8221; link had magically changed to &#8220;ANGIELSKI&#8221;. </p>
<p>The only issue we&#8217;ve had&#8212;which we&#8217;re hoping to work around&#8212;was deciding on an initial translation when the visitor arrived for the first time. Some parts of the system seemed to need l10n&#8212;the plumping for a specific version of the site&#8212;before Django&#8217;s <code>LocaleMiddleware</code> had done that for us, so we&#8217;ve had to force English until the user states otherwise. I&#8217;m hoping that a version 2 rewrite will fix that.</p>
<p><strong>PostgreSQL</strong> might seem an odd choice of database to the LAMP community, who are used to MySQL&#8217;s minuscule overhead and often actively work around its deficiencies to keep that low overhead. But PostgreSQL&#8217;s stability and maturity outweigh the performance issues which&#8212;with a little care and attention&#8212;it&#8217;s possible to at least partly mitigate. </p>
<p>Compared to MySQL or Oracle, PostgreSQL also has the accolade of <a href="http://docs.djangoproject.com/en/dev/ref/databases/" >being supported by Django but having no outstanding database issues</a>. Whether that&#8217;s because nobody else uses it, or because the integration is tight and relatively bug-free, I daren&#8217;t comment, but we&#8217;ve had nothing but seamless, transparent behaviour thus far.</p>
<p>Our Django-oriented hosting is with <a href="http://www.webfaction.com/hosting/django-hosting" >Webfaction</a>, who are geared up for one-click <strong>mod_python</strong> Django deployments. The Django app sits in its own <strong>Apache</strong> process, while static files are served by the <strong>Linux</strong> server&#8217;s main Apache process: differential serving of content lets us take advantage of differential hosting fees. The hosting is a pretty good package, although TimeToLead.eu (despite being a pretty small application) is already finding the maximum memory package a bit restrictive, so we&#8217;ll need to keep an eye on that.</p>
<p>On top of this, Django has to a greater or lesser extent enabled us to use a whole host of other neat little technologies to integrate the site and improve both the user and the developer&#8217;s experience:</p>
<ul>
<li>The main Flash widget is written in <a href="http://www.adobe.com/products/flex/" >Flex</a>: I&#8217;m no particular fan of Flash myself, but it fulfills the remit admirably here. Because of text flow issues, there are actually six versions of the Flash file, one for each language, but the majority of the explanatory text is then picked up from an XML feed provided by Django, so can be retranslated by the client without recompiling Flex.</li>
<li><a href="http://code.google.com/p/swfobject/" >Swfobject</a> serves up our main widget and the YouTube file. It degrades well, but bear in mind that the API to it has changed considerably in the most recent major version.</li>
<li>Unit tests in Django cover a number of stress points in the code (although not all, owing to the timescale). Towards the end, we were testing any major functionality changes by writing the tests first, which I was really pleased with.</li>
<li>BeautifulSoup, which <a href="/blog/2008/09/07/spliticket-running-again-with-beautifulsoup/" >I mentioned a few days ago</a>, is employed in the unit tests to check that certain content is coming through on the front end. It also managed to help us proof against a browser-dependent <a href="http://www.bluerobot.com/web/css/fouc.asp/" >FOUC</a> we encountered during development.</li>
<li>Translations are managed by the Django application <a href="http://code.google.com/p/django-rosetta/" >Rosetta</a>: the most recent stable version is for Django 0.96 but the bleeding-edge repository copy seems to work OK on 1.0b1</li>
</ul>
<p>Django has been the saviour here, living up to its promise as a framework for rapid application development. No framework is ever perfect, and there&#8217;ll always be arguments over the right way to proceed, reconciling what the framework developers want you to do with what the end developer wants to do, but this particular brief sojourn alongside <a href="http://docs.djangoproject.com/en/dev/internals/committers/" >Holovaty, Kaplan-Moss, Willison <i>et al</i></a> has been tremendous fun.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/09/11/the-timetoleadeu-technical-stack-django-and-flex/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Improving REST performance is all about negotiation</title>
		<link>http://www.jpstacey.info/blog/2008/09/04/improving-rest-performance-is-all-about-negotiation/</link>
		<comments>http://www.jpstacey.info/blog/2008/09/04/improving-rest-performance-is-all-about-negotiation/#comments</comments>
		<pubDate>Thu, 04 Sep 2008 09:31:50 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[efficiency]]></category>

		<category><![CDATA[formats]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[network]]></category>

		<category><![CDATA[quickies]]></category>

		<category><![CDATA[standards]]></category>

		<category><![CDATA[architecture]]></category>

		<category><![CDATA[resource]]></category>

		<category><![CDATA[rest]]></category>

		<category><![CDATA[royfielding]]></category>

		<category><![CDATA[roytfielding]]></category>

		<category><![CDATA[state]]></category>

		<category><![CDATA[understandable]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/?p=200</guid>
		<description><![CDATA[Ceci n'est pas un obj&#233;t... nécessairement.]]></description>
			<content:encoded><![CDATA[<blockquote><p>
Web architects must understand that resources are just consistent mappings from an identifier to some set of views on server-side state. If one view doesn’t suit your needs, then feel free to create a different resource that provides a better view (for any definition of “better”). These views need not have anything to do with how the information is stored on the server, or even what kind of state it ultimately reflects. It just needs to be understandable (and actionable) by the recipient.
</p></blockquote>
<p>&#8212; Roy Fielding on <a href="http://roy.gbiv.com/untangled/2008/paper-tigers-and-hidden-dragons" >creating new resources for REST architectures</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/09/04/improving-rest-performance-is-all-about-negotiation/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Variable assignment in Django templates, sort of</title>
		<link>http://www.jpstacey.info/blog/2008/05/29/variable-assignment-in-django-templates-sort-of/</link>
		<comments>http://www.jpstacey.info/blog/2008/05/29/variable-assignment-in-django-templates-sort-of/#comments</comments>
		<pubDate>Thu, 29 May 2008 13:08:33 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[efficiency]]></category>

		<category><![CDATA[hacking]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[paradigms]]></category>

		<category><![CDATA[assignment]]></category>

		<category><![CDATA[django]]></category>

		<category><![CDATA[equals]]></category>

		<category><![CDATA[hack]]></category>

		<category><![CDATA[include]]></category>

		<category><![CDATA[template]]></category>

		<category><![CDATA[variable]]></category>

		<category><![CDATA[with]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2008/05/29/variable-assignment-in-django-templates-sort-of/</guid>
		<description><![CDATA[Use and abuse of the "with" programmatic statement to make your Django template code less mad.]]></description>
			<content:encoded><![CDATA[<p>Django&#8217;s templating language is intentionally quite restrictive. The idea is that you have to do all your data munging in the control-ish sublayer of the view layer, in the method registered as handling the view in <code>urls.py</code>. In principle this simplifies templates, but in practice it can make life for the developer more difficult: you have to really think ahead, and assemble your variables properly, so that the templating language can use simple iterative loops to prepare your HTML.</p>
<p>What if you want the same output for several different data structures? Let&#8217;s say we have three people, and we&#8217;d like to run the following <a href="http://www.djangoproject.com/documentation/templates/#include" >include</a>, saved as <code>name.html</code>, on all three:</p>
<blockquote class="code"><p>&lt;p>{{ person.firstname }} {{ person.surname }}&lt;/p></p>
</blockquote>
<p>Ideally you&#8217;d have the three people in a structure called <code>people</code>, and be able to run:</p>
<blockquote class="code"><p>{% for person in people %}{% include &#8220;name.html&#8221; %}{% endfor %}</p>
</blockquote>
<p>But what if the order matters? What if you need <code>account_director</code> to do different things in the template from <code>project_manager</code>? You&#8217;d want to send them through as separate data structures, and this:</p>
<blockquote class="code"><p>{% include &#8220;name.html&#8221; %}<br />
{% include &#8220;name.html&#8221; %}<br />
{% include &#8220;name.html&#8221; %}</p>
</blockquote>
<p>would just do nothing, because it references <code>person</code> and not <code>account_director</code>. An alternative is to turn <code>name.html</code> into a custom tag, taking arguments; but if the content of <code>name.html</code> were any more HTML-heavy then you might want to abstract it into the include nonetheless, and then you&#8217;d just be running a custom tag method, to pass variables to a template fragment, to render the fragment, to pass it back up to the template. It&#8217;d be nicer if we could do this with core functionality instead.</p>
<p>Using the <a href="http://www.djangoproject.com/download/" >latest development version of Django</a>, we can implement basic variable assignment&#8212;getting <code>person</code> to reference whatever we want in the template layer&#8212;using the <a href="http://www.djangoproject.com/documentation/templates/#with" ><code>with</code> template tag</a>. This means that we can wrap each <code>include</code> tag to produce the required output:</p>
<blockquote class="code"><p>{% with account_director as person %}<br />
&nbsp;&nbsp;{% include &#8220;name.html&#8221; %}<br />
{% endwith %}<br />
{% with project_manager as person %}<br />
&nbsp;&nbsp;{% include &#8220;name.html&#8221; %}<br />
{% endwith %}<br />
{% with lead_developer as person %}<br />
&nbsp;&nbsp;{% include &#8220;name.html&#8221; %}<br />
{% endwith %}</p>
</blockquote>
<p>It seems like overkill for our <code>name.html</code> example, but there are plenty of situations&#8212;I just found one, hence this post&#8212;where it makes sense to factor out code into an include: being able to fiddle with the template context before including it is a lightweight alternative to template tags and a tidy alternative to dumping the HTML right there in the primary template.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/05/29/variable-assignment-in-django-templates-sort-of/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Django laziness at all levels</title>
		<link>http://www.jpstacey.info/blog/2008/05/12/django-laziness-at-all-levels/</link>
		<comments>http://www.jpstacey.info/blog/2008/05/12/django-laziness-at-all-levels/#comments</comments>
		<pubDate>Mon, 12 May 2008 19:03:55 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[efficiency]]></category>

		<category><![CDATA[hacking]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[chaining]]></category>

		<category><![CDATA[django]]></category>

		<category><![CDATA[evaluation]]></category>

		<category><![CDATA[filter]]></category>

		<category><![CDATA[forms]]></category>

		<category><![CDATA[lazy]]></category>

		<category><![CDATA[modelform]]></category>

		<category><![CDATA[newforms]]></category>

		<category><![CDATA[orm]]></category>

		<category><![CDATA[queryset]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2008/05/12/django-laziness-at-all-levels/</guid>
		<description><![CDATA[Lazy evaluation in Django means you can wait till the last possible moment before modifying what you want from the database.]]></description>
			<content:encoded><![CDATA[<p>Following on from Simon Willison&#8217;s <a href="http://simonwillison.net/2008/May/1/orm/">recent post about Django&#8217;s ORM</a>, I&#8217;ve found both the lazy evaluation and chaining properties of Django&#8217;s querysets to be really useful quite deep within Django&#8217;s own view-layer framework.</p>
<p>Django has its own library for building forms, currently called <code>newforms</code> (to distinguish it from the old library, deprecated but left around for compatibility reasons). The <code>ModelForm</code> class in this library interrogates a particular class from the model layer and builds an HTML form&#8212;complete with post-submit validation, field widgets, the lot. The beauty of this is that you can run:</p>
<blockquote class="code"><p>
import django.newforms as forms<br />
from myproject.models import MyObject</p>
<p>class MyForm(forms.ModelForm):<br />
&nbsp;&nbsp;&nbsp;&nbsp;class Meta:<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;model = MyObject
</p>
</blockquote>
<p>and <em>that&#8217;s it</em>: you can then use an instance of MyForm anywhere in your view layer to add or edit MyObject instances in the database.</p>
<p>Problems arise, of course, when the automatically generated form doesn&#8217;t provide exactly what you want. It might, for example produce select dropdowns for you with thousands of items, whereas with better knowledge of the page your user is on then you might be able to whittle that list down to a handful of items. </p>
<p>Let&#8217;s imagine your objects can belong to Django users i.e. in your <code>models.py</code> you have:</p>
<blockquote class="code"><p>
class MyObject(models.Model):<br />
&nbsp;&nbsp;&nbsp;&nbsp;owner = models.ForeignKey(User)
</p>
</blockquote>
<p>This means that when you try to create an object, the HTML from <code>newforms</code> will give you a select dropdown of all the users in the database. But what if you have loads of inactive users, cluttering up the dropdown? How do you get rid of them with the minimal amount of coding i.e. without having to build your own form if possible?</p>
<p>Luckily, <code>newforms</code> sticks the queryset it creates somewhere that you can fiddle with it. With lazy evaluation, you get a chance to change the queryset before it hits the database; with chaining, you don&#8217;t have to worry about rebuilding the queryset yourself, but can just add to it instead. </p>
<p>In the <code>MyForm</code> class, you can override the inherited <code>__init__</code> method as follows:</p>
<blockquote class="code"><p>
def __init__(self, *a, **kw):<br />
&nbsp;&nbsp;&nbsp;&nbsp;forms.ModelForm.__init__(self, *a, **kw)<br />
&nbsp;&nbsp;&nbsp;&nbsp;self.fields['owner'].queryset = self.fields['owner'].queryset.filter(is_active=True)
</p>
</blockquote>
<p>This calls the existing superclass constructor first, passing along any values. Then, after the queryset has been created for the select dropdown <em>but before it&#8217;s been evaluated</em>, the queryset is modified to whittle down the resultant set of users to only those where the &#8220;is_active&#8221; flag is set to True.</p>
<p>Laziness and chaining make Django&#8217;s ORM layer very effective; <code>forms</code> and <code>newforms</code> help to greatly reduce the amount of coding required by the view and control layers; together they can really simplify a codebase.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/05/12/django-laziness-at-all-levels/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Deserving of a serious LAMPing</title>
		<link>http://www.jpstacey.info/blog/2008/03/31/deserving-of-a-serious-lamping/</link>
		<comments>http://www.jpstacey.info/blog/2008/03/31/deserving-of-a-serious-lamping/#comments</comments>
		<pubDate>Mon, 31 Mar 2008 11:09:47 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[culture]]></category>

		<category><![CDATA[efficiency]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[subtleties]]></category>

		<category><![CDATA[constrain]]></category>

		<category><![CDATA[database]]></category>

		<category><![CDATA[foreign]]></category>

		<category><![CDATA[integrity]]></category>

		<category><![CDATA[key]]></category>

		<category><![CDATA[lamp]]></category>

		<category><![CDATA[mysql]]></category>

		<category><![CDATA[postgresql]]></category>

		<category><![CDATA[primary]]></category>

		<category><![CDATA[referential]]></category>

		<category><![CDATA[wtf]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2008/03/31/deserving-of-a-serious-lamping/</guid>
		<description><![CDATA[Reinventing primary keys, one horrifying integrity error at a time.]]></description>
			<content:encoded><![CDATA[<p>The LAMP-stack community frequently shows their disdain for foreign and primary keys in databases, and sometimes with reason borne of experience. MySQL historically has been little more than a nice language for comparing a set of unrelated spreadsheets, so referential integrity has had to happen at the application layer or not at all. As such, careful MySQL users put a lot of work into ensuring referential integrity, without the help of the database.</p>
<p>But the eyes of even the most hardened LAMPer would widen, if he were to dip into a conversation to find someone saying the following about something they&#8217;ve built over PostgreSQL:</p>
<blockquote><p>Yeah, I could add the current date/time to the end, so as long as you don&#8217;t add more than one [entry] with the same name within 1 second, it&#8217;ll be fine.</p>
</blockquote>
<p>It might take you a while to work out (a) what that&#8217;s referring to (b) what kind of error in thinking it demonstrates and (c) what it implies about the quality of the underlying code. By that time&#8212;if you&#8217;ve worked at all with that sort of programming&#8212;your head may well be in your hands.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/03/31/deserving-of-a-serious-lamping/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Cheaper rail journeys with Matthew and Spliticket</title>
		<link>http://www.jpstacey.info/blog/2008/03/13/cheaper-rail-journeys-with-matthew-and-spliticket/</link>
		<comments>http://www.jpstacey.info/blog/2008/03/13/cheaper-rail-journeys-with-matthew-and-spliticket/#comments</comments>
		<pubDate>Thu, 13 Mar 2008 21:39:28 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[formats]]></category>

		<category><![CDATA[hacking]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[useability]]></category>

		<category><![CDATA[accessibility]]></category>

		<category><![CDATA[mashup]]></category>

		<category><![CDATA[matthew]]></category>

		<category><![CDATA[rail]]></category>

		<category><![CDATA[somerville]]></category>

		<category><![CDATA[split]]></category>

		<category><![CDATA[spliticket]]></category>

		<category><![CDATA[tickets]]></category>

		<category><![CDATA[train]]></category>

		<category><![CDATA[traintimes]]></category>

		<category><![CDATA[wiki]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2008/03/13/cheaper-rail-journeys-with-matthew-and-spliticket/</guid>
		<description><![CDATA[The rail industry's biggest fares secret: exposed, and now given an interface.]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve built something, just in time for me to crowbar it awkwardly into conversations at OKCon.</p>
<p>Matthew Somerville is well known for his accessible takes on rubbish websites: most useful is <a href="http://traintimes.org.uk/">Traintimes</a>, a layer on top of one of the equally poor commercial British rail sites; most notorious is <a href="http://www.dracos.co.uk/odeon/" >Accessible Odeon</a>, a fixing of the Odeon Cinema&#8217;s website that put their own substandard development to shame so much that in 2004 they had Matthew&#8217;s version taken down with legal threats. Remember: just because you&#8217;re successful doesn&#8217;t mean you&#8217;re not stupid.</p>
<p>Recently, Matthew gave an <a href="http://oxford.geeknights.net/" ><abbr title="Oxford Geek Nights">OGN</abbr></a> <a href="http://oxford.geeknights.net/2007/july-25th/talks/MatthewSomerville.mp4" title="'Train in Vain' by Matthew Somerville (mp4 video)">talk on split tickets</a>. These are train journeys which one cross-country train company sells at exhorbitant rates, whereas the components of the journey can be bought separately from the local companies for considerably less. This is what we infer; the rail websites try to keep it all quiet, in the hope that you might lose interest and stump up. The whole headshakeworthy situation is either a stinging indictment of the stupidity of privatizing a rail network and making passengers jump through ridiculous hoops to glean even the tiniest advantage, or a perfect demonstration of how the choice-enriched consumer can leverage capitalism in action: take your pick. </p>
<p>To come to the point: Matthew has set up a wiki where people have been adding the <a href="http://www.dracos.co.uk/wiki/Trains/SplitTickets" >split tickets they&#8217;ve worked out</a> in an <i>ad-hoc</i> fashion. Last Sunday I added K&#8217;s astonishing 47% saving for Oxford&#8211;Cardiff to the end of it, and there&#8217;s been little activity since. The page hasn&#8217;t got fantastic Google juice&#8212;&#8221;split tickets&#8221; means too many other things&#8212;and is just non-dynamic HTML, editable through the wiki but otherwise searchable only by eye.</p>
<p>At the moment, of course, there&#8217;s no reason for Matthew to expend any more effort on such a small and barely popular data set. But last Sunday I had a sudden instinctive jolt, to the effect that: more people would be likely to take advantage of split tickets if there was an easier way of looking up other people&#8217;s discoveries (my colleagues were recently trying to find split tickets and the wiki page was harder to use than Traintimes, for that very purpose).</p>
<p>With this in mind, here&#8217;s my take on accessificating the wiki page: <a href="http://www.jpstacey.info/applications/spliticket/">Spliticket</a>. It accesses a cached version of the page, and then using Python&#8217;s HTMLParser to hack away at the HTML it returns either a HTML or XML representation of any journey it finds. The idea is that it&#8217;s a bit easier to use on your mobile device, and lets you pin down journeys better. I&#8217;ve also included an option for searching strictly for the same journey: I&#8217;m not sure if split tickets&#8212;even returns&#8212;always work when the journey is reversed.</p>
<p>Spliticket also supports friendly URLs (inspired by Traintimes itself), so York to Edinburgh in HTML becomes &lt;<a href="http://www.jpstacey.info/applications/spliticket/html/york/edinburgh/" >http://www.jpstacey.info/applications/spliticket/html/york/edinburgh/</a>&gt; and the aforementioned Oxford to Cardiff route in XML becomes: &lt;<a href="http://www.jpstacey.info/applications/spliticket/xml/oxford/cardiff/">http://www.jpstacey.info/applications/spliticket/xml/oxford/cardiff/</a>&gt; .</p>
<p>Mostly this was just something to fill idle hours, and also to convince myself of some ideas I&#8217;ve had recently about loose coupling, data reuse and open data. Hopefully at the same time (a) Matthew won&#8217;t take it as a dig, (b) others might find some casual use for it see it, and (c) a select few might see it as a testament to the ease of freeing information from solid if unsemantic markup. And maybe in six months&#8217; time we&#8217;ll all be booking split tickets as a matter of course; by then, of course, Spliticket will probably be gratefully obsolete, replaced by the fully-fledged application that poor, embittered rail passengers deserve. I&#8217;m sure Matthew will build it if so.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2008/03/13/cheaper-rail-journeys-with-matthew-and-spliticket/feed/</wfw:commentRss>
<enclosure url="http://oxford.geeknights.net/2007/july-25th/talks/MatthewSomerville.mp4" length="15044283" type="video/mp4" />
		</item>
		<item>
		<title>Whatever I might have $_REQUESTed, I didn&#8217;t ask for that</title>
		<link>http://www.jpstacey.info/blog/2007/10/13/whatever-i-might-have-_requested-i-didnt-ask-for-that/</link>
		<comments>http://www.jpstacey.info/blog/2007/10/13/whatever-i-might-have-_requested-i-didnt-ask-for-that/#comments</comments>
		<pubDate>Sat, 13 Oct 2007 11:45:55 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[hacking]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[aggregated]]></category>

		<category><![CDATA[cookie]]></category>

		<category><![CDATA[drupal]]></category>

		<category><![CDATA[index]]></category>

		<category><![CDATA[listing]]></category>

		<category><![CDATA[override]]></category>

		<category><![CDATA[pager]]></category>

		<category><![CDATA[paging]]></category>

		<category><![CDATA[pifpafpoof]]></category>

		<category><![CDATA[request]]></category>

		<category><![CDATA[snippet]]></category>

		<category><![CDATA[theme]]></category>

		<category><![CDATA[theme_pager]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2007/10/13/whatever-i-might-have-_requested-i-didnt-ask-for-that/</guid>
		<description><![CDATA[Imagine you have a Drupal site: I have several, so that&#8217;s easy for me. Say you&#8217;ve got an index aggregating lots of your content in a listing, and it spans lots of pages. It&#8217;s probably a view created with the views module, slicing through your content in a special way.
Drupal has a core paging module [...]]]></description>
			<content:encoded><![CDATA[<p>Imagine you have a Drupal site: I have several, so that&#8217;s easy for me. Say you&#8217;ve got an index aggregating lots of your content in a listing, and it spans lots of pages. It&#8217;s probably a view created with the views module, slicing through your content in a special way.</p>
<p>Drupal has a core paging module that sorts out links along the lines of:</p>
<blockquote><p>[&laquo; first] [&lt; previous] [1] [2] [3] &#8230; [next &gt;] [last &raquo;]</p>
</blockquote>
<p>Great. But if, like me, you use the PHP <code>$_REQUEST</code> scope to stick lots of useful internal variables in (e.g. theme configuration, for in case you ever want to move your theme&#8217;s <code>javascript/</code> directory in the future and don&#8217;t want it hardwired everywhere) then the pager code goes completely mad. Instead of links like:</p>
<blockquote class="code"><p>http://&#8230;&#38;page=2</p>
</blockquote>
<p>It adds all the <code>$_REQUEST</code> variables onto the link too, creating:</p>
<blockquote class="code"><p>http://&#8230;&#38;page=2&#38;sekrit_variable[]=quux&#38;something_else=bar&#38;blithely_generating_long_links=true</p>
</blockquote>
<p>This isn&#8217;t a bug; it&#8217;s a feature. I&#8217;ve checked in <code>theme_pager()</code> and it does this on purpose.</p>
<p>So how to pare back the pager code, so it can still be reused if you want to also get some sort of use out of your <code>$_REQUEST</code> scope? Well, below is a snippet of code which in one sense <em>overrides</em> the default pager theme; but in another sense it just wraps around the core pager code. This is preferable to stubbing out the whole of <code>theme_pager()</code> yourself and then fiddling with it, because you&#8217;re introducing fewer maintenance issues by avoiding repetition of 95% the same code.</p>
<p>The principle is for <code>phptemplate_pager()</code> to swap everything briefly out of the <code>$_REQUEST</code> scope, run core <code>theme_pager()</code>, save the returned content somewhere, swap everything back into <code>$_REQUEST</code> and return the core-generated pager HTML as if nothing happened. Presto changeo:</p>
<blockquote class="code"><p>
/**<br />
&nbsp;* Implementation of theme_pager<br />
&nbsp;*<br />
&nbsp;* Prevents $_REQUEST[] madness<br />
&nbsp;* @ingroup themeable<br />
&nbsp;*/<br />
function phptemplate_pager($tags = array(), $limit = 10, $element = 0, $parameters = array()) {<br />
&nbsp;&nbsp;$temp_request = array();</p>
<p>&nbsp;&nbsp;// Transfer $_REQUEST variables into array and delete from $_REQUEST<br />
&nbsp;&nbsp;foreach($_REQUEST as $rk =&gt; $rv) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;$temp_request[$rk] = $rv;<br />
&nbsp;&nbsp;&nbsp;&nbsp;unset($_REQUEST[$rk]);<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;// Run theme_pager as before<br />
&nbsp;&nbsp;$ret = theme_pager($tags, $limit, $element, $parameters);</p>
<p>&nbsp;&nbsp;// Transfer variables back<br />
&nbsp;&nbsp;foreach($temp_request as $rk =&gt; $rv) {<br />
&nbsp;&nbsp;&nbsp;&nbsp;$_REQUEST[$rk] = $rv;<br />
&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;return $ret;<br />
}
</p>
</blockquote>
<p>Feel free to use. I see that <code>theme_pager()</code> also does the same thing to the <code>$_COOKIE</code> scope. I know, I know. But you&#8217;re on your own with that one. Teach a man to fish, and he can fish for cookies.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2007/10/13/whatever-i-might-have-_requested-i-didnt-ask-for-that/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Programming shouldn&#8217;t be degrading</title>
		<link>http://www.jpstacey.info/blog/2007/09/19/programming-shouldnt-be-degrading/</link>
		<comments>http://www.jpstacey.info/blog/2007/09/19/programming-shouldnt-be-degrading/#comments</comments>
		<pubDate>Wed, 19 Sep 2007 13:33:42 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[layers]]></category>

		<category><![CDATA[paradigms]]></category>

		<category><![CDATA[standards]]></category>

		<category><![CDATA[accessibility]]></category>

		<category><![CDATA[convert]]></category>

		<category><![CDATA[css]]></category>

		<category><![CDATA[design]]></category>

		<category><![CDATA[development]]></category>

		<category><![CDATA[didhesayparadigm]]></category>

		<category><![CDATA[enhancement]]></category>

		<category><![CDATA[form]]></category>

		<category><![CDATA[html]]></category>

		<category><![CDATA[javascript]]></category>

		<category><![CDATA[progressive]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2007/09/19/programming-shouldnt-be-degrading/</guid>
		<description><![CDATA[Steve compares &#8220;graceful degradation&#8221; with &#8220;progressive enhancement.&#8221; Mostly he takes issue (rightly) with the rhetorical spin that the former applies to the idea of building a website. But I think you can compare them with each other as if they were two different types of crowbar instead: two ways of prising open the task in [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://nascentguruism.com/journal/grace">Steve compares &#8220;graceful degradation&#8221; with &#8220;progressive enhancement.&#8221;</a> Mostly he takes issue (rightly) with the rhetorical spin that the former applies to the idea of building a website. But I think you can compare them with each other as if they were two different types of crowbar instead: two ways of prising open the task in hand.</p>
<p>What I like most about <a href="http://www.makemineatriple.com/2007/04/the-highland-fling/" title="Bryan talks about The Highland Fling conference">progressive enhancement</a> is that it gives me a way of tackling the age-old problem of turning a design for a client into a workable, accessible page without going completely mental. Before, if I saw a dropdown on the designs without a submit button or some such, I&#8217;d think, well, the HTML won&#8217;t have a submit button either, so I need to build the Javascript at the same time and get it all to work at once. This led me into all sorts of tangles&#8212;each one on its own easy to extricate myself from&#8212;and at the end of it I would have a form that would work or look right without <em>every</em> layer of technology in place.</p>
<p>But if you&#8217;re thinking progressively at the back of your mind all the time, you can start with an unstyled, unscripted form that works, and then use subtle styling to get exactly where you want to be. Some onloaded Javascript can give you a <code>body.js</code> class with which to hide unwanted submit buttons. At the end, if nothing else, the form still submits when Javascript is turns off, and it looks OK because you checked at the outset. </p>
<p>Progressive enhancement, ironically for the amount of work it implies, actually places the least stress (and maintenance workload) on the developer, and makes them learn a thing or two about layering and modularity at the same time. And as Steve points out, in this model there is no degradation: just the near-hotswapping of a range of technologies on top of a sturdy markup language that has been around in various forms for over a decade.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2007/09/19/programming-shouldnt-be-degrading/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Image manipulation in the browser</title>
		<link>http://www.jpstacey.info/blog/2007/06/06/image-manipulation-in-the-browser/</link>
		<comments>http://www.jpstacey.info/blog/2007/06/06/image-manipulation-in-the-browser/#comments</comments>
		<pubDate>Wed, 06 Jun 2007 19:15:53 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[hacking]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[projects]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[browser]]></category>

		<category><![CDATA[flash]]></category>

		<category><![CDATA[framework]]></category>

		<category><![CDATA[image]]></category>

		<category><![CDATA[javascript]]></category>

		<category><![CDATA[mochikit]]></category>

		<category><![CDATA[mootools]]></category>

		<category><![CDATA[picnik]]></category>

		<category><![CDATA[prototype]]></category>

		<category><![CDATA[rotate]]></category>

		<category><![CDATA[swf]]></category>

		<category><![CDATA[swfir]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2007/06/06/image-manipulation-in-the-browser/</guid>
		<description><![CDATA[While Picnik gradually converts the functionality of desktop image processing software into online tools (in the hope, presumably, of being bought up by one of the big players), Flash has found other, more piecemeal uses in augmenting the image and font functionality of your average browser. Hot on the heels of sIFR, which replaces text [...]]]></description>
			<content:encoded><![CDATA[<p>While <a href="http://www.picnik.com/">Picnik</a> gradually converts the functionality of desktop image processing software into online tools (in the hope, presumably, of being bought up by one of the big players), Flash has found other, more piecemeal uses in augmenting the image and font functionality of your average browser. Hot on the heels of <a href="http://wiki.novemberborn.net/sifr">sIFR</a>, which replaces text with the equivalent image in a font of your choosing, comes <a href="http://www.swfir.com/">swfIR</a>, a pair of of Flash and Javascript chunks which, between them, permit your browser to rotate and resize images, adding shadows and rounded-corner borders, <i>in situ</i> after they&#8217;ve been retrieved from the server.</p>
<p>I&#8217;ve never been a fan of gratuitous Flash, but I&#8217;ve recently agreed to implement a complex page design (using swfIR) to help a client reuse existing content. Portrait images of users need to look &#8220;normal&#8221; on an index page, but then be rotated to line up with a fake &#8220;polaroid&#8221; background at around 2.5 degrees on the user profile page. Rather than have the client upload at least two copies of every image, which seemed a bit mean if we could avoid it, swfIR could do the rotation for us.</p>
<p>Apart from an initial problem, swfIR worked very well, the Javascript interface to the Flash proving very easy to use and the resultant image quality fine for our purposes. There&#8217;s the standard background problem with freeware Flash plugins (the background of the bounding box of the plugin&#8217;s influence on the page cannot be transparent, and thus chips away at the background polaroid blank on Ubuntu/Firefox) but otherwise it integrated seamlessly into both the design of the site and the technology of our existing Javascript workflow. It also coexisted happily in the browser with the <a href="http://jquery.com/">jQuery</a> and <a href="http://www.mochikit.com/">MochiKit</a> frameworks, as we&#8217;d already restricted the latter&#8217;s scope to just the <code>window.MochiKit</code> object: compare this with the declarations on the swfIR site that neither <a href="http://mootools.net/">MooTools</a> nor <a href="http://www.prototypejs.org/"> Prototype</a> sit well with their system.</p>
<p>The initial problem I mention above was one of URL paths. The path to the <code>.swf</code> half of the swfIR pair is hard-coded into the object returned by the <code>window.swfir()</code> function. Not only that, but it&#8217;s a relative path: just <code>"swfir.swf"</code>. When the visitor was viewing the page <code>.../node/100</code>, the Javascript was creating a Flash embedded object that effectively referenced <code>.../node/100/swfir.swf</code>; but then when the visitor navigated to the aggregated view at <code>.../view/all_users</code>, a completely different file was being referenced by the embed. This situation was hardly helped by many of these apparent directories actually not existing on the server at all, but being phantoms, handled by Drupal and Apache cleverness.</p>
<p>The fix for this was eventually fairly simple, but it did involve hacking the Javascript. In <code>swfir.js</code> I changed the function declaration (line 354) to:</p>
<blockquote class="code"><p>function swfir(path)</p>
</blockquote>
<p>I then permitted this path to be brought in at the front of the existing hardwired path (line 364):</p>
<blockquote class="code"><p>this.src = (typeof path == &#8220;undefined&#8221; ? &#8220;&#8221; : path) + &#8220;swfir.swf&#8221;;</p>
</blockquote>
<p>I could in principle have just overridden it entirely and dropped the hardwired bit, but decided against it for backwards-compatibility reasons. On reflection, too, the object&#8217;s property <code>this.src</code> could probably have been modified post-instantiation, but I have enough JS headaches already, working out what different browsers consider to be &#8220;mutability,&#8221; to want to add another to the contents of my weary cranium.</p>
<p>That&#8217;s it, then: the full fix. I&#8217;ll mention the issue to the swfIR people shortly. A more foresighted solution would cause that function to accept a whole object as a parameter, an object whose properties could then be used to override any one of the configuration variables currently hardwired around line 360. But this quick and dirty change worked fine. I just wish I could show off the results, but the site itself is still embargoed. As soon as it appears in the real world then you can guarantee that I&#8217;ll point everyone I know at it, as it&#8217;s arguably one of the best, completest, tightest websites I&#8217;ve done, thanks in no small part to the efforts of my co-workers in design and additional development.</p>
<p>Among all this Flash hysteria, of course, it&#8217;s important to remember the wise words of <a href="http://hughw.blogspot.com/2007/05/ria-fill-er-up.html" title="RIA -- fill 'er up!">Hugh Winkler (</a><a href="http://seanmcgrath.blogspot.com/2007_04_29_seanmcgrath_archive.html#9180786898079500068">via the otherwise ever-quotable Sean McGrath</a>)</p>
<blockquote><p>Users have voted with their mice, and they&#8217;ve voted for the web experience &#8212; exploring the web information space using hyperlinks &#8212; as far more important than whizzy UI. Ask eBay. Ask MySpace&#8230;. It <em>is</em> good to have super expressive widgets &#8212; hear hear. But if you&#8217;re not pushing a bunch of hypertext down to my browser, you&#8217;re not helping me explore the space.</p>
</blockquote>
<p>The lesson, I suppose is to use Flash, but use it sparingly, as a <a href="http://hesketh.com/publications/progressive_enhancement_paving_way_for_future.html">progressive enhancement</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2007/06/06/image-manipulation-in-the-browser/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Taking Drupal to pieces</title>
		<link>http://www.jpstacey.info/blog/2007/04/17/taking-drupal-to-pieces/</link>
		<comments>http://www.jpstacey.info/blog/2007/04/17/taking-drupal-to-pieces/#comments</comments>
		<pubDate>Tue, 17 Apr 2007 15:36:36 +0000</pubDate>
		<dc:creator>jps</dc:creator>
		
		<category><![CDATA[diagnostics]]></category>

		<category><![CDATA[hacking]]></category>

		<category><![CDATA[layers]]></category>

		<category><![CDATA[software]]></category>

		<category><![CDATA[api]]></category>

		<category><![CDATA[blog]]></category>

		<category><![CDATA[cms]]></category>

		<category><![CDATA[content]]></category>

		<category><![CDATA[debug]]></category>

		<category><![CDATA[debug_print_backtrace]]></category>

		<category><![CDATA[drupal]]></category>

		<category><![CDATA[node]]></category>

		<category><![CDATA[open-source]]></category>

		<category><![CDATA[php]]></category>

		<category><![CDATA[template]]></category>

		<category><![CDATA[wordpress]]></category>

		<category><![CDATA[wsod]]></category>

		<guid isPermaLink="false">http://www.jpstacey.info/blog/2007/04/17/taking-drupal-to-pieces/</guid>
		<description><![CDATA[Since listening to Garrett Coakley speak at the first Geek Night on the topic of Drupal, I&#8217;ve been sniffing round that open-source CMS. He kindly came to speak to us again, and very inspiring it was too. We&#8217;re now having a deeper look at it, seeing what it can do, what are its strengths and [...]]]></description>
			<content:encoded><![CDATA[<p>Since listening to <a href="http://polytechnic.co.uk/">Garrett Coakley</a> speak at the first <a href="http://oxford.geeknights.net/">Geek Night</a> on the topic of <a href="http://drupal.org">Drupal</a>, I&#8217;ve been sniffing round that open-source CMS. He kindly came to speak to us again, and very inspiring it was too. We&#8217;re now having a deeper look at it, seeing what it can do, what are its strengths and weaknesses; that sort of thing.</p>
<p>Drupal is certainly very interesting. Its notion of presentation is remarkable in that, at a certain level, all content consists of homogeneous nodes, whether that consists of uploaded files, images, blog posts, taxonomy categories, or embedded YouTube videos. In addition its API for templating, both as a library of functions and as a workflow that one can hook into, probably rivals WordPress in its scope and power. At the same time, though, the implicit homogeneity makes it hard to structure fundamentally heterogeneous sites; and the API hooks are very difficult to unravel: frequently you&#8217;ll want to get at a function some ten levels deep, and probably three of those levels can be overridden by your own code, but which, and how?</p>
<p>I want to mention more at a later date, to do Drupal justice, but suffice it to say for now that the complex hierarchy of the hook-in workflow is almost entirely opaque in PHP, a language that provides rather terse error reporting, without the function <a href="http://uk.php.net/manual/en/function.debug-print-backtrace.php"><span class="code">debug_print_backtrace()</span></a>. Well worth a look if you&#8217;re debugging spaghetti-code, especially when all you can see is the <a href="http://drupal.org/node/135637">White Screen of Death</a>. Sprinkle it around as the gentle British rain from heaven: lightly, but often.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.jpstacey.info/blog/2007/04/17/taking-drupal-to-pieces/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
