<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6799088</id><updated>2012-01-23T18:51:04.666Z</updated><category term='ethics'/><category term='images'/><category term='indexes'/><category term='amqp'/><category term='sonar'/><category term='styling'/><category term='solution'/><category term='sysadmin'/><category term='news'/><category term='movies'/><category term='lawyers'/><category term='last.fm'/><category term='hosting'/><category term='feedwordpress'/><category term='privacy'/><category term='migrate'/><category term='scaling'/><category term='cf'/><category term='lion'/><category term='query'/><category term='war'/><category term='various'/><category term='webserver'/><category term='crowdfunding'/><category term='nginx'/><category term='spam'/><category term='rails'/><category term='rss'/><category term='gas'/><category term='developer'/><category term='neologisms'/><category term='work'/><category term='rant'/><category term='facebook'/><category term='i18n'/><category term='simple-rss'/><category term='tipping point'/><category term='authentication'/><category term='humour'/><category term='legal'/><category term='trampoline'/><category term='concurrency'/><category term='networking'/><category term='wordpress'/><category term='irish'/><category term='climbing'/><category term='build'/><category term='mac'/><category term='innovation'/><category term='pain'/><category term='statistics'/><category term='astroturf'/><category term='blogging'/><category term='lise'/><category term='subversion'/><category term='svn'/><category term='cooking'/><category term='ruby'/><category term='activerecord'/><category term='challenge'/><category term='comment'/><category term='goalkeeper'/><category term='social software'/><category term='ec2'/><category term='flattery'/><category term='idc'/><category term='manipulation'/><category term='systemtimer'/><category term='flat'/><category term='github'/><category term='invalidation'/><category term='explosion'/><category term='1.9'/><category term='police'/><category term='hollywood'/><category term='content-type'/><category term='nirvana'/><category term='offer'/><category term='code'/><category term='cragwag'/><category term='naming'/><category term='scripts'/><category term='helpers'/><category term='repository'/><category term='meme evel knievel humour'/><category term='cookies'/><category term='migration'/><category term='userexperience'/><category term='music'/><category term='notation'/><category term='atheism'/><category term='size'/><category term='syndication'/><category term='databases'/><category term='wikipedia'/><category term='phishing'/><category term='recipe'/><category term='utc'/><category term='sql'/><category term='words'/><category term='identity'/><category term='adsense'/><category term='twitter'/><category term='investment'/><category term='2.3'/><category term='team'/><category term='eafnosupport'/><category term='social media'/><category term='devnotes'/><category term='macports'/><category term='annoying'/><category term='writing'/><category term='vc'/><category term='timeout'/><category term='charset'/><category term='management'/><category term='pictures'/><category term='hungarian'/><category term='statsvn'/><category term='auto_complete'/><category term='erlang'/><category term='ads'/><category term='storage'/><category term='disk'/><category term='projects'/><category term='wtf'/><category term='user generated content'/><category term='date'/><category term='home'/><category term='gruntfuddlr'/><category term='nlp'/><category term='burglary'/><category term='css'/><category term='web 2.0'/><category term='society'/><category term='ot'/><category term='function'/><category term='warped'/><category term='terabyte'/><category term='performance'/><category term='raid'/><category term='cfoutput'/><category term='laptop'/><category term='ami'/><category term='qotw'/><category term='business'/><category term='scalability'/><category term='mysql'/><category term='security'/><category term='buyout'/><category term='utf-8'/><category term='language'/><category term='ux'/><category term='cloud'/><category term='bash'/><category term='fedex'/><category term='oracle'/><category term='style'/><category term='regulation'/><category term='timezone'/><category term='extortion'/><category term='filesystem'/><category term='due diligence'/><category term='baby'/><category term='michael kelly'/><category term='coding'/><category term='atom'/><category term='memcached'/><category term='busy'/><category term='fun'/><category term='version control'/><category term='release'/><category term='architecture'/><category term='nsfw'/><category term='scam'/><category term='request'/><category term='vista'/><category term='prototype'/><category term='responsibility'/><category term='scotland'/><category term='contract'/><category term='optimisation'/><category term='jdbc'/><category term='cache'/><category term='graphs'/><category term='evolution'/><category term='footie'/><category term='eu'/><category term='mapped domains'/><category term='sex'/><category term='developers'/><category term='flip'/><category term='transactions'/><category term='ibm'/><category term='crime'/><category term='amazon'/><category term='script'/><category term='windows'/><category term='rabbitmq'/><category term='age'/><category term='roy orbison'/><category term='football'/><category term='recruitment'/><category term='vector'/><category term='fusebox'/><category term='imitation'/><category term='rake'/><category term='database'/><category term='clingfilm'/><category term='apache'/><category term='ant'/><category term='lasagne'/><category term='office'/><category term='social engineering'/><category term='bad luck'/><category term='old'/><category term='personal'/><category term='ajax'/><category term='thin'/><category term='programming'/><category term='public domain'/><category term='views'/><category term='culture'/><category term='nevermind'/><category term='techies'/><category term='racket'/><category term='instance'/><category term='award'/><category term='config'/><category term='nationalities'/><category term='time'/><category term='jobs'/><category term='god'/><category term='donkey'/><category term='aggregation'/><category term='tagging'/><category term='fail'/><category term='equity'/><category term='satire'/><category term='identity theft'/><category term='db'/><title type='text'>Instant Badger</title><subtitle type='html'>Rants, raves and random thoughts on Ruby, Rails and Rabbit, plus Java, CFMX, methodologies, and development in general. And too much alliteration.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default?start-index=101&amp;max-results=100'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>174</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6799088.post-2180820823067314074</id><published>2012-01-23T18:32:00.000Z</published><updated>2012-01-23T18:33:03.220Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='humour'/><category scheme='http://www.blogger.com/atom/ns#' term='fail'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='wtf'/><category scheme='http://www.blogger.com/atom/ns#' term='language'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>How many WTFs per language?</title><content type='html'>Recently I've had to work with PHP an increasing amount. I've not been enjoying it. A few recent IRC conversations also got me thinking - is there an entirely arbitrary but kind of fun (and hey aren't they all arbitrary, really?) metric of programming language FAIL / coder FAILs per-language ?&lt;br /&gt;&lt;br /&gt;So on a whim, I decided to invent one: number of entries on &lt;a href="http://thedailywtf.com/"&gt;The Daily WTF&lt;/a&gt; which mention a given language.&lt;br /&gt;&lt;br /&gt;Here we go - "web" languages only, numbers correct at time of writing:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;.NET -&amp;nbsp;&lt;a href="http://www.google.co.uk/search?q=.net+site:thedailywtf.com"&gt;13,000&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Java -&amp;nbsp;&lt;a href="http://www.google.co.uk/search?q=java+site:thedailywtf.com"&gt;11,300&lt;/a&gt;&lt;/li&gt;&lt;li&gt;PHP -&amp;nbsp;&lt;a href="http://www.google.co.uk/search?q=php+site:thedailywtf.com"&gt;8,100&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Javascript -&amp;nbsp;&lt;a href="http://www.google.co.uk/search?q=javascript+site:thedailywtf.com"&gt;6,020&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Perl - &lt;a href="http://www.google.co.uk/search?q=perl+site:thedailywtf.com"&gt;3,520&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Python -&amp;nbsp;&lt;a href="http://www.google.co.uk/search?q=python+site:thedailywtf.com"&gt;2,270&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Ruby -&amp;nbsp;&lt;a href="http://www.google.co.uk/search?q=ruby+site:thedailywtf.com"&gt;1,570&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Coldfusion -&amp;nbsp;&lt;a href="http://www.google.co.uk/search?q=coldfusion+site:thedailywtf.com"&gt;459&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Or in chart form:&lt;/p&gt;&lt;div id="pieChart1" style="text-align: centre;"&gt;&lt;/div&gt;&lt;script type="text/javascript" src="https://www.google.com/jsapi"&gt;&lt;/script&gt;    &lt;script type="text/javascript"&gt;console.log('here!');      google.load("visualization", "1", {packages:["corechart"]});      google.setOnLoadCallback(drawChart);function drawChart() {console.log('in drawChart()');  // Create and populate the data table.  var data = new google.visualization.DataTable();  data.addColumn('string', 'Language');  data.addColumn('number', 'WTFs');  data.addRows([          ['.NET', 13000],          ['Java', 11300],          ['PHP', 8100],          ['Javascript', 6020],          ['Perl', 3520],          ['Python', 2270],          ['Ruby', 1570],          ['Coldfusion', 459]        ]);      var options = {          width: 500, height: 300,          title: 'WTFs per language',          is3D: true        };        var chart = new google.visualization.PieChart(document.getElementById('pieChart1'));        chart.draw(data, options);}&lt;/script&gt;&lt;br /&gt;So what does this prove? Well, nothing - to draw any conclusions at all, we'd have to normalise this for all kinds of factors such as&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;number of coders / lines of code using each language (anyone have a decent set of figures?)&lt;/li&gt;&lt;li&gt;average experience level of coder (I'm pretty sure that more beginners use PHP than Perl, for instance)&lt;/li&gt;&lt;li&gt;you name it&lt;/li&gt;&lt;/ul&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;...but it is kind of fun. Let the flamewar &lt;strike&gt;commence&lt;/strike&gt; continue!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-2180820823067314074?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/2180820823067314074/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=2180820823067314074' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2180820823067314074'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2180820823067314074'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2012/01/how-many-wtfs-per-language.html' title='How many WTFs per language?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-2144098020176223281</id><published>2012-01-10T12:03:00.001Z</published><updated>2012-01-10T14:54:09.826Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='wordpress'/><category scheme='http://www.blogger.com/atom/ns#' term='aggregation'/><category scheme='http://www.blogger.com/atom/ns#' term='mapped domains'/><category scheme='http://www.blogger.com/atom/ns#' term='feedwordpress'/><title type='text'>FeedWordpress duplicate posts with YD Domain Mapping</title><content type='html'>&lt;br /&gt;Using FeedWordPress to pull in aggregated feeds to a Wordpress blog? Also using the Wordpress MU Domain Mapping plugin to map an arbitrary domain to your blog? Seeing posts get duplicated? Then read on ....&lt;br /&gt;&lt;br /&gt;I've isolated the duplicate posts issue to some kind of interaction with the&amp;nbsp;Wordpress MU&amp;nbsp;Domain Mapping plugin. We're using this so that we can map arbitrary domains to our network sites.&lt;br /&gt;&lt;br /&gt;E.g.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;our Wordpress Network is set up as wp-network.foo.com, on a subdomain setup&lt;/li&gt;&lt;li&gt;a network site 'bar' would then be bar.wp-network.foo.com&lt;/li&gt;&lt;li&gt;we setup the feedwordpress plugin to pull in posts from a tag:uri feed&lt;/li&gt;&lt;li&gt;we get unique posts in, with GUIDs of the form bar.wp-network.foo.com?guid=(some guid)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;All well and good. BUT, &amp;nbsp;then we use the domain mapping plugin to map blog.bar.com to that network site...&lt;br /&gt;&lt;br /&gt;It seems that when we hit a network site on its mapped domain (blog.bar.com), it instantly duplicates the syndicated posts with guids of the form blog.bar.com?guid=(the same guid)&lt;br /&gt;&lt;br /&gt;I set up 2 identical blogs, subscribed them both to the same feed, and applied a domain mapping to one but not the other. All was fine until I hit the blog homepage on the new mapped domain- &amp;nbsp;at which point all the posts got replicated with mapped domain GUIDs as above.&lt;br /&gt;&lt;br /&gt;So I don't know if this is an issue with FeedWordPress or with the&amp;nbsp;Wordpress MU&amp;nbsp;Domain Mapping plugin, but they definitely don't seem to play nicely with each other.&lt;br /&gt;&lt;br /&gt;Hope this helps someone!&lt;br /&gt;&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-2144098020176223281?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/2144098020176223281/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=2144098020176223281' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2144098020176223281'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2144098020176223281'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2012/01/feedwordpress-duplicate-posts-with-yd.html' title='FeedWordpress duplicate posts with YD Domain Mapping'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-335764319091938355</id><published>2011-11-22T14:49:00.001Z</published><updated>2011-11-23T14:00:16.678Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='helpers'/><category scheme='http://www.blogger.com/atom/ns#' term='2.3'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='1.9'/><category scheme='http://www.blogger.com/atom/ns#' term='lion'/><title type='text'>Porting a Rails 2.3 app to Ruby 1.9</title><content type='html'>We finally managed to get enough space in the schedule to take the plunge and port our monolithic Rails 2.3 app to Ruby 1.9, with a view to increasing scalability of our app. An upgrade to Rails 3 is also on the cards for later, but... one thing at a time.&lt;br /&gt;&lt;br /&gt;As ever, the path to true Ruby nirvana is paved with good intentions, and tends to detour into dependency hell for a good portion of the way. Here's a quick shortlist of some of the issues we found along the way, and what we did to get round them.&lt;br /&gt;&lt;br /&gt;In no particular order, here we go.....&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;MySQL2 version should be no later than 0.2.x&amp;nbsp;&lt;/h3&gt;If you get the dreaded &lt;q&gt;Please install the mysql2 adapter: `gem install activerecord-mysql2-adapter`&lt;/q&gt; error message, what it &lt;em&gt;really&lt;/em&gt; means is &lt;q&gt;You can't use MySQL2 version 0.3+ with Rails version less than 3&lt;/q&gt;&lt;br /&gt;If you're already using MySQL2 0.2.x, and you're on Mac OS X, and you're &lt;em&gt;still&lt;/em&gt; getting the error, then the &lt;em&gt;other&lt;/em&gt; thing that it really means is &lt;q&gt;I couldn't find the dynamic library libmysqlclient.18.dylib&lt;/q&gt;. There are two proposed fixes for this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;code&gt;sudo install_name_tool -change libmysqlclient.18.dylib /usr/local/mysql/lib/libmysqlclient.18.dylib /Users/YOUR_USER_NAME/.rvm/gems/1.8/gems/mysql2-0.2&lt;/code&gt; - I couldn't get this working&lt;/li&gt;&lt;li&gt;&lt;code&gt;sudo ln -s /usr/local/mysql/lib/libmysqlclient.18.dylib /usr/lib/libmysqlclient.18.dylib&lt;/code&gt; - This worked for me on Lion&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h3&gt;Use Bundler&lt;/h3&gt;&lt;p&gt;If you weren't using it before (we weren't), use it now. Stop fighting the inevitable, just give in, bend over and take it - and use Bundler. Seriously, it makes things easier in the long run.&lt;/p&gt;&lt;h3&gt;EventMachine does not compile (update: &lt;em&gt;..easily..&lt;/em&gt;) on 1.9.2&lt;/h3&gt;&lt;p&gt;...at least not on my Lion Macbook Pro. It kept giving me the compiler error: &lt;code&gt;cc1plus: error: unrecognized command line option ‘-Wshorten-64-to-32’&lt;/code&gt;. Under 1.9.3, however - no problems, compiled first time every time.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; actually, I &lt;em&gt;did&lt;/em&gt; eventually get EventMachine 0.12.10 to compile under 1.9.2 with an evil hack. The error "-Wshorten-64-to-32" above made me think - the compiler is not part of RVM, so the only way the compiler would recognise a command-line option in one Ruby but not in another... is if it's &lt;strong&gt;not the same compiler!&lt;/strong&gt; So I tried this:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;$ rvm use 1.9.3@1.9.3rails2.3&lt;br /&gt;Using /Users/aldavidson/.rvm/gems/ruby-1.9.3-p0 with gemset 1.9.3rails2.3&lt;br /&gt;$ irb&lt;br /&gt;ruby-1.9.3-p0 :002 &gt; require 'mkmf'&lt;br /&gt; =&gt; true &lt;br /&gt;ruby-1.9.3-p0 :004 &gt;  CONFIG['CXX']&lt;br /&gt; =&gt; "g++-4.2" &lt;br /&gt;ruby-1.9.3-p0 :005 &gt; exit&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Now let's see what 1.9.2 is using:&lt;pre&gt;&lt;code&gt;$ rvm use 1.9.2@1.9.2rails2.3&lt;br /&gt;Using /Users/aldavidson/.rvm/gems/ruby-1.9.2-p290 with gemset 1.9.2rails2.3&lt;br /&gt;$ irb &lt;br /&gt;ruby-1.9.2-p290 :001 &gt; require 'mkmf'&lt;br /&gt; =&gt; true &lt;br /&gt;ruby-1.9.2-p290 :002 &gt; CONFIG['CXX']&lt;br /&gt; =&gt; "g++" &lt;br /&gt;ruby-1.9.2-p290 :006 &gt; exit&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;Ah-hah! So they are in fact using different C++ compilers! And although many native gems (e.g. MySQL) will respect and pass-through the CXX environment variable to the Makefile, sadly EventMachine is not one of them. So, the easiest fix was a bit of evil hackery - move g++ out of the way and symlink it to g++-4.2:&lt;pre&gt;&lt;code&gt;$ which g++&lt;br /&gt;/usr/local/bin/g++&lt;br /&gt;&lt;br /&gt;$ which g++-4.2&lt;br /&gt;/usr/bin/g++-4.2&lt;br /&gt;&lt;br /&gt;$ sudo mv /usr/local/bin/g++ /usr/local/bin/g++.bak &amp;&amp; sudo ln -s /usr/bin/g++-4.2 /usr/local/bin/g++&lt;/code&gt;&lt;/pre&gt;looking good - let's go for it:&lt;pre&gt;&lt;code&gt;gem install eventmachineBuilding native extensions.  This could take a while...&lt;br /&gt;Successfully installed eventmachine-0.12.10&lt;br /&gt;1 gem installed&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Yay!&lt;/p&gt;&lt;h3&gt;Rails 2.3 is NOT SUPPORTED under Ruby 1.9.3!&lt;/h3&gt;&lt;p&gt;There was a big fuss about Rails loading times under 1.9.3. The way that 'require' statements work has been changed, and it no longer checks to see if the file is there before requiring it. As a result, &lt;em&gt;&lt;a href="http://groups.google.com/group/rubyonrails-core/browse_thread/thread/81be70a119260e59"&gt;every controller MUST have a corresponding helper file&lt;/a&gt;, even if it's just a stub.&lt;/em&gt; This is majorly annoying, as we have 73 controllers across 3 namespaces, and only 17 helpers. The Rails team have "frozen" the 2.3 branch, and say that only security fixes will go in after 2.3.14 - in other words, they're not going to fix this. The good-old monkeypatch-via-plugin method doesn't work either, as the files get required AS Rails is loaded, not once it's initialised.&lt;/p&gt;&lt;p&gt;So there are two options, either:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Create a stub helper for each controller&lt;br /&gt;...which seems a bit fugly to me, or..&lt;/li&gt;&lt;li&gt;Fork Rails, fix the issue, use that fork in the meantime until we can port to Rails 3.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;strike&gt;This seems better, and who knows, if enough people complain about it, maybe we'll get a patch release (2.3.15?). I'm not holding my breath, mind.... but if we're in this situation, I'm sure others are too, so this will hopefully help some other people who are having the same problem.&lt;br /&gt;So, here's the &lt;a href="https://github.com/itrigga/rails"&gt;forked Rails 2.3.14 that we will fix the helper requires problem on&lt;/a&gt; (note: WILL fix, not "have already fixed"! :)&lt;/strike&gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;UPDATE:&lt;/strong&gt; After having got round the EventMachine issue above, we no longer need to do this - so we're carrying on with 1.9.2 and standard rails 2.3.14. &lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-335764319091938355?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/335764319091938355/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=335764319091938355' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/335764319091938355'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/335764319091938355'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2011/11/porting-rails-23-app-to-ruby-19.html' title='Porting a Rails 2.3 app to Ruby 1.9'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-6173191131779970499</id><published>2011-09-29T10:59:00.000+01:00</published><updated>2011-09-29T10:59:09.155+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='optimisation'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>MySQL idle connections still holding locks</title><content type='html'>We had an interesting problem today. We were seeing very slow single-row updates (&gt;30s) on our (innodb) scheduled_jobs table, and a large number of the update queries were failing with a LOCK WAIT TIMEOUT. The updates were using the primary key, so they should be pretty fast - but they weren't.&lt;br /&gt;&lt;br /&gt;So we fired up a console and ran SHOW INNODB STATUS, and saw lots of transactions with long-held locks - &lt;br /&gt;&lt;br /&gt;&lt;code&gt;---TRANSACTION 0 200086649, ACTIVE 3000 sec, process no 29791, OS thread id 140353331377936&lt;br /&gt;12 lock struct(s), heap size 3024, 6 row lock(s), undo log entries 10&lt;br /&gt;MySQL thread id 243, query id 314676 ip-10-94-245-79.ec2.internal 10.94.245.79 tn&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;- however, when we cross-referenced the MySQL thread IDs with a SHOW PROCESSLIST, we found that lots of the threads weren't actually doing anything:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;+------+------------+-----------+-----------------+----------------+------+--------------+------------------+&lt;br /&gt;| Id   | User       | Host      | db              | Command        | Time | State        | Info             |&lt;br /&gt;+------+------------+-----------+-----------------+----------------+------+--------------+------------------+&lt;br /&gt;|  243 | tn         | (..snip..)| tn_production   | Sleep          | 3000 |              | NULL           &lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;This was strange - but after a bit of a Googling and a few minutes thought, we realised the cause.&lt;br /&gt;&lt;br /&gt;When one of our worker processes dies - e.g. either because monit has killed it for taking up too much resource for too long, or because the EC2 instance it's running on has become unresponsive to ssh and been automatically restarted by our detect-and-rescue script - it may be in the middle of a transaction at the point it's killed.&lt;br /&gt;&lt;br /&gt;If the transaction was holding locks, and never got to the "COMMIT" stage, &lt;em&gt;and&lt;/em&gt; the connection wasn't closed normally, then &lt;strong&gt;that connection will persist - and maintain its locks - until it gets timed-out by the MySQL server&lt;/strong&gt;.... and the default wait_timeout variable is 28800s - &lt;strong&gt;8 hrs!&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;So, we killed the idle connections, and the update queries started going through much more quickly again. We've now brought our wait_timeout variable down to 10 mins, and we'll see how we get on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-6173191131779970499?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/6173191131779970499/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=6173191131779970499' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6173191131779970499'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6173191131779970499'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2011/09/mysql-idle-connections-still-holding.html' title='MySQL idle connections still holding locks'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-7812540240384498202</id><published>2011-05-11T10:30:00.000+01:00</published><updated>2011-05-11T10:30:24.848+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cookies'/><category scheme='http://www.blogger.com/atom/ns#' term='regulation'/><category scheme='http://www.blogger.com/atom/ns#' term='eu'/><category scheme='http://www.blogger.com/atom/ns#' term='privacy'/><title type='text'>New EU Cookie / Privacy Law - Should you panic?</title><content type='html'>There's been a lot of fuss recently about the new "EU Cookie Law", and what effect it will have on EU- and UK-based online businesses. The EU directive has been around and &lt;a href="http://eu.techcrunch.com/2011/03/09/stupid-eu-cookie-law-will-hand-the-advantage-to-the-us-kill-our-startups-stone-dead/"&gt;discussed with varying degrees of hyperbole&lt;/a&gt; for a while, what's caused the recent kerfuffle has been the adoption into UK legislation pretty-much as-is.&lt;br /&gt;&lt;br /&gt;So, should you be panicking in order to meet the implementation date of 26th May 2011? &lt;br /&gt;&lt;br /&gt;Well... maybe, but I'm not. &lt;br /&gt;&lt;br /&gt;Allow me to explain.&lt;br /&gt;&lt;br /&gt;There is cause for concern - just like we saw with the RIPA act circa 2000, the legislation is clearly well-intentioned, but has just-as-clearly been implemented by people with little understanding of the problem domain. The full legislation is available from the horse's mouth here: &lt;a href="http://www.legislation.gov.uk/uksi/2011/1208/contents/made"&gt;The Privacy and Electronic Communications (EC Directive) (Amendment) Regulations 2011&lt;/a&gt;, but I would recommend the &lt;a href="http://www.ico.gov.uk/~/media/documents/library/Privacy_and_electronic/Practical_application/advice_on_the_new_cookies_regulations.pdf"&gt;implementation guidelines from the Information Commissioner's Office&lt;/a&gt; for a slightly lighter read. &lt;br /&gt;&lt;br /&gt;Credit where it's due, the guidelines give a welcome degree of balance and reasonableness to the situation, but even so they manage to contradict themselves.&lt;br /&gt;&lt;br /&gt;The important section is "What do the new rules say?", where it quotes the relevant section of the new legislation:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;6 (1)     Subject to paragraph (4), a person shall not store or gain access to information stored, in the terminal equipment of a subscriber or user unless the requirements of paragraph (2) are met. &lt;br /&gt;(2)     &lt;strong&gt;The requirements are that the subscriber or user&lt;/strong&gt; of that terminal equipment-- &lt;br /&gt;(a)     is provided with clear and comprehensive information about the purposes of the storage of, or access to, that information; and &lt;br /&gt;(b)     &lt;strong&gt;has given his or her consent.&lt;/strong&gt;&lt;br /&gt;(3)     Where an electronic communications network is used by the same person to store or access information in the terminal equipment of a subscriber or user on more than one occasion, it is sufficient for the purposes of this regulation that the requirements of paragraph (2) are met in respect of the initial use. &lt;br /&gt;&lt;br /&gt;(3A) For the purposes of paragraph (2), &lt;strong&gt;consent may be signified by a subscriber who amends or sets controls on the internet browser&lt;/strong&gt; which the subscriber uses or by using another application or programme to signify consent.&lt;br /&gt;&lt;br /&gt;(4)     Paragraph (1) shall not apply to the technical storage of, or access to, information-- &lt;br /&gt;(a)     for the sole purpose of carrying out the transmission of a communication over an electronic communications network; or &lt;br /&gt;(b)     where such storage or access is strictly necessary for the &lt;br /&gt;provision &lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;Now, correct me if I'm wrong, but that seems fairly clear - if the user sets the controls on their internet browser to accept cookies, consent may be taken to be signified, right? Riiiiight?&lt;br /&gt;&lt;br /&gt;Well, apparently not. A little bit further down the document, it says:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I have heard that browser settings can be used to indicate &lt;br /&gt;consent – can I rely on that?&lt;br /&gt;&lt;em&gt;(...)&lt;/em&gt;&lt;br /&gt;At present, most browser settings are not sophisticated enough to &lt;br /&gt;allow you to assume that the user has given their consent to allow &lt;br /&gt;your website to set a cookie&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;Er...wut? Most browsers are set to accept cookies by default, but can be changed to reject them, or to reject third-party cookies, or prompt for each one. How is that &lt;em&gt;not&lt;/em&gt; giving consent? And how does this guidance interact with paragraph 3A from the regulations themselves?&lt;br /&gt;&lt;br /&gt;There's another implementation question as well. Let's walk through a workflow.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;User X arrives at a site&lt;/li&gt;&lt;li&gt;Site wants to set a cookie so that it can identify that User X's next click comes from User X, and not any of its other users.&lt;/li&gt;&lt;li&gt;..So Site has to ask for consent, presumably by an irritating and obtrusive pop-up window, or some other interstitial means. Remember that cookies are sent back as part of the HTTP response, so the decision regarding whether or not to set a cookie is taken on the server, before a response is sent back to the user. &lt;/li&gt;&lt;li&gt;If the user says "YES", all well and good - Site sets a cookie and remembers that choice.&lt;/li&gt;&lt;li&gt;BUT... what if the user says no? How do you remember that choice, without using a cookie? You can't, right? So you're going to have to ask every request...&lt;/li&gt; &lt;/ol&gt;&lt;br /&gt;OK, that's admittedly a simplified scenario for the purposes of making a point. But the point is valid - it's going to be extremely tricky to think of ways of implementing this directive in any meaningful way without destroying your user experience. And what about the ubiquitous third-party cookies that form the basis of services such as Google Analytics? Again, the guidelines are specific:&lt;br /&gt;&lt;br /&gt;&lt;q&gt;An analytic cookie might not appear to be as intrusive as others that might track a user across multiple sites but you still need consent&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;And &lt;em&gt;THAT&lt;/em&gt;, paradoxically, is why I'm not panicking. Laws that are very difficult to comply with are, in practice, difficult to enforce. This law, if it ever does get enforced, is most likely to be used as a political club to attack a high-profile mega-corporate that couldn't be brought down any other way - think MS and the eventually farcical browser-bundling lawsuit. Think Al Capone, and the fact that the only crime he ever got convicted of was tax evasion.&lt;br /&gt;&lt;br /&gt;There have also been some &lt;a href="http://www.itpro.co.uk/631717/businesses-urged-to-prepare-for-eu-cookie-laws"&gt;surprisingly reasonable quotes from the Information Commissioner's Office and the Government&lt;/a&gt; :&lt;br /&gt;&lt;br /&gt;&lt;q&gt;[Information Commissioner Christopher] Graham said the ICO was clear the changes should not have a detrimental impact on consumers nor cause “an unnecessary burden on UK businesses.”&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;and &lt;br /&gt;&lt;br /&gt;&lt;q&gt;we do not expect the ICO to take enforcement action in the short term against businesses and organisations as they work out how to address their use of cookies&lt;/q&gt; (Ed Vaizey, Culture Minister)&lt;br /&gt;&lt;br /&gt;Regardless, there may well end up being a high-profile test case at some point, which will garner huge amounts of publicity and huge amounts of lawyers saying things like "this is a very interesting case" - which, to paraphrase Terry Pratchett, is lawyer-speak for "at least six months at three grand a day, plus expenses, per lawyer, with a minimum team of ten". It will eventually establish a precedent for judicial interpretation of the act, and at that point the necessary course of action will become clear. &lt;br /&gt;&lt;br /&gt;Until then, I'm not panicking. And neither should you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-7812540240384498202?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/7812540240384498202/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=7812540240384498202' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7812540240384498202'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7812540240384498202'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2011/05/new-eu-cookie-privacy-law-should-you.html' title='New EU Cookie / Privacy Law - Should you panic?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3082422145544114313</id><published>2011-02-22T20:51:00.000Z</published><updated>2011-02-22T20:51:44.750Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='timeout'/><category scheme='http://www.blogger.com/atom/ns#' term='systemtimer'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='nginx'/><category scheme='http://www.blogger.com/atom/ns#' term='request'/><category scheme='http://www.blogger.com/atom/ns#' term='thin'/><title type='text'>Rails requests not timing out in time?</title><content type='html'>The web server CPU was mostly idle, the 5 thin instances didn't seem to be doing much, but the requests were still taking &lt;em&gt;forever&lt;/em&gt; to return anything. So what the flimmin' flip was taking so long?&lt;br /&gt;&lt;br /&gt;Nginx was configured to timeout requests after 60s, but it didn't seem to be working. In fact, the key to figuring out what was going on, was that while tailing the logs, I saw an occasional query come through with a ridiculously long execution time - 400 seconds or more... Yowch!&lt;br /&gt;&lt;br /&gt;So, if requests were supposed to time out after 60 seconds, how come a query was allowed to stay executing for 400 or more?&lt;br /&gt;&lt;br /&gt;It all comes down to threads. We're still on CRuby 1.8.7 (MRI) in production - we'll move to 1.9 and JRuby at some point, but previous experience has made me careful to test thoroughly, test some more, then test again and again and &lt;em&gt;again&lt;/em&gt; before making the move, and we're not quite ready to shift yet.&lt;br /&gt;&lt;br /&gt;Now, the default (MRI) CRuby interpreter uses &lt;a href="http://en.wikipedia.org/wiki/Green_threads"&gt;green threads&lt;/a&gt;, and the eventmachine gem underlying Thin server uses - like most Ruby libraries - timeout.rb for its timeout mechanism. This article &lt;a href="http://ph7spot.com/musings/system-timer"&gt;gives the full details&lt;/a&gt;, but it boils down to the fact that:&lt;br /&gt;&lt;q&gt;it is a well-known limitations of green threads that when a green thread performs a blocking system call to the underlying operating systems, none of the green threads in the virtual machine will run until the system call returns ... From the operating system perspective, there is only a single thread in the Ruby interpreter (the native one)&lt;/q&gt; &lt;br /&gt;&lt;br /&gt;So essentially, the monitor thread which is responsible for timing out the request has to &lt;em&gt;wait&lt;/em&gt; for the blocking IO call to return, before it can resume. This means that when you have a long-running, block IO call (say, for instance, an ActiveRecord query that takes 400 seconds to run) running under MRI, the default timeout mechanism is useless.&lt;br /&gt;&lt;br /&gt;The solution? If you can't just switch to JRuby, then here's a simple 5-minute fix that did the trick for us:&lt;br /&gt;&lt;br /&gt;in environment.rb :&lt;br /&gt;&lt;br /&gt;&lt;code class="ruby"&gt;&lt;br /&gt;  config.gem 'SystemTimer', :lib =&gt; 'system_timer'&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;in application_controller.rb :&lt;br /&gt;&lt;br /&gt;&lt;code class="ruby"&gt;&lt;pre&gt;  around_filter :force_timeout&lt;br /&gt;&lt;br /&gt;  def force_timeout( timeout=nil, &amp;block )&lt;br /&gt;    timeout ||= 60 # &lt;= or whatever value is appropriate&lt;br /&gt;    if defined?(SystemTimer) &amp;&amp; timeout.to_i &gt; 0&lt;br /&gt;      begin&lt;br /&gt;        SystemTimer.timeout_after(timeout) do&lt;br /&gt;          yield&lt;br /&gt;        end&lt;br /&gt;      rescue Timeout::Error =&gt; e&lt;br /&gt;        logger.error( "Timed out request after #{timeout}s!")&lt;br /&gt;        raise "RequestTimeout" &lt;br /&gt;      end&lt;br /&gt;    else&lt;br /&gt;      yield&lt;br /&gt;    end&lt;br /&gt;  end&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3082422145544114313?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3082422145544114313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3082422145544114313' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3082422145544114313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3082422145544114313'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2011/02/rails-requests-not-timing-out-in-time.html' title='Rails requests not timing out in time?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-5062820269295278555</id><published>2010-07-02T14:27:00.003+01:00</published><updated>2010-07-02T15:35:32.960+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='macports'/><category scheme='http://www.blogger.com/atom/ns#' term='rabbitmq'/><category scheme='http://www.blogger.com/atom/ns#' term='amqp'/><category scheme='http://www.blogger.com/atom/ns#' term='erlang'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><title type='text'>"cannot redeclare exchange" error on amqp and RabbitMQ 1.8 - fixed! (at last)</title><content type='html'>I've been having some trouble with an inherited MacBook Pro, on Leopard (OS X 10.5), trying to get RabbitMQ and the tmm1-amqp gem up and running. The annoying thing is that it was all working fine a few weeks ago, but I ended up having to completely remove MacPorts and do several sudo rm -rf  wipeouts and pretty much rebuild the dev environment from scratch. Since then, I re-installed MacPorts and did:&lt;br /&gt;&lt;code&gt;sudo port install rabbitmq-server&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;After a bit of fiddling about, I got this error (using exactly the same code that worked a few weeks ago):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;vendor/gems/tmm1-amqp-0.6.4/lib/mq.rb:225:in `process_frame': PRECONDITION_FAILED - cannot redeclare exchange 'jobs' in vhost 'trigganews.com' with different type, durable or autodelete value in AMQP::Protocol::Exchange::Declare on 1 (MQ::Error)&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;....several hours of frustrating googling later.....&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;It turns out that I'd innocently upgraded my version of RabbitMQ 1.8, whereas previously I'd had v1.7.2. This is significant because RMQ 1.8 uses AMQP 0.9, whereas v1.7.2 uses AMQP 0.8 - and &lt;em&gt;this&lt;/em&gt; matters because I'm using the tmm1-amqp gem to connect to it, and &lt;a href="http://groups.google.com/group/ruby-amqp/browse_thread/thread/999b33e0d76e0be9"&gt;tmm1-amqp v0.6.4 only supports AMQP 0.8&lt;/a&gt;...&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(HINT - check the files under the protocol directory to see what version your gem supports)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;So - time to downgrade RabbitMQ to 1.7.2 then. Should be straightforward, no? Well, no.&lt;br /&gt;&lt;br /&gt;It &lt;em&gt;also&lt;/em&gt; turns out that the version of Erlang in MacPorts is now R14A - and guess what? &lt;a href="https://trac.macports.org/ticket/25377"&gt;RabbitMQ 1.7.2 doesn't build in Erlang R14A&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So I had to create a local MacPorts repo (using the very helpful instructions &lt;a href="http://journal.bitshaker.com/articles/2007/10/20/install-old-versions-of-ports-using-macports/"&gt;HERE&lt;/a&gt;) from the &lt;a href="http://trac.macports.org/browser/trunk/dports/lang/erlang/Portfile?rev=64916#L3"&gt;previous checkin of the Erlang R13B4 portfile&lt;/a&gt; and point ports at that.&lt;br /&gt;&lt;br /&gt;  &lt;br /&gt;Then, with a &lt;code&gt;sudo port uninstall rabbitmq-server; sudo port install rabbitmq-server @1.7.2&lt;/code&gt;, followed by a blat of the mnesia database files:&lt;br /&gt;&lt;code&gt;sudo rm -rf /opt/local/var/lib/rabbitmq/mnesia/rabbit&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;- and it all works again. YAY!&lt;br /&gt;&lt;br /&gt;(PS - I had to port uninstall and re-install rabbitmq-server &lt;em&gt;twice&lt;/em&gt; for it to work properly. So that's always worth a try too - I swear MacOS gets more and more like Windows every day.....:)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-5062820269295278555?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/5062820269295278555/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=5062820269295278555' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/5062820269295278555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/5062820269295278555'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2010/07/cannot-redeclare-exchange-error-on-amqp.html' title='&quot;cannot redeclare exchange&quot; error on amqp and RabbitMQ 1.8 - fixed! (at last)'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-592784451940181511</id><published>2010-06-21T15:28:00.002+01:00</published><updated>2010-06-21T15:42:46.728+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rabbitmq'/><category scheme='http://www.blogger.com/atom/ns#' term='eafnosupport'/><category scheme='http://www.blogger.com/atom/ns#' term='amqp'/><category scheme='http://www.blogger.com/atom/ns#' term='mac'/><category scheme='http://www.blogger.com/atom/ns#' term='networking'/><title type='text'>EAFNOSUPPORT socket connection issue on Mac OS X</title><content type='html'>A frustrating problem cropped up this afternoon while trying to get a home-grown Ruby &lt;a href="http://rabbitmq.com/"&gt;RabbitMQ&lt;/a&gt; client to talk to the broker on a MacBook.&lt;br /&gt;&lt;br /&gt;The client is based on the &lt;a href="http://rubyeventmachine.com/"&gt;EventMachine&lt;/a&gt; and &lt;a href="http://github.com/tmm1/amqp"&gt;AMQP&lt;/a&gt; gems, and although for once it worked perfectly on Windows -&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(.... yes, frame that last sentence for posterity folks....)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;- it was giving bizarre EAFNOSUPPORT socket errors on Mac.&lt;br /&gt;&lt;br /&gt;It turns out that, for various obscure reasons, resolving "localhost" is not entirely straightforward on the Mac. There are two quick-and-easy ways round it:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;If you have IPv6 mappings for localhost in your /etc/hosts file, comment them out&lt;/li&gt;&lt;li&gt;Replace "localhost" in your connection params with the explicit "127.0.0.1" instead&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;Sorted!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-592784451940181511?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/592784451940181511/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=592784451940181511' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/592784451940181511'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/592784451940181511'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2010/06/eafnosupport-socket-connection-issue-on.html' title='EAFNOSUPPORT socket connection issue on Mac OS X'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-4795107464542068189</id><published>2009-12-08T20:54:00.003Z</published><updated>2009-12-08T22:28:46.766Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='invalidation'/><category scheme='http://www.blogger.com/atom/ns#' term='naming'/><category scheme='http://www.blogger.com/atom/ns#' term='memcached'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='cache'/><title type='text'>Memcached Cache Invalidation Made Easy</title><content type='html'>&lt;p&gt;&lt;q&gt;There are only two hard problems in computer science - cache invalidation, and naming things&lt;/q&gt;&lt;br /&gt;Phil Karlton&lt;/p&gt;&lt;br /&gt;It's an oft-quoted truism that brings a knowing smile to most hardened programmers, but it's oft-quoted precisely because it's true - and during a recent enforced rush job to implement a cache, I came across a nifty solution to the first problem by judicious use of the second.&lt;br /&gt;&lt;br /&gt;First, the problem - someone posted &lt;a href="http://cragwag.com"&gt;Cragwag&lt;/a&gt; on &lt;a href="http://stumbleupon.com"&gt;StumbleUpon&lt;/a&gt;, which led to an immediate spike in traffic on top of the slow increase I've been getting since I made it &lt;a href="http://twitter.com/cragwag"&gt;Tweet the latest news&lt;/a&gt;. All the optimisation work that I knew I needed to do at some point was more than a few hours work, and I had to get something out quickly - enter &lt;a href="http://memcached.org/"&gt;memcached&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Memcached is a simple, distributed-memory caching server that basically stores whatever data you give it in memory, associated with a given key. Rails has a built-in client that you can use simply as follows:&lt;br /&gt;&lt;br /&gt;&lt;code class="ruby"&gt;my_data = Cache.get(key) do { &lt;br /&gt;  ... do stuff to generate data&lt;br /&gt;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;If the cache has an entry for the given key, it will return it straight from the cache. If not, the block will be called, and whatever is returned from the block will be cached with that key.&lt;br /&gt;&lt;br /&gt;So far so good - but what exactly should you cache, and how should you do it?&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;The Complicated Way To Do It&lt;/h4&gt;&lt;br /&gt;A common pattern is to cache ActiveRecord objects, say by wrapping the finder method in a cache call, and generating a key of the class name and primary key. But this only works for single objects, which are usually pretty quick to retrieve anyway, and is no use for the more expensive queries, such as lists of objects plus related objects and metadata, or - often particularly slow - searches.&lt;br /&gt;&lt;br /&gt;So you could extend that simple mechanism to cache lists of objects and search results, say by using the method name and the given parameters. But then you have an all-new headache - an object might be cached in many different collections, so how do you know which cache keys to purge? You have two options:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Try and keep track of which cache keys are caching which objects? Eep - that's starting to sound nasty - you're effectively creating a meta-index of cached entries and keys, which would almost certainly be comparable in size to your actual cache... and where's that index going to live and how are you going to make sure that it's faster to search this potentially large and complex index than to just hit the damn database?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Sidestep the invalidation problem by invalidating the entire cache whenever data is updated. This is much simpler, but there doesn't seem to be a "purge all" method - so you'd need to keep track of what keys are generated somewhere, then loop round them and delete them individually. You could do this with, say, an ActiveRecord class and delete the cache keys on a destroy_all - but still, that's icky.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;The Easy Way To Do It&lt;/h4&gt;&lt;br /&gt;After a few minutes Googling, I found &lt;a href="http://blog.leetsoft.com/2007/5/22/the-secret-to-memcached"&gt;this post&lt;/a&gt; on the way &lt;a href="http://shopify.com"&gt;Shopify&lt;/a&gt; have approached it, and suddenly it all became clear. You can solve the problem of Cache Invalidation by being cunning about Naming Things - in particular, your cache keys.&lt;br /&gt;&lt;br /&gt;The idea is very simple - Be Specific about exactly what you're caching. Read that post for more details, or read on for how I've done it.&lt;br /&gt;&lt;br /&gt;So I ripped out all of my increasingly-over-complicated caching code from the model, and went for a simple approach of caching the generated html in the controllers. At the start of each request, in a before_filter, I have one database hit - load the current CacheVersion - which just retrieves one integer from a table with only one record. Super fast - and if the data is cached, that's the only db hit for the whole request. &lt;br /&gt;&lt;br /&gt;The current cache version number is stored as an instance variable of the application controller, and prepended to all cache keys. The rest of the key is generated from the controller name, the action, and a string constructed out of the passed parameters. Any model methods that aren't just simple retrievals but affect data, can just bump up the current cache version, and hey presto - everything then gets refreshed on next hit, and the old version just gets expired on the least-recently-used-goes-first rule.&lt;br /&gt;&lt;br /&gt;This has a few very nice architectural benefits:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The caching code is then in the "right" place - in the bit you want to speed up - i.e. the interface&lt;/li&gt;&lt;li&gt;You also eliminate the overhead of rendering any complicated views - you just grab the html (or xml, or json) straight from the cache and spit it back.&lt;/li&gt;&lt;li&gt;It utilises, and fits in with, one of the fundamental ideas of resource-based IA - that the URL (including the query string) should uniquely identify the resource(s) requested&lt;/li&gt;&lt;li&gt;The application controller gives you a nice central place to generate your keys&lt;/li&gt;&lt;li&gt;If you have to display different data to users, no problem - just put the user id as part of the key.&lt;/li&gt;&lt;li&gt;Rails conveniently puts the controller and action names into the params hash, so your cache key generation is very simple&lt;/li&gt;&lt;li&gt;The admin interface can then easily work off up-to-date data&lt;/li&gt;&lt;li&gt;You can also provide an admin "Clear the cache" button that just has to bump up the current cache version number.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Etc etc - I could go on, but I won't. The net result is that pages which used to take several seconds to render now take just a few milliseconds, it's much much simpler and more elegant this way, and if you're not convinced by now, just give it a try. &amp;lt;mrsdoyle&amp;gt;Go on - ah go on now, ah you will now, won't you Father?&amp;lt;/mrsdoyle&amp;gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;app/models/cache_version.rb&lt;/h4&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="ruby"&gt;class CacheVersion &lt; ActiveRecord::Base&lt;br /&gt;    def self.current&lt;br /&gt;      CacheVersion.find(:last) || CacheVersion.new(:version=&gt;0)&lt;br /&gt;    end&lt;br /&gt;&lt;br /&gt;    def self.increment&lt;br /&gt;      cv = current &lt;br /&gt;      cv.version = cv.version + 1&lt;br /&gt;      cv.save&lt;br /&gt;    end &lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;h4&gt;app/controllers/application_controller.rb&lt;/h4&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="ruby"&gt;require 'memcache_util'&lt;br /&gt;&lt;br /&gt;class ApplicationController &lt; ActionController::Base&lt;br /&gt;  # load the current cache_version from the db&lt;br /&gt;  # this is used to enable easy memcache "expiration"&lt;br /&gt;  # by simply bumping up the current version whenever data changes&lt;br /&gt;  include Cache&lt;br /&gt;  before_filter :get_current_cache_version&lt;br /&gt;&lt;br /&gt;private&lt;br /&gt;&lt;br /&gt;  def cache_key&lt;br /&gt;    "#{@cache_version.version}_#{params.sort.to_s.gsub(/ /, '_')}"&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def get_current_cache_version&lt;br /&gt;    @cache_version = CacheVersion.current&lt;br /&gt;  end&lt;br /&gt;&lt;br /&gt;  def with_cache( &amp;block )&lt;br /&gt;    @content, @content_type = Cache.get(cache_key) do&lt;br /&gt;      block.call&lt;br /&gt;      [@content, @content_type]&lt;br /&gt;    end&lt;br /&gt;    render :text=&gt;@content, :content_type=&gt;(@content_type||"text/html") &lt;br /&gt;  end&lt;br /&gt;end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;in your actual controller:&lt;/h4&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="ruby"&gt;  def index &lt;br /&gt;    with_cache {&lt;br /&gt;      # get data&lt;br /&gt;      # NOTE: you must render to string and store it in @content&lt;br /&gt;      respond_to do |format|&lt;br /&gt;        format.html { &lt;br /&gt;          @content = render_to_string :action =&gt; "index", :layout =&gt; "application" &lt;br /&gt;        }&lt;br /&gt;        format.xml {&lt;br /&gt;          @content_type = "text/xml"&lt;br /&gt;          @content = render_to_string :xml =&gt; @whatever, :layout=&gt; false &lt;br /&gt;        }&lt;br /&gt;      end&lt;br /&gt;    }&lt;br /&gt;  end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-4795107464542068189?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/4795107464542068189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=4795107464542068189' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4795107464542068189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4795107464542068189'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/12/memcached-cache-invalidation-made-easy.html' title='Memcached Cache Invalidation Made Easy'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-4478224402426388593</id><published>2009-10-26T15:57:00.003Z</published><updated>2009-10-26T16:04:04.807Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='timezone'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='utc'/><category scheme='http://www.blogger.com/atom/ns#' term='time'/><category scheme='http://www.blogger.com/atom/ns#' term='date'/><title type='text'>Specs failing with Daylight Saving Time change?</title><content type='html'>So I've been banging my head for the past hour or so,  trying to work out why some of our specs have suddenly started failing without the code having been touched, and it comes down to an inconsistency in how Rails is handling UTC offsets when Time objects are manipulated:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt;&gt; Time.now&lt;br /&gt; =&gt; Mon Oct 26 15:55:20 0000 2009&lt;br /&gt;&gt;&gt; Time.now.utc&lt;br /&gt;=&gt; Mon Oct 26 15:55:26 UTC 2009&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;OK, that's fine. Now let's try a calculation:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;&gt;&gt; (Time.now - 1.day).utc&lt;br /&gt;[Sun Oct 25 14:55:41 UTC 2009&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;What? Where did that one hour offset come from??&lt;br /&gt;&lt;br /&gt;It turns out THAT is the source of all the specs which suddenly failed for no apparent reason this morning.&lt;br /&gt;&lt;br /&gt;The good news is, it's easy to fix:&lt;br /&gt;&lt;br /&gt;&gt;&gt; Time.now.utc - 1.day&lt;br /&gt;=&gt; Sun Oct 25 15:56:08 UTC 2009&lt;br /&gt;&lt;br /&gt;MUCH better!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-4478224402426388593?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/4478224402426388593/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=4478224402426388593' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4478224402426388593'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4478224402426388593'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/10/specs-failing-with-daylight-saving-time.html' title='Specs failing with Daylight Saving Time change?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3677227190033472654</id><published>2009-10-26T13:41:00.003Z</published><updated>2009-10-26T13:46:15.483Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='award'/><category scheme='http://www.blogger.com/atom/ns#' term='social software'/><category scheme='http://www.blogger.com/atom/ns#' term='innovation'/><category scheme='http://www.blogger.com/atom/ns#' term='idc'/><title type='text'>Ooh, we're in IDC's "10 most innovative software companies"!</title><content type='html'>Oooh, shiny! We just got named as one of IDC's &lt;a href="http://www.reuters.com/article/pressRelease/idUS108772+26-Oct-2009+BW20091026"&gt;10 most innovative sub-$100m software companies to watch&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I guess we scored more highly on the "Web 2.0-like functionality moves into the enterprise" category more than the other two, and by itself it might not mean much, but it's still great to be thought of in those terms. Makes me feel all warm and fuzzly.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3677227190033472654?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3677227190033472654/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3677227190033472654' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3677227190033472654'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3677227190033472654'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/10/ooh-were-in-idcs-10-most-innovative.html' title='Ooh, we&apos;re in IDC&apos;s &quot;10 most innovative software companies&quot;!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1384708022344101481</id><published>2009-10-13T01:08:00.008+01:00</published><updated>2009-10-21T07:58:04.408+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='lasagne'/><category scheme='http://www.blogger.com/atom/ns#' term='recipe'/><category scheme='http://www.blogger.com/atom/ns#' term='cooking'/><title type='text'>Al's Ultimate Lasagne Recipe</title><content type='html'>OK, ok, so I've never posted a recipe on here before. BUT, pretty much everyone I've ever made this lasagne for has said &lt;q&gt;wow, you've GOT to give me the recipe for that!&lt;/q&gt; - the most recent example being a guy who had spent years living in Italy, no less - and I can't sleep tonight, so here goes.&lt;br /&gt;&lt;br /&gt;Last year I added some refinements from &lt;a href="http://www.npr.org/templates/story/story.php?storyId=6530258"&gt;Heston Blumenthal's 'Perfect' bolognese&lt;/a&gt;, but mercifully this version takes about 3-4hrs, rather than 3 days. You can add whatever embellishments you like, here I'm just going to describe the basic sauce preparation.&lt;br /&gt;&lt;br /&gt;You will need:&lt;br /&gt;&lt;h4&gt;Pans&lt;/h4&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;A heavy-bottomed frying pan (our &lt;a href="http://www.amazon.co.uk/Creuset-Satin-Black-Interior-Frying/dp/B000RK2DA8/ref=sr_1_9?ie=UTF8&amp;s=kitchen&amp;qid=1255393075&amp;sr=8-9"&gt;Le Creuset&lt;/a&gt; pan was perfect for this) that can get really hot&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;A large sauce pot / casserole dish&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;A large lasagne dish - deep enough for at least 3 layers of sauce, plus about half an inch of bechamel sauce&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;A medium sauce pan for the bechamel sauce&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;Ingredients&lt;/h4&gt;&lt;p&gt;&lt;em&gt;NOTE: all quantities are approximate. Don't be the kind of cook who has to measure everything to the 3rd significant figure - taste often and see what you think it needs, it's much more fun!&lt;/em&gt;&lt;/p&gt;&lt;ul&gt;&lt;br /&gt;  &lt;li&gt;About 1kg of lean minced beef (preferably organic, or at least free range - we like Waitrose's, and 2 of their &lt;a href="http://www.ocado.com/webshop/product/Organic-Lean-Ground-Beef-10-Fat-Waitrose/26383011"&gt;500g packs&lt;/a&gt; works nicely)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;About 400g of lardons (again, 2 packs of the &lt;a href="http://www.shopwiki.co.uk/Free+Range+Smoked+Dry+Cure+Lardons+Waitrose"&gt;Waitrose Free-Range lardons&lt;/a&gt; work nicely)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;1 red onion, 1 white onion&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;1 stick of celery&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;1 or 2 carrots, depending on size - we're aiming for roughly equal quantities of diced carrot, red onion and celery, so adjust as needed&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;3 tins of chopped tomatoes&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;3 or 4 mushrooms - we like &lt;a href="http://www.ocado.com/webshop/product/Organic-Portabellini-Mushrooms-Waitrose/18872011?parentContainer=SEARCHmushrooms"&gt;Portabellini&lt;/a&gt;&lt;br /&gt;  &lt;li&gt;a bulb of garlic, the fresher the better&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;2 large bay leaves&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;2 large/4 small star anise&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;about 1tbsp Thai fish sauce (we like &lt;a href="http://www.squidbrand.com/squideng/product.php#"&gt;Squid Brand&lt;/a&gt;, which you should be able to get from any good Chinese food store)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;about 1tbsp Lea &amp; Perrins Worcester Sauce&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;about a third of a bottle of red wine&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Maldon sea salt &amp; freshly ground black pepper&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;A tablespoon of Marmite&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Butter. Probably about 100g, maybe a bit more&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Olive oil - it's best to NOT use extra virgin, you're going to be frying with it - but really, it doesn't make that much difference in the end&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Lasagne sheets&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;A decent handful of basil leaves (MUST be fresh - dried is no good here)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;A decent handful of fresh oregano (ditto)&lt;/li&gt; &lt;br /&gt;  &lt;li&gt;The &lt;em&gt;vine&lt;/em&gt; from some vine tomatoes (that's where most of the smell comes from, not the tomato itself)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;For the bechamel sauce - maybe 50g of plain flour and about 1/3 pint of milk, plus about 50g of good mature cheddar (&lt;a href="http://www.ocado.com/webshop/product/Cathedral-City-Extra-Mature-Cheddar/27789011?parentContainer=SEARCHcathedral%20city"&gt;Cathedral City Extra Mature&lt;/a&gt; works well).&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;Preparation (20 mins)&lt;/h4&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;Dice the carrot, the red onion and the celery. We're aiming for equal quantities of each, in equal-sized pieces.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Chop the white onion - these pieces don't need to be the same size as the previous lot, they can be bigger and rougher.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Slice the mushrooms, so they're maybe half a centimetre thick&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Lightly crush and peel all the cloves of garlic. I use a good whack with my fist on top of a large knife on top the clove. It doesn't need to be obliterated, just kind of half-crushed, so the skin comes off easily.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Take half of the semi-crushed garlic and chop it finely.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;In a small bowl, crush (with your fingers is fine) a good tablespoon of sea salt and grind about an equal quantity of black pepper (you're going to be handling raw beef next - you don't want the juices from your hands to hang around on the pepper mill, do you?)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Season the beef - I tip one pack of beef onto the other, then separate the strands back into the empty pack. Each time you make a full layer, sprinkle on some salt and pepper&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;h4&gt;Cooking stage 1 (20 mins)&lt;/h4&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;In the big pot, pour a good layer of olive oil. Heat over a low-to-medium heat.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Add the semi-crushed (not chopped) garlic cloves. Cook until they're just going golden but still soft (burnt garlic is bitter and grim), then remove them and save for later&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Increase the heat slightly, and add the diced red onion, carrot, and celery. Stir these occasionally while they soften.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Put the heavy-bottom frying pan over a medium-to-high heat, and add some olive oil&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;In the frying pan, add the chopped white onion and star anise. Fry until the onions are caramelising - i.e. going golden brown. Then tip the contents of the frying pan into the big pot (&lt;strong&gt;before&lt;/strong&gt; the onions start to burn and go bitter.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;In the frying pan, turn the heat up to full and start to sear the beef. Do this layer-by-layer - ALL the beef must be touching the pan, otherwise it'll broil instead of searing, so just do a little at a time. Keep it moving until it's browned outside but pink in the middle, then tip into the big pot. Repeat until all the beef is seared.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;...remember to keep stirring the big pot every so often!&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;In the frying pan, add a big lump of butter - probably 50g or so. This will sizzle and spit for a while. Once it's stopped sizzling, all the water has gone out of it, so then add the chopped garlic and mushrooms and saute these until they're golden brown at the edges but still plump and juicy. Then tip them into the large pot (the butter will help give the sauce a nice sheen)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Make sure the frying pan is hot, then sear the lardons. You're looking for golden crispy edges, but plump and juicy pinkness. These will probably release a fair amount of fat into the pan - this is fine, it's all good for the sauce. When done, tip everything into the big pot.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Now de-glaze the frying pan by tipping the red wine into it and keep stirring it and scraping the tasty bits off the bottom until the wine is reduced by about half. Tip into the large pot. You can now wash the frying pan - everything from now on takes place in the large pot (except the bechamel sauce)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;In the large pot, add the tomatoes and stir well.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Add the Thai fish sauce and Worcester sauce, stir, and reduce the heat to low-to-medium&lt;/li&gt; &lt;br /&gt;  &lt;li&gt;Crack the bay leaves in half, and add to the pot&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Chop and add the lightly-browned garlic that you used to flavour the oil with, right back at the start&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;h4&gt;Reducing the sauce (1hr-2hrs)&lt;/h4&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;Leave the sauce pot simmering with the lid half-on for about an hour, stirring occasionally and adding pepper if needed. (Taste!)&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Sometimes I add a tablespoon of Marmite if the sauce needs more depth, sometimes not - depends on the ingredients&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;It's done when it's done - i.e. when it looks and tastes like really good bolognese-style sauce, and isn't too runny or too dry.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;When it *is* done, turn the heat off and leave it to cool with the lid on for about 45mins&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Once it's cool, chop the basil and oregano and stir through. At this point, you can add the vine of the tomatoes and leave it to rest and infuse for about half an hour, then take the vine out again.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;h4&gt;Layer the lasagne (5 mins)&lt;/h4&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;Put the oven onto 180 degrees C to heat up&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Put a layer of sauce in the bottom of the lasagne dish, then add a layer of lasagne&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Repeat at least once, preferably twice (depending on the size of your dish and how much sauce you have) until you've used all the sauce - but make sure that your top layer is sauce, not pasta!&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;h4&gt;Bechamel Sauce (5 mins)&lt;/h4&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;In the saucepan, melt about 50g of butter over a medium heat&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Add the flour and stir quickly until you have a good consistency - still "smearable", not a dry lump&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Begin adding milk a little at a time and stirring until absorbed. Make sure you don't add too much too quickly or it'll curdle.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;When you have a nice medium-thick-but-still-easily-pourable sauce, add the grated cheese and grind some more pepper into it.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Stir until the cheese is all melted, then pour over the top of your lasagne, making sure it's all covered&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Grate a bit more cheese on top&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;h4&gt;Final baking (45 mins)&lt;/h4&gt;&lt;ol&gt;&lt;br /&gt;  &lt;li&gt;Bake the lasagne in the oven at 180 degrees C for about 45 mins, or until the top is golden brown and ever so slightly crispy around the edges.&lt;/li&gt;&lt;br /&gt;  &lt;li&gt;Remove from the oven and leave to rest for about ten minutes before serving&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;And that's it! Quality of ingredients counts for a lot, but the biggest clincher is the care taken to make sure that each ingredient is individually well prepared and cooked before adding to the pot. Enjoy!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1384708022344101481?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1384708022344101481/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1384708022344101481' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1384708022344101481'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1384708022344101481'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/10/als-ultimate-lasagne-recipe.html' title='Al&apos;s Ultimate Lasagne Recipe'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-4052475235707126676</id><published>2009-10-03T10:03:00.003+01:00</published><updated>2009-10-03T11:33:17.273+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='atom'/><category scheme='http://www.blogger.com/atom/ns#' term='simple-rss'/><category scheme='http://www.blogger.com/atom/ns#' term='github'/><category scheme='http://www.blogger.com/atom/ns#' term='cragwag'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>Blogspot ATOM in Feed-normalizer / Simple-RSS</title><content type='html'>Both &lt;a href="http://cragwag.com"&gt;Cragwag&lt;/a&gt; and &lt;a href="http://sybilline.com"&gt;Sybilline&lt;/a&gt; are using the excellent &lt;a href="http://feed-normalizer.rubyforge.org/"&gt;Feed-normalizer&lt;/a&gt; for parsing RSS and ATOM feeds, but there's been a &lt;a href="http://code.google.com/p/feed-normalizer/issues/detail?id=30"&gt;niggling problem with the ATOM generated by Blogger / Blogspot in particular&lt;/a&gt; - the resulting links on each entry end up pointing to the comments, not the post itself.  &lt;br /&gt;&lt;br /&gt;So I just forked simple-rss at github and fixed this.&lt;br /&gt;&lt;br /&gt;Turns out that simple-rss is just taking the first link tag that it comes across and using that as the link for a post, which in the case of Blogspot ATOM is the comments link.&lt;br /&gt;&lt;br /&gt;On inspection of the &lt;a href="http://www.ietf.org/rfc/rfc4287.txt"&gt;ATOM RFC&lt;/a&gt; it says (section 4.2.7.2) :&lt;br /&gt;&lt;br /&gt;&lt;q&gt;atom:link elements MAY have a "rel" attribute that indicates the link relation type.  If the "rel" attribute is not present, the link element MUST be interpreted as if the link relation type is "alternate".&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;Looking at the Blogspot ATOM, it looks like every element has a link rel="alternative" that points to the URL you would see if you navigated to the post from the blog homepage, so I've made it choose that link if it exists.&lt;br /&gt;&lt;br /&gt;Github should build the gem automatically - but it's taking a long time to do it, so in the meantime, you can download it from &lt;a href="http://github.com/aldavidson/simple-rss"&gt;http://github.com/aldavidson/simple-rss&lt;/a&gt; and build it locally:&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;code&gt;gem uninstall simple-rss&lt;br /&gt;cd (source root)&lt;br /&gt;rake gem&lt;br /&gt;cd pkg&lt;br /&gt;gem install -l simple-rss&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;That should fix the problem&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-4052475235707126676?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/4052475235707126676/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=4052475235707126676' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4052475235707126676'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4052475235707126676'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/10/blogspot-atom-in-feed-normalizer-simple.html' title='Blogspot ATOM in Feed-normalizer / Simple-RSS'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-6139783032381003496</id><published>2009-09-08T19:44:00.004+01:00</published><updated>2009-11-07T18:18:06.487Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='scripts'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='amazon'/><category scheme='http://www.blogger.com/atom/ns#' term='ec2'/><category scheme='http://www.blogger.com/atom/ns#' term='hosting'/><category scheme='http://www.blogger.com/atom/ns#' term='ami'/><category scheme='http://www.blogger.com/atom/ns#' term='instance'/><title type='text'>How to create and save an AMI image from a running instance</title><content type='html'>One snag I encountered early on in my migration of &lt;a href="http://cragwag.com"&gt;Cragwag&lt;/a&gt; and &lt;a href="http://sybilline.com"&gt;Sybilline&lt;/a&gt; to Amazon's EC2 Cloud, was that I needed to take a snapshot of my running instance and save it as a new Amazon Machine Image (AMI).&lt;br /&gt;&lt;br /&gt;I'd created a bare-bones Debian image from a public AMI (32-bit Lenny, 5.0, not much else) and then installed a few standard software packages on it - mysql, ruby, apache, etc etc etc. Once I'd got them configured the way I wanted, it had taken a couple of hours (I'll go into the configuration relating to EBS in a separate post) so I wanted to snapshot this instance as a new AMI image. That way, if and when I needed to create a new instance, all of this work would already have been done.&lt;br /&gt;&lt;br /&gt;It actually took a fair amount of time to find out (well, more than a few seconds Googling, which is just &lt;em&gt;eternity&lt;/em&gt; these days, y'know?) so I'll save you the pain and just give you the solution.&lt;br /&gt;&lt;br /&gt;First, install Amazon's AMI tools, and API tools:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;br /&gt;export $EC2_TOOLS_DIR=~/.ec2 #(or choose a directory here)&lt;br /&gt;cd $EC2_TOOLS_DIR&lt;br /&gt;mkdir ec2-ami-tools&lt;br /&gt;cd ec2-ami-tools&lt;br /&gt;wget http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.zip&lt;br /&gt;unzip ec2-ami-tools.zip&lt;br /&gt;ln -s ec2-ami-tools-* current&lt;br /&gt;cd ..&lt;br /&gt;mkdir ec2-api-tools&lt;br /&gt;cd ec2-api-tools&lt;br /&gt;wget http://s3.amazonaws.com/ec2-downloads/ec2-api-tools.zip&lt;br /&gt;unzip ec2-api-tools.zip&lt;br /&gt;ln -s ec2-api-tools-* current&lt;br /&gt;&lt;br /&gt;echo "export EC2_AMITOOL_HOME=`dirname $EC2_TOOLS_DIR`/ec2-ami-tools/current" &gt;&gt; ~/.bashrc&lt;br /&gt;echo "export EC2_APITOOL_HOME=`dirname $EC2_TOOLS_DIR`/ec2-api-tools/current" &gt;&gt; ~/.bashrc&lt;br /&gt;echo "export PATH=${PATH}:`dirname $AMI_TOOLS_DIR`/ec2-ami-tools/current/bin:`dirname $AMI_TOOLS_DIR`/ec2-api-tools/current/bin" &gt;&gt; ~/.bashrc&lt;br /&gt;source ~/.bashrc&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Next, you'll need to get your security credentials. You can get a reminder of - or create as needed - these on the AWS "Your Account" &gt; "Security Credentials" page.&lt;br /&gt;&lt;br /&gt;I recommend you saving your X.509 certificate and your private key somewhere under /mnt/ - this directory is excluded from the bundled image. Quite important that, as otherwise your credentials would be bundled up in the image - and if you ever shared that image with anyone else, you'd be sharing your credentials too!&lt;br /&gt;&lt;br /&gt;You'll also need to note your AWS access details - especially your access key and secret key - plus your Amazon account ID. &lt;br /&gt;&lt;br /&gt;Now, we're at the main event.&lt;br /&gt;&lt;br /&gt;To take a snapshot of your running instance:&lt;br /&gt;&lt;br /&gt;First, choose a name for your AMI snapshot. We'll call it ami-instance-name :)&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="bash"&gt;&lt;br /&gt;# make a directory for your image:&lt;br /&gt;mkdir /mnt/ami-instance-name&lt;br /&gt;&lt;br /&gt;# create the image (this will take a while!)&lt;br /&gt;ec2-bundle-vol -d /mnt/ami-instance-name -k /path/to/your/pk-(long string).pem -c /path/to/your/cert-(long string).pem -u YOUR_AMAZON_ACCOUNT_ID_WITHOUT_DASHES&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Once that's done, you should have a file called image.manifest.xml in your /mnt/ami-instance-name directory, along with all the bundle parts. Sometimes it will say &lt;q&gt;Unable to read instance meta-data for product-codes&lt;/q&gt; - but this doesn't seem to cause any problems, and I've successfully ignored it so far :)&lt;br /&gt;&lt;br /&gt;Next, upload the AMI image to S3. This command will create an S3 bucket of the given name if it doesn't exist - I've found it convenient to call my buckets the same as the instance name: &lt;br /&gt;&lt;br /&gt;&lt;code class="bash"&gt;&lt;pre&gt;ec2-upload-bundle -b ami-instance-name -m /mnt/ami-instance-name/image.manifest.xml -a YOUR_AWS_ACCESS_KEY -s YOUR_AWS_SECRET_KEY&lt;/code&gt;&lt;/pre&gt; &lt;br /&gt;&lt;br /&gt;You should then be able to register the instance. I've done that using the rather spiffy AWS Management Console web UI, but you can also do it from the command line using:&lt;br /&gt;&lt;br /&gt;&lt;code class="bash"&gt;&lt;pre&gt;ec2-register ami-instance-name/image.manifest.xml&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;And that's it!&lt;br /&gt;&lt;br /&gt;Of course, you could be cunning and create a script that does it all in one. I've got my AWS/EC2 credentials stored in environment variables from my .bashrc:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="bash"&gt;export EC2_PRIVATE_KEY=/mnt/keys/pk-(long string).pem&lt;br /&gt;export EC2_CERT=/mnt/keys/cert-(long string).pem&lt;br /&gt;export AWS_ACCOUNT_ID=(my account id)&lt;br /&gt;export AWS_ACCESS_KEY=(my AWS access key)&lt;br /&gt;export AWS_SECRET_KEY=(my AWS secret key)&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;which means I can make, upload and register an instance in one, by running this script:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="bash"&gt;#!/bin/bash&lt;br /&gt;&lt;br /&gt;$AMI_NAME=$1&lt;br /&gt;&lt;br /&gt;ec2-bundle-vol -d /mnt/images/$1 -k $EC2_PRIVATE_KEY -c $EC2_CERT -u $AWS_ACCOUNT_ID&lt;br /&gt;ec2-upload-bundle -b $1 -m /mnt/images/$1/image.manifest.xml -a $AWS_ACCESS_KEY  -s $AWS_SECRET_KEY&lt;br /&gt;ec2-register $1/image.manifest.xml&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...and giving it a parameter of ami-instance-name. I have that script saved as make_ami.sh, so I can just call, for instance:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code class="bash"&gt;make_ami.sh webserver-with-sites-up-and-running&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...and go have a cup of coffee while it does it's thing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-6139783032381003496?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/6139783032381003496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=6139783032381003496' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6139783032381003496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6139783032381003496'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/09/how-to-create-and-save-ami-image-from.html' title='How to create and save an AMI image from a running instance'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-747725458057273569</id><published>2009-09-08T18:43:00.004+01:00</published><updated>2009-09-08T19:42:45.359+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='scaling'/><category scheme='http://www.blogger.com/atom/ns#' term='cloud'/><category scheme='http://www.blogger.com/atom/ns#' term='amazon'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><category scheme='http://www.blogger.com/atom/ns#' term='architecture'/><category scheme='http://www.blogger.com/atom/ns#' term='ec2'/><category scheme='http://www.blogger.com/atom/ns#' term='hosting'/><title type='text'>Moving a site to The Cloud</title><content type='html'>Last week I did a lot of reading and research into cloud hosting. The "Cloud" has been a buzzword for a while now, often bandied about by those who know no better as a simple sprinkle-on solution for all of your scale problems - much in the same way as AJAX was touted around a few years ago as a magic solution to all of your interface problems. &lt;br /&gt;&lt;br /&gt;The perception can sometimes seem to be &lt;q&gt;Hey, if we just shift X to The Cloud, we can scale it infinitely!&lt;/q&gt;. The reality, of course, is something rather more qualified. Yes, &lt;em&gt;in theory&lt;/em&gt;, the cloud has all the capacity you're likely to need, unless you're going to be bigger than, say, Amazon - &lt;em&gt;(are you? Are you reeeeeeeally? C'mon, be honest...)&lt;/em&gt;  - provided - and it's a big proviso - that you architect it correctly. You can't just take an existing application and dump it into the cloud and expect to never have a transaction deadlock again, for instance. That's an application usage pattern issue that needs to be dealt with in your application, and no amount of hardware, physical or virtual, will solve it.&lt;br /&gt;&lt;br /&gt;There are also some constraints that you'll need to work around, that may seem a little confusing at first. But once I got it, the light went on, and I became increasingly of the opinion that the cloud architecture is just sheer bloody genius.&lt;br /&gt;&lt;br /&gt;What kind of constraints are they? Well, let's focus on Amazon's EC2, as it's the most well-known....&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Your cloud-hosted servers are instances of an image&lt;/strong&gt;&lt;br/&gt;They're not physical machines - you can think of them as copies of a template Virtual Machine, if you like. Like a VMWare image. OK, that one's fairly straightforward. Got it? Good. Next:&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Instances are transient - they do not live for ever&lt;/strong&gt;&lt;br /&gt;Bit more of the same, this means that you create and destroy instances as you need. The flipside is that there is no &lt;em&gt;guarantee&lt;/em&gt; that the instance you created yesterday will still be there today. It should be, but it might not be. &lt;a href="http://getsatisfaction.com/cohesiveft/topics/why_do_my_google_app_engine_sdk_ec2_instances_keep_dying"&gt;EC2 instances do die&lt;/a&gt;, and when they do, they can't be brought back - you need to create a new one. This is by design. Honestly!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Anything you write to an instance's disk after creation is &lt;em&gt;non-persistent&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;Now we're getting down to it. This means that if you create an instance of, say, a bare-bones Linux install, and then install some more software onto it, and set up a website, then the instance dies - everything you've written to that instance's disk is &lt;strong&gt;GONE&lt;/strong&gt;. There are good strategies for dealing with this, which we'll come onto next, but this is &lt;em&gt;also&lt;/em&gt; by design. Yes, it is...&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;You can attach &lt;a href="http://aws.amazon.com/ebs"&gt;EBS&lt;/a&gt; persistent storage volumes to an instance - but only to &lt;em&gt;one instance per volume&lt;/em&gt;&lt;/strong&gt;&lt;br /&gt;This one is maybe the most obscure constraint but is quite significant. Take a common architecture of two load-balanced web servers with a separate database server. It's obvious that the database needs to be stored on a persistent EBS volume - but what if the site involves users uploading files? Where do they live? A common pattern would be to have a shared file storage area mounted onto both web servers - but if an EBS volume can only be attached to one instance, you can't do that.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Think about that for a few seconds - this has some pretty serious implications for the architecture of a cloud-hosted site. BUT - and here's the &lt;q&gt;sheer bloody genius&lt;/q&gt; - these are the kind of things you'd have to deal with for scaling out a site on physical servers &lt;em&gt;anyway&lt;/em&gt;. Physical hardware and especially disks are not infallible and shouldn't be relied on. Servers can and do go down. Disks conk out. Scaling out horizontally needs up-front thought put into the architecture. The cloud constraints simply force you to accept that, and deal with it by designing your applications with horizontal scaling in mind from the start. And, coincidentally, provide some kick-ass tools to help you do that.&lt;br /&gt;&lt;br /&gt;Take, for example, the last bullet point above - that EBS volumes can only be attached to one instance. So how do you have file storage shared between N load-balanced web servers? Well, the logical thing to do is to have a separate instance with a big persistent EBS volume attached to it, and have the web servers access it by some defined API - WebDAV, say, or something more application-specific.&lt;br /&gt;&lt;br /&gt;But hang on.... isn't that what you should be doing &lt;em&gt;anyway?&lt;/em&gt;. Isn't that a more scalable model? So that when your fileserver load becomes large, you could, say, create more instances to service your file requests, and maybe load-balance &lt;em&gt;those&lt;/em&gt;, and.... &lt;br /&gt;&lt;br /&gt;See? It forces you to &lt;em&gt;do the right thing&lt;/em&gt; - or, at least, put in the thought up front as to how you'll handle it. And if you then decide to stubbornly go ahead and do the &lt;em&gt;wrong&lt;/em&gt; thing, then that's up to you... :)&lt;br /&gt;&lt;br /&gt;So, anyway, I wanted to get my head round it, and thought I'd start by shifting &lt;a href="http://cragwag.com"&gt;Cragwag&lt;/a&gt; and &lt;a href="http://sybilline.com"&gt;Sybilline&lt;/a&gt; onto Amazons EC2 cloud hosting service. I did this over a two day period - most of which, it has to be said, was spent setting up Linux the way I was used to, rather than the cloud config - and I'll be blogging a few small, self-contained articles with handy tips I've learned along the way. Stay tuned....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-747725458057273569?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/747725458057273569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=747725458057273569' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/747725458057273569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/747725458057273569'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/09/moving-site-to-cloud.html' title='Moving a site to The Cloud'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-7417303017685758640</id><published>2009-08-17T22:58:00.015+01:00</published><updated>2009-08-17T23:42:24.294+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='styling'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><category scheme='http://www.blogger.com/atom/ns#' term='auto_complete'/><title type='text'>Styling the Rails auto_complete plugin</title><content type='html'>Over the weekend I was having a fiddle around with the layout on &lt;a href="http://cragwag.com"&gt;Cragwag&lt;/a&gt;, as the existing design was a bit, shall we say, &lt;q&gt;emergent&lt;/q&gt;. I'd being trying to avoid a LHS column, because everything always seems to end up with one, but in the end I had to give up and just go with it. The auto-tag cloud just makes more sense over there, and there's no point fighting it.&lt;br /&gt;&lt;br /&gt;Anyway, the next question was what else to put there? I was looking for an intermediate stop-gap search until I got round to putting a proper &lt;a href="http://lucene.apache.org"&gt;Lucene&lt;/a&gt;-based search in there, and so the thought struck - I'd been looking for a way to browse tags that weren't necessarily in the top 20 (e.g. &lt;q&gt;show me all the posts about &lt;a href="http://davemcleod.blogspot.com"&gt;Dave McLeod's&lt;/a&gt; 2006 &lt;a href="http://cragwag.com/posts?term=E11"&gt;E11&lt;/a&gt; route), so why not try an auto-suggest tag search? &lt;br /&gt;&lt;br /&gt;So I found DHH's &lt;a href="http://github.com/rails/auto_complete/tree/master"&gt;auto_complete plugin&lt;/a&gt; (it used to be part of Rails core until version 2) and got to work. This should be easy, right?&lt;br /&gt;&lt;br /&gt;Well...&lt;br /&gt;&lt;br /&gt;I'll cut out the frustrations and skip to the solutions. :)&lt;br /&gt;&lt;br /&gt;The documentation on this plugin is virtually non-existent, and there some extra bits you'll need - but I found &lt;a href="http://codeintensity.blogspot.com/2008/02/auto-complete-text-fields-in-rails-2.html"&gt;this post&lt;/a&gt; very helpful.&lt;br /&gt;&lt;br /&gt;One minor irritation I found was that it writes out a bunch of css attributes directly into your HTML inside a style tag - &lt;em&gt;including&lt;/em&gt; a &lt;a href="http://github.com/rails/auto_complete/blob/7b18f05af68dec0f9e45b1a01d6b7c94e306caeb/lib/auto_complete_macros_helper.rb"&gt;hard-coded absolute width of 350px.&lt;/a&gt; (see the auto_complete_stylesheet method)&lt;br /&gt;&lt;br /&gt;&lt;q&gt;Argh!&lt;/q&gt; said I, as my search needed to fit into a 150px width.&lt;br /&gt;&lt;br /&gt;So how can you get round this? Simple - you &lt;em&gt;can&lt;/em&gt; override the inline CSS in your stylesheet, provided you provide a CSS selector with &lt;em&gt;higher specificity&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Now, &lt;a href="http://www.smashingmagazine.com/2007/07/27/css-specificity-things-you-should-know/"&gt;CSS specificity can be a fairly complicated topic&lt;/a&gt;, but I usually just remember it like this - if you've got two rules that apply to a particular thing, the &lt;em&gt;more specific&lt;/em&gt; rule wins.&lt;br /&gt;&lt;br /&gt;In this case, the inline CSS selector from the plugin:&lt;br /&gt;&lt;pre&gt;&lt;code&gt; div.auto_complete {&lt;br /&gt;  width: 350px;&lt;br /&gt;  background: #fff;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;gets trumped by Cragwag's more specific selector:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;div#content div#lh_sidebar div.auto_complete {&lt;br /&gt;  width: 150px;&lt;br /&gt;  background: #fff;&lt;br /&gt;}&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The first applies to any div with class="auto_complete", but the second applies only to divs with class="auto_complete" &lt;em&gt;which are inside a div with id="lh_sidebar" which is inside a div with id="content"&lt;/em&gt;. So that's a &lt;em&gt;more specific&lt;/em&gt; rule, so it wins.&lt;br /&gt;&lt;br /&gt;Yay!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-7417303017685758640?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/7417303017685758640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=7417303017685758640' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7417303017685758640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7417303017685758640'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/08/styling-rails-autocomplete-plugin.html' title='Styling the Rails auto_complete plugin'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-670502300541950025</id><published>2009-08-12T10:53:00.004+01:00</published><updated>2009-08-12T12:26:36.487+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='work'/><category scheme='http://www.blogger.com/atom/ns#' term='activerecord'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='jdbc'/><title type='text'>OutOfMemoryError in ActiveRecord-JDBC on INSERT SELECT</title><content type='html'>During some scale testing the other day, we came across this unusual / mildly amusing error in a database-bound command that just funnels INSERT SELECT statements down the ActiveRecord JDBC driver:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;java/util/Arrays.java:2734:in `copyOf': java.lang.OutOfMemoryError: Java heap space (NativeException)&lt;br /&gt; from java/util/ArrayList.java:167:in `ensureCapacity'&lt;br /&gt; from java/util/ArrayList.java:351:in `add'&lt;br /&gt; from com/mysql/jdbc/StatementImpl.java:1863:in `getGeneratedKeysInternal'&lt;br /&gt; from com/mysql/jdbc/StatementImpl.java:1818:in `getGeneratedKeys'&lt;br /&gt; from org/apache/commons/dbcp/DelegatingStatement.java:318:in `getGeneratedKeys'&lt;br /&gt; from jdbc_adapter/JdbcAdapterInternalService.java:668:in `call'&lt;br /&gt; from jdbc_adapter/JdbcAdapterInternalService.java:241:in `withConnectionAndRetry'&lt;br /&gt; from jdbc_adapter/JdbcAdapterInternalService.java:662:in `execute_insert'&lt;br /&gt;  ... 25 levels...&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, there's a couple of things here that are worth pointing out.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I &lt;em&gt;REALLY LOVE&lt;/em&gt; the fact that it blew heap space in a method called &lt;q&gt;ensureCapacity&lt;/q&gt;. That makes me smile.&lt;/li&gt;&lt;li&gt;Why is it calling getGeneratedKeys() for an INSERT SELECT?&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;The getGeneratedKeys() method retrieves all the primary keys that are generated when you execute an INSERT statement. Fair enough - BUT the issue here is that we'd specifically structured the process and the SQL statements involved so as to be done with INSERT SELECTS, and hence &lt;em&gt;avoid&lt;/em&gt; great chunks of data being transferred backwards and forwards between the app and the database.&lt;br /&gt;&lt;br /&gt;It turns out that the ActiveRecord JDBC adapter is doing this : &lt;br /&gt;(lib/active_record/connection_adapters/JdbcAdapterInternalService.java)&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;@JRubyMethod(name = "execute_insert", required = 1)&lt;br /&gt;    public static IRubyObject execute_insert(final IRubyObject recv, final IRubyObject sql) throws SQLException {&lt;br /&gt;        return withConnectionAndRetry(recv, new SQLBlock() {&lt;br /&gt;            public IRubyObject call(Connection c) throws SQLException {&lt;br /&gt;                Statement stmt = null;&lt;br /&gt;                try {&lt;br /&gt;                    stmt = c.createStatement();&lt;br /&gt;                    smt.executeUpdate(rubyApi.convertToRubyString(sql).getUnicodeValue(), Statement.RETURN_GENERATED_KEYS);&lt;br /&gt;                    return unmarshal_id_result(recv.getRuntime(), stmt.getGeneratedKeys());&lt;br /&gt;                } finally {&lt;br /&gt;                    if (null != stmt) {&lt;br /&gt;                        try {&lt;br /&gt;                            stmt.close();&lt;br /&gt;                        } catch (Exception e) {&lt;br /&gt;                        }&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        });&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;...in other words, explicitly telling the driver to return all the generated keys.&lt;br /&gt;Hmm, OK, can we get round this by NOT calling the execute_insert method, and instead calling a raw execute method that &lt;em&gt;doesn't&lt;/em&gt; return all the keys?&lt;br /&gt;&lt;br /&gt;Well, no, unfortunately, because it also turns out that the ruby code is doing this:&lt;br /&gt;(activerecord-jdbc-adapter-0.9/lib/active_record/connection_adapters/jdbc_adapter.rb)&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;      # we need to do it this way, to allow Rails stupid tests to always work&lt;br /&gt;      # even if we define a new execute method. Instead of mixing in a new&lt;br /&gt;      # execute, an _execute should be mixed in.&lt;br /&gt;      def _execute(sql, name = nil)&lt;br /&gt;        if JdbcConnection::select?(sql)&lt;br /&gt;          @connection.execute_query(sql)&lt;br /&gt;        elsif JdbcConnection::insert?(sql)&lt;br /&gt;          @connection.execute_insert(sql)&lt;br /&gt;        else&lt;br /&gt;          @connection.execute_update(sql)&lt;br /&gt;        end&lt;br /&gt;      end&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...and the JdbcConnection::insert? method is detecting if something's an insert by doing this:&lt;br /&gt;(JdbcAdapterInternalService.java again)&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;@JRubyMethod(name = "insert?", required = 1, meta = true)&lt;br /&gt;    public static IRubyObject insert_p(IRubyObject recv, IRubyObject _sql) {&lt;br /&gt;        ByteList bl = rubyApi.convertToRubyString(_sql).getByteList();&lt;br /&gt;&lt;br /&gt;        int p = bl.begin;&lt;br /&gt;        int pend = p + bl.realSize;&lt;br /&gt;&lt;br /&gt;        p = whitespace(p, pend, bl);&lt;br /&gt;&lt;br /&gt;        if(pend - p &gt;= 6) {&lt;br /&gt;            switch(bl.bytes[p++]) {&lt;br /&gt;            case 'i':&lt;br /&gt;            case 'I':&lt;br /&gt;                switch(bl.bytes[p++]) {&lt;br /&gt;                case 'n':&lt;br /&gt;                case 'N':&lt;br /&gt;                    switch(bl.bytes[p++]) {&lt;br /&gt;                    case 's':&lt;br /&gt;                    case 'S':&lt;br /&gt;                        switch(bl.bytes[p++]) {&lt;br /&gt;                        case 'e':&lt;br /&gt;                        case 'E':&lt;br /&gt;                            switch(bl.bytes[p++]) {&lt;br /&gt;                            case 'r':&lt;br /&gt;                            case 'R':&lt;br /&gt;                                switch(bl.bytes[p++]) {&lt;br /&gt;                                case 't':&lt;br /&gt;                                case 'T':&lt;br /&gt;                                    return recv.getRuntime().getTrue();&lt;br /&gt;                                }&lt;br /&gt;                            }&lt;br /&gt;                        }&lt;br /&gt;                    }&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return recv.getRuntime().getFalse();&lt;br /&gt;    }&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;...in other words, if the sql contains the word &lt;q&gt;INSERT&lt;/q&gt;, then it's an INSERT, and should be executed with an execute_insert call.&lt;br /&gt;&lt;br /&gt;So, looks like we're a bit knacked here. There are two possible solutions:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The &lt;q&gt;proper&lt;/q&gt; solution - fix the AR JDBC adapter (and, arguably, the MySQL connector/J as well, to stop it blowing heap space), submit a patch, wait for it to be accepted and make it into the next release.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Or, the &lt;q&gt;pragmatic&lt;/q&gt; solution - rewrite the SQL-heavy command as a stored procedure and just call it with an EXECUTE and sod the &lt;a href="http://www.loudthinking.com/arc/2005_09.html"&gt;DHH dogma&lt;/a&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;We went with option 2 :)&lt;br /&gt;&lt;br /&gt;Big kudos to &lt;a href="http://www.drmaciver.com/"&gt;D. R. MacIver&lt;/a&gt; for tracking down the source of the fail in double-quick time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-670502300541950025?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/670502300541950025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=670502300541950025' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/670502300541950025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/670502300541950025'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/08/outofmemoryerror-in-activerecord-jdbc.html' title='OutOfMemoryError in ActiveRecord-JDBC on INSERT SELECT'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-4081647300851864037</id><published>2009-08-03T10:24:00.006+01:00</published><updated>2009-08-03T12:13:28.202+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='projects'/><category scheme='http://www.blogger.com/atom/ns#' term='vector'/><category scheme='http://www.blogger.com/atom/ns#' term='tagging'/><category scheme='http://www.blogger.com/atom/ns#' term='nlp'/><category scheme='http://www.blogger.com/atom/ns#' term='news'/><category scheme='http://www.blogger.com/atom/ns#' term='cragwag'/><title type='text'>Introducing Cragwag.com!</title><content type='html'>You know those conversations you end up having in the pub, where after a couple of beers you end up saying "you know what there should be? There should be a site that does X" (where X can be anything at all)&lt;br /&gt;&lt;br /&gt;I've had so many of those over the years, and never quite managed to work up the free time / motivation to actually get on and put the ideas into practice..... (and then what's tended to happen is that a couple of years later, someone else goes and does them and makes a fortune, but that's just sods law) &lt;br /&gt;&lt;br /&gt;Well a few months ago, I decided enough was enough, and the next time I had one of those ideas, I should just stop talking about it and actually &lt;em&gt;do&lt;/em&gt; it - so in my evening and weekends here and there, I've been noodling away on a couple of ideas, mainly just for my own amusement, and to keep me in the habit of actually following through on things.&lt;br /&gt;&lt;br /&gt;So here's one of them - &lt;a href="http://cragwag.com"&gt;Cragwag&lt;/a&gt; - a climbing news aggregator.&lt;br /&gt;&lt;br /&gt;Those of you who know me in person know that I'm a keen amateur climber. I'm under no illusions - I'm &lt;em&gt;definitely&lt;/em&gt; in the "amateur" category for good reason :) - but it struck me that although there's a "definitive" go-to site for uk-centric climbing news ( &lt;a href="http://ukclimbing.com"&gt;UK Climbing.com&lt;/a&gt; ) it's still editorially filtered - an editor keeps himself up to date on everything that's going on in the scene, and then publishes what he thinks is significant.&lt;br /&gt;&lt;br /&gt;That's all well and good, but typically what's significant is what's going on at the forefront of ability. I felt there was also a place for the (admittedly by-now-a-little-cliched) long tail of climbing-related blogs - unfiltered, un-edited, everybody's tales. Whether of heart-stopping epics in the Himalaya, or scrabbling up a Stanage slab. If you felt it enough to blog about it, then someone wants to hear about it.&lt;br /&gt;&lt;br /&gt;Plus it was an experiment in automatic news tagging and cross-relating of posts based on content, so it was kind of techie fun too. Which is important :) I'd like to do something with a crowd-sourced google map and iphone gps too, but that's much more experimental. Need to learn the iPhone SDK first...&lt;br /&gt; &lt;br /&gt;So that's &lt;a href="http://cragwag.com"&gt;Cragwag - all the climbing news from punters and pros alike&lt;/a&gt;. Just for fun, for the sake of an experiment, and - to paraphprase the most famous answer to "why?" of all time - because it wasn't there :) Yay!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-4081647300851864037?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://cragwag.com' title='Introducing Cragwag.com!'/><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/4081647300851864037/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=4081647300851864037' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4081647300851864037'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4081647300851864037'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/08/introducing-cragwagcom.html' title='Introducing Cragwag.com!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-5437725704207527374</id><published>2009-07-29T09:42:00.003+01:00</published><updated>2009-07-29T10:06:37.206+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='crowdfunding'/><category scheme='http://www.blogger.com/atom/ns#' term='trampoline'/><category scheme='http://www.blogger.com/atom/ns#' term='investment'/><category scheme='http://www.blogger.com/atom/ns#' term='work'/><category scheme='http://www.blogger.com/atom/ns#' term='vc'/><title type='text'>VC 2.0 - Crowdfunding is go!</title><content type='html'>The Series B VC market sucks right now. The boom times of the pre-credit-crunch 2.0 world seem very far away. Although there's still enthusiasm to invest in early-stage companies at Angel or Series A rounds (small investment, large slice of equity, potential for large return) and Series C/D+ (where the company is commercialised and probably generating revenue or damn close to it, and therefore the risk is much lower) there's been a huge withdrawal of funds and appetite for Series B investments. &lt;br /&gt;&lt;br /&gt;Which is were we are right now. &lt;br /&gt;&lt;br /&gt;So in typical Trampo style, we've decided not to go down that route... and today we launched &lt;a href="http://crowdfunding.trampolinesystems.com"&gt;Trampoline's Crowdfunding process&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The idea is, simply put, that rather than persuading a few people to part with a large sum of cash, to go the Obama way and offer tranches of shares for much smaller amounts to many more people. This also opens up great possibilities for utilising the power of the extended network of many investors, all the way from getting a large network of potential contacts (it's almost like hiring a hundred part-time salesmen) to exploiting the wisdom of crowdfunders on key strategic decisions.&lt;br /&gt;&lt;br /&gt;Of course, nothing's ever quite as simple as it sounds, and there are many FSA regulations about this kind of thing designed to protect Mom and Pop investors from the plethora of possible boiler room-type scams that could be represented in this light, such as:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;We can't actively solicit investment in any public setting.&lt;br /&gt;So this post is &lt;em&gt;FYI only&lt;/em&gt;, ok? Ok...&lt;/li&gt;&lt;br /&gt;&lt;li&gt;We can't discuss any details with anyone, unless they're&lt;ol&gt;&lt;li&gt;A certified &lt;q&gt;Sophisticated Investor&lt;/q&gt;, or&lt;/li&gt;&lt;li&gt;A certified &lt;q&gt;High Net Worth Individual&lt;/q&gt;, or&lt;/li&gt;&lt;li&gt;A friend or family member (yes, apparently that's ok!)&lt;/li&gt;&lt;/ol&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;So this is emphatically &lt;strong&gt;NOT&lt;/strong&gt; an exchange-less IPO! 'Cause those are illegal! It's more of a self-build consortium, kind of thing&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;Anyway, bearing in mind the FSA regulations, that's about as much as I can say, apart from point you at &lt;a href="http://www.ft.com/cms/s/0/c037ae5c-7b92-11de-9772-00144feabdc0.html"&gt;Trampoline's Crowdfunding article in todays Financial Times&lt;/a&gt; (also on page 12 of the print edition), and the &lt;a href="http://crowdfunding.trampolinesystems.com"&gt;Crowdfunding Website&lt;/a&gt; if you want more information&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-5437725704207527374?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://crowdfunding.trampolinesystems.com' title='VC 2.0 - Crowdfunding is go!'/><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/5437725704207527374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=5437725704207527374' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/5437725704207527374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/5437725704207527374'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/07/vc-20-crowdfunding-is-go.html' title='VC 2.0 - Crowdfunding is go!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1656878452067323045</id><published>2009-05-22T10:57:00.003+01:00</published><updated>2009-05-22T11:30:02.478+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='databases'/><category scheme='http://www.blogger.com/atom/ns#' term='transactions'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='fail'/><category scheme='http://www.blogger.com/atom/ns#' term='activerecord'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>This should not be possible - transactional fail?</title><content type='html'>We're having a bizarre problem with MySQL 5.0.32 on Debian in a highly-concurrent environment, using the ActiveRecord JDBC adapter. We've also seen it on 5.0.51 on Ubuntu.&lt;br /&gt;&lt;br /&gt;There's a particular sequence of 3 queries, all executed from within a ActiveRecord::Base.transaction { } block:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// Statement 1:&lt;br /&gt;  INSERT INTO (InnoDB table 1)&lt;br /&gt;         (type, ...loads of other fields...)&lt;br /&gt;  SELECT 'ProtoMessageItem', ...other fields...&lt;br /&gt;  FROM   (MyISAM table 1) LEFT OUTER JOIN (InnoDB table 2)&lt;br /&gt;  WHERE  (anti-duplication clause)&lt;br /&gt;  GROUP BY (synthetic identifier on MyISAM table 1, to be a unique key on InnoDB table 1)&lt;br /&gt; &lt;br /&gt;// Statement 2:&lt;br /&gt;  INSERT INTO (InnoDB table 2)&lt;br /&gt;  SELECT (fields)&lt;br /&gt;  FROM   (MyISAM table 1) LEFT OUTER JOIN (InnoDB table 3) INNER JOIN (InnoDB table 1)&lt;br /&gt;  WHERE  (anti-duplication clause)&lt;br /&gt;&lt;br /&gt;// Statement 3:&lt;br /&gt;  UPDATE  (InnoDB table 1)&lt;br /&gt;  SET     type = 'MessageItem' &lt;br /&gt;  WHERE   type = 'ProtoMessageItem'&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now, all of this takes place within an explicit transaction, and works well - this approach provides huge performance gains over the more simple and obvious methods.&lt;br /&gt;&lt;br /&gt;The problem is that just very very occasionally, it breaks. We don't know how or why, but once in a blue moon, statement 2 throws a DUPLICATE KEY exception, and the transaction &lt;em&gt;DOESN'T&lt;/em&gt; get rolled back. &lt;strong&gt;Statement 1 still gets COMMITTED&lt;/strong&gt;. We can see ProtoMessageItems in the DB, and this blocks up the pipeline and stops further MessageItems from being created.&lt;br /&gt;&lt;br /&gt;ProtoMessageItems should &lt;em&gt;NEVER&lt;/em&gt; be visible - they're only ever created and then updated to full MessageItems in the same 3-statement transaction. Seeing them from outside that transaction is like seeing a &lt;a href="http://en.wikipedia.org/wiki/Naked_singularity"&gt;naked singularity&lt;/a&gt; - it just shouldn't be possible.&lt;br /&gt;&lt;br /&gt;This is all kinds of wrong in a very wrong way, as it means one of the following - either:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;The DB is getting the correct BEGIN/COMMIT/ROLLBACK statements, but transactional atomicity is not holding for some reason&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The JDBC driver is not sending the correct ROLLBACK statements, but thinks it's in autocommit mode&lt;/li&gt;&lt;br /&gt;&lt;li&gt;There's an obscure concurrency edge case somewhere with the ActiveRecord JDBC adapter, that means once in a while, for some reason, it starts that transaction in autocommit mode.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;All of these possibilities are worrying. We've eliminated the possibility of it being some quirk of the particular data that causes this problem. We've turned on MySQL statement logging and watched it for hours, and as far as we can see, the correct ROLLBACK statements seem to be issued every time. &lt;br /&gt;&lt;br /&gt;Has anyone encountered a similar problem? Any known issues with INSERT/SELECT on combinations of (transactional) InnoDB and (non-transactional) MyISAM tables?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1656878452067323045?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1656878452067323045/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1656878452067323045' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1656878452067323045'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1656878452067323045'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/05/this-should-not-be-possible.html' title='This should not be possible - transactional fail?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3510703247000378968</id><published>2009-05-19T16:53:00.004+01:00</published><updated>2009-05-20T15:03:20.870+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='humour'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='gruntfuddlr'/><title type='text'>Suggestions for GruntFuddlr !</title><content type='html'>Following on from the previous post on &lt;a href="http://instantbadger.blogspot.com/2009/05/twitter-will-soon-die-second-tipping.html"&gt;The imminent death of Twitter&lt;/a&gt;, after several comments along the lines of "Oh Al, you have SOOOO got to buy that domain" followed by a brief moment of hastiness with the credit card, I am now the proud owner of GruntFuddlr.com !&lt;br /&gt;&lt;br /&gt;So all I need to do now is decide what to &lt;em&gt;do&lt;/em&gt; with it.... add your suggestions to the comments! The winner will win - erm - something to be decided!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3510703247000378968?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3510703247000378968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3510703247000378968' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3510703247000378968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3510703247000378968'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/05/suggestions-for-gruntfuddlr.html' title='Suggestions for GruntFuddlr !'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-2953682012996859129</id><published>2009-05-15T17:02:00.007+01:00</published><updated>2009-05-18T16:37:09.832+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='facebook'/><category scheme='http://www.blogger.com/atom/ns#' term='evolution'/><category scheme='http://www.blogger.com/atom/ns#' term='twitter'/><category scheme='http://www.blogger.com/atom/ns#' term='tipping point'/><category scheme='http://www.blogger.com/atom/ns#' term='business'/><title type='text'>Twitter Will Soon Die - the second Tipping Point approaches</title><content type='html'>Maybe I'm sticking my neck out, but it's inevitable - hear me out here. By now you're surely familiar with the concept of &lt;a href="http://en.wikipedia.org/wiki/The_Tipping_Point_(book)"&gt;The Tipping Point&lt;/a&gt; as espoused by Malcolm Gladwell - &lt;br /&gt;&lt;q&gt;the moment of critical mass, the threshold, the boiling point&lt;/q&gt;&lt;br /&gt;&lt;q&gt;the point at which momentum for change becomes unstoppable&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;This has been adopted as a central tenet of the Web 2.0+ business model, and is surely now almost as ubiquitous on a web startup's business plan as a long tail reference or hockey stick graph. This post is about the Tipping Point concept specifically as applied to social networking services.&lt;br /&gt;&lt;br /&gt;It's certainly true in my experience that every successful social software service goes through some standard stages of growth:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Early adoption&lt;/strong&gt;&lt;br/&gt;The service gets adopted by a set of keenly curious early users. This is often through an initial restricted beta program, or through the conference circuit, or friends and families of those involved in the development. Many never get past this stage - maybe it's just not a particularly good idea, or useful service, or well-executed, or maybe it's just unlucky. Assuming the service &lt;em&gt;does&lt;/em&gt; get past this stage, they move on to...&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Viral spread&lt;/strong&gt;&lt;br/&gt;The early adopters have judged and found it worthy. Here's where they get to gain geek cool points by name-dropping it amongst their friends, on the conference circuit, in Friday-night bars, etc... often taking advantage of the opportunity to faintly patronise those who are still using an inferior / more mass-market / older service - I mean, how many times have you heard lines like these?&lt;br /&gt;&lt;br /&gt;&lt;q&gt;....Oh Digg &lt;em&gt;totally&lt;/em&gt; jumped the shark, I moved off there to Reddit and now I've started using MangWurdlr &lt;em&gt;(or whatever)&lt;/em&gt;&lt;/q&gt;&lt;br /&gt;&lt;q&gt;...Yeah I use GruntFuddlr for &lt;em&gt;all&lt;/em&gt; my &lt;em&gt;(whatever)&lt;/em&gt;-ing now, it's nice, they've got a really cool UI, and ....etc...etc...&lt;/q&gt;&lt;br /&gt;&lt;q&gt;...Oh, you should have a look at PigNudgr - they're still in beta, but it looks really cool, and they're a great bunch of guys - I used to work with Andy at &lt;em&gt;(name of now-defunct late-nineties web agency, usually some combination of colour and animal)&lt;/em&gt; and he's doing some really neat stuff now...&lt;/q&gt;&lt;br /&gt;etc etc.&lt;br /&gt;&lt;br /&gt;Every time I have one of those conversations I'm reminded of when I was in the sixth form &lt;em&gt;(that's school from 16-18, when you've passed your first exams - sorry I don't know what the US equivalent is)&lt;/em&gt; and people would compete for alpha-cool status by getting into ever-more obscure bands on ever-smaller labels. &lt;br /&gt;&lt;br /&gt;Remember those? Bands like "Anna", "Spontaneous Cattle Combustion", "Generic me-too grunge band #241" - when they appeared in NME for the first time they were the coolest thing since sliced bread, and if you didn't have a copy of their debut EP you were &lt;em&gt;nobody&lt;/em&gt;, maaannn - ? It's the same phenomenon. Web startups / bands - same thing, just slightly older protagonists. Same silly haircuts though. &lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;The first Tipping Point&lt;/strong&gt;&lt;br/&gt;&lt;br /&gt;This is the Holy Grail for earlier-stage web services. This is where you succeed in creating enough word-of-mouth buzz that suddenly, you're unstoppable. You tip over into the mainstream. People's &lt;em&gt;parents&lt;/em&gt; have heard of you (well, those that read the more techno-savvy broadsheets anyway) A whole new wave of joiners is poised to come on-board, often purely because of who is already on there. Prime example - Stephen Fry on Twitter. &lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Inflation&lt;/strong&gt;&lt;br/&gt;&lt;br /&gt;&lt;em&gt;(apologies to &lt;a href="http://en.wikipedia.org/wiki/Alan_Guth"&gt;Alan Guth&lt;/a&gt;!)&lt;/em&gt;&lt;br /&gt;This is where the whole thing goes crazy. Faster growth than they ever thought possible. All those times the engineers said "Well, that's a high-class problem to have, and we'll just have to deal with that when we get there.." well - they're there right now. Seems like every week there's another digit on the hit counts. Often characterised by regular outages due to scale fail in completely unexpected ways ("Holy crap, we've run out of &lt;em&gt;inodes???&lt;/em&gt; That's &lt;em&gt;possible?&lt;/em&gt;"). They're sticking in new servers by the dozen all the time, burning through all that VC funding and not making any money yet..&lt;br /&gt;&lt;br /&gt;Meanwhile the user base seems to take on a life of its own. It's no longer a community, it instead takes on the characteristics of a population - splits, fragments, in-fighting, single-issue activism - often &lt;em&gt;against&lt;/em&gt; the decisions of the founders, who in their increasingly urgent need to get revenue to support the spiralling infrastructure costs are taking commercially-led decisions and seen as increasingly out-of-touch with the still-trying-to-be-faithful users.&lt;br /&gt;&lt;br /&gt;There's conflicting senses of excitement and disappointment, that it's just not the same as it used to be. And who are all these ****wits joining now anyway? What - my &lt;em&gt;MUM&lt;/em&gt; just joined? And she wants to be my friend? Oh crap, better get rid of all of those tales of drunken debauchery and undeniable photographic evidence. Your boss probably joins up, adding further fuel to the conflict of identity. Is this personal? A work tool? Both?&lt;/li&gt;&lt;br /&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;The second Tipping Point&lt;/strong&gt;&lt;br/&gt;&lt;br /&gt;All good things must come to an end, and this is the end of the line for the early adopters. It's not new, edgy or cool anymore, now that it's talked about on Question Time and even the Government are starting to use it in some god-awful, half-assed but completely cringe-making attempt to be Down With Tha Kids. Meanwhile, you keep getting friend requests from people at school who you weren't friends with in the first place and have no intention of pretending to be friends with now.&lt;br /&gt;&lt;br /&gt;The whole world and his hamster seems to be trying to spam you about it or on it. The number of z-list celebrities on it just keeps growing - Peter Andre? Really? Christ... - and they don't even post themselves, it's done via their PR person, or if they do they're just pushing their warez (yes, &lt;A href="http://twitter.com/WilliamShatner"&gt;@WilliamShatner&lt;/a&gt;, I'm looking at you). &lt;br /&gt;&lt;br /&gt;&lt;em&gt;(sigh)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;You've had enough. You can barely be bothered to check it any more, and all the third-party apps built on the API are just getting on your nerves, and all the cool kids have moved on to DangDurdlr.com anyway... apparently it's really cool, and apparently Dave from AvocadoAardvaark.com got together with Si from PurplePlatypus to start it up, and they've got a really neat UI, and it &lt;em&gt;doesn't have all of these ****wits on it&lt;/em&gt;...&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;So that's the Second Tipping Point. When all of the people who joined before the first Tipping Point leave, partly due to their constant need for something new, partly because services just naturally evolve, but mostly because of all the people who joined just &lt;em&gt;after&lt;/em&gt; the first. Who, in their turn, had mainly joined because of all the people who were already on it...&lt;br /&gt;&lt;br /&gt;Facebook got there a few months ago. Twitter is rapidly approaching it, and you know what? It's natural. It's part of the inevitable evolution of ideas and the services built upon them. It's not that Twitter has anything fundamentally wrong with it, it's mainly that the problem with software that puts people in touch with people..... is people. The cliques and cooler-than-thouism that ruled school society never went away, they just changed form and went online.&lt;br /&gt;&lt;br /&gt;MySpace took over from first-gen social software like SmartGroups, and while its user numbers may technically still be growing, in new-frontier terms, it's now dying. It was supplanted by Facebook, which has become supplanted by Twitter, which will soon be supplanted by some other new kid on the block. No-one knows what it will be yet, but that's part of the whole fun of this business - natural selection in the full glare of the web, with all the competition, cross-breeding, malformed offspring and occasional apparently-arbitrary successes. I love it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-2953682012996859129?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/2953682012996859129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=2953682012996859129' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2953682012996859129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2953682012996859129'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/05/twitter-will-soon-die-second-tipping.html' title='Twitter Will Soon Die - the second Tipping Point approaches'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-701749064154847828</id><published>2009-04-07T15:15:00.005+01:00</published><updated>2009-04-09T01:01:56.856+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='footie'/><category scheme='http://www.blogger.com/atom/ns#' term='sonar'/><category scheme='http://www.blogger.com/atom/ns#' term='busy'/><category scheme='http://www.blogger.com/atom/ns#' term='work'/><category scheme='http://www.blogger.com/atom/ns#' term='cragwag'/><title type='text'>Another day, another deployment</title><content type='html'>Posts have been increasingly thin on the ground recently, due to a couple of things: my discovery of a reasonable lightweight API client for Twitter, but mainly lack of time - which itself is down to two things:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I'm organising my &lt;a href="http://lisaandalswedding.blogspot.com"&gt;wedding&lt;/a&gt; to the ludicrously lovely Lise, and&lt;/li&gt;&lt;li&gt;we've been getting SONAR installed and running at two large corporates recently (who shall remain nameless)&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;It's a good feeling to have seen a product go right through from initial idea on a post-it note to being installed at a super-huge financial client, and it makes all the work worthwhile when you see people actually using it to do their job. I wish I could talk more about the details, but confidentiality dictates not, sadly.&lt;br /&gt;&lt;br /&gt;I've also been meaning to blog about the app I whipped up to manage the football team I organise - being a geek at heart, it's amazing the amount of time and effort I devote to being effectively lazy, and this little Rails app which started out as a quick time-saver ended up being a case study in the power of stupidity as applied to a linear optimisation problem - "given this set of players, each of whom have a set of positions they prefer to play in, what is the best possible line-up ?" I'll get round to blogging about that at some point, honest..&lt;br /&gt;&lt;br /&gt;Another little personal project that I started as a tool for myself and then ended up generalising and meaning to add to whenever I get time &lt;em&gt;(chortle)&lt;/em&gt; is &lt;a href="http://cragwag.com"&gt;Cragwag&lt;/a&gt; - an RSS aggregator, plus some - aggregating many various climbing-related blogs, an only-just-started map of climbing areas plus B&amp;B's plus parking plus convenient deli's, which I may end up mashing up with weather reports to answer questions like "where should we go climbing this weekend?"&lt;br /&gt;&lt;br /&gt;Bleh. So that's why I've ended up micro-blogging rather than macro-blogging these days. I will try to get used to writing in more than 140 characters again soon...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-701749064154847828?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/701749064154847828/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=701749064154847828' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/701749064154847828'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/701749064154847828'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/04/another-day-another-deployment.html' title='Another day, another deployment'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1895543455969417212</id><published>2009-03-24T10:36:00.001Z</published><updated>2009-03-24T10:36:06.836Z</updated><title type='text'>And a jolly good time was had by all</title><content type='html'>&lt;div style="float: right; margin-left: 10px; margin-bottom: 10px;"&gt;&lt;a href="http://www.flickr.com/photos/drsnooks/3382080064/" title="photo sharing"&gt;&lt;img src="http://farm4.static.flickr.com/3598/3382080064_b162a31114_m.jpg" alt="" style="border: solid 2px #000000;" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-size: 0.9em; margin-top: 0px;"&gt;&lt;a href="http://www.flickr.com/photos/drsnooks/3382080064/"&gt;Karters&lt;/a&gt;&lt;br /&gt;Originally uploaded by &lt;a href="http://www.flickr.com/people/drsnooks/"&gt;Dr Snooks&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;Stag do - survived!&lt;br /&gt;Physically unscathed.&lt;br /&gt;Some of the mental scars may take a little longer to expunge....&lt;br clear="all" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1895543455969417212?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1895543455969417212/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1895543455969417212' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1895543455969417212'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1895543455969417212'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/03/and-jolly-good-time-was-had-by-all.html' title='And a jolly good time was had by all'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm4.static.flickr.com/3598/3382080064_b162a31114_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-6309511470925604742</id><published>2009-01-29T17:20:00.003Z</published><updated>2009-01-29T17:29:47.084Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='flattery'/><category scheme='http://www.blogger.com/atom/ns#' term='imitation'/><category scheme='http://www.blogger.com/atom/ns#' term='sonar'/><category scheme='http://www.blogger.com/atom/ns#' term='work'/><category scheme='http://www.blogger.com/atom/ns#' term='ibm'/><title type='text'>What was that about imitation / flattery?</title><content type='html'>So, way back in April 2008, Techcrunch ran this story about us: &lt;a href="http://www.techcrunch.com/2008/04/24/the-enterprise-social-network-auto-generated-and-visually-mapped/"&gt;The Enterprise Social Network Auto-Generated and Visually Mapped&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Imagine our surprise here when we discovered this post today on TechcrunchIT:&lt;br /&gt;&lt;a href="http://www.techcrunchit.com/2009/01/29/when-ibm-beats-facebook-and-twitter-discover-relevant-people-within-your-network/"&gt;When IBM Beats Facebook and Twitter&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So IBM are now &lt;em&gt;also&lt;/em&gt; doing a product called SONAR, that does almost exactly the same thing as our existing product called &lt;a href="http://www.trampolinesystems.com/products/SONAR/benefits"&gt;SONAR&lt;/a&gt; (Social Networks And Relevance)??? Spooky!&lt;br /&gt;The article says that it's called SAND (Social Networks And Discovery), but the screenshots clearly show SONAR everywhere, and that's how it's referred to on the &lt;a href="http://www.haifa.ibm.com/projects/imt/sonar/index.shtml"&gt;IBM Research Labs page&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I felt compelled to respond to the claim that "No one else in consumer or enterprise is doing this yet.", so I posted a comment about an hour ago. Hasn't appeared yet though.....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-6309511470925604742?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/6309511470925604742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=6309511470925604742' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6309511470925604742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6309511470925604742'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/01/what-was-that-about-imitation-flattery.html' title='What was that about imitation / flattery?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-892763132935515988</id><published>2009-01-07T12:13:00.003Z</published><updated>2009-01-07T12:17:33.118Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='meme evel knievel humour'/><title type='text'>...but can Evel jump it?</title><content type='html'>Happy New Year to all! &lt;br /&gt;&lt;br /&gt;It's time for my first bold tech prediction&lt;sup&gt;&lt;a href="#disclaimer"&gt;*&lt;/a&gt;&lt;/sup&gt; of 2009 - this year's "....but will it blend?" will be &lt;a href="http://caneveljumpit.com"&gt;"...but can Evel jump it?"&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Get your suggestions in - the more surreal, the better!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;a name="disclaimer"&gt;*&lt;/a&gt; Disclaimer: OK, OK, so for "bold tech prediction", read "blatant attempt to spread a meme and drive some traffic to my mate's site" :)&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-892763132935515988?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://caneveljumpit.com' title='...but can Evel jump it?'/><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/892763132935515988/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=892763132935515988' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/892763132935515988'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/892763132935515988'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2009/01/but-can-evel-jump-it.html' title='...but can Evel jump it?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1265544418832321234</id><published>2008-10-02T09:56:00.003+01:00</published><updated>2008-10-02T10:15:19.477+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='football'/><category scheme='http://www.blogger.com/atom/ns#' term='goalkeeper'/><category scheme='http://www.blogger.com/atom/ns#' term='team'/><title type='text'>Goalkeeper wanted!</title><content type='html'>It's off-topic, I know, but then I don't get much time to post anything at all right now, so here goes anyway.&lt;br /&gt;&lt;br /&gt;Those who know me, know I'm a keen footballer. I play for a team in a midweek 11-a-side league, and we're currently &lt;em&gt;DESPERATE&lt;/em&gt; for a goalie. &lt;br /&gt;&lt;br /&gt;The matches are all on astroturf, so there's no studs and very few sliding tackles, and they vary in day, location and kick-off time as follows:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://snipurl.com/cca-map"&gt;Willesden CCA&lt;/a&gt; 8pm kickoff, Mondays or Tuesdays&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://snipurl.com/prg-map"&gt;Paddington Recreation Ground&lt;/a&gt; 7:30pm kickoff, Mondays&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Very occasionally, we may have a match on a Wednesday or Thursday night at &lt;a href="http://snipurl.com/marketroad-map"&gt;Market Road&lt;/a&gt; or &lt;a href="http://snipurl.com/wws-map"&gt;Linford Christie Stadium&lt;/a&gt; but these are rare.&lt;br /&gt;&lt;br /&gt;The team vary in ages between 20-ish to 40-ish, and there's a similar variance in abilities. No-one is anywhere near Ronaldo standard, put it that way.&lt;br /&gt;&lt;br /&gt;If you're interested, email me directly (address is on the right, obscured for spambots) or add a comment at the bottom. We've got our next match on Monday, so don't hang about!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1265544418832321234?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1265544418832321234/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1265544418832321234' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1265544418832321234'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1265544418832321234'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/10/goalkeeper-wanted.html' title='Goalkeeper wanted!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-750669744260594908</id><published>2008-08-06T22:46:00.003+01:00</published><updated>2008-08-06T22:51:56.932+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sonar'/><category scheme='http://www.blogger.com/atom/ns#' term='ux'/><category scheme='http://www.blogger.com/atom/ns#' term='userexperience'/><title type='text'>Holistic User Experience</title><content type='html'>SONAR v1.0 is just around the corner, and we've been thinking a lot about the user experience we offer. One of the key differences this time round is the recognition that User Experience is not just about the interface. Users in this sense are not just end users - they're *anyone* who interacts in any way with your software or your company. They include the sysadmins who have to install your stuff and keep it up and running, the managers who have to make a purchasing decision about whose software to spend their hard-won budget on. &lt;br /&gt;&lt;br /&gt;So we believe that User Experience is about looking at the whole experience the user has in interacting with your software and you. It's about making the whole thing as easy as possible for them, right from the way they hear about it, through purchasing and finding the information they need to make an informed decision, through installation, configuration, administration, ongoing support, right the way through to migrating away from it if they want to.&lt;br /&gt;&lt;br /&gt;Yes, I did say that you have to make it easy for them to migrate away from your &lt;br /&gt;software. No, that's not a dumb thing to do. &lt;br /&gt;&lt;br /&gt;There's two ways of looking at it - self-centred or user-centred. The first way often involves making it difficult to get their data out of your system once it's in there, end-of-lifing old versions and occasionally making new versions incompatible so that they have to keep buying your upgrades or they lose access to their data.&lt;br /&gt;&lt;br /&gt;This is bad, m'kay? It's called Vendor Lock-In, and although it can do good things for your bottom line in the short term, ultimately it can create frustration and resentment among your users - Bad User Experience.&lt;br /&gt;&lt;br /&gt;The thing to remember is that these days, people move around frequently. They change jobs, they change cities and countries at the drop of a hat, so if you create a Bad User Experience your bad reputation will spread along with the people who had it. The democratised web has created a situation where reputation has become &lt;em&gt;more&lt;/em&gt; of a crucial commodity than ever before.&lt;br /&gt;&lt;br /&gt;If you had to make a purchasing decision between system x and system y, and both of them had pretty much the same features for a pretty similar price, what factors would you use to make that decision? The first thing I'd do is ask around for anyone who's used either system before, and see what they thought of it. If I heard stories like "Yeah, it's OK, but once you buy it you're stuck with it" I'd hear alarm bells. I'm far more likely to go with a system that lets me get my data out easily if i change my mind for any reason.&lt;br /&gt;&lt;br /&gt;You'd also look at questions like - whats the support like? Is there an active user community? Do they have forums / wikis / comprehensive FAQs? Do the reported bugs look they get fixed? Is it easy to find the information you need on the website? &lt;br /&gt;&lt;br /&gt;You might give them a call to ask some questions, which opens up a whole new set of potential decision influencers - how quickly did they answer the phone? Did they sound happy to help? Did you have to navigate a labyrinthine set of automated menus for ten minutes before you actually got to speak to someone? Or even worse - did the menus have a voice recognition system that couldn't understand your accent?&lt;br /&gt;&lt;br /&gt;All of the above factors - and many more - contribute to the user's experience of your company and your software, and all are potential factors that influence whether someone decides to spend their money on you. We know we've a long way to go in a lot of these areas, but we recognise this, and we're looking - and that's a great start.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-750669744260594908?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/750669744260594908/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=750669744260594908' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/750669744260594908'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/750669744260594908'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/08/holistic-user-experience.html' title='Holistic User Experience'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3694010560353758635</id><published>2008-06-26T19:22:00.002+01:00</published><updated>2008-06-26T19:24:33.231+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='humour'/><category scheme='http://www.blogger.com/atom/ns#' term='atheism'/><category scheme='http://www.blogger.com/atom/ns#' term='god'/><title type='text'>Atheistic Error Message Of The Day</title><content type='html'>while trying to install &lt;a href="http://god.rubyforge.org"&gt;GOD&lt;/a&gt;, the Ruby process mgmt gem, I got a sudden attack of poignance:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;[sonar@tryfan sonar-solr]$ gem install god&lt;br /&gt;ERROR:  While executing gem ... (Gem::RemoteSourceException)&lt;br /&gt;    HTTP Response 404&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Discuss, in not less than 3000 words...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3694010560353758635?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3694010560353758635/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3694010560353758635' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3694010560353758635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3694010560353758635'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/06/atheistic-error-message-of-day.html' title='Atheistic Error Message Of The Day'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1658990860924623627</id><published>2008-04-03T14:20:00.002+01:00</published><updated>2008-04-03T15:03:03.887+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='rake'/><title type='text'>Rake gotcha on Windows</title><content type='html'>We use &lt;a href="http://rake.rubyforge.org"&gt;Rake&lt;/a&gt; as our build system of choice for all of our projects, as it's much more flexible and pleasant to work with than make or ANT, and by and large it's pretty cross-platform. Except..... &lt;br /&gt;&lt;br /&gt;I just spent about two hours tearing my hair out over something which &lt;em&gt;should&lt;/em&gt; have worked transparently on Windows aswell as *NIX, but it just wasn't playing.&lt;br /&gt;&lt;br /&gt;For reasons which would be tedious to go into, inside one particular rake task I needed to cd to a different directory and execute rake in there, to pick up a completely different set of tasks and ActiveRecord model classes, etc, and then resume execution of the containing task. &lt;br /&gt;&lt;br /&gt;The following code worked fine on UNIX, but just completely failed to do anything on Windows:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;Dir.chdir("../sonar-web") { system( "rake", "db:name_of_other_task" ) }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;No error messages, nothing, it just wasn't doing anything.&lt;br /&gt;&lt;br /&gt;I'll spare you the headbanging frustration and exhaustive list of things I tried that didn't work, and jump straight to the solution - &lt;br /&gt;&lt;br /&gt;&lt;strong&gt;rake&lt;/strong&gt; on Windows needs to execute c:/ruby/bin/&lt;strong&gt;rake.bat&lt;/strong&gt;, not c:/ruby/bin/rake !&lt;br /&gt;&lt;br /&gt;rake.bat will in turn call ruby.exe c:/ruby/bin/rake and pass on the command line parameters.&lt;br /&gt;&lt;br /&gt;So at the top of my rakefile, I just added:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;# on Windows, you can't invoke rake via a "system" cmd, as rake actually should invoke rake.bat&lt;br /&gt;RAKE_CMD      = RUBY_PLATFORM.match(/win/) ? "rake.bat" : "rake"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and then changed my system call to :&lt;br /&gt;&lt;br /&gt;&lt;code&gt;system( "#{RAKE_CMD}", "name_of_other_task" )&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;....and everything worked fine.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1658990860924623627?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1658990860924623627/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1658990860924623627' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1658990860924623627'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1658990860924623627'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/04/rake-gotcha-on-windows.html' title='Rake gotcha on Windows'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-4958984401661940376</id><published>2008-02-26T09:50:00.015Z</published><updated>2008-02-26T12:06:26.909Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='cfoutput'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='cf'/><category scheme='http://www.blogger.com/atom/ns#' term='coding'/><title type='text'>I still miss CFOUTPUT</title><content type='html'>&lt;p&gt;It's been about a year now since I last coded CF in anger, and what coding I've done since then has been mainly in Java and Ruby On Rails. These days, most of my coding is done in my spare time, which is a resource in increasingly short supply - so coding actually tends to be done during my random bouts of insomnia. Like many similar turncoats, I've found RoR development to be settling into a fairly steady cycle :&lt;/p&gt;&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;Do some really complicated stuff really quickly.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Nod appreciatively and make coo-ing noises.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Stop yourself just before standing up and announcing that your name is Boris and &lt;a href="http://www.youtube.com/watch?v=cKO0RrM7LvI&amp;feature=related"&gt;you are invincible&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Try to do something ostensibly simple, get stuck&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Spend ages scouring the web for the simple solution that surely must be out there somewhere&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Get pissed off because you found a blog post telling you that you shouldn't want to do that because it's &lt;a href="http://web.archive.org/web/20060418215514/http://www.loudthinking.com/arc/000516.html"&gt;"not the rails way"&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Figure out a really ugly cumbersome way of doing it step-by-step&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Fail to get to sleep because it's just bugging you that something so simple should be so hard in a framework that makes so many other more complicated things so simple&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Two days later, discover entirely by accident that the fifty lines of hackery could have been done in one long line of twenty method calls &lt;em&gt;( things.each do{ |foo| foo.do.some.other.thing }.and.then.do.some.thing.else( bar ) )&lt;/em&gt; if only you'd known that the method you needed was called &lt;em&gt;(insert counter-intuitive method name here)&lt;/em&gt; and that there was a plugin called &lt;em&gt;(insert name of obscure plugin here)&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Go back and redo your hack with the one long line of twenty method calls. Feel like a l33t h4x0r d00d because you just replaced fifty lines with one.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Discover that somewhere in your one long line of twenty method calls, one of them is returning nil. &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Think "hey, it's ok, I can debug this easily with the console!". Feel smug.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Spend what seems like an aeon starting the console, reproducing the conditions in which you get your nil, changing something, then restarting the console so you can test your change.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Resolve to write better unit tests in future.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Pine a little for the good old days of "make your change then hit f5" to see if something works.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Start a trawl through the source code looking for the cause of the problem&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Reflect that while duck typing is indeed an orgy of sheer loveliness, in this particular case it would be nice if just this once you could know for sure that this particular object is a Foo and therefore all you need to know would be found in foo.rb&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Discover that the cause of your woe is that at least one of your magical plugins doesn't work on Oracle, or SQL Server, or indeed anything other than MySQL.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Go back to the blog on which you found the plugin to see if it's a known problem with a new version&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Discover that the blog is down.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Write a plugin to patch the plugin to work on Oracle&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Feel vaguely uneasy that you now have a chain of umpteen plugins patching plugins patching plugins patching plugins patching ActiveRecord to do something that you apparently shouldn't want to do, but dammit, you needed to get it done by 5pm.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Go out and pull scary faces at small children to make them cry for a while until you feel better.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Come back feeling much better now that you've spread a little frustration around. Reflect that you're probably just not thinking about things in the "right" way.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Adjust your thought-angle, and come at it again&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Repeat from step 1&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;p&gt;To be clear, I do think that Ruby has some wonderful features. Blocks, open classes, method_missing - all of these little niceties make some fantastically cool things possible. The &lt;a href="http://www.ruby-doc.org/core/classes/Enumerable.html#M003151"&gt;Enumerable#sort_by&lt;/a&gt; method in particular was one discovery that just gave me a wonderful warm fuzzy feeling - e.g. &lt;/p&gt;&lt;code&gt;some_collection.sort_by { |element| [ element.method1, element.method2, element.method3 ] }&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Rails, also, has some really great features, and makes some of the donkey work so easy it's almost laughable. But sometimes it feels like all the thinking went into the elegance of the back-end design, and not enough thought went into the templating. RHTML feels like a tacked-on afterthought. HAML is better in some respects, but it still feels awkward. I haven't yet found any templating language that even comes close to the sheer simplicity and ease-of-use of CFOUTPUT.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The example that triggered this rant was grouped output. Let's keep this simple for the purpose of example - say I have a recordset with three fields:&lt;/p&gt;&lt;br /&gt;&lt;table style="width:90%"&gt;&lt;br /&gt;&lt;thead&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;th&gt;Type&lt;/th&gt;&lt;br /&gt;  &lt;th&gt;Sub-type&lt;/th&gt;&lt;br /&gt;  &lt;th&gt;Title&lt;/th&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/thead&gt;&lt;br /&gt;&lt;tbody&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Foo 1&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Foo 2&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Foo 3&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 2&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Bar 1&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 2&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Bar 2&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 1&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 2&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Bar 3&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 2&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 3&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Foobar 1&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 2&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 3&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Foobar 2&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;tr&gt;&lt;br /&gt;  &lt;td&gt;Type 2&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Sub-type 4&lt;/td&gt;&lt;br /&gt;  &lt;td&gt;Foobar 3&lt;/td&gt;&lt;br /&gt;&lt;/tr&gt;&lt;br /&gt;&lt;/tbody&gt;&lt;br /&gt;&lt;/table&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;- etc etc.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;If you wanted to output these with headers and sub-headers whenever the type or sub-type changed, it would be almost trivially easy in CF:&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;cfoutput query="myRecordset" group="type"&amp;gt;&lt;br /&gt;  &amp;lt;h2&amp;gt;#myRecordset.type#&amp;lt;/h2&amp;gt;&lt;br /&gt;  &amp;lt;cfoutput group="subtype"&amp;gt;&lt;br /&gt;    &amp;lt;h3&amp;gt;#myRecordset.subtype#&amp;lt;/h3&amp;gt;&lt;br /&gt;      &amp;lt;ul&amp;gt;&lt;br /&gt;      &amp;lt;cfoutput&amp;gt;&lt;br /&gt;         &amp;lt;li&amp;gt;#myRecordset.title#&amp;lt;/li&amp;gt;&lt;br /&gt;      &amp;lt;/cfoutput&amp;gt;&lt;br /&gt;      &amp;lt;/ul&amp;gt;&lt;br /&gt;  &amp;lt;/cfoutput&amp;gt;&lt;br /&gt;&amp;lt;/cfoutput&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;- but in Rails? I'm still at step 5. I know I'm not the first person to need to do this, not by a long shot, but I just don't yet know what the method that surely must exist would be called. I know you can use Enumerable#group_by to group an array of &lt;br /&gt;objects into something approximating the raw recordset above, but that's kind of working backwards to me.&lt;br /&gt;&lt;br /&gt;I also know that someone will probably post the answer in a comment, probably with some kind of dismissive one-word instruction like "Read." or "Learn." linking to the relevant part of the docs. And that's all well and good. I'm just in a temporary bout of misty-eyed nostalgia for the things that CF made easy - particularly CFOUTPUT.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-4958984401661940376?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/4958984401661940376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=4958984401661940376' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4958984401661940376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4958984401661940376'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/02/i-still-miss-cfoutput.html' title='I still miss CFOUTPUT'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-8075278760981986635</id><published>2008-01-25T13:25:00.000Z</published><updated>2008-01-25T13:49:29.813Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='humour'/><category scheme='http://www.blogger.com/atom/ns#' term='fun'/><category scheme='http://www.blogger.com/atom/ns#' term='office'/><category scheme='http://www.blogger.com/atom/ns#' term='war'/><category scheme='http://www.blogger.com/atom/ns#' term='nationalities'/><title type='text'>Don't Mention The War!</title><content type='html'>Well, in an office containing two Poles, two Germans, two Americans and many Brits, it had to happen sooner or later....&lt;br /&gt;&lt;br /&gt;In response to an email about the &lt;a href="http://petitions.pm.gov.uk/remembermonday/"&gt;petition for a Remembrance Day Bank Holiday&lt;/a&gt;, one of the Poles in the office said :&lt;br /&gt;&lt;q&gt;Personally I think it's a totally lame idea. People who want to go to Iraq and other remote places to shoot others should get a shrink not a holiday&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;At which one of the Brits said:&lt;br /&gt;&lt;q&gt;Hang on, what about the people who went to remote places like Poland and shot Germans?&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;And then the Poles started on about the lack of help they got in the early days of WWII, and just as that was kicking off, in walked one of the German guys.... and then the fun &lt;em&gt;really&lt;/em&gt; started ...!&lt;br /&gt;&lt;br /&gt;Ah, you can't beat a bit of good-natured dredging up national stereotypes from half a century ago to really get that Friday afternoon feeling into a place - it's still going, this is great, it's better than a movie! Where's the popcorn?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-8075278760981986635?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/8075278760981986635/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=8075278760981986635' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/8075278760981986635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/8075278760981986635'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/01/dont-mention-war.html' title='Don&apos;t Mention The War!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1960803241819116770</id><published>2008-01-23T13:23:00.001Z</published><updated>2008-01-23T13:23:35.170Z</updated><title type='text'>Ironically Apt Screenshot Of The Day</title><content type='html'>&lt;div style="float: right; margin-left: 10px; margin-bottom: 10px;"&gt; &lt;a href="http://www.flickr.com/photos/drsnooks/2213725011/" title="photo sharing"&gt;&lt;img src="http://farm3.static.flickr.com/2399/2213725011_c89c9860c2_m.jpg" alt="" style="border: solid 2px #000000;" /&gt;&lt;/a&gt; &lt;br /&gt; &lt;span style="font-size: 0.9em; margin-top: 0px;"&gt;  &lt;a href="http://www.flickr.com/photos/drsnooks/2213725011/"&gt;Ironically Apt Screenshot Of The Day&lt;/a&gt;  &lt;br /&gt;  Originally uploaded by &lt;a href="http://www.flickr.com/people/drsnooks/"&gt;Dr Snooks&lt;/a&gt; &lt;/span&gt;&lt;/div&gt;Tomasz was trying to install the Java Enterprise Edition SDK. For some strange reason it was only giving him this one single button....&lt;br clear="all" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1960803241819116770?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1960803241819116770/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1960803241819116770' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1960803241819116770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1960803241819116770'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/01/ironically-apt-screenshot-of-day.html' title='Ironically Apt Screenshot Of The Day'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm3.static.flickr.com/2399/2213725011_c89c9860c2_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-2836075789777258141</id><published>2008-01-22T12:59:00.000Z</published><updated>2008-01-23T11:17:46.959Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='recruitment'/><category scheme='http://www.blogger.com/atom/ns#' term='ethics'/><category scheme='http://www.blogger.com/atom/ns#' term='sysadmin'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>A Question (or two) of Ethics (part 1)</title><content type='html'>A couple of thorny ethical questions have raised their head recently, and like most of these things, figuring out what's the "right" thing to do has not been easy. &lt;br /&gt;&lt;br /&gt;The first situation came out of an interview. It's a classic problem - one that I've heard asked as a hypothetical "what would you do?" in interviews for a management post - but it does happen, and it happened to us.&lt;br /&gt;&lt;br /&gt;We've been searching high and low for a damn good sysadmin, without much success, for months. We finally found a candidate with all the skills and a great attitude, he would have been absolutely perfect for us...   there was just one small niggle - in his younger days, he'd hacked into a major credit card provider. Not just for a look-see, either, he actually figured out a way to use people's credit card numbers without their accounts being charged, and used it.&lt;br /&gt;&lt;br /&gt;He didn't really know how much he took in total, but in the end he turned himself in, and did hard time for it. Now a completely reformed character, he was looking for a job that used his obviously abundant skills in a positive way.&lt;br /&gt;&lt;br /&gt;To his credit, he was very open about this, and he talked quite freely about it. We really liked him, and we were satisfied that he wouldn't be a security problem. However, then there's the matter of our customers.....&lt;br /&gt;&lt;br /&gt;We do work for some pretty security-conscious clients. Some of them have US Top Secret classified projects. Although we were quite happy that this guy wouldn't be a security risk, would they be? We're a small startup, battling for contracts along with the rest, and it's hard enough at times to get big corporations to take you at all seriously even at the moment. Anyone who has ever jumped through the zillion flaming hoops that they put you through in order to get a whiff of business from these kinds of companies knows just how insane their security procedures can be. (I'm still smiling at the badge I had to wear on one site visit recently - big bold capital letters, red font - &lt;em&gt;"FOREIGN PERSON! ESCORT REQUIRED!"&lt;/em&gt;) If one of them got wind of us having a genuine time-served cracker working for us, we'd be dead in the water.&lt;br /&gt;&lt;br /&gt;In the end we had to make the uncomfortable decision that we couldn't hire this guy, despite him being one of the best candidates we've had. We're &lt;a href="http://www.trampolinesystems.com/weblog/sysadmin-needed/"&gt;still looking for a suitable sysadmin&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;That was a few months ago. The second incident is much more recent - yesterday, in fact - and quite similar, it's kind of approaching the same question of reform and change, but from an opposite direction. Mind you, this post has gotten quite long, so I'll put that in a post of it's own.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-2836075789777258141?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/2836075789777258141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=2836075789777258141' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2836075789777258141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2836075789777258141'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/01/question-or-two-of-ethics-part-1.html' title='A Question (or two) of Ethics (part 1)'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3670734811478129942</id><published>2008-01-09T11:46:00.001Z</published><updated>2008-01-09T11:46:53.759Z</updated><title type='text'>Error Message Of The Day - Sharepoint</title><content type='html'>&lt;div style="float: right; margin-left: 10px; margin-bottom: 10px;"&gt; &lt;a href="http://www.flickr.com/photos/drsnooks/2180556924/" title="photo sharing"&gt;&lt;img src="http://farm3.static.flickr.com/2196/2180556924_043bd34a93_m.jpg" alt="" style="border: solid 2px #000000;" /&gt;&lt;/a&gt; &lt;br /&gt; &lt;span style="font-size: 0.9em; margin-top: 0px;"&gt;  &lt;a href="http://www.flickr.com/photos/drsnooks/2180556924/"&gt;Error Message Of The Day - Sharepoint&lt;/a&gt;  &lt;br /&gt;  Originally uploaded by &lt;a href="http://www.flickr.com/people/drsnooks/"&gt;Dr Snooks&lt;/a&gt; &lt;/span&gt;&lt;/div&gt;I love sharepoint. &lt;br /&gt;It's great. &lt;br /&gt;&lt;br /&gt;It's sooo easy to configure and debug. &lt;br /&gt;And so nice and rewarding to use.&lt;br clear="all" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3670734811478129942?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3670734811478129942/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3670734811478129942' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3670734811478129942'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3670734811478129942'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2008/01/error-message-of-day-sharepoint.html' title='Error Message Of The Day - Sharepoint'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm3.static.flickr.com/2196/2180556924_043bd34a93_t.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-6163328280744033458</id><published>2007-12-21T16:30:00.001Z</published><updated>2007-12-21T23:34:18.454Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='challenge'/><category scheme='http://www.blogger.com/atom/ns#' term='query'/><category scheme='http://www.blogger.com/atom/ns#' term='qotw'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='solution'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>SQL QOTW - Solution!</title><content type='html'>To anyone who has been bashing their head against the desk with the &lt;a href="http://instantbadger.blogspot.com/2007/12/sql-qotw-select-all-rows-up-to.html"&gt;SQL brain-buggerer&lt;/a&gt; I posted earlier, here is the solution I came up with. There may well be others, that may be more elegant, but here's the one that occurred to me...&lt;br /&gt;&lt;br /&gt;OK, so you want to get all the documents up to a given cumulative total byte size. &lt;br /&gt;A straightforward get-me-all-the-docs query looks like this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;SELECT id, filename, bytesize&lt;br /&gt;FROM documents&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We want to calculate the cumulative total size for all the records we've got so far, and then stop once we're about to pass the total. So the first problem is &lt;em&gt;how do we work out the cumulative total?&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Well, we're essentially dealing with an ordered list of records. We effectively want to work down the ordered list, calculating the cumulative total as we go. &lt;br /&gt;&lt;br /&gt;So first lets impose an order on the list:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;SELECT id, filename, bytesize&lt;br /&gt;FROM documents&lt;br /&gt;ORDER BY id&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now for each record, we want to work out the cumulative total. I had many thoughts on how to keep a running total, but I eventually realised that you don't actually need to. Following the old physics student's guiding principle of "if you find yourself with a problem you can't solve, rewrite it into a problem that you &lt;em&gt;can&lt;/em&gt; solve"&lt;sup&gt;&lt;a href="#footnote1"&gt;**&lt;/a&gt;&lt;/sup&gt;, here's the trick - we don't actually need to calculate this as "x + (next x)" - as long as we use the same imposed order, we can get this with a rather cunning subquery:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;SELECT id, filename, bytesize,&lt;br /&gt;       ( SELECT SUM(bytesize) &lt;br /&gt;         FROM documents d2 &lt;br /&gt;         WHERE d2.id &lt;= documents.id &lt;br /&gt;       ) AS cumulative_total&lt;br /&gt;FROM documents&lt;br /&gt;ORDER BY id&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You see how it works? If we're working down an ordered list of ids- &lt;br /&gt;&lt;code&gt;&lt;br /&gt;1000&lt;br /&gt;1001&lt;br /&gt;1002&lt;br /&gt;1003&lt;br /&gt;1004&lt;br /&gt;&lt;em&gt;...&lt;br /&gt;etc&lt;/em&gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;- then keeping a running total is exactly equivalent to querying for the sum of all rows up-to-and-including the current row. So we can now move the subquery from the SELECT clause into the WHERE clause, and it gives us a sufficient criteria for accepting or rejecting rows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;SELECT id, filename, bytesize&lt;br /&gt;FROM   documents&lt;br /&gt;WHERE  ( SELECT SUM(bytesize) &lt;br /&gt;         FROM documents d2 &lt;br /&gt;         WHERE d2.id &lt;= documents.id &lt;br /&gt;       ) &lt; &lt;em&gt;@max_cumulative_size&lt;/em&gt; * 1024 * 1024&lt;br /&gt;ORDER BY id&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;( where @max_cumulative_size is the maximum total size in megabytes, obviously )&lt;br /&gt;&lt;br /&gt;It all hinges on the ordering. The one proviso with this query is that, although you are free to order the results by whatever you choose, the ordering &lt;strong&gt;must&lt;/strong&gt; be the same as the implicit ordering in the subquery. &lt;br /&gt;&lt;br /&gt;And - shock, horror, step back in amazement - that sql works as-is across MySQL and Oracle!&lt;br /&gt;&lt;br /&gt;Yay!&lt;br /&gt;&lt;br /&gt;So I don't know about you, but after that I feel the need to go hit the climbing wall and have a recuperative pint or two afterwards.&lt;br /&gt;&lt;br /&gt;Have a great Christmas everyone!&lt;br /&gt;&lt;br /&gt;&lt;hr /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;a name="footnote1"&gt;&lt;sup&gt;**&lt;/sup&gt;&lt;/a&gt;This is a really useful skill to have, and it's not just applicable to Physics exams either. In fact, taken to extremes, it's perfectly possible, with a bit of cunning, to change a question about something you haven't revised, into a question about something you *have* revised....&lt;br /&gt;&lt;br /&gt;I first realised the power of this at school, in English -  &lt;br /&gt;&lt;q&gt;Q2.4 : Is Hamlet really mad? What is the purpose of his madness in the play and by the play? Discuss in not less than 3,000 words.&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;Answer : &lt;q&gt;In any discussion concerning Hamlet, it is important to note that had he been Scottish, his situation would have been very similar to that of Macbeth....&lt;/q&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;:-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-6163328280744033458?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/6163328280744033458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=6163328280744033458' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6163328280744033458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6163328280744033458'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/12/sql-qotw-solution.html' title='SQL QOTW - Solution!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-8471342636466347099</id><published>2007-12-21T10:11:00.000Z</published><updated>2007-12-21T10:38:40.454Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='query'/><category scheme='http://www.blogger.com/atom/ns#' term='qotw'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>SQL QOTW - SELECT all rows up to a cumulative total</title><content type='html'>Here's a little SQL test that arose here the other day. Let's say you have a DB table called documents, in which you have the following fields:&lt;br /&gt;&lt;br /&gt;id&lt;br /&gt;filename&lt;br /&gt;bytesize&lt;br /&gt;date_created&lt;br /&gt;full_content&lt;br /&gt;&lt;br /&gt;full_content is a BLOB, containing all the text extracted from whatever document the record represents. &lt;br /&gt;bytesize is, cunningly enough, the size of the content in bytes.&lt;br /&gt;&lt;br /&gt;You want to get all the documents, and do something to them - &lt;em&gt;what&lt;/em&gt; you want to do is not important here, just that it involves the full_content. However, a moment of pondering will show the nasty bit of this problem... Let's say you may have tens of millions of documents, and the average bytesize is around a meg. Clearly attempting to read the full table into memory is not a good idea (at least until we get Terabytes of RAM in our servers). &lt;br /&gt;&lt;br /&gt;A far better idea is to limit the maximum memory usage to some value, and only process up to X MB of documents in one batch. The next time round, you'll get the next X MB of docs, and so on.&lt;br /&gt;&lt;br /&gt;We turned this problem over and over for a while, and we thought of a few ways of doing it, but all involved multiple queries, and/or things like temporary tables... and, dammitall, it &lt;strong&gt;SHOULD&lt;/strong&gt; be do-able in one query, dammit man! &lt;em&gt;(cue much indignant harumphing and twizzling of large mutton chop side whiskers, in a Victorian man-of-letters kind of way)&lt;/em&gt; Oh yeah, and here's the killer - it has to be portable across Oracle 10g and MySQL.&lt;br /&gt;&lt;br /&gt;So the problem is this - given this table, can you construct a &lt;em&gt;single&lt;/em&gt; query that will read all the documents &lt;em&gt;up to a given maximum number of megabytes&lt;/em&gt;, that will run on Oracle 10g AND MySQL? &lt;br /&gt;&lt;br /&gt;Well, after pondering this in several isolated attempts for a few days, I went back to it yesterday, and the answer just popped out straight away - and it's actually deceptively simple. I was going to just give you the answer, but I thought it might be a bit more fun to leave it as a challenge, so a big fat Brucie Bonus goes to the first person who gets it - I'll put the answer up here later on today, if no-one gets it in the meantime.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-8471342636466347099?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/8471342636466347099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=8471342636466347099' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/8471342636466347099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/8471342636466347099'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/12/sql-qotw-select-all-rows-up-to.html' title='SQL QOTW - SELECT all rows up to a cumulative total'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-701541190361593519</id><published>2007-11-19T10:41:00.000Z</published><updated>2007-11-19T12:40:23.080Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='function'/><category scheme='http://www.blogger.com/atom/ns#' term='indexes'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>Beware Case-Insensitive Comparisons on Oracle!</title><content type='html'>Let's say you have a large database with lots of People in it. Each Person can have many EmailAddresses. Let's say that your code needs to be portable across multiple DB systems (specifically, MySQL and Oracle).&lt;br /&gt;&lt;br /&gt;Let's also say that you process up to a million emails per day through your system, and for each email you check the sender and recipient addresses against your big DB. So that's potentially an awful lot of reads, and a potential performance bottleneck.&lt;br /&gt;&lt;br /&gt;"No Problem!" you say, "I'll just stick an index on the EmailAddress.address field, and I'm sorted"&lt;br /&gt;&lt;br /&gt;...but that's not quite it. EmailAddresses are case-insensitive, so that FOO@BAR.COM is the same as foo@bar.com. MySQL does a case-insensitive comparison by default, but Oracle doesn't.&lt;br /&gt;&lt;br /&gt;So how do you get Oracle to do a case-insensitive comparison? Well, to cut a &lt;a href="http://www.orafaq.com/node/91"&gt;long story&lt;/a&gt; short, there are two parameters you can set (so long as you're on at least 10gR2):&lt;br /&gt;&lt;code&gt;alter session set NLS_COMP=LINGUISTIC;&lt;br /&gt;alter session set NLS_SORT=BINARY_CI;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Setting NLS_COMP to LINGUISTIC tells Oracle to perform strict case insensitivity operations, and NLS_SORT governs the sort order. The _CI suffix tells it to be case-insensitive with respect to sorting.&lt;br /&gt;&lt;br /&gt;Lovely jubbly, smashin' sorted and great - well, actually, no. Not sorted and great at all, because there's a lovely implementation gem there - &lt;a href="http://download-uk.oracle.com/docs/cd/B14117_01/server.101/b10755/initparams131.htm"&gt;"Setting NLS_SORT to anything other than BINARY causes a sort to use a &lt;strong&gt;full table scan&lt;/strong&gt;, regardless of the path chosen by the optimizer."&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;So Oracle's implementation of a case-insensitive comparison is, basically, "i'm going to go through every single row in the table one-by-one, checking to see if it matches".&lt;br /&gt;&lt;br /&gt;As Mel Smith used to say - "Aw, it's &lt;em&gt;marvellous&lt;/em&gt;, innit?"&lt;br /&gt;&lt;br /&gt;So what can you do? Obviously a full table scan is BAAAAD, so we need to find a way to make it use an index. There are two things you can do&lt;sup&gt;**&lt;/sup&gt;:&lt;br /&gt;&lt;br /&gt;1) Add a explicit lower-case-address field, that duplicates the address field and explicitly lower-cases the address before writing. You can then add an index on this field, and check against that. This means you duplicate info (which is bad) and use more storage than you need (which is also bad) but it's totally portable (which is &lt;em&gt;good&lt;/em&gt;)&lt;br /&gt;&lt;br /&gt;2) Add a functional index to the address field:&lt;br /&gt;&lt;code&gt;CREATE INDEX ix_my_index_name ON email_addresses( LOWER(address) )&lt;/code&gt;&lt;br /&gt;This means you gain the vastly superior performance of an index range scan as opposed to a full table scan, but guess what - MySQL doesn't have functional indexes yet.&lt;br /&gt;&lt;br /&gt;GRAAAAAAAAAAAAAAAAAAAARGH!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;**OK, so there is a third and possibly fourth option in this particular case, to just lower-case the address field anyway, but the point of this post was meant to be general and it does have a downside too.&lt;br /&gt;&lt;br /&gt;You potentially lose capitalisation of the personal part of an email address. For instance:&lt;br /&gt;&lt;code&gt;"Alistair Davidson" &amp;lt;apdavidson@some-domain-im-not-going-to-tell-you.com&amp;gt;&lt;/code&gt;&lt;br /&gt;would get flattened to &lt;code&gt;"alistair davidson" &amp;lt;apdavidson@some-domain-im-not-going-to-tell-you.com&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;So you want to try to keep the capitalisation if possible. &lt;br /&gt;&lt;br /&gt;Probably the best thing to do here is actually to split the personal part from the address, and have the personal part stored as-is, with the address part lower-cased.&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-701541190361593519?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/701541190361593519/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=701541190361593519' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/701541190361593519'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/701541190361593519'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/11/beware-case-insensitive-comparisons-on.html' title='Beware Case-Insensitive Comparisons on Oracle!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-4323132822740135773</id><published>2007-10-29T10:05:00.000Z</published><updated>2007-10-29T10:24:05.205Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='adsense'/><category scheme='http://www.blogger.com/atom/ns#' term='ads'/><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><category scheme='http://www.blogger.com/atom/ns#' term='business'/><title type='text'>Custom Blog Ads Rates?</title><content type='html'>In blatant defiance of all known precedent, someone has not only read my &lt;A href="http://dynamove.blogspot.com"&gt;climbing/mountaineering/great-outdoors-in-general blog&lt;/a&gt;, but actually thinks it's worth advertising on! &lt;br /&gt;&lt;br /&gt;They emailed me to ask how much I'd charge per month for text ads - trouble is, I don't really know, I, like 99% of the rest of the blogosphere, just shoved AdSense on there and forgot about it. Like, &lt;em&gt;really&lt;/em&gt; forgot about it, I haven't even had my first Google cheque yet. &lt;br /&gt;&lt;br /&gt;So, er.... hmmm.... I don't want to undersell myself, but equally I don't want to give them a price that instantly marks me out as a no-hoper. &lt;em&gt;Ponder, ponder, stick finger in the air...&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;But I'm a physicist by training, dammit, I should be able to work this out, &lt;em&gt;surely&lt;/em&gt;. So let's do a quick calculation.....&lt;br /&gt;&lt;br /&gt;It's a pretty low-traffic blog (a couple of hundred hits a day), but it's quite targetted, and AdSense shows an effective CPM of around $3 with a clickthrough ratio of just over 1%.&lt;br /&gt;&lt;br /&gt;So, if I'm getting H hits per day, and the CPM (cost per thousand, I think?) is C, then the price per month should be (avg number of days in a month) * (H / 1000) * C, right?&lt;br /&gt;&lt;br /&gt;That gives me a baseline price of around $30 / month. Obviously I can adjust that depending on page placement, as ads just below the banner get more clicks than in-page, but does that sound about right for a low-traffic, specialist enthusiast blog?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-4323132822740135773?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/4323132822740135773/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=4323132822740135773' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4323132822740135773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/4323132822740135773'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/10/custom-blog-ads-rates.html' title='Custom Blog Ads Rates?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1659869419338672883</id><published>2007-10-29T09:43:00.001Z</published><updated>2007-10-29T09:43:15.285Z</updated><title type='text'>I didn't know they were so cheap either!</title><content type='html'>&lt;div style="float: right; margin-left: 10px; margin-bottom: 10px;"&gt; &lt;a href="http://www.flickr.com/photos/clurr/1774080000/" title="photo sharing"&gt;&lt;img src="http://farm3.static.flickr.com/2092/1774080000_0f5946cc4e_m.jpg" alt="" style="border: solid 2px #000000;" /&gt;&lt;/a&gt; &lt;br /&gt; &lt;span style="font-size: 0.9em; margin-top: 0px;"&gt;  &lt;a href="http://www.flickr.com/photos/clurr/1774080000/"&gt;i'm hurt we're so cheap&lt;/a&gt;  &lt;br /&gt;  Originally uploaded by &lt;a href="http://www.flickr.com/people/clurr/"&gt;clurr&lt;/a&gt; &lt;/span&gt;&lt;/div&gt;&lt;br clear="all" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1659869419338672883?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1659869419338672883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1659869419338672883' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1659869419338672883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1659869419338672883'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/10/i-didn-know-they-were-so-cheap-either.html' title='I didn&amp;#39;t know they were so cheap either!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm3.static.flickr.com/2092/1774080000_0f5946cc4e_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1239824456977449074</id><published>2007-10-13T00:36:00.000+01:00</published><updated>2007-10-18T12:46:50.743+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='terabyte'/><category scheme='http://www.blogger.com/atom/ns#' term='disk'/><category scheme='http://www.blogger.com/atom/ns#' term='raid'/><category scheme='http://www.blogger.com/atom/ns#' term='filesystem'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><category scheme='http://www.blogger.com/atom/ns#' term='storage'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='size'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>Planning for a 1.5 Terabyte Database</title><content type='html'>Here's something to make you stroke your chin and stare into the distance for a moment or two -&lt;br /&gt;&lt;br /&gt;A customer asked us about crunching a large amount of email. Some rough back-of-an-envelope calculations lead us to expect up to about 150GB of it in one go. Our &lt;a href="http://enron.trampolinesystems.com"&gt;experiments with the Enron emails&lt;/a&gt; on MySQL produced about a 1:10 ratio of data-in to database size, a ratio of roughly 1:1 on data-in to language model size and full-text index, and a roughly linear (not 1:1 though!) increase in processing time per email with elapsed cumulative crunching time.  &lt;br /&gt;&lt;br /&gt;So that means we can expect a database size of up to 1.5 &lt;em&gt;Terabytes&lt;/em&gt;, plus another 300GB of language models and full-text index...&lt;br /&gt;&lt;br /&gt;(We'd be using Oracle for the DB, as it comes with some very handy out of the box management and monitoring tools, performance advisor alerts, and all that kaboodle, and it may be that the 1:10 ratio is different on Oracle - we're looking at that at the moment)&lt;br /&gt;&lt;br /&gt;So how do you go about planning for a database of that size? &lt;br /&gt;&lt;br /&gt;We can't even defer the question and scale-as-we-go, as it's going to be growing to that size very quickly, within days of kicking off. It's got to be right, straight from the word go. I've worked with very large email datasets before, on Smartgroups, but in Freeserve we had a big team of specialist UNIX engineers to manage it.&lt;br /&gt;&lt;br /&gt;How do you spec up the disk configuration, knowing that the database files are going to be that huge? We've tended to go for RAID 1 (mirrored) by default, as an until-now acceptable balance between simplicity and resilience. But this means that if you have, say, 2 x 500GB disks, you only actually get 500GB of storage. Dell provide up to 1TB disks on some servers, but &lt;em&gt;damn&lt;/em&gt; it's pricey... we'll no doubt end up going for a combination of mirrored, striped configs, but that means more complexity of course.&lt;br /&gt;&lt;br /&gt;On that subject, how do you organise the file system? And, crucially, how do you go about arranging &lt;em&gt;back-ups&lt;/em&gt; for all that data? The last thing you want is for 1.5TB of data to get lost with no backup, and have to be re-calculated from scratch. The backups have to be stored somewhere, and even just the process of shifting that amount of data from one storage device to another is non-trivial. I mean, shifting 1.5x10&lt;sup&gt;13&lt;/sup&gt; bits, even over a dedicated, max-ed out Gigabit ethernet is going to take at least 1.5x10&lt;sup&gt;4&lt;/sup&gt; seconds - or just over 4hrs...&lt;br /&gt;&lt;br /&gt;Hmm, questions, questions, questions, chin stroke, thousand-yard-stare, tap mouth absently... I'll be pondering this for a while, I think.&lt;br /&gt;&lt;br /&gt;Mind you, my old university mate &lt;a href="http://www.surfability.com/about/team.php"&gt;Dan&lt;/a&gt; once emailed me back in his PhD days, saying "Hey, you know about databases, don't you? Could you give me a quick intro? I've got to write one to handle data from the SLAC particle accelerator - it's  got to handle &lt;em&gt;terabytes of data per day&lt;/em&gt;...."&lt;br /&gt;- and that was way back in about 1997 or so, &lt;a href="http://www.littletechshoppe.com/ns1625/winchest.html"&gt;when a 4GB drive would cost you nearly $500&lt;/a&gt;. Maybe time to give him a ping on Facebook....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1239824456977449074?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1239824456977449074/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1239824456977449074' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1239824456977449074'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1239824456977449074'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/10/planning-for-15-terabyte-database.html' title='Planning for a 1.5 Terabyte Database'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3217366262284914034</id><published>2007-10-12T23:55:00.000+01:00</published><updated>2007-10-13T00:21:45.751+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='various'/><category scheme='http://www.blogger.com/atom/ns#' term='busy'/><title type='text'>It Lives..!</title><content type='html'>OK, so posts have been a bit thin on the ground lately, due to being very busy at work but mostly on pre-sales stuff or client-confidential stuff.... but in general, just being so damn &lt;i&gt;busy&lt;/i&gt; all the time.&lt;br /&gt;&lt;br /&gt;I've been to the States on a client visit, where we also went to see the supremely-smashing &lt;a href="http://www.youtube.com/watch?v=WXYgziSxxt8&amp;mode=related&amp;search="&gt;Nellie McKay&lt;/a&gt; play at &lt;a href="http://www.birchmere.com/"&gt;The Birchmere&lt;/a&gt;. I'd never heard her before, and Peter just said "She's kind of hard to describe..." - I ended up describing the gig as like Frank Zappa and Paul Simon jamming with Suzanne Vega and Phoebe from Friends, and I think that's about as close as I can get.&lt;br /&gt;&lt;br /&gt;I've been sport-climbing in the magnificent &lt;a href="http://en.wikipedia.org/wiki/El_Chorro"&gt;El Chorro&lt;/a&gt; in Andalucia, which I'll post more about on &lt;a href="http://dynamove.blogspot.com"&gt;Dynamove&lt;/a&gt; (which I've also been neglecting lately)&lt;br /&gt;&lt;br /&gt;I've been studying the &lt;a href="http://www.trampolinesystems.com/weblog/social-dynamics-of-werewolves-and-lynch-mobs/"&gt;Social Dynamics of Werewolves and Lynch Mobs&lt;/a&gt;...and if you want an atmospheric bar in which to do so, you couldn't get better than &lt;a href="http://www.london-se1.co.uk/forum/read/1/68618"&gt;Shunt&lt;/a&gt; - it was like walking into the bar in &lt;a href="http://en.wikipedia.org/wiki/An_American_Werewolf_in_Paris"&gt;An American Werewolf In Paris&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I've also been coding more in the last couple of weeks, working on LDAP importers and such, and I've come up against some interesting questions of scale, which I'll post more about later....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3217366262284914034?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3217366262284914034/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3217366262284914034' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3217366262284914034'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3217366262284914034'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/10/it-lives.html' title='It Lives..!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-151145826680488188</id><published>2007-09-07T14:12:00.000+01:00</published><updated>2007-09-07T14:24:40.373+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='racket'/><category scheme='http://www.blogger.com/atom/ns#' term='comment'/><category scheme='http://www.blogger.com/atom/ns#' term='spam'/><category scheme='http://www.blogger.com/atom/ns#' term='extortion'/><title type='text'>Comment Spam Extortion Racket?</title><content type='html'>&lt;a href="http://www.trampolinesystems.com/weblog/enron-explorer-40-000-visitors-from-100-countries/"&gt;This comment&lt;/a&gt; on Trampoline Systems' blog made me smile -&lt;br /&gt;&lt;br /&gt;&lt;q&gt;hello , my name is Richard and I know you get a lot of spammy comments , I can help you with this problem . I know a lot of spammers and I will ask them not to post on your site. It will reduce the volume of spam by 30-50% .In return Id like to ask you to put a link to my site on the index page of your site. The link will be small and your visitors will hardly notice it , its just done for higher rankings in search engines. Contact me icq 454528835 or write me tedirectory(at)yahoo.com , i will give you my site url and you will give me yours if you are interested. thank you&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;I know you get a lot of spammy comments , I can help you with this problem . I know a lot of spammers....&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Kind of like walking into a newsagent and saying "ya know, I can't help noticing you got a lot of paper in here... that's kind of a fire trap, ya know what I'm sayin?"&lt;br /&gt;&lt;br /&gt;If this guy really &lt;strong&gt;does&lt;/strong&gt; know a lot of spammers, I'm sure someone out there knows a lot of Federal Agents who'd like to talk to him - his ICQ number and email address are in the quote. ;-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-151145826680488188?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/151145826680488188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=151145826680488188' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/151145826680488188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/151145826680488188'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/09/comment-spam-extortion-racket.html' title='Comment Spam Extortion Racket?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3331646107111669008</id><published>2007-09-01T10:33:00.000+01:00</published><updated>2007-09-03T15:50:23.599+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='management'/><title type='text'>OMG, I'm a PHB....</title><content type='html'>Last week was something of a milestone for me. While gathering my thoughts for the daily stand-up on Friday, the realisation suddenly dawned - I'd had a really busy week, but I &lt;em&gt;hadn't actually written a single line of code all week&lt;/em&gt;. I hadn't even fired up my IDE....&lt;br /&gt;&lt;br /&gt;So that's it - I am now officially "Management". How did this happen? Seems like just five minutes ago that we were four guys around a table - one CEO and three techies, building stuff. I'd sometimes spend entire days in the office coding away on my own. Now there's 13 of us, and that's just the permanent staff, not counting contractors and non-exec directors, and I've just spent the whole week running around like the proverbial blue-arsed fly, organising, cabling, battling with MS Project, on the phone with suppliers, dealing with customers, in meetings and conference calls, discussing product strategy, allocating work.... etc etc etc... anything, in fact, except &lt;em&gt;coding&lt;/em&gt;.&lt;br /&gt;&lt;br /&gt;It's funny how things change. Ten years ago I was the hardcore techie sniggering at the back of the room, thinking that the managers just didn't get it and that I would never be in their place. Now &lt;strong&gt;I'm&lt;/strong&gt; the Pointy-Headed-Boss - and I'm actually &lt;em&gt;OK&lt;/em&gt; with that... this stuff needs to be done, and I'd rather I dealt with it than have my team disturbed.&lt;br /&gt;&lt;br /&gt;...I mentioned this in the stand-up, and was of course greeted with an appropriate chorus of cat-calls, ironic cheers, and a cry of "MAAAANAGEMEEENT WAAAAAAANNNNKKKKEEEEEERRRR!" (thanks &lt;a href="http://www.trampolinesystems.com/news/company-news/microsoft-hotshot-peter-biddle-joins-london-s-tr/"&gt;Peter&lt;/a&gt;!)&lt;br /&gt;So it's nice to know that although the faces change, the culture doesn't.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3331646107111669008?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3331646107111669008/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3331646107111669008' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3331646107111669008'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3331646107111669008'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/09/omg-im-phb.html' title='OMG, I&apos;m a PHB....'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-5839672835194309062</id><published>2007-08-16T20:49:00.000+01:00</published><updated>2007-09-11T23:06:37.421+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='syndication'/><category scheme='http://www.blogger.com/atom/ns#' term='identity'/><category scheme='http://www.blogger.com/atom/ns#' term='authentication'/><category scheme='http://www.blogger.com/atom/ns#' term='rss'/><title type='text'>The Perennial RSS Authentication Dilemma</title><content type='html'>It's a common problem, one that has cropped up many times for me over the last few years. You build a secure system, locked up behind a login so that only authenticated users can access the tightly-controlled data, and everything's fine - and then you come to the RSS feeds.&lt;br /&gt;&lt;br /&gt;Simply put, RSS feeds and the corresponding use-case of syndicating data &lt;em&gt;out&lt;/em&gt; of the application into another application - be it a desktop RSS reader, a web-based aggregator, or even another context within the same system - is in direct contradiction to your security. You can't have an RSS reader log in to your app using the standard login form, and most readers certainly don't support cookies, so you have to provide a bypass.... but what mechanism?&lt;br /&gt;&lt;br /&gt;I've tried numerous approaches - most often HTTP AUTH (which some readers support, but not many) or an encrypted url token.&lt;br /&gt;&lt;br /&gt;HTTP AUTH is somewhat like an old faithful, it's been around forever, every web server and browser worth mentioning has supported it for years, and it's simple to implement. But it has the disadvantage that once authenticated, the only way to log out is to close your browser completely. Also, many RSS readers don't support it.&lt;br /&gt;&lt;br /&gt;Encrypting the users security credentials into a token that you can pass on the URL, is guaranteed to work on anything that can pass on a url correctly, but it has the disadvantage that then anyone who gets access to that url, to all intents and purposes, &lt;span style="font-style: italic;"&gt;is&lt;/span&gt; that user - so you still have to be careful what you expose in the feed itself.&lt;br /&gt;&lt;br /&gt;The main thing, as ever, is to establish exactly what behaviour is "intended". If the  brief is for the user to be able to copy-and-paste RSS urls into readers / emails / other sites, then make sure everyone is clear on the implications of that - you're essentially allowing people and / or applications to impersonate a user &lt;strong&gt;without&lt;/strong&gt; going through the login process. &lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Requirements, of course, vary wildly from app to app, but the approach I've tended to settle on is a combination of multiple methods - if there is a cookie identifying the user, then use that to establish ID, else if you have HTTP AUTH credentials, use them, authenticating appropriately as required. But remember that if you are dealing with automatic requests from readers via HTTP AUTH or encrypted tokens, you should &lt;strong&gt;ALWAYS&lt;/strong&gt; clear out any session variables at the end of the request, otherwise you can quickly end up with &lt;a href="http://instantbadger.blogspot.com/2005/12/rss-ate-my-server-spiralling-jrun.html"&gt;thousands of persistent sessions for no apparent reason&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-5839672835194309062?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/5839672835194309062/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=5839672835194309062' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/5839672835194309062'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/5839672835194309062'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/08/perennial-rss-authentication-dilemma.html' title='The Perennial RSS Authentication Dilemma'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-8122914610979604445</id><published>2007-08-15T16:08:00.000+01:00</published><updated>2007-08-15T16:12:58.567+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='manipulation'/><category scheme='http://www.blogger.com/atom/ns#' term='ethics'/><category scheme='http://www.blogger.com/atom/ns#' term='astroturf'/><category scheme='http://www.blogger.com/atom/ns#' term='social media'/><title type='text'>Astroturfing in action : social media manipulation exchange</title><content type='html'>"Astroturfing" is the art of faking "grassroots" - social media manipulation, in other words.&lt;br /&gt;&lt;br /&gt;And &lt;a href="http://forums.digitalpoint.com/forumdisplay.php?f=105"&gt;here&lt;/a&gt; is where you can buy, sell, or exchange Diggs, Stumbles, Reddits, blog posts, reviews, you name it....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-8122914610979604445?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/8122914610979604445/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=8122914610979604445' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/8122914610979604445'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/8122914610979604445'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/08/astroturfing-in-action-social-media.html' title='Astroturfing in action : social media manipulation exchange'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-7106939014269256550</id><published>2007-08-13T18:12:00.000+01:00</published><updated>2007-08-13T18:21:32.215+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='webserver'/><category scheme='http://www.blogger.com/atom/ns#' term='charset'/><category scheme='http://www.blogger.com/atom/ns#' term='utf-8'/><category scheme='http://www.blogger.com/atom/ns#' term='content-type'/><category scheme='http://www.blogger.com/atom/ns#' term='i18n'/><category scheme='http://www.blogger.com/atom/ns#' term='config'/><category scheme='http://www.blogger.com/atom/ns#' term='apache'/><title type='text'>Apache Proxied Rewrite Character Set Gotcha</title><content type='html'>Character sets, character sets, I'm perenially beseiged by character sets.... I just got to the bottom of a particularly strange character set issue with one of our clients' website.&lt;br /&gt;&lt;br /&gt;Their domain points to our apache web server, which rewrites any requests for collaboration engine urls (e.g. somedomain.org/groups/ or somedomain.org/people ) in order to send them to the collab platform, and rewrites any other urls to go directly to a third-party hosted CMS-driven website.&lt;br /&gt;&lt;br /&gt;The problem they were having was that when viewing the live, proxied site, strange characters were appearing in the generated content - e.g. "?" and that funny square box that handily tells you "you got the character set wrong, fool" - but when they looked at the site directly on the third-party CMS, it was fine.&lt;br /&gt;&lt;br /&gt;Using &lt;a href="http://www.getfirebug.com/"&gt;Firebug&lt;/a&gt; to inspect the headers showed that when viewing the third-party CMS, the Content-Type was:&lt;br /&gt;&lt;br /&gt;   &lt;code&gt;Content-Type    text/html&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;but when proxied via our Apache web server, it was:&lt;br /&gt;&lt;br /&gt;   &lt;code&gt;Content-Type    text/html; charset=UTF-8&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;The meta tag in the HTML itself was :&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt; META http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" &gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;- and the ensuing character set mismatch was causing the dodgy character funkiness.&lt;br /&gt;&lt;br /&gt;It turns out that buried deep within the bowels of our Apache config was this line:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;#&lt;br /&gt;# Specify a default charset for all content served; this enables&lt;br /&gt;# interpretation of all content as UTF-8 by default.  To use the&lt;br /&gt;# default browser choice (ISO-8859-1), or to allow the META tags&lt;br /&gt;# in HTML content to override this choice, comment out this&lt;br /&gt;# directive:&lt;br /&gt;#&lt;br /&gt;AddDefaultCharset UTF-8&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Which was doing exactly what it said on the tin - forcing UTF-8 interpretation whatever the META tag said. Rather than changing it in the central config, I overrode it in their specific config with this line :&lt;br /&gt;&lt;br /&gt;&lt;code&gt;AddDefaultCharset Off&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;and it seems to work fine now. So that's a big "yay" for Firebug, a dependent "yay" for Firefox's plugin/extension architecture that allows and encourages third parties to develop these funky plugins, and a big &lt;a href="http://www.youtube.com/watch?v=ZMKCLyhBBwI"&gt;slap round the face with a large fish&lt;/a&gt; for anyone who's not using UTF-8 in this day and age....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-7106939014269256550?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/7106939014269256550/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=7106939014269256550' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7106939014269256550'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7106939014269256550'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/08/apache-proxied-rewrite-character-set.html' title='Apache Proxied Rewrite Character Set Gotcha'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1554159488811145575</id><published>2007-07-31T16:08:00.000+01:00</published><updated>2007-07-31T16:24:47.929+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='migration'/><category scheme='http://www.blogger.com/atom/ns#' term='migrate'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='activerecord'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Rails Migration Version Headache - D'oh!</title><content type='html'>I've been having real headbanging frustrations with Rails ActiveRecord migrations not picking up the correct target version. On the face of it, this should be about as simple as you can get -&lt;br /&gt;&lt;br /&gt;- To determine the current version, the migrate task looks in your database for a table called "schema_info", and reads the value of the "version" field from it. If the table is not there, or is empty, then version is nil, and nil.to_i = 0. &lt;br /&gt;&lt;br /&gt;- To determine the target version, the task gets the highest-numbered file from the db/migrate directory and parses an integer out of the filename.&lt;br /&gt;&lt;br /&gt;OR&lt;br /&gt;&lt;br /&gt;- You can override the target version by passing in a command-line parameter like so: &lt;br /&gt;&lt;code&gt;rake db:migrate VERSION=XXX&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;So, as the famous "outraged of Tunbridge Wells" might say, why oh why oh why was my migration task &lt;em&gt;always&lt;/em&gt; reverting back to previous versions, despite having migration files numbered sequentially right the way up to version 19 (and counting) ?&lt;br /&gt;&lt;br /&gt;In vain did I bang my head against the desk, curse migrations and all they stood for, implore the heavens to rain down brimstone upon the head of DHH and all his acolytes, and impugn the parentage of my PC, Windows, Oracle and databases in general....&lt;br /&gt;&lt;br /&gt;On a trawl through the Railties source code, it turns out there's a third, more surreptitious way to tell the migrate task which version you'd like to migrate to - if there's an environment variable called VERSION, it will take the value from that. And when I looked at my System Variables in XP, what did I find?&lt;br /&gt;&lt;br /&gt;&lt;code&gt;VERSION=3.5.0&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Ah.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;OK.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;My bad...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1554159488811145575?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1554159488811145575/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1554159488811145575' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1554159488811145575'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1554159488811145575'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/07/rails-migration-version-headache-doh.html' title='Rails Migration Version Headache - D&apos;oh!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1835841138007538067</id><published>2007-06-28T12:13:00.000+01:00</published><updated>2007-06-29T15:39:45.779+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='optimisation'/><category scheme='http://www.blogger.com/atom/ns#' term='mysql'/><category scheme='http://www.blogger.com/atom/ns#' term='views'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><category scheme='http://www.blogger.com/atom/ns#' term='db'/><title type='text'>A Four-Way Grudge Match: Oracle vs. MySQL vs. Views vs. Derived Tables, eek..!</title><content type='html'>&lt;p&gt;I &lt;em&gt;like&lt;/em&gt; Views. SQL Views are great. Unless, that is, you have to support certainly two (MySQL and Oracle) and ideally three (plus SQLServer) of the major DB systems with the same code. Yesterday I was forced into a dastardly hack that made me wince with it's ickiness, but was actually the most practical solution given the constraints.&lt;/p&gt;&lt;h3&gt;The Situation&lt;/h3&gt;&lt;p&gt;We have a view which combines a LEFT OUTER JOIN with an AVG and a GROUP BY, to "collapse" what may be many related records down in to one. The AVG gets an average link weighting, to give you an effective measure of how closely two items are related by all the various different linking mechanisms.&lt;/p&gt;&lt;p&gt;This view references a table - item_links - that is expected to grow very rapidly, to the size of many millions of records.&lt;/p&gt;&lt;p&gt;We INNER JOIN onto this view in application code, to get item records related to the "current" item.&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Problem&lt;/h3&gt;&lt;p&gt;On Oracle, this view works really well. Nice and fast, at most a tenth of a second or so per query. Doing an EXPLAIN in Oracle's SQL Developer shows that the execution plan uses all the expected indexes. Luvvly jubbly, bosh, sorted, etc.&lt;/p&gt;On MySQL, however, view support is much less mature. The query optimiser can only use the underlying indexes if the view is created with CREATE ALGORITHM=MERGE and &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/create-view.html"&gt;the docs&lt;/a&gt; say:&lt;br /&gt;"The MERGE algorithm requires a one-to-one relationship between the rows in the view and the rows in the underlying table. If this relationship does not hold, a temporary table must be used instead. Lack of a one-to-one relationship occurs if the view contains any of a number of constructs:&lt;q&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Aggregate functions (SUM(), MIN(), MAX(), COUNT(), and so forth)&lt;/li&gt;&lt;li&gt;DISTINCT&lt;/li&gt;&lt;li&gt;GROUP BY&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;...&lt;/q&gt;&lt;br /&gt;&lt;p&gt;... so instead, what MySQL does is select &lt;em&gt;all&lt;/em&gt; rows into a temporary table, which is then used for executing the statement against.&lt;/p&gt;&lt;p&gt;Doing an EXPLAIN in MySQL shows that for the crucial operation of selecting the required rows from this potentially-huge table, there are no indexes used AT ALL. It is in fact selecting EVERY row into the temp table.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;All of which leaves me - and, I must say, in blatant defiance of &lt;a href="http://flickr.com/photos/mccraig/sets/72157600227724046/"&gt;all known precedent&lt;/a&gt; - in the truly strange position of &lt;em&gt;cursing&lt;/em&gt; MySQL whilst &lt;em&gt;praising&lt;/em&gt; Oracle.....&lt;/p&gt;&lt;p&gt;Which worries me - I've never been here before.... :-)&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Solutions That Won't Work&lt;/h3&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Get rid of the view and just put the sql into each query,&lt;/strong&gt;&lt;br /&gt;i.e. change INNER JOIN vw_item_links to INNER JOIN item_links ON (blah) LEFT OUTER JOIN link_prefs ON (blah) etc etc&lt;br /&gt;Hmmm.... it's possible, but I would say it's a last resort, because that would mean that we'd then have to GROUP BY &lt;em&gt;every single&lt;/em&gt; field in every query, not just the fields in the view. This can get majorly cumbersome, because although MySQL allows you to just group by just-enough-fields-to-get-uniqueness &lt;em&gt;(and thus gets a yay for ease-of-use)&lt;/em&gt;, Oracle insists on grouping by every single field that's not an aggregate &lt;em&gt;(and just about gets a begrudging yay for insisting on standards compliance)&lt;/em&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Eliminate the view and the GROUP BY  through using subqueries&lt;/strong&gt;&lt;br /&gt;i.e. SELECT (item fields), (SELECT AVG(weight)  FROM item_links WHERE ...) AS weight, (SELECT other_item_id)&lt;br /&gt;This is a bit of a null option, unfortunately, as we'd still have to INNER JOIN onto item_links in the FROM clause to get the related records, so we're not really gaining anything, and in fact we'd be introducing more overhead through a correlated subquery executed for every row. &lt;/li&gt;&lt;/ol&gt;&lt;p&gt;So what else can we try? Anyone? Ah yes, you boy, there at the back....&lt;/p&gt;&lt;br /&gt;&lt;h3&gt;The Solution That Did Work&lt;/h3&gt;You can, in fact, use subqueries  in the FROM clause to create &lt;em&gt;DERIVED TABLES&lt;/em&gt;, like so:&lt;br /&gt;&lt;code  class="clearfix"&gt;&lt;pre&gt;SELECT (fields) &lt;br /&gt;FROM items &lt;br /&gt;INNER JOIN ( &lt;em&gt;your view definition SQL&lt;/em&gt; ) inline_vw_item_links&lt;br /&gt;        ON inline_vw_item_links.other_item_id = items.id&lt;br /&gt;WHERE inline_vw_item_links.item_id = ...&lt;br /&gt;ORDER BY inline_vw_item_links.weight DESC&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;in effect, putting your view SQL into the query &lt;em&gt;as if it were a regular table&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;This does mean that you lose some of the nice encapsulation of the view, and if the view definition is complex it makes your queries look ugly, but at least it works across both DB platforms. You can also get round the ickiness problem by holding the view definition SQL in a variable :&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;SELECT &lt;em&gt;(fields)&lt;/em&gt;&lt;br /&gt;FROM items &lt;br /&gt;INNER JOIN ( &lt;strong&gt;#getViewDefinitionSql()#&lt;/strong&gt; ) inline_vw_item_links&lt;br /&gt;        ON inline_vw_item_links.other_item_id = items.id&lt;br /&gt;WHERE inline_vw_item_links.item_id = ...&lt;br /&gt;ORDER BY inline_vw_item_links.weight DESC&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;But hang on, there's one more subtlety - (and this is the icky bit)&lt;br /&gt;&lt;br /&gt;An EXPLAIN on Oracle shows that this query still uses all the appropriate indexes, as it should. Smashin'.&lt;br /&gt;&lt;br /&gt;But MySQL, although it's now recognising that there are indexes there to be used, is still selecting ALL rows from item_links. Which, to be fair, is what the query is *strictly* telling it to do. &lt;br /&gt;&lt;br /&gt;So here's the icky hack:&lt;br /&gt;&lt;br /&gt;We put the item_links WHERE clause restrictions into the inline view definition SQL, so that it looks like this:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;SELECT (fields) &lt;br /&gt;FROM items &lt;br /&gt;INNER JOIN ( &lt;br /&gt;    SELECT AVG(weight) AS weight, other_item_id, (etc...)&lt;br /&gt;    FROM item_links LEFT OUTER JOIN (blah) ON (whatever)&lt;br /&gt;    &lt;strong&gt;WHERE item_links.item_id = #my_item_id# AND shared=1&lt;/strong&gt;&lt;br /&gt; ) inline_vw_item_links&lt;br /&gt;        ON inline_vw_item_links.other_item_id = items.id&lt;br /&gt;WHERE inline_vw_item_links.item_id = ...&lt;br /&gt;ORDER BY inline_vw_item_links.weight DESC&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;which you can then prettify a bit for encapsulation like this:&lt;br /&gt;&lt;br /&gt;&lt;code class="clearfix"&gt;&lt;pre&gt;SELECT (fields) &lt;br /&gt;FROM items &lt;br /&gt;INNER JOIN ( &lt;br /&gt;    &lt;strong&gt;#getViewDefinitionSql( "WHERE item_links.item_id = #my_item_id# AND shared=1" )&lt;/strong&gt;&lt;br /&gt; ) inline_vw_item_links&lt;br /&gt;        ON inline_vw_item_links.other_item_id = items.id&lt;br /&gt;WHERE inline_vw_item_links.item_id = ...&lt;br /&gt;ORDER BY inline_vw_item_links.weight DESC&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Now this makes me feel icky. It's (effectively) mixing your selection criteria in with your join criteria, which violates my rules for "good" sql, and goes against what I've always thought of as best practise - letting the DB engine's query optimiser work out the execution plan for itself. And it's just...&lt;em&gt;wrong&lt;/em&gt;...&lt;br /&gt;&lt;br /&gt;...But it works. An EXPLAIN shows that MySQL is now using the indexes that it should be using, and only selecting the rows that it needs from the potentially-many-millions-of-rows item_links tables. And the benefit is blatantly obvious in execution times, turning what was a several-second query execution time into a few tenths of a second. And Oracle is still happy. I suspect that it was previously optimising the view SQL into effectively this query anyway, under the hood.  So I'm swallowing my SQL purist pomposity, and going with it. But if anyone can think of a more "proper" way of doing this, I'm all ears.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1835841138007538067?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1835841138007538067/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1835841138007538067' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1835841138007538067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1835841138007538067'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/06/do-you-do-when-view-wont-do.html' title='A Four-Way Grudge Match: Oracle vs. MySQL vs. Views vs. Derived Tables, eek..!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-45572551323432120</id><published>2007-06-28T11:06:00.000+01:00</published><updated>2007-06-28T11:14:13.465+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='football'/><title type='text'>Anyone fancy a football game TONIGHT? 8pm, Willesden CCA</title><content type='html'>Due to some last-minute drop-outs, we're two men short for our 11-a-side match tonight, 8pm, at Willesden CCA. We're desperate enough, in fact, for me to test the power of the online social network and the blogosphere blah blah blah.... to the limit by posting this.&lt;br /&gt;&lt;br /&gt;We ideally need a right-back, and a goalie, but to be honest we'd settle for any two reasonably competent players in any position, cause we're all fairly flexible.&lt;br /&gt;&lt;br /&gt;So, if anyone fancies it, let me know ASAP!&lt;br /&gt;&lt;br /&gt;Things to know:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;We're not exactly stuffed full of Ronaldinhos, but you should at least be able to control and pass a ball, and run around for a full 90 minutes. Or play in goal!&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It's on astroturf, so there's no studs and very few sliding tackles&lt;br /&gt;&lt;/li&gt;&lt;li&gt;I can bring spare shorts, shirt, socks, shinpads and a pair of size 11 trainers if needed - obviously it's best if you have your own trainers&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-45572551323432120?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/45572551323432120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=45572551323432120' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/45572551323432120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/45572551323432120'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/06/anyone-fancy-football-game-tonight-8pm.html' title='Anyone fancy a football game TONIGHT? 8pm, Willesden CCA'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3394248656657558730</id><published>2007-06-26T14:41:00.000+01:00</published><updated>2007-06-26T14:49:50.961+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='old'/><category scheme='http://www.blogger.com/atom/ns#' term='images'/><category scheme='http://www.blogger.com/atom/ns#' term='nevermind'/><category scheme='http://www.blogger.com/atom/ns#' term='pictures'/><category scheme='http://www.blogger.com/atom/ns#' term='nirvana'/><category scheme='http://www.blogger.com/atom/ns#' term='age'/><category scheme='http://www.blogger.com/atom/ns#' term='baby'/><title type='text'>How to make yourself feel old, instantly</title><content type='html'>The baby on Nirvana's "Nevermind" album cover -&lt;br /&gt;&lt;img src="http://www.abc.net.au/myfavouritealbum/albumart/img/nevermind.jpg" /&gt;&lt;br /&gt;&lt;br /&gt;- now looks like this:&lt;br /&gt;&lt;img src="http://www.aldavidson.co.uk/baby-on-nevermind-album-cover.JPG" /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;....sheesh....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3394248656657558730?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3394248656657558730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3394248656657558730' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3394248656657558730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3394248656657558730'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/06/how-to-make-yourself-feel-old-instantly.html' title='How to make yourself feel old, instantly'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-2066308006211448354</id><published>2007-06-22T11:02:00.000+01:00</published><updated>2007-06-22T11:08:22.331+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='neologisms'/><category scheme='http://www.blogger.com/atom/ns#' term='words'/><category scheme='http://www.blogger.com/atom/ns#' term='social software'/><category scheme='http://www.blogger.com/atom/ns#' term='annoying'/><category scheme='http://www.blogger.com/atom/ns#' term='language'/><title type='text'>"Folksonomy" : The Most Irritating Word On Teh Intarweb!</title><content type='html'>It's official! &lt;br /&gt;&lt;br /&gt;At least, &lt;a href="http://www.chinadaily.com.cn/world/2007-06/22/content_899724.htm"&gt;according to YouGov&lt;/a&gt; that is.&lt;br /&gt;&lt;br /&gt;And the second and third most annoying words on the internet?&lt;br /&gt;"Blogosphere", followed by "blog". "Wiki" came in tenth - so evidently wikis still have some way to go before people are sick to death of them. Maybe as they gain more traction in the enterprise, the word will rise through the ranks?&lt;br /&gt;&lt;br /&gt;So next time you trumpet the social software manifesto, bear this in mind...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-2066308006211448354?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/2066308006211448354/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=2066308006211448354' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2066308006211448354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2066308006211448354'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/06/folksonomy-most-irritating-word-on-teh.html' title='&quot;Folksonomy&quot; : The Most Irritating Word On Teh Intarweb!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-6982633750679391103</id><published>2007-06-14T15:44:00.001+01:00</published><updated>2007-06-14T15:44:23.690+01:00</updated><title type='text'>When Facial Hair Becomes Compulsory</title><content type='html'>&lt;div style="float: right; margin-left: 10px; margin-bottom: 10px;"&gt; &lt;a href="http://www.flickr.com/photos/mccraig/547591310/" title="photo sharing"&gt;&lt;img src="http://farm2.static.flickr.com/1400/547591310_0384be7e1e_m.jpg" alt="" style="border: solid 2px #000000;" /&gt;&lt;/a&gt; &lt;br /&gt; &lt;span style="font-size: 0.9em; margin-top: 0px;"&gt;  &lt;a href="http://www.flickr.com/photos/mccraig/547591310/"&gt;DSC_5998&lt;/a&gt;  &lt;br /&gt;  Originally uploaded by &lt;a href="http://www.flickr.com/people/mccraig/"&gt;mccraig&lt;/a&gt; &lt;/span&gt;&lt;/div&gt;It had to happen eventually - and today it did. By directorial decree, strange facial hair moved from a mere convention to a compulsory requirement at Trampoline. No exceptions!&lt;br /&gt;&lt;br /&gt;I was aiming for a Zappa look, but unfortunately it turned out to be alarmingly close to George Michael... look out for me slumped at the wheel of a beemer in a Hampstead layby near you...&lt;br clear="all" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-6982633750679391103?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/6982633750679391103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=6982633750679391103' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6982633750679391103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/6982633750679391103'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/06/when-facial-hair-becomes-compulsory.html' title='When Facial Hair Becomes Compulsory'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm2.static.flickr.com/1400/547591310_0384be7e1e_t.jpg' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-9054709727048843404</id><published>2007-05-30T15:27:00.000+01:00</published><updated>2007-05-30T15:39:42.950+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='last.fm'/><category scheme='http://www.blogger.com/atom/ns#' term='web 2.0'/><category scheme='http://www.blogger.com/atom/ns#' term='buyout'/><category scheme='http://www.blogger.com/atom/ns#' term='business'/><title type='text'>Last.fm - yours for £140m, squire....</title><content type='html'>Woah - the Web 2.0 buyout bubble comes to Shoreditch, with our near 'n' dear neighbours &lt;a href="http://www.last.fm"&gt;last.fm&lt;/a&gt; announcing today that &lt;a href="http://news.bbc.co.uk/1/hi/technology/6701863.stm"&gt;they've been bought out by none other than CBS&lt;/a&gt; for the eye-watering, pant-moistening, gast-flabbering sum of £140 million. &lt;br /&gt;&lt;br /&gt;Congratulations guys! And there we were thinking these kind of deals only happened in California... &lt;em&gt;GIT'&lt;/em&gt;chaw web 2 businesses 'ere, mate, get'em while they're hot...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-9054709727048843404?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/9054709727048843404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=9054709727048843404' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/9054709727048843404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/9054709727048843404'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/05/lastfm-yours-for-140m-squire.html' title='Last.fm - yours for £140m, squire....'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1046331250980347096</id><published>2007-05-23T10:26:00.000+01:00</published><updated>2007-05-23T11:12:04.200+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ethics'/><category scheme='http://www.blogger.com/atom/ns#' term='society'/><category scheme='http://www.blogger.com/atom/ns#' term='social software'/><category scheme='http://www.blogger.com/atom/ns#' term='responsibility'/><title type='text'>Anti-Social Social Software - How far is Too Far?</title><content type='html'>The social software revolution has been, more or less, about openness. Transparency and freedom have been the order of the day. "Information wants to be free!" goes the mantra, and by and large it's hard to argue against it. All kinds of information that has always been publically available is becoming gathered together, cross referenced and made searchable by the new wave of social software mashups and visualisations, and those who expressed doubt or indignation about this approach have been dismissed as relics of a bygone era, who "just don't get it". &lt;br /&gt;&lt;br /&gt;&lt;em&gt;(I'm including myself in this, by the way. Trampoline's &lt;a href="http://enron.trampolinesystems.com"&gt;Enron Explorer&lt;/a&gt; is a perfect case in point. The information was already public domain, and we weren't even the first to visualise it - we just put a Web 2.0 face on it, and got lucky with the timing.)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;But now and again there comes an application of this philosophy that makes me wonder how far is too far? Enter the charmingly named &lt;a href="http://yro.slashdot.org/article.pl?sid=07/05/22/1234229&amp;from=rss"&gt;www.whosarat.com&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;According to the &lt;a href="http://www.nytimes.com/2007/05/22/washington/22plea.html?_r=1&amp;th&amp;emc=th&amp;oref=slogin"&gt;New York Times&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;q&gt;The site was started by Sean Bucci in 2004, after he was indicted in federal court in Boston on marijuana charges based on information from an informant.&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;Initially free, it now charges charges between $7.99 for a week and $89.99 for life. You don't have to be as cynical as me to take a guess as to the kind of people who will happily pay to see names and mugshots of the 4,300 informers and 400 undercover agents that the site says it has identified.&lt;br /&gt;&lt;br /&gt;So is this too far? &lt;br /&gt;Is it possible to define a clear moral line between a "good" use of public-domain data and "bad" ? Even if it is possible, should we?&lt;br /&gt;Should some usages be permitted but not others? How do we tell the difference?&lt;br /&gt;&lt;br /&gt;What is it that actually makes me uneasy about this?&lt;br /&gt;&lt;br /&gt;The trouble is that any time you actually try to define what's acceptable and what isn't, in clear unambiguous language, you can always find a counter-example that destroys your mental partitioning of the world and means you have to start again.&lt;br /&gt;&lt;br /&gt;The only way I can put my finger on what bothers me about the site is to refer to the intent behind it. Where the intent behind a mashup like &lt;a href="http://www.chicagocrime.org/"&gt;Chicago Crime&lt;/a&gt; could reasonably be argued to be beneficial to society in some way, Who's A Rat is clearly not. &lt;br /&gt;&lt;br /&gt;But even that definition is full of ambiguity. Who's A Rat may well be of immense benefit to certain segments of society (Hal Helms' "Vinny" persona, for one) and Chicago Crime could be argued to be detrimental to, say, property owners in high-crime areas who are looking to sell. &lt;br /&gt;&lt;br /&gt;I guess the hoary old cliche applies just as much to information as to people : &lt;q&gt;with great freedom comes great responsibility&lt;/q&gt;. We can't on the one hand argue that information needs to be free, yet on the other hand argue that it can only be free for usages that we like. Either the information is out there or it isn't, and I guess that if we want to gain the benefits of information freedom, we have to be prepared to tolerate the drawbacks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1046331250980347096?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1046331250980347096/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1046331250980347096' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1046331250980347096'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1046331250980347096'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/05/anti-social-social-software-how-far-is.html' title='Anti-Social Social Software - How far is Too Far?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1686939610472265067</id><published>2007-05-11T10:49:00.000+01:00</published><updated>2007-05-11T13:12:20.969+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='laptop'/><category scheme='http://www.blogger.com/atom/ns#' term='vista'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='pain'/><title type='text'>Come Back XP, All Is Forgiven</title><content type='html'>A couple of months ago, after the &lt;a href="http://instantbadger.blogspot.com/2007/02/curse-continues.html"&gt;burglary&lt;/a&gt;, I replaced the nicked big-red-beast of a laptop with a shiny new Core 2 Duo / 2GB of RAM laptop, powerful enough to run &lt;a href="http://www.trampolinesystems.com"&gt;SONAR&lt;/a&gt;, all its dependencies, and a Java IDE at the same time without dying. It's still heavier than your average laptop, but not quite so much as to generate its own gravitational pull, like the old one. (It's so much more calming to be able to site down and browse t'intarweb without small objects orbiting your laptop, you know? The accretion disk of pens and paperclips always got in my way, and the time distortions played hell with my google calendar...)&lt;br /&gt;&lt;br /&gt;Anyway, at the time I bought it, the *ONLY* OS you could get from Dell was Vista. I ummed and aahed, and debated and discussed, and eventually decided to ignore my own standard policy of wait-till-at-least-service-pack-1-before-installing-any-new-MS-OS and take the plunge. It arrived, and I admit I was pleasantly surprised..... at first. Until I tried to put any software on it.&lt;br /&gt;&lt;br /&gt;Firefox often (about half the time) won't start, the process just sits there using ~ 4M of RAM and never even getting the splash screen, and the process can't be killed. I have to reboot the laptop and try again.&lt;br /&gt;&lt;br /&gt;The Apache 2.2 service won't start.  &lt;br /&gt;&lt;br /&gt;The Bluedragon 7 JX service will start, but immediately stop. &lt;br /&gt;&lt;br /&gt;Windows Firewall drives me so nuts I had to turn it off to prevent an unfortunate episode involving me, a shopping mall, a chainsaw, and visions of GTA3.&lt;br /&gt;&lt;br /&gt;User Account Control - yes, yes, &lt;strong&gt;I KNOW&lt;/strong&gt; it's off! I turned it off deliberately, dammit! Stop telling me about it!&lt;br /&gt;&lt;br /&gt;Office 2007 - a brave idea, to depart from the established interface so completely, but where the hell is the "Save As..." command? Where's it gone?! The help file tells you how to use it, but doesn't tell you where it is! Can't I load a document, make some changes and then save it to a different filename anymore? I'm downgrading.&lt;br /&gt;&lt;br /&gt;There's a pre-installed copy of CineLink PowerDVD that I've &lt;strong&gt;NEVER&lt;/strong&gt; run, but seems to be permanently resident and using up to 200MB of RAM - is this a DRM thing..?&lt;br /&gt;&lt;br /&gt;Roll on service pack 1....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1686939610472265067?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1686939610472265067/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1686939610472265067' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1686939610472265067'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1686939610472265067'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/05/come-back-xp-all-is-forgiven.html' title='Come Back XP, All Is Forgiven'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-2075140643252384049</id><published>2007-03-21T09:39:00.000Z</published><updated>2007-03-21T09:48:02.893Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='trampoline'/><category scheme='http://www.blogger.com/atom/ns#' term='recruitment'/><category scheme='http://www.blogger.com/atom/ns#' term='developers'/><category scheme='http://www.blogger.com/atom/ns#' term='work'/><category scheme='http://www.blogger.com/atom/ns#' term='jobs'/><category scheme='http://www.blogger.com/atom/ns#' term='techies'/><title type='text'>New Trampolinees Needed!</title><content type='html'>&lt;a href="http://www.trampolinesystems.com/weblog/we-want-fresh-engineer-blood/"&gt;We're hiring!&lt;/a&gt; Oh yes indeedy - techies are needed at Trampoline, with experience of some or all of these :&lt;br /&gt;&lt;br /&gt;Java, Hibernate, Spring framework, JMS, Ruby, Ruby on Rails, Transactions, Asynchronous systems, Transactional messaging, SQL, MySQL, Oracle, Linux sysadmin, Windows sysadmin&lt;br /&gt;&lt;br /&gt;But far more important than that is that they just &lt;em&gt;fit&lt;/em&gt;. Come help us drag the enterprise market kicking and screaming into the 2.0 / 3.0 / buzzword-of-your-choice world. It's fun! It's hard work, but it's fun.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-2075140643252384049?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/2075140643252384049/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=2075140643252384049' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2075140643252384049'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2075140643252384049'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/03/new-trampolinees-needed.html' title='New Trampolinees Needed!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-8169321052380388307</id><published>2007-03-16T10:21:00.000Z</published><updated>2007-03-16T10:36:44.197Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='developer'/><category scheme='http://www.blogger.com/atom/ns#' term='work'/><category scheme='http://www.blogger.com/atom/ns#' term='contract'/><category scheme='http://www.blogger.com/atom/ns#' term='equity'/><category scheme='http://www.blogger.com/atom/ns#' term='offer'/><title type='text'>"Will Code For Equity" - sound like anyone you know?</title><content type='html'>A friend of mine has an old CF/Fusebox site based around moving house, and finding companies to help you do that. She wants to completely revamp it and bring it in to the 2.0 era with  user feedback, recommendations and reputations, etc, and she needs a developer. &lt;br /&gt;&lt;br /&gt;Trouble is, although her existing site has been supporting her for two years, she doesn't have much money up front for a dev to revamp and relaunch it. &lt;br /&gt;&lt;br /&gt;So the deal she's offering is, basically, take charge of the site and get free reign to do whatever you think is a good idea, in return for a decent chunk of equity and a corresponding share of the profits. &lt;br /&gt;&lt;br /&gt;I'm not taking on any side work, I've got my hands more than full with &lt;a href="http://www.trampolinesystems.com"&gt;Trampoline&lt;/a&gt;, but I told her I'd ask around, so if this sounds like anyone you know (and, crucially, you'd be happy to recommend them - she &lt;em&gt;is&lt;/em&gt; a friend, after all, and I don't want to see her get ripped off) please give me a shout. Email address is in the "Other Links" section on the right, just under my gurning fizzog&lt;a href="#footnote1"&gt;**&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Sheesh. Coding for equity - does this sound like Bubble 2.0 to anyone else....?&lt;br /&gt;&lt;br /&gt;&lt;a name="footnote1"&gt;**&lt;/a&gt;&lt;small&gt;&lt;em&gt;Translation for non-Northern people: grinning face&lt;/em&gt;&lt;/small&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-8169321052380388307?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/8169321052380388307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=8169321052380388307' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/8169321052380388307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/8169321052380388307'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/03/will-code-for-equity-sound-like-anyone.html' title='&quot;Will Code For Equity&quot; - sound like anyone you know?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1128421653367013164</id><published>2007-03-15T11:19:00.000Z</published><updated>2007-03-15T11:57:49.548Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='trampoline'/><category scheme='http://www.blogger.com/atom/ns#' term='investment'/><category scheme='http://www.blogger.com/atom/ns#' term='legal'/><category scheme='http://www.blogger.com/atom/ns#' term='lawyers'/><category scheme='http://www.blogger.com/atom/ns#' term='due diligence'/><category scheme='http://www.blogger.com/atom/ns#' term='business'/><title type='text'>Finally, I Can Say It!</title><content type='html'>Phew, at &lt;em&gt;last&lt;/em&gt; I can say what's been going on, why I haven't been blogging much recently, etc etc - it's because we've been working so hard to close THIS: &lt;a href="http://www.trampolinesystems.com/weblog/trampoline-systems-receives-%C2%A33-million-funding/"&gt;Trampoline Systems receives &amp;pound;3 million funding&lt;/a&gt;. YAAAAAAAAAAAAY!!! We did it!!! Woo and Yay and quite possibly Hoopla aswell! It's going to make a huge difference to the business, and let us step up a level or two in scale and approach.&lt;br /&gt;&lt;br /&gt;I can also reveal that Dante was wrong. There is an eighth circle of hell - it's called "Due Diligence" and is full of lawyers. Lots of lawyers asking lots of questions that make you feel like you're being gradually eaten away by a swarm of bees, and make you want to stick your face in a deep fat fryer just to be rid of them. &lt;br /&gt;&lt;br /&gt;But you'd have to make sure you kept paper copies, in triplicate, of the license agreement from the fryer company that gave you explicit permission to use it for face-boiling purposes, &lt;em&gt;and&lt;/em&gt; prove that you complied with every letter of the fryer license in distributing the bits of hot fat around the room...&lt;br /&gt;&lt;br /&gt;So here's a bit of advice. If you think that there's even the slightest possibility of your company &lt;em&gt;ever&lt;/em&gt; getting outside investment, or being taken over, or floated in an IPO, make DAMN SURE that you :&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Keep a record&lt;/strong&gt; of all open-source software you use, whether you redistribute it or not&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;keep copies of the license agreements&lt;/strong&gt; for ANY open-source software that you use, whether you distribute it or not&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;keep proof&lt;/strong&gt; that anything you re-distribute has been distributed in accordance with the terms of the license&lt;br /&gt;- i.e. if it's an MIT license, you have to provide a copy of the license and copyright notices along with any redistribution, so just keep a copy of what you provide to anyone else&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;read and understand the license terms&lt;/strong&gt; before you redistribute!&lt;br /&gt;- this is important, really! For instance, if you provide anyone with a copy of, say, the Oracle OCI client driver, pay close attention to the documentation requirements that it puts on you, and the implicit permission that gives Oracle to audit you at any time.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;It's quite straightforward to do this whenever you download some open-source stuff. Just keep a directory somewhere that you can dump license agreements and copyright notices, and keep, say, a Google doc spreadsheet that just records the name of the library, the URL to download it, and the type of license it is under. &lt;br /&gt;&lt;br /&gt;If you keep these up to date as and when you download OSS, it only takes a few minutes each time. If, like us, and I'm sure, most people, you didn't think to do it as you went along and waited till you were asked for &lt;em&gt;all&lt;/em&gt; the information on &lt;em&gt;everything&lt;/em&gt;, &lt;em&gt;right now&lt;/em&gt; - then you're in for an awfully long slog of awfully tedious work. A little bit of diligence now saves a massive amount of retro-diligence in future.&lt;br /&gt;&lt;br /&gt;Now, where's those Trampoline-branded space hoppers, baths full of champagne, and toilet roll stock option dispensers ? Boo.com 2.0, here we come.....&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1128421653367013164?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1128421653367013164/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1128421653367013164' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1128421653367013164'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1128421653367013164'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/03/finally-i-can-say-it.html' title='Finally, I Can Say It!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-2888817465872173361</id><published>2007-03-02T13:10:00.000Z</published><updated>2007-03-02T13:14:28.530Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='humour'/><category scheme='http://www.blogger.com/atom/ns#' term='sex'/><category scheme='http://www.blogger.com/atom/ns#' term='irish'/><category scheme='http://www.blogger.com/atom/ns#' term='donkey'/><title type='text'>Irish Donkey Sex Case Brings Down Galway First</title><content type='html'>I'm speechless - there are so many things to love about this story I don't even know where to start, right down to the name of the receptionist and the guy's claim that it was a "super-rabbit"... and is there really such a thing as the "Unlawful Accommodation of Donkeys Act"? &lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.theregister.co.uk/2007/03/02/donkey_shocker/"&gt;Irish Donkey Sex Case Shocks Net&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Was there really so much dubious accomodation of donkeys going on in 1837 that they felt the need to pass a specific law against it?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-2888817465872173361?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/2888817465872173361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=2888817465872173361' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2888817465872173361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2888817465872173361'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/03/irish-donkey-sex-case-brings-down.html' title='Irish Donkey Sex Case Brings Down Galway First'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-356893181671387787</id><published>2007-03-01T09:54:00.000Z</published><updated>2007-03-01T10:07:14.758Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='hollywood'/><category scheme='http://www.blogger.com/atom/ns#' term='culture'/><category scheme='http://www.blogger.com/atom/ns#' term='movies'/><category scheme='http://www.blogger.com/atom/ns#' term='ot'/><category scheme='http://www.blogger.com/atom/ns#' term='satire'/><title type='text'>[OT] [Rant] That's it, that's the last straw</title><content type='html'>That's it - we can't pretend any more, it's finally undeniable - Hollywood has officially run out of ideas completely. &lt;br /&gt;&lt;br /&gt;Honestly, in the last couple of years we've had execrable remakes of classic Asian films like The Ring, Ju-on (aka The Grudge) and The Eye, and now they're even &lt;a href="http://richardleggett.co.uk/blog/index.php/2007/03/01/the_condemned"&gt;plundering Battle Royale&lt;/a&gt;. I'm not even going to mention the appalling crime against humanity that was the remake of The Wicker Man. &lt;br /&gt;&lt;br /&gt;I give up. I really do. Satire has become the new truth. You can't even satirize anymore, there's no point - reality has become the ultimate ironic piss-take of itself. Just take a look at this article from The Onion - &lt;a href="http://www.theonion.com/content/node/29830"&gt;U.S. Dept. Of Retro Warns: 'We May Be Running Out Of Past'&lt;/a&gt; dated 1997, and try to tell me it hasn't actually come to pass. &lt;br /&gt;&lt;br /&gt;The saddest thing is that most westeners will only see the remakes, and so they'll associate the titles of these fantastic movies with the watered-down, over-produced, bastardised Hollywood interpretations that are (as far as I can see) mediocre at best, rather than the classic originals. &lt;br /&gt;&lt;br /&gt;Honestly, can anyone give me &lt;em&gt;one original idea&lt;/em&gt; that Hollywood has produced in the last couple of years? And yet, it's undoubtedly piracy that's causing declining revenues, oh yes - not the fact that virtually everything they put out these days is utter shite, oh no. It's them evil pirates, yes sir...&lt;br /&gt;&lt;br /&gt;&lt;em&gt;*sigh*&lt;/em&gt; I need coffee.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-356893181671387787?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/356893181671387787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=356893181671387787' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/356893181671387787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/356893181671387787'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/03/ot-rant-thats-it-thats-last-straw.html' title='[OT] [Rant] That&apos;s it, that&apos;s the last straw'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-7729921731318637957</id><published>2007-02-23T12:21:00.000Z</published><updated>2007-02-23T14:02:46.905Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='statistics'/><category scheme='http://www.blogger.com/atom/ns#' term='statsvn'/><category scheme='http://www.blogger.com/atom/ns#' term='graphs'/><category scheme='http://www.blogger.com/atom/ns#' term='version control'/><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='repository'/><category scheme='http://www.blogger.com/atom/ns#' term='svn'/><title type='text'>StatSVN - Ruby vs. Java and "Developer Of The Month"</title><content type='html'>&lt;p&gt;I'm a big fan of &lt;a href="http://subversion.tigris.org"&gt;SVN&lt;/a&gt;, and I know it's in very common use throughout the development community, so I thought I'd give a shout out to an interesting project that extracts some fascinating SVN stats and draws lots of pretty pictures - &lt;a href="http://www.statsvn.org/"&gt;StatSVN&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;We've now added a StatSVN task to our build scripts, and the figures it kicks out are just great - once you start looking, you can lose yourself in there for ages....&lt;/p&gt;&lt;br /&gt;&lt;p&gt;To give some random samples:&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;As of three days ago, there were 75130 lines of code in the Sonar trunk, of which Craig has contributed 33.6%, myself 32.1% and Jan 22.2%. As Jan has mostly been working on the Ruby On Rails interface, and Craig and I have mostly been writing the Java backend, what does this say about the relative expressive efficiency and productivity of each language?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Craig took "Developer Of The Month" for February, with 5286 lines, taking over from me in January with 7546 lines.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Over 80% of my changes are additions, with under 20% being updates. Craig is more or less the same, with a few percent more modifications - probably due to fixing my occasional "EVIL" quick hacks...&lt;/li&gt;&lt;br /&gt;&lt;li&gt;By far and away the most commits &lt;em&gt;overall&lt;/em&gt; get done between 4pm and 5pm, but personally, I do three times as many commits between 2pm and 3pm than any other hour. Probably because I keep saying to myself &lt;q&gt;got to finish this before I go to lunch...&lt;/q&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Jan has not done a single commit before 12 noon :-) However, he's the only person to have committed between 1am and 2am!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I do a huge number of commits on a Monday (400+), followed by successively fewer every day until Friday (~60). Does this mean I'm fresher and more enthusiastic after the weekend, or is it that when I start the week, I go for the easiest tasks first, and ramp up towards the more difficult tasks as I go on? On the other hand, &lt;em&gt;overall&lt;/em&gt; the most commits are done on a Friday, followed by Tuesday&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;You can also measure some metrics around files, rather than developers:&lt;/p&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Overall, across all file types, we have an average of around 40 lines of code per file&lt;/li&gt;&lt;br /&gt;&lt;li&gt;58.7% of the files in the repository are .java, but they contribute 73.3% of the total lines of code&lt;/li&gt;&lt;br /&gt;&lt;li&gt;On average, each .rhtml template is just 26.6 lines of code&lt;/li&gt;&lt;br /&gt;&lt;li&gt;The most amended file in the whole repository is the Rakefile!&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;I could go on for hours....but it's nearly 2pm and I need to commit some code, dammit! So install &lt;a href="http://www.statsvn.org/"&gt;StatSVN&lt;/a&gt; on your repository and have a play yourself. It's fun.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-7729921731318637957?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/7729921731318637957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=7729921731318637957' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7729921731318637957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7729921731318637957'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/02/statsvn-ruby-vs-java-and-developer-of.html' title='StatSVN - Ruby vs. Java and &quot;Developer Of The Month&quot;'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-7383082067302067284</id><published>2007-02-21T10:42:00.000Z</published><updated>2007-02-21T11:21:45.996Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='humour'/><category scheme='http://www.blogger.com/atom/ns#' term='michael kelly'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><category scheme='http://www.blogger.com/atom/ns#' term='clingfilm'/><category scheme='http://www.blogger.com/atom/ns#' term='roy orbison'/><category scheme='http://www.blogger.com/atom/ns#' term='writing'/><category scheme='http://www.blogger.com/atom/ns#' term='warped'/><title type='text'>Roy Orbison In Clingfilm In Print!</title><content type='html'>&lt;em&gt;(ahem)&lt;/em&gt; Allow me to explain that title.... A few years back, myself and some of the Freeserve guys stumbled across the cringe-inducingly hysterical &lt;a href="http://www.michaelkelly.fsnet.co.uk/karl.htm"&gt;Ulli's Roy Orbison In Clingfilm site&lt;/a&gt;, a pastiche of &lt;a href="http://en.wikipedia.org/wiki/Slash_fiction"&gt;Slash Fiction&lt;/a&gt; consisting of a selection of stories involving Roy Orbison turning up unexpectedly, and by increasingly unlikely chains of events, ending up wrapped in clingfilm.&lt;br /&gt;&lt;br /&gt;If you haven't read it, and you fancy skirting that fine line between weeing yourself laughing and feeling unaccountably disturbed, go have a look now, and then come back... go on, it's ok, I'll wait...&lt;br /&gt;&lt;br /&gt;Back? Good. I'll continue.&lt;br /&gt;&lt;br /&gt;"Ulrich Haarbürste" is a nom-de-guerre of &lt;a href="http://www.michaelkelly.fsnet.co.uk/"&gt;Michael Kelly&lt;/a&gt;, whose &lt;a href="http://www.michaelkelly.fsnet.co.uk/"&gt;Page of Misery&lt;/a&gt; is a veritable treasure trove of warped, cynical, misanthropic comedy gold, and has been the source of several viral emails that you might have had at some point ( &lt;a href="http://www.michaelkelly.fsnet.co.uk/exis.htm"&gt;French Intellectuals In Afghanistan, anyone?&lt;/a&gt; ). &lt;br /&gt;&lt;br /&gt;Anyway, when we discovered the delights of Roy Orbison In Clingfilm, I felt so inspired that &lt;a href="http://www.last.fm/music/Dr+Snooks/Roy+Orbison+In+Clingfilm"&gt;I set the first story to music&lt;/a&gt;, using the Microsoft Agent control to read the text in a Steven Hawking stylee, and using various Cubase plugins (e.g. Voice Designer) to change the pitch and formants of the voices for Roy and Ulli. It was quite a fun experiment, and once I was done, I sent a copy to Michael. He loved it - and so did a  growing number of people - if you Google for &lt;a href="http://www.google.co.uk/search?hl=en&amp;q=al+davidson+roy+orbison+in+cling+film&amp;btnG=Search&amp;meta="&gt;"Al Davidson Roy Orbison In Cling Film"&lt;/a&gt; it turns up in a surprising number of playlists...&lt;br /&gt;&lt;br /&gt;So I'm genuinely chuffed to hear that Michael Kelly has produced a &lt;a href="http://www.michaelkelly.fsnet.co.uk/book.htm"&gt;Roy Orbison In Clingfilm book!&lt;/a&gt; You can &lt;a href="http://www.troubador.co.uk/book_info.asp?bookid=434"&gt;buy it HERE&lt;/a&gt; - go on, you know you want to, it'll make a delightful coffee table discussion piece, guaranteed to break the ice at parties, and scare off the neighbours' children too!&lt;br /&gt;&lt;br /&gt;I should just add that I'm not getting any referral kickbacks off this - I just think the esteemed Mr Kelly's talent deserves all the exposure it can get, and I hope he gets showered with riches and future publication contracts from his book. Tell your friends. And after the week I've had, a bit of warped humour is exactly what I need. Tell the world! Lets see if we can get Roy Orbison In Clingfilm into some best seller lists!&lt;br /&gt;&lt;br /&gt;&lt;em&gt;PS - To celebrate the occasion, I moved the song from its previous home onto a more permanent home at &lt;a href="http://www.last.fm/music/Dr+Snooks/Roy+Orbison+In+Clingfilm"&gt;last.fm&lt;/a&gt;, along with a couple of other self-indulgent noodly guitar instrumentals. I keep meaning to finish off the hundreds of tunes-in-progress I've got kicking around on my hard drive and upload those too, but somehow there's never quite enough time...&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-7383082067302067284?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/7383082067302067284/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=7383082067302067284' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7383082067302067284'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7383082067302067284'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/02/roy-orbison-in-clingfilm-in-print.html' title='Roy Orbison In Clingfilm In Print!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-7764213223888678891</id><published>2007-02-20T09:26:00.000Z</published><updated>2007-02-20T10:22:59.708Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='flat'/><category scheme='http://www.blogger.com/atom/ns#' term='bad luck'/><category scheme='http://www.blogger.com/atom/ns#' term='gas'/><category scheme='http://www.blogger.com/atom/ns#' term='explosion'/><title type='text'>The Icing On The Cake</title><content type='html'>Did someone break a mirror or something? &lt;br /&gt;&lt;br /&gt;I spent the weekend clearing up after the &lt;a href="http://instantbadger.blogspot.com/2007/02/curse-continues.html"&gt;burglary&lt;/a&gt; and running around like a blue-arsed fly, getting locks replaced and notifying the insurance company, getting some alarms and setting them up, trying and failing to get someone to come and replace the back door with its now-gaping-hole-where-the-catflap-used-to-be, and ending up hacking together a temporary repair with two plastic chopping boards and big tube of superglue (I kid you not!), and was finally starting to feel secure in my own home again. &lt;br /&gt;&lt;br /&gt;So I made a conscious decision to try and relax on Sunday evening. I put my feet up, had a couple of beers, settled into the sofa for a wind-down in front of the TV followed by an early night, when about nine o' clock the phone rang. It was the tenant from one of Lisa's parents' flats. &lt;br /&gt;&lt;br /&gt;"Can you ask him to call us urgently?" she said. "There's been an explosion!"&lt;br /&gt;&lt;br /&gt;Lisa's parents own the whole building, and rent out the bottom floor to a restaurant, and the two flats above it to private tenants. We used to live in the first floor flat. &lt;br /&gt;&lt;br /&gt;As it happens, the restauranteurs had been having some renovation work done, and there'd been a gas leak in the kitchen.... and on Sunday night, it went BOOM. Quite loudly, apparently. The police were round, and needed to get the landlord's details. One of the tenants in the first floor flat was hurt and had to go to hospital. The guy who'd been doing the renovation work has disappeared back to Morocco, and can't be contacted.&lt;br /&gt;&lt;br /&gt;I'd already had a couple of beers, so I couldn't drive anywhere that night. So I dashed round there 7am the next morning to take a look at the first floor flat - the gas had obviously gone up the middle of the wall between the kitchen and bathroom, and when it ignited, it blew out that wall on both sides. The kitchen units were wrecked, and there's damage to the wall and ceiling. &lt;br /&gt;&lt;br /&gt;Do you ever get the feeling that the universe is trying to tell you something?&lt;br /&gt;&lt;br /&gt;At least I've still got my guitars - I feel a country song coming on...!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-7764213223888678891?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/7764213223888678891/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=7764213223888678891' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7764213223888678891'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7764213223888678891'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/02/icing-on-cake.html' title='The Icing On The Cake'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-7043644944415329697</id><published>2007-02-16T07:58:00.000Z</published><updated>2008-01-22T10:25:35.942Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='lise'/><category scheme='http://www.blogger.com/atom/ns#' term='personal'/><category scheme='http://www.blogger.com/atom/ns#' term='crime'/><category scheme='http://www.blogger.com/atom/ns#' term='burglary'/><category scheme='http://www.blogger.com/atom/ns#' term='police'/><category scheme='http://www.blogger.com/atom/ns#' term='home'/><title type='text'>The Curse Continues....</title><content type='html'>Every time Lise goes away without me, something bad happens - we joke about it, but I'm not really in the mood for laughing right now. She's currently in Malaysia with her family, eating superhuman quantities of food and visiting relatives, and me..? Well, I got back from work yesterday to discover we'd been burgled.&lt;br /&gt;&lt;br /&gt;I knew something was strange as soon as I got back from work about 8pm, because I couldn't get in. My key was turning in the Yale lock, but it wasn't opening. I had to knock on the neighbour's door and ask them to let me through into their back garden, so  that I could get into &lt;em&gt;our&lt;/em&gt; garden and see if I'd left a window unlocked anywhere, although I was sure I hadn't. &lt;br /&gt;&lt;br /&gt;&lt;em&gt;At this point I thought I'd just managed to lock myself out somehow, or something had gone wrong with the lock... I fitted it myself, so it's perfectly possible that I messed it up somehow - I inherited lots of things from my Dad, including a tall slim build, a stubbornly-resilient head of hair and an overpowering love of guitars, but sadly his skill with DIY completely passed me by.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;So, bumbling about in the back garden with my neighbour, I tried the back door, and it was open. "Yay for my lax security!" I cried, big slightly-embarrased grin with the neighbour, &lt;em&gt;gaw-blimey-what-a-muppet-I-am-guvnor, forget-me-own-head-if-it-wasnt-screwed-on&lt;/em&gt; expression on my face as I went inside. And it was as soon as I walked into the kitchen that I realised what had happened.&lt;br /&gt;&lt;br /&gt;It looks like they kicked in the catflap in the back door, then managed to get to the key, which was in the lock - either with a very long thin arm, or some bent coat-hanger kind of contraption, I don't know. But I'm sure that's how they got in. I figure they must have then gone straight through to the front door and flipped the deadlock switch - so they wouldn't be disturbed if I came home early and tried to let myself in through the front door.&lt;br /&gt;&lt;br /&gt;The bastards had torn everything off the shelves and emptied every box onto the floor, in every room, evidently looking for money or small items they could fence quickly, like jewellery. They tore all our clothes out of the cupboards and drawers, went through everything, and trampled lots of back-garden mud into our beautiful fluffy white deep-pile bedroom carpets in the process.&lt;br /&gt;&lt;br /&gt;Luckily, they didn't steal much. They must have been in and out within a few minutes. They took :&lt;br /&gt;&lt;ul&gt;&lt;li&gt;a Nikon Coolpix 6MP digital camera&lt;/li&gt;&lt;li&gt;possibly an old Sony Ericcson mobile, but most importantly&lt;/li&gt;&lt;li&gt;Lisa's Dad's Toshiba laptop.&lt;/li&gt;&lt;li&gt;possiby some of Lisa's jewellery.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Anyone who's seen me present at a London CFUG will probably remember the laptop - it's HUGE (17"), shiny and red, and weighs an absolute ton. So if by any chance you see it cropping up on eBay or in your local Cash Converters, email me.&lt;br /&gt;&lt;br /&gt;It could have been far worse, of course. They &lt;em&gt;didn't&lt;/em&gt; take:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;any of my guitar collection&lt;/li&gt;&lt;li&gt;my main PC&lt;/li&gt;&lt;li&gt;the TV, DVD player, X-box, etc&lt;/li&gt;&lt;li&gt;my passport - phew! - despite all my paperwork living in orderly, clearly-labelled box files&lt;/li&gt;&lt;li&gt;Lisa's treasured Jimmy Choo shoes&lt;/li&gt;&lt;li&gt;any of the climbing/mountaineering equipment, despite going through it all&lt;/li&gt;&lt;li&gt;my stupidly expensive mountaineering watch, which I bought with my leaving present (gift vouchers) from Headshift&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;em&gt;(Sadly they didn't even take the all-in-one printer that I ordered online ages ago, without realising what a huge great monstrous carbuncle it was going to be, and have regretted buying ever since)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;And they didn't leave any nasty surprises on the carpet / up the wall / etc. When I got burgled back in Bridlington (by a guy who I thought was my best friend at the time) they left a nice big turd in the middle of the carpet, and killed the cat in the process. When my mum got burgled a few years ago, they smeared shit up the walls and left what I'll just call a "deposit" in her underwear drawer. I'm thankful that they didn't do any of that in our flat.&lt;br /&gt;&lt;br /&gt;It must be a sign of the times that I was far more worried about them stealing my &lt;em&gt;identity&lt;/em&gt; and &lt;em&gt;information&lt;/em&gt; than my posessions. They now know my name, what I look like, where I live and where I work. I'm still worried about what was on the laptop. Did I have any banking information on there? Did Lise? Too late to check now. Hopefully if they didn't steal my passport, they're not sharp enough to realise what they have, and they'll just format the disk before flogging it on - but you just don't know, do you - and that's going to worry me.&lt;br /&gt;&lt;br /&gt;Things like this make you realise, in fact, that when someone breaks in and steals your stuff, it's just &lt;em&gt;stuff&lt;/em&gt; - and stuff doesn't actually matter that much. Stuff can be replaced. Mess can be tidied up - it took me until 2:30am this morning, but I cleared it all up. What matters is people, and I'm just grateful that Lise wasn't here when they came, and that she didn't get the huge shock I did when I walked in, and that she didn't have to see the mess they made of our beautiful home.&lt;br /&gt;&lt;br /&gt;What gets me, though, is that feeling of being &lt;em&gt;violated&lt;/em&gt;. &lt;a href="http://www.phrases.org.uk/bulletin_board/8/messages/1239.html"&gt;"An Englishman's home is his castle"&lt;/a&gt; as the saying goes, and my castle feels...&lt;em&gt;soiled&lt;/em&gt;. Fuckers.&lt;br /&gt;&lt;br /&gt;I've now got a fun-packed day to look forward to today. The police were too busy to come round last night, so they're coming this morning, probably just to take my particulars and give me a crime number for the insurance. I'm sure they've got more urgent priorities, like trying to stop &lt;a href="http://news.bbc.co.uk/1/hi/england/london/6367301.stm"&gt;fifteen-year-olds shooting each other&lt;/a&gt;, which seems to be the preferred pastime-du-jour. I've got to file the insurance claim, get the locks changed, and try to get the back door replaced, if I can.  &lt;br /&gt;&lt;br /&gt;And I also have to tell Lise, and her Dad, over a dodgy mobile phone reception and an 8hr time difference, and I just know that it's going to break her heart. It;s going to ruin the remaining week of her holiday, and she's going to worry herself sick every day until she comes home. &lt;br /&gt;&lt;br /&gt;So let this be a cautionary tale. Never leave your key in the lock! And if you, like me, keep looking at your door or windows and thinking "hmm, really ought to get round to getting that properly fixed-up at some point..." then get round to it TODAY! OK? Ok...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-7043644944415329697?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/7043644944415329697/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=7043644944415329697' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7043644944415329697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/7043644944415329697'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/02/curse-continues.html' title='The Curse Continues....'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-908969552254279900</id><published>2007-02-08T15:15:00.000Z</published><updated>2007-03-12T10:22:28.656Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='devnotes'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='prototype'/><category scheme='http://www.blogger.com/atom/ns#' term='fusebox'/><category scheme='http://www.blogger.com/atom/ns#' term='flip'/><title type='text'>DevNotes On AJAX</title><content type='html'>As a long-time user of the &lt;a href="http://trac.fuseboxframework.org/fusebox/wiki/FLiPMethodology"&gt;FLiP methodology&lt;/a&gt;, there have been many times where I've created a prototype and used &lt;a href="http://www.halhelms.com"&gt;Hal Helms'&lt;/a&gt; &lt;a href="http://www.halhelms.com/code/resources/DevNotes.zip"&gt;DevNotes&lt;/a&gt; as a plonk-and-play way of capturing clients' thoughts and feedback.&lt;br /&gt;&lt;br /&gt;It's a nifty little tool that does the job it sets out to do - but since it was released way back when, teh intarweb has moved on, and I've had a few niggly things that I had to work around when using it :&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;it's rendered using "old-school" HTML, with things like FONT tags&lt;/li&gt;&lt;br /&gt;&lt;li&gt;sometimes you can get conflicts between the DevNotes form submissions and parameters that the containing prototype app requires&lt;/li&gt;&lt;br /&gt;&lt;li&gt;the dependency on cfx_make_tree.dll didn't play nicely with a non-Windows OS&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;So, while noodling around over the weekend recently, I decided to give it a bit of a facelift. One thing led to another, and it ended up as a pretty major update. So laydeez and gennullmun, allow me to present....&lt;em&gt;(drum roll)&lt;/em&gt;&lt;br /&gt;&lt;h3&gt;DevNotes On AJAX!&lt;/h3&gt;&lt;br /&gt;&lt;h4&gt;Main Changes:&lt;/h4&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Removed dependence on CFX_Make_Tree.dll&lt;/strong&gt;,&lt;br /&gt;replaced with udfMakeTree.cfm from &lt;a href="http://www.cflib.org"&gt;cflib.org&lt;/a&gt;, by &lt;a href="http://www.adobe.com/support/forums/team_macromedia/team_members/9.html"&gt;Michael Dinowitz&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Ported to be based on Ajax requests&lt;/strong&gt;,&lt;br /&gt;de-coupling the DevNotes requests from the Prototype page requests, and allowing people to stay on the same Prototype page while adding notes&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Added RSS syndication of notes.&lt;/strong&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Prefixed all request variables and JS functions with DEVNOTES&lt;/strong&gt;&lt;br /&gt;to prevent any potential conflicts with the containing app&lt;br /&gt;Required parameters in the containing app are now:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// these fields required for DevNotes&lt;br /&gt;request.DEVNOTES.devAppName = "UniqueAppname";&lt;br /&gt;request.DEVNOTES.attributeToKeyOn = "page";&lt;br /&gt;request.DEVNOTES.DevNotesDSN = "YourDSNName";&lt;br /&gt;request.DEVNOTES.devnotesRSSTitle = "Title for the RSS feed";&lt;br /&gt;request.DEVNOTES.DevNotesRSSDescription = "Description for the RSS feed";&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Updated HTML to be more semantic and standards-based.&lt;/strong&gt;&lt;br /&gt;Admittedly it might not be &lt;em&gt;100%&lt;/em&gt; XHTML-compliant, but I don't really have time to check it on that - be my guest...&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Introduced CSS styling&lt;/strong&gt;&lt;br /&gt;and &lt;a href="http://www.brandspankingnew.net/archive/2006/12/hohoho.html"&gt;grayscale mini icons from Brand Spanking New&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;Re-factored the code into a fusebox3-stylee app structure.&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h4&gt;Example usage:&lt;/h4&gt;&lt;br /&gt;Extract the archive into a subfolder called "DevNotes" under the root of your prototype app.&lt;br /&gt;&lt;br /&gt;Make sure that the following variables are set up somewhere in your app - fbx_settings.cfm is usually the best place :&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;br /&gt;// these fields required for DevNotes&lt;br /&gt;request.DEVNOTES.devAppName = "UniqueAppname";&lt;br /&gt;request.DEVNOTES.attributeToKeyOn = "page";&lt;br /&gt;request.DEVNOTES.DevNotesDSN = "YourDSNName";&lt;br /&gt;request.DEVNOTES.devnotesRSSTitle = "Title for the RSS feed";&lt;br /&gt;request.DEVNOTES.DevNotesRSSDescription = "Description for the RSS feed";&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Then in your prototype's LAYOUT file, just add&lt;br /&gt;&lt;code&gt;&amp;lt;cfinclude template="../DevNotes/DevNotes.cfm" /&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;em&gt;(replace the path as necessary)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;NOTE: don't put this in OnRequestEnd.cfm, as it will get triggered for each AJAX request and land you in an infinite recursive loop! The layout file is the best place for it, as it's display-generating code.&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Usual usage policy : you can do what you like with this code, so long as&lt;br /&gt;a) you maintain the attribution comments&lt;br /&gt;b) you accept that it's provided entirely as-is, with no warranty of any sort&lt;br /&gt;c) you don't violate any terms in the other code that it includes (see credits within the code)&lt;br /&gt;and most importantly&lt;br /&gt;d) you don't have too much of a go at me for not commenting it particularly well!&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Enjoy !&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.aldavidson.co.uk/code/DevNotes.zip"&gt;DevNotes On AJAX&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-908969552254279900?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/908969552254279900/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=908969552254279900' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/908969552254279900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/908969552254279900'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/02/devnotes-on-ajax.html' title='DevNotes On AJAX'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-9176774247657295092</id><published>2007-02-08T12:44:00.001Z</published><updated>2007-02-07T17:33:55.939Z</updated><title type='text'>We Have A New Paperweight</title><content type='html'>&lt;div style="float: right; margin-left: 10px; margin-bottom: 10px;"&gt; &lt;a href="http://www.flickr.com/photos/drsnooks/383632049/" title="photo sharing"&gt;&lt;img src="http://farm1.static.flickr.com/33/383632049_6299f86c61_m.jpg" alt="" style="border: solid 2px #000000;" /&gt;&lt;/a&gt; &lt;br /&gt; &lt;span style="font-size: 0.9em; margin-top: 0px;"&gt;  &lt;a href="http://www.flickr.com/photos/drsnooks/383632049/"&gt;New Paperweight&lt;/a&gt;  &lt;br /&gt;  Originally uploaded by &lt;a href="http://www.flickr.com/people/drsnooks/"&gt;Dr Snooks&lt;/a&gt;. &lt;/span&gt;&lt;/div&gt;We scooped a TrampoGong last night - the Oracle Partner Innovation Award for 2006. Yaaaay! Although, after sampling the varied delights of the hospitality bar, it's hard to tell whether the award or the coffee was more appreciated first thing this morning....&lt;br clear="all" /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-9176774247657295092?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/9176774247657295092/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=9176774247657295092' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/9176774247657295092'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/9176774247657295092'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/02/we-have-new-paperweight.html' title='We Have A New Paperweight'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm1.static.flickr.com/33/383632049_6299f86c61_t.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-3189626964382928400</id><published>2007-02-07T16:23:00.000Z</published><updated>2007-02-07T17:33:56.171Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='scotland'/><category scheme='http://www.blogger.com/atom/ns#' term='sonar'/><category scheme='http://www.blogger.com/atom/ns#' term='award'/><category scheme='http://www.blogger.com/atom/ns#' term='climbing'/><category scheme='http://www.blogger.com/atom/ns#' term='busy'/><category scheme='http://www.blogger.com/atom/ns#' term='work'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>Oracle, Sonar and Schnow, oh my</title><content type='html'>'ello, yes, I am still alive... bleh - been busy busy busy recently, it's been quite a week or two....&lt;br /&gt;&lt;br /&gt;We had our first major deployment of &lt;a href="http://www.trampolinesystems.com/products/sonar"&gt;Sonar&lt;/a&gt; to get out, so none of us got home until after midnight until Thursday. Eventually got it done, though, to huge relief and slaps on the back all round, thanks to some Herculean efforts from everyone on the team. &lt;br /&gt;&lt;br /&gt;After a week like that, I needed to get right away from it all, so &lt;a href="http://flickr.com/photos/drsnooks/sets/72157594520954506/"&gt;a weekend in the mountains of Scotland for some ice climbing&lt;/a&gt; was just the job. I learned quite a lot over that weekend - &lt;br /&gt;&lt;br /&gt;- supporting my 12-and-a-half-stone on four tiny metal spikes sticking out of my toes gets pretty damn painful on the calves after four hours or so&lt;br /&gt;- just because the weather is &lt;a href="http://flickr.com/photos/drsnooks/381608647/in/set-72157594520954506/"&gt;gorgeous&lt;/A&gt; right now, doesn't mean that it's not going to be &lt;a href="http://flickr.com/photos/drsnooks/381610881/in/set-72157594520954506/"&gt;bloody grim&lt;/a&gt; soon&lt;br /&gt;- and many more&lt;br /&gt;&lt;br /&gt;I got back to work yesterday, with just one day to recover before tonight's extravaganza at the Oracle Partner Innovation Awards - we've been nominated for the "Hottest Tech Prospect In The UK" award, so we're off to meet 'n' greet 'n' press the flesh with the industry bigwigs.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;...which was nice...&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Hopefully some point soon I'll be able to get back to some &lt;em&gt;technical&lt;/em&gt; posts on this 'ere blog, as I have a couple of goodies to impart (even one CF-related, shock, horror) and I'll post them as soon as I get chance to package them up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-3189626964382928400?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/3189626964382928400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=3189626964382928400' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3189626964382928400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/3189626964382928400'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/02/oracle-sonar-and-schnow-oh-my.html' title='Oracle, Sonar and Schnow, oh my'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-2729826137744311769</id><published>2007-01-22T09:45:00.000Z</published><updated>2007-01-22T11:52:23.090Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='social engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='phishing'/><category scheme='http://www.blogger.com/atom/ns#' term='fedex'/><category scheme='http://www.blogger.com/atom/ns#' term='identity theft'/><category scheme='http://www.blogger.com/atom/ns#' term='scam'/><category scheme='http://www.blogger.com/atom/ns#' term='identity'/><title type='text'>"Fed-Ex" Social Engineering / ID Theft Scam?</title><content type='html'>At 7am this morning I was woken up by the phone ringing. I didn't make it to the phone in time, and it rang off. "Who the hell is ringing me at 7am?" I thought, in my usual semi-comatose, semi-neanderthal pre-coffee state, but a 1471 said "we do not have the caller's number". Hmmm.... &lt;br /&gt;&lt;br /&gt;Twenty minutes later it rang again. This time I got there in time to pick up -&lt;br /&gt;&lt;br /&gt;"Hello?" I said. &lt;br /&gt;&lt;em&gt;(Well, admittedly, at that time in the morning it was probably more like "blrglhmph?", but you'll have to perform the necessary transliterations yourself for the rest of this transcript)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;A man with a very strong Indian-type accent replied:&lt;br /&gt;"Hello, can I speak to Mr. Alistair Davidson?"&lt;br /&gt;&lt;br /&gt;"Speaking..."&lt;br /&gt;&lt;br /&gt;"Is your address &lt;em&gt;[my address and postcode]&lt;/em&gt; ?"&lt;br /&gt;&lt;br /&gt;"Yes...."&lt;br /&gt;&lt;br /&gt;"This is Fed-Ex, we have a package to deliver to you this afternoon"&lt;br /&gt;&lt;br /&gt;"Oh ok..." &lt;em&gt;...so why do you need to ring me at 7am about it?&lt;/em&gt; I thought&lt;br /&gt;&lt;br /&gt;"I need to confirm some security details - can you give me Mr Alistair Davidson's date of birth?"&lt;br /&gt;&lt;br /&gt;Now at this point my tinfoil-hat alarm bells started ringing - &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;why would Fed Ex possibly want my date of birth in order to deliver a package?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;come to think of it, how would they know my telephone number?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;in fact, how did I know this guy was from Fed Ex at all? He &lt;em&gt;said&lt;/em&gt; he was calling from Fed Ex, but I could ring up anyone at random and claim to be Kylie Minogue, it wouldn't make it true...&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I decided to play it cautious - &lt;br /&gt;&lt;br /&gt;"Why do you need my date of birth?" I asked&lt;br /&gt;&lt;br /&gt;"I need to confirm these security details to deliver the package" he said&lt;br /&gt;&lt;br /&gt;"Well I'm not going to give that information out," I replied&lt;br /&gt;&lt;br /&gt;"But we won't be able to deliver this package" he was starting to get a little tell-tale this-isn't-the-way-it's-supposed-to-go tone of voice&lt;br /&gt;&lt;br /&gt;"Ok," I paused, thinking - &lt;em&gt;in fact, how did they get my number?&lt;/em&gt; I've never sent anything by Fed-Ex, but I'm fairly sure they won't require any information about the addressee other than the address? Now I was &lt;em&gt;really&lt;/em&gt; suspicious - time to challenge back....&lt;br /&gt;&lt;br /&gt;"So who's this package from?" I asked&lt;br /&gt;&lt;br /&gt;"Er - it's a cash, door-to-door delivery, I can't tell you who it's from" he replied. &lt;em&gt;Well, I'll give him a 5.7 on artistic impression for speedy improvisation,&lt;/em&gt; I thought, &lt;em&gt;but there was a definite hesitation there, for which the Russian judges would mark him down on technical merit. Besides, if they'll happily accept packages for delivery without any information about the sender, then they &lt;strong&gt;certainly&lt;/strong&gt; wouldn't need to know anything about the recipient&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;"So can you tell me Mr Alistair Davidson's date of birth?" he returned to what I was increasingly convinced was his script.&lt;br /&gt;&lt;br /&gt;"No, I'm sorry, I'm not going to give that information out," I said firmly.&lt;br /&gt;&lt;br /&gt;...and he hung up on me. A swift 1471 showed that he'd suppressed the number before he called.&lt;br /&gt;&lt;br /&gt;OK, so there's a couple of possibilities here -&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;He really was from Fed Ex, and they really do have a policy of incredibly bizarre security procedures, ringing their addressees up at 7am, and hanging up on their customers. Call me Mr Idealistic, but I think that's unlikely&lt;/li&gt;&lt;br /&gt;&lt;li&gt;He, or an associate, got my name and address from somewhere - maybe from something as simple as a spam letter I threw away, or come to think of it, I'm in the phone book - and decided to try some phishing.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;If I was a betting man, I'd put money on the second.&lt;br /&gt;&lt;br /&gt;Which makes me wonder -&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Does he / do they  normally try this on US numbers? We don't really &lt;em&gt;do&lt;/em&gt; Fed-Ex here in blighty, any delivery is far more likely to be via Parcel Force or even UPS.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Do they (I'll assume for now that there's more than one of these scammers) deliberately call early in the morning, to get people while they're fuzzy and bleary and not thinking straight?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;How many other people have they managed to get identity details from like this?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I'm a suspicious sod, and paranoid about my personal information - it's a known side effect of having worked in "teh intarweb" for too long :-) - but I know plenty of people - like my mum - who will be &lt;em&gt;incredibly&lt;/em&gt; dubious about giving their credit card details to an online retailer, but will quite happily answer any question asked by any random punter who rings them up and says "hi, this is (say) HSBC, can we check your account number and sort-code please?"&lt;br /&gt;&lt;br /&gt;It's a sad fact that the weakest link in any security system is the people involved, and until there's a fundamental shift in human nature, that's unlikely to change any time soon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-2729826137744311769?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/2729826137744311769/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=2729826137744311769' title='21 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2729826137744311769'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/2729826137744311769'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/01/fed-ex-social-engineering-id-theft-scam.html' title='&quot;Fed-Ex&quot; Social Engineering / ID Theft Scam?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>21</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-5892166301985772460</id><published>2007-01-15T19:15:00.000Z</published><updated>2007-01-15T19:20:01.258Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='hungarian'/><category scheme='http://www.blogger.com/atom/ns#' term='notation'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><category scheme='http://www.blogger.com/atom/ns#' term='style'/><title type='text'>In Defence Of Hungarian Notation</title><content type='html'>I started typing a comment on Pete Bell's post &lt;a href="http://www.pbell.com/index.cfm/2007/1/15/Why-Not-Hungarian"&gt;Why Not Hungarian&lt;/a&gt;, but it got a bit too long so I've put my two-penn'orth here.&lt;br /&gt;&lt;br /&gt;Hungarian notation, for me, can be very useful, but maybe not in the form that's most commonly understood. I've heard the argument that the original intent behind Hungarian notation was to declare what &lt;strong&gt;KIND OF THING&lt;/strong&gt; a variable is - but this got mistranslated into what &lt;strong&gt;TYPE&lt;/strong&gt; a variable is, and the two are not the same -&lt;br /&gt;&lt;br /&gt;For instance, in the last big CF project I did, I took to declaring strings that contained markup with a "htm" prefix, and strings that may have character codes - such as from textarea input  - with a "raw" prefix. Similarly URL-encoded strings get an "enc" prefix. They're all strings, and traditional Hungarian notation might give them all the same prefix - but if you do it this way, as soon as you started typing a line like:&lt;br /&gt;&lt;br /&gt;&amp;lt;cfset htmContentForDisplay = rawInput /&amp;gt;&lt;br /&gt;&lt;br /&gt;you immediately know that something is wrong. The variable prefixes themselves make you think "Hmm - that's not going to work.... I need to do some intermediate processing to convert the formats there"&lt;br /&gt;&lt;br /&gt;In a strongly-typed, pre-compiled language like Java or C++, then Hungarian notation can just get in the way - you've got the compiler as a type-safety net, and anyone who's ever tried to do anything meaningful with MFC will certainly testify to the horrors of things like "lpszhwndmyWindowHandle". But I think that in loosely-typed languages such as CF, Hungarian notation has it's place &lt;em&gt;if you use it properly&lt;/em&gt; - remember, "a Hungarian is the only man who can follow you into a revolving door and come out first" :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-5892166301985772460?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/5892166301985772460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=5892166301985772460' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/5892166301985772460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/5892166301985772460'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2007/01/in-defence-of-hungarian-notation.html' title='In Defence Of Hungarian Notation'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-1965940106177650106</id><published>2006-12-20T15:44:00.000Z</published><updated>2006-12-20T15:49:09.441Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='wikipedia'/><category scheme='http://www.blogger.com/atom/ns#' term='nsfw'/><category scheme='http://www.blogger.com/atom/ns#' term='humour'/><category scheme='http://www.blogger.com/atom/ns#' term='user generated content'/><category scheme='http://www.blogger.com/atom/ns#' term='public domain'/><title type='text'>(NSFW) Taking "User Generated Content" a bit too far</title><content type='html'>The Register is reporting on a heroic altruist's solution to a sticky situation regarding copyright-infringing contributions to Wikipedia :&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.theregister.co.uk/2006/12/20/wikipedia_aphrodites_araldite/"&gt;Wikipedia semen shortage filled by User Generated Content&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;....well, it's certainly very public-spirited of the honourable member.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-1965940106177650106?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.theregister.co.uk/2006/12/20/wikipedia_aphrodites_araldite/' title='(NSFW) Taking &quot;User Generated Content&quot; a bit too far'/><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/1965940106177650106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=1965940106177650106' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1965940106177650106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/1965940106177650106'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/12/nsfw-taking-user-generated-content-bit.html' title='(NSFW) Taking &quot;User Generated Content&quot; a bit too far'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116601918270957730</id><published>2006-12-13T13:53:00.000Z</published><updated>2006-12-13T14:13:02.733Z</updated><title type='text'>Going loco with Mo'MoSoSo</title><content type='html'>Nice to see that m'former'colleague &lt;a href="http://rik.typepad.com"&gt;Rik Abel&lt;/a&gt; is valiantly continuing his &lt;a href="http://rik.typepad.com/blog/2006/12/mo_mososo.html"&gt;one-man crusade&lt;/a&gt; to get the gloriously-silly acronym &lt;a href="http://en.wikipedia.org/wiki/mososo"&gt;MoSoSo&lt;/a&gt; more widely adopted, whilst simultaneously pointing out its majestic silliness in an ironic, post-modernist, self-referential way. Or something....&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Don't get me wrong, I think Mobile Social Software could well be the killer app of Web 2-point-whatever-we're-up-to-now, but seriously folks - &lt;em&gt;MoSoSo?&lt;/em&gt; That's &lt;em&gt;Well Jackson!&lt;sup&gt;*&lt;/sup&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Make sure you don't target your MoSoSo applications at the Small office / Home Office market, 'cause that would be &lt;strong&gt;SoHoMoSoSo&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;And if your users wore black eyeshadow and jaggedy-cut hairstyles, would that make it SoHoEmoMoSoSo ?&lt;br /&gt;&lt;br /&gt;And if it was in a Bohemian New Romantic style...? (BohoRomoSoHoEmoMoSoSo)&lt;br /&gt;&lt;br /&gt;Yikes, this one could run...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;sup&gt;*&lt;/sup&gt; - &lt;a href="http://en.wikipedia.org/wiki/Nathan_Barley"&gt;look here if you don't get the reference&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116601918270957730?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116601918270957730/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116601918270957730' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116601918270957730'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116601918270957730'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/12/going-loco-with-momososo.html' title='Going loco with Mo&apos;MoSoSo'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116592168839851018</id><published>2006-12-12T10:05:00.000Z</published><updated>2006-12-12T11:50:54.883Z</updated><title type='text'>Trusting The Magic Pixies - Hibernate HQL</title><content type='html'>&amp;lt;rant&amp;gt;&lt;br /&gt;I'm having trouble trusting the Magic Pixies. I admit, it's a common theme of mine, and maybe it's just &lt;acronym title="Not Invented Here"&gt;NIH&lt;/acronym&gt; syndrome-by-proxy, but Magic Pixies - able and willing though they are - can only do your job for you if you ask them in just the right way. And sometimes they seem like they're being deliberately dumb and obstinate little buggers that just won't do as they're &lt;em&gt;told&lt;/em&gt;, dammit!&lt;br /&gt;&lt;br /&gt;(ahem) Perhaps I should elaborate here...&lt;br /&gt;&lt;br /&gt;The Magic Pixies in question here are the ones that do the "hard" work for you in &lt;a href="http://www.hibernate.org"&gt;Hibernate&lt;/a&gt;, the near-as-dammit de facto standard ORM system for Java. Or Persistence Management. Or whatever term you prefer. &lt;a href="http://www.pbell.com"&gt;Pete Bell&lt;/a&gt; has been blogging in great detail about the process of writing his own cf-based ORM framework, so if you're not familiar with ORM systems then you're probably best going to check out his blog, because this is a pure and simple straightforward bit of spleen venting. &lt;br /&gt;&lt;br /&gt;The basic idea of Hibernate is that you can write your object model just as you choose, and then map your objects to persistent entities using (surprise surprise) an XML config file - or, if you're using Java 5 and EJB 2 and Hibernate 3, then you can dispense with the XML config file (yay!) and use annotations instead (double yay!) So long as you construct your mappings correctly, then you "don't need to worry about" the SQL - the Magic Pixies of Hibernate will auto-generate your db schema and SQL queries, and magically do your &lt;acronym title="Create, Read, Update, Delete"&gt;CRUD&lt;/acronym&gt; for you with a sprinkling of their Magic Pixie Dust&amp;trade;. &lt;br /&gt;&lt;br /&gt;"But what if I want to do something a bit more complex than basic CRUD?" I cried.&lt;br /&gt;&lt;br /&gt;"Like what?" said the Magic Pixies&lt;br /&gt;&lt;br /&gt;"Like a left outer join?" I replied&lt;br /&gt;&lt;br /&gt;"Oh, you don't need to worry about all that nasty SQL" said the Magic Pixies, " because that would tie your code to your database system, and that's a BAD THING! BAD Al! BAAAAAD!"&lt;br /&gt;&lt;br /&gt;"Gosh, sorry Magic Pixies," I said, rather sheepishly, "I promise to use that nice abstracted Criteria API that you so generously provided in future"&lt;br /&gt;&lt;br /&gt;"And so you should!" said the Magic Pixies, "Remember, &lt;em&gt;you&lt;/em&gt; do the code, &lt;em&gt;we&lt;/em&gt; do the data, otherwise we'll have harsh words with our union rep, OK?" &lt;br /&gt;&lt;br /&gt;"Ok! Ok!" I said. "Now could you stop hitting me with that rolled-up newspaper please?"&lt;br /&gt;&lt;br /&gt;"So long as you promise to be good" &lt;br /&gt;&lt;br /&gt;"I do! I do!"&lt;br /&gt;&lt;br /&gt;"Ok then"&lt;br /&gt;&lt;br /&gt;"But what if I want to do something more complex than that?" I asked.&lt;br /&gt;&lt;br /&gt;The Magic Pixies looked a bit puzzled.&lt;br /&gt;&lt;br /&gt;"What on Earth could you want to do that's more complex than that?" they replied.&lt;br /&gt;&lt;br /&gt;"Well, what if I wanted to find a set of objects of type X that &lt;em&gt;didn't&lt;/em&gt; have any corresponding objects of type Y that match certain criteria?"&lt;br /&gt;&lt;br /&gt;"Pfffft!" said the Magic Pixies, " that's easy, you just create Criteria along the association paths!"&lt;br /&gt;&lt;br /&gt;"Er, huh?" I said.&lt;br /&gt;&lt;br /&gt;"You just create a Criteria object for class X, and then create another Criteria object using &lt;em&gt;that&lt;/em&gt; Criteria object by passing the name of the property of class X which refers to the encapsulated class Y contained within class X!" &lt;br /&gt;&lt;br /&gt;"Huh?" I said.&lt;br /&gt;&lt;br /&gt;"Or if you really want to, you can use HQL"&lt;br /&gt;&lt;br /&gt;"HQL?"&lt;br /&gt;&lt;br /&gt;"Yes, Hibernate Query Language. It's almost like SQL, but not quite. Because we wouldn't want you using SQL - SQL's tied to database platforms, and that's BAAAAAD"&lt;br /&gt;&lt;br /&gt;"So how do I do it HQL?"&lt;br /&gt;&lt;br /&gt;"You create a query that queries along the association paths and properties of the objects"&lt;br /&gt;&lt;br /&gt;"Oh, right, OK" I said. "But what if class X doesn't have class Y as a property?"&lt;br /&gt;&lt;br /&gt;"Er..... huh?" said the Magic Pixies.&lt;br /&gt;&lt;br /&gt;"Class X has no property that refers to class Y"&lt;br /&gt;&lt;br /&gt;"Well then, you won't need to query for it, will you?" said the Magic Pixies, a touch too smugly for my liking.&lt;br /&gt;&lt;br /&gt;"But I do!" I insisted.&lt;br /&gt;&lt;br /&gt;"Er.... huh?" said the Magic Pixies.&lt;br /&gt;&lt;br /&gt;"Well, say if I had a table of ItemLinks...." I began.&lt;br /&gt;&lt;br /&gt;"A &lt;em&gt;WHAT&lt;/em&gt; of ItemLinks????"&lt;br /&gt;&lt;br /&gt;"Sorrysorrysorry! I mean an ItemLink object..."&lt;br /&gt;&lt;br /&gt;"That's better!"&lt;br /&gt;&lt;br /&gt;"...that represented a weighted link between two Items, such as might be calculated by some very complicated fuzzy logic and Natural Language Processing"&lt;br /&gt;&lt;br /&gt;"OK"&lt;br /&gt;&lt;br /&gt;"...and a separate LinkPreference object that represented an preference expressed by a Person as to whether their ItemLink to a particular object would be public or not"&lt;br /&gt;&lt;br /&gt;"Erm... can you give us an example?"&lt;br /&gt;&lt;br /&gt;"Sure - this clever NLP stuff might detect that Bob from SysAdmin has been talking a lot about clustering database servers, and he might want to share that link so that he is known as an expert in that field."&lt;br /&gt;&lt;br /&gt;"OK, with you so far..."&lt;br /&gt;&lt;br /&gt;"But it might detect that the boss has been talking with his secretary about a dirty weekend in Brighton, and they really wouldn't want that shared at all, would they?"&lt;br /&gt;&lt;br /&gt;"Erm, isn't that an outmoded stereotype that just reinforces age-old gender-typecast notions of sycophantic star-crossed secretaries as prey for the equally-stereotypical notion of amoral boss-as-alpha-male-predator?"&lt;br /&gt;&lt;br /&gt;"Alright, alright, but you get the idea!"  &lt;em&gt;(that Magic Pixie was really starting to get on my titty ends)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;"Yes, I follow you"&lt;br /&gt;&lt;br /&gt;"So what if I want to query for all ItemLinks that have been created in the last, say, two weeks, and that &lt;em&gt;don't&lt;/em&gt; have a corresponding LinkPreference?"&lt;br /&gt;&lt;br /&gt;"Well, you could query for ItemLinks that have LinkPreference set to null"&lt;br /&gt;&lt;br /&gt;At this point I was starting to snort quite heavily.&lt;br /&gt;&lt;br /&gt;"But I &lt;strong&gt;told&lt;/strong&gt; you, ItemLink &lt;em&gt;doesn't have&lt;/em&gt; a property that refers to LinkPreference! The two are completely independent!"&lt;br /&gt;&lt;br /&gt;"Well then you shouldn't want to query for them" said the Magic Pixies&lt;br /&gt;&lt;br /&gt;"But I DO!"&lt;br /&gt;&lt;br /&gt;"Well, can't you follow the association path up from ItemLink to Item and then down to LinkPreference?"&lt;br /&gt;&lt;br /&gt;"Well, yes, I could, but wouldn't that result in the Items table being read in the query when there's absolutely no need for it?"&lt;br /&gt;&lt;br /&gt;The Magic Pixies looked down at their feet&lt;br /&gt;&lt;br /&gt;"...might do..."&lt;br /&gt;&lt;br /&gt;"And isn't that horribly inefficient?"&lt;br /&gt;&lt;br /&gt;They started fiddling with their shorts&lt;br /&gt;&lt;br /&gt;"...might be..."&lt;br /&gt;&lt;br /&gt;"And it's not that simple anyway, because it's a compond join on TWO properties!"&lt;br /&gt;&lt;br /&gt;&lt;small&gt;"...yes..."&lt;/small&gt;&lt;br /&gt;&lt;br /&gt;"WHAT was that?"&lt;br /&gt;&lt;br /&gt;"yes!" said the Magic Pixies, with bottom lip sticking out.&lt;br /&gt;&lt;br /&gt;"So can you perform this raw SQL query in your own way?"&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;&lt;pre&gt;SELECT item_links.* &lt;br /&gt;FROM    item_links&lt;br /&gt;LEFT OUTER JOIN link_prefs &lt;br /&gt;             ON item_links.item_id = link_prefs.owner_item_id &lt;br /&gt;            AND item_links.other_item_id = link_prefs.linked_item_Id&lt;br /&gt;WHERE&lt;br /&gt; item_links.created_at &lt; ?&lt;br /&gt;AND link_prefs.shared IS NULL&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;"...might do, if you ask us nicely..."&lt;br /&gt;&lt;br /&gt;(sigh) "OK, can you pleeeeeeease do it?"&lt;br /&gt;&lt;br /&gt;They conferred for a moment in hushed whispers, and then turned back with a very smug-looking smile, and said &lt;br /&gt;&lt;br /&gt;"Yes, we can - but we're not going to"&lt;br /&gt;&lt;br /&gt;"WHAT?"&lt;br /&gt;&lt;br /&gt;"You have to ask us in the right way"&lt;br /&gt;&lt;br /&gt;Steam was starting to emerge from my ears&lt;br /&gt;&lt;br /&gt;"And what IS the right way to ask you?"&lt;br /&gt;&lt;br /&gt;They grinned even wider&lt;br /&gt;&lt;br /&gt;"We're not going to tell you!"&lt;br /&gt;&lt;br /&gt;And I stormed out of the room.&lt;br /&gt;&lt;br /&gt;You see, the trouble I have with ORM systems is that they're all well and good as far as they go, and yes they can save large amounts of "donkey work" But sooner or later you nearly always come up against something that would be almost trivially easy to do with raw SQL, but the nice insulated ORM abstraction just can't deal with. I know that I'm probably looking at this from the "wrong" direction, I'm thinking about the data rather than the objects, but until the Magic Pixies start to play a bit more nicely, I'm always going to be a bit suspicious of them.&lt;br /&gt;&amp;lt;/rant&amp;gt;&lt;br /&gt;&lt;br /&gt;(deep breaths..... calm.... happy thoughts..... nearly Christmas....)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116592168839851018?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116592168839851018/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116592168839851018' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116592168839851018'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116592168839851018'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/12/trusting-magic-pixies-hibernate-hql.html' title='Trusting The Magic Pixies - Hibernate HQL'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116558008271044162</id><published>2006-12-08T10:58:00.000Z</published><updated>2006-12-08T12:14:43.186Z</updated><title type='text'>Friday Brainf**k : How Unique Is A Phrase?</title><content type='html'>Eep - posts have been a bit thin on the ground recently, due to big crunch time on &lt;A href="http://www.trampolinesystems.com/products/sonar-platform-social-networks-and-relevance/"&gt;SONAR&lt;/a&gt;, but here's an interesting question that's just cropped up, and I'm not sure of the answer to, and blogging the question might just help me get my own thoughts on it straight....&lt;br /&gt;&lt;br /&gt;First, some context. &lt;a href="#question"&gt;(Yeah, yeah, skip the context and go to the question)&lt;/a&gt; I'm writing the DAO's for the system as a layer of abstract interfaces, with a default implementation based on &lt;a href="http://hibernate.org/"&gt;Hibernate&lt;/a&gt;, and using the everything-is-an-item pattern that I've blogged about before. &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;em&gt;Aside: This pattern, in conjunction with Java Generics - a Java 5 mechanism that's kind of like C++'s Templating system, but without the horrors that infest the STL Standard Template Library - has lead to a really nice way of getting lots of basic operations (e.g. CRUD) "for free", which deserves a post of its own and I'll blog about it next time I get chance.&lt;/em&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;In this design, for every type of item, there's a corresponding DAO. In the DAO, there's a save() method which either adds the passed object to the DB, or updates it if it already exists. &lt;br /&gt;&lt;br /&gt;This save() method is also responsible for throwing an exception if the given object can't be saved, as it would break the application's business rules for uniqueness. So each implementation of the save() method calls an isDuplicate() method, which is defined by default on the abstract ItemDAO, and can be overridden as appropriate on the subclass DAOs. For instance, it's not acceptable to have two Email records with the same messageID, but it's perfectly fine to have two Person records called John Smith - and this is where the interesting question arises...&lt;br /&gt;&lt;br /&gt;In our model, a Theme is also an Item. A Theme, in this case, being a word &lt;em&gt;or phrase&lt;/em&gt; that has been extracted from the content of an Item due to it being "potentially interesting". There's a whole load of extreme Lisp cleverness being worked on by M'Colleague &lt;a href="http://www.trampolinesystems.com/people/craig"&gt;Craig McMillan&lt;/a&gt;, he of the &lt;a href="http://flickr.com/photos/mccraig/204906091/"&gt;prodigious beard and piratical proclivities&lt;/a&gt;, regarding how you determine a word or group of words is potentially interesting, but in the words of Frank Drebin, that's not important right now. &lt;br /&gt;&lt;br /&gt;&lt;a name="question"&gt;The question is, what makes a Theme unique?&lt;/a&gt; On a superficial level, you can say that a Theme is a group of characters that make up words, so that group of characters must be unique. In other words, no two Theme records must exist with the same String in the "Title" field. But if you think about it a bit more, that might not actually be the case. &lt;br /&gt;&lt;br /&gt;For instance, the problems of &lt;a href="http://en.wikipedia.org/wiki/Homonym"&gt;homonyms&lt;/a&gt; (a word that has the same pronunciation and spelling as another word, but a different meaning - e.g. "bat" the animal and "bat" in cricket) and &lt;a href="http://en.wikipedia.org/wiki/Polysemy"&gt;polysemy&lt;/a&gt; (capacity of a word or phrase with multiple, related meanings that derive from the same etymology - e.g. "bank on it" with bank meaning to &lt;em&gt;rely&lt;/em&gt; upon something, which derives from the reputation of bank-the-financial-instution for reliability) are perennial problems for Natural Language Processing. Should we take that into account in the model? &lt;em&gt;Can&lt;/em&gt; we? &lt;br /&gt;&lt;br /&gt;In the above cases, I can probably take the reasonable position that because the words are spelled the same, I can assume that they are actually related in this sense, and if two emails refer frequently to "bush", they should be pointing to the same DB record for the "bush" theme, regardless of whether they were talking about a US President, a shrub, or the Australian outback - and homonymy and polysemy can just be swept under the carpet. &lt;em&gt;(Hmm, starting to get a lot of bulges under this carpet in the office here...where's my hammer?)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;However, when you start to think about the possibility for the system to be dealing with multiple locales and even multiple languages, which may well be the case in large multi-national corporates, another, related, linguistic term starts to rear it's ugly head - the &lt;a href="http://en.wikipedia.org/wiki/Heterologue"&gt;Heterologue&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;A Heterologue is a word that occurs in multiple languages, possibly with completely different meanings. For instance, the syllable "bat" in Cantonese means the number eight (at least, when pronounced with a high inflection), and having seen the way the lovely &lt;a href="http://flickr.com/photos/drsnooks/tags/lise"&gt;Lisa&lt;/a&gt; flits between English and Cantonese sometimes several times a sentence, even in emails where she types out the Cantonese word phonetically with English letters, this may well occur. &lt;br /&gt;&lt;br /&gt;It's not just languages that show heterologues either - the same word or phrase in the same language &lt;em&gt;in a different locale&lt;/em&gt; can have completely different meanings - the web is strewn with plenty of examples of British / American English ambiguities, the classic one being at &lt;a href="http://www.rocom.co.uk"&gt;Rocom&lt;/a&gt; when my newly-immigrated American colleague &lt;a href="http://bluegroove.net"&gt;Stuart&lt;/a&gt; asked what &lt;a href="http://1pixelout.net"&gt;Martin&lt;/a&gt; and I were doing for lunch, and I replied that we were driving into town so that Martin could pick up some fags, and did he want to come too?&lt;br /&gt;&lt;br /&gt;So it boils down to this - should I include the locale of a Theme in the check for uniqueness along with the title (i.e. the actual string) or not? Is a given string of characters unique just within a particular locale, or globally? I'm tempted to say globally, but I have a nagging feeling that at some point in the not too distant future, that choice may turn round and bite me.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116558008271044162?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116558008271044162/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116558008271044162' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116558008271044162'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116558008271044162'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/12/friday-brainfk-how-unique-is-phrase.html' title='Friday Brainf**k : How Unique Is A Phrase?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116404345194683928</id><published>2006-11-20T16:59:00.000Z</published><updated>2006-11-20T17:24:15.673Z</updated><title type='text'>Integrating Applets With AJAX</title><content type='html'>For our new &lt;a href="http://www.trampolinesystems.com/products/sonar-platform-social-networks-and-relevance/"&gt;SONAR &lt;/a&gt;product, as demo'ed in the &lt;a href="http://enron.trampolinesystems.com"&gt;Enron Explorer&lt;/a&gt;, we needed a way of visualising the social network of up to 80,000 people. Our first thought was of course, Flash, but we found that it just wasn't up to the job&lt;sup&gt;&lt;a href="#footnote1"&gt;*&lt;/a&gt;&lt;/sup&gt; of visualising large numbers of nodes. If only there was a way of combining the scalability of a Java applet with a slick, whizz-bang AJAX interface... well, as luck would have it, there is! &lt;br /&gt;&lt;br /&gt;There are certain problems you have to get round, particularly related to the issue of the applet showing through anything that you place over it, and the browser reloading the VM if you hide the applet in any way , but these are fairly straightforward to get round once you've figured it out (hint: try moving it off screen!) - m'colleague &lt;a href="http://www.trampolinesystems.com"&gt;Jan Berkel&lt;/a&gt; blogs about the technique in more detail &lt;a href="http://www.trampolinesystems.com/weblog/bringing-new-life-to-applets-with-ajax/"&gt;here&lt;/a&gt;.  &lt;br /&gt;&lt;br /&gt;It would be overstepping the mark to suggest that we invented the technique as speculative articles have been written on this subject before, but the feedback we've been getting from the Enron Explorer shows that a lot of people are taken most of all by the interface, and as far as we know we're the first to use it in a production application. &lt;br /&gt;&lt;br /&gt;All we need now for the meme to take hold, of course, is a snappy acronym....&lt;br /&gt;- APAX?&lt;br /&gt;- APJAX?&lt;br /&gt;- JAPAX?&lt;br /&gt;&lt;br /&gt;Hmmm..... there &lt;em&gt;HAS&lt;/em&gt; to be an amusing acronym we can tease out of this - all suggestions gratefully received!&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;em&gt;&lt;a name="footnote1"&gt;*&lt;/a&gt; OK, I'm fairly sure that, given enough time, we could probably have found a way of getting round the scalability issues associated with doing it in Flash, but as Jan mentions in the article, we had a large amount of pre-written Java code that it would have been a shame to waste, and what you also get with Java is a vast library of free code and APIs out there, plus complete flexibility over how you use them.&lt;/em&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116404345194683928?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.trampolinesystems.com/weblog/bringing-new-life-to-applets-with-ajax/' title='Integrating Applets With AJAX'/><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116404345194683928/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116404345194683928' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116404345194683928'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116404345194683928'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/11/integrating-applets-with-ajax.html' title='Integrating Applets With AJAX'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116342116458411649</id><published>2006-11-13T11:46:00.000Z</published><updated>2006-11-14T12:05:48.353Z</updated><title type='text'>10 Things to Check for Supporting International Characters In Your Web App</title><content type='html'>These days, more and more of our web apps have to be ready and able to support international characters. It's a non-trivial problem, and one that causes many furrowed brows, because usually by the time you notice it, you've already screwed up some data. I've dealt with it many times, and found that it's generally much easier to prepare for the problem &lt;em&gt;before&lt;/em&gt; it occurs, rather than hack a solution together after the fact. This is not a post about i18n-ing your display templates, that's a whole topic in itself, even though some standard mechanisms are pretty well-defined by now. It's about issues involved in storing and displaying content with non-English characters.&lt;br /&gt;&lt;br /&gt;A full discussion of character encodings, and the headaches thereof, is WAAAY beyond the scope of this post. It could fill a pretty weighty multi-volume book all by itself. So I'll just refer you to Joel Spolsky's article on the topic - &lt;a href="http://www.joelonsoftware.com/articles/Unicode.html"&gt;The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets (No Excuses!)&lt;/a&gt; - and say that the character set that's most commonly used for international characters is &lt;a href="http://en.wikipedia.org/wiki/UTF-8"&gt;UTF-8&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;So here's a quickie list of things to check for and bear in mind:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;String Processing Issues&lt;/h3&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Use Unicode strings&lt;/strong&gt;&lt;br /&gt;Internally, CFMX is entirely based on Java, and so it "should" use Unicode strings by default. The main things that you need to to worry about are when data goes &lt;em&gt;in&lt;/em&gt; - into the database - and when it comes &lt;em&gt;out&lt;/em&gt; - gets presented to the user. Of these two, the most important is the first - so long as data is being stored correctly, you'll always be able to get it out again. If it's being stored incorrectly, you're a bit screwed :)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;If you're using any Regular Expressions for processing or validating strings, BE CAREFUL!&lt;/strong&gt;&lt;br /&gt; It's very common to use expressions such as [a-zA-Z] to check for letters, or [a-zA-Z0-9] to check for alphanumerics. What do you think is going to happen if you pass an accented character such as à or é through this reg ex? Yup - é is NOT between a and z, so it will &lt;em&gt;not&lt;/em&gt; match. How best to handle this is up to you - the POSIX regex elements such as [[:alpha:]] are good enough for some situations, but not others. For instance, POSIX does not allow more than 20 characters to be categorized as digits, whereas there are many more than 20 digit characters in Unicode. There's more detail on &lt;a href="http://unicode.org/reports/tr18/#Compatibility_Properties"&gt;unicode.org&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h3&gt;Data Storage&lt;/h3&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Store &lt;em&gt;everything&lt;/em&gt; in UTF-8&lt;/strong&gt;&lt;br /&gt;Even if you're designing an app that "won't ever need anything except English", a little thought and effort at the design stage will mean that next time you have to write a similar app that &lt;em&gt;does&lt;/em&gt; need international characters, you can re-use the code. Besides, it's just good practise, and the extra storage overhead of storing up-to-4-bytes-per-character is relatively easy to deal with in these days of 750GB disks for &amp;pound;250.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Check the collation on your databases, tables, and columns&lt;/strong&gt;&lt;br /&gt;Collations can be set at all levels of specificity, from the server right down to individual columns. Make sure that they're &lt;strong&gt;ALL&lt;/strong&gt; UTF-8. Easiest way to do this is to generate a CREATE... script for your database (SQL Server) or a mysqldump file (MySQL), open it up in a text editor and search for COLLATE &lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;In SQL Server, make sure that any character-based field that can be populated from user-entered data in any way, is specified as a Unicode field&lt;/strong&gt;&lt;br /&gt;i.e. the type starts with an N. varchar fields should be nvarchar. text fields should be ntext.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Plan ahead!&lt;/strong&gt;&lt;br /&gt;Plan to cope with multiple character sets up front, and you know your database can handle just about anything you're likely to throw at it. Sweeping it under the carpet and saying "we'll worry about that when it happens" is likely to end up with you taking your app offline while you rebuild tables and text indices. This can easily take several hours, for anything above a couple of thousand rows. Long downtimes make unhappy users.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;h3&gt;Presentation&lt;/h3&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;strong&gt;Explicitly declare the UTF-8 character set on every page&lt;/strong&gt;&lt;br /&gt;Make sure that every page has a meta tag like this:&lt;br /&gt;&lt;div class="code"&gt;&amp;lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&amp;gt;&lt;/div&gt;&lt;br /&gt;...and also make sure that it's the FIRST tag in the Head of your doc, because as soon as a browser encounters a charset declaration, it starts re-parsing from the top.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;You "should" specify the primary language in the HTML tag&lt;/strong&gt;&lt;br /&gt;In ordinary HTML, this is just :&lt;br /&gt;&lt;div class="code"&gt;&amp;lt;html lang="en-GB"&amp;gt;&lt;/div&gt;&lt;br /&gt;In XHTML 1.0, it's slightly different :&lt;br /&gt;&lt;div class="code"&gt;&amp;lt;html lang="en-GB" xml:lang="en-GB" xml‍ns="http://www.w3.org/1999/xhtml"&amp;gt;&lt;/div&gt;&lt;br /&gt;And in XHTML 1.1, you don't need the lang attribute:&lt;br /&gt;&lt;div class="code"&gt;&amp;lt;html xml:lang="en-GB" xml‍ns="http://www.w3.org/1999/xhtml"&amp;gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This makes it much easier for text readers, translation software, and - crucially - search engines to recognise the language and take appropriate action. &lt;a href="http://www.w3.org/International/questions/qa-lang-why"&gt;More details at W3.org&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;For multi-language markup, you can provide a comma-separated list of languages&lt;/strong&gt;&lt;br /&gt;You can (and should) also specify the language on any element within the page that is in a different language to the primary language. If your document structure doesn't break down to a logical tag that encompasses the different language part, then use a span tag:&lt;br /&gt;&lt;div class="code"&gt;&amp;lt;span xml:lang="fr-CA" &amp;gt;....&amp;lt;/span&amp;gt;&lt;/div&gt; &lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;strong&gt;Language is a CSS pseudo-class&lt;/strong&gt;&lt;br /&gt;This means that you can specify different styling for different languages, like so:&lt;br /&gt;&lt;div class="code"&gt;/* smaller font for documents in German */&lt;br /&gt;HTML:lang(de){ font-size:90%; }&lt;br /&gt;/* italicise any bits of French in any document */&lt;br /&gt;:lang(fr){ font-style : italic; }&lt;br /&gt;/* change the quotation marks for any Q tag INSIDE a French element */&lt;br /&gt;:lang(fr) &gt; Q { quotes: '« ' ' »' }&lt;br /&gt;&lt;/div class="code"&gt;&lt;br /&gt;More details at &lt;a href="http://www.w3.org/TR/REC-CSS2/selector.html#lang"&gt;W3.org&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;There are many many more things to think about, and this list is by no means exhaustive, but it should be enough to give you a starting point. Lots of the issues that are thrown up by this tend to be general "take-a-step-back" kind of issues that make you question your workflows and assumptions, rather than your programming expertise - e.g. if we're letting people enter their own nickname, and then using that nickname as part of the url, then what's going to happen if someone enters a nickname entirely in Russian? How are we going to handle generated emails to them? What about if they're text-only emails? &lt;em&gt;(Hint: Content-type: text/plain; charset=utf-8 !)&lt;/em&gt; &lt;br /&gt;&lt;br /&gt;But there are also deeper issues involved - if we start accepting and labelling content in different languages, what facilities do we need to provide to our users in order to filter out - or focus exclusively on - particular languages and character sets? Do we need to create a separate site for each language? Or do we accomodate all the content in one site, with filters? &lt;br /&gt;&lt;br /&gt;Answers to &lt;em&gt;those&lt;/em&gt; kinds of questions, I leave up to you :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116342116458411649?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116342116458411649/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116342116458411649' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116342116458411649'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116342116458411649'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/11/10-things-to-check-for-supporting.html' title='10 Things to Check for Supporting International Characters In Your Web App'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116290605772828804</id><published>2006-11-07T12:19:00.000Z</published><updated>2006-11-07T13:27:37.856Z</updated><title type='text'>Alas, poor Smartgroups, I knew it well....</title><content type='html'>It's not without a tinge of sadness and wistful sigh that I noticed that, in possibly the least-surprising announcement I've seen for a long time, &lt;a href="http://www.theregister.co.uk/2006/11/02/orange_smartgroups_die/"&gt;Orange is to finally pull the plug on Smartgroups&lt;/a&gt; &lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.smartgroups.com"&gt;Smartgroups&lt;/a&gt; was, in its day, the Daddy of first-generation community applications, and I worked on it for just over two years just after it got bought out by Freeserve, who became wholly-owned by Wanadoo, then France Telecom, then finally Orange UK.&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(Remember those "Ten signs you are in a dotcom company" emails that went round about the turn of the millenium? One of them particularly stuck in my mind - "You've sat at the same desk for two years, and worked for four different companies")&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;I learnt a lot about managing large-scale systems from that job - SG handled more than 50 million emails per month - and a good percentage of the stories I'll come out with after several late-night-beers-with-other-techies hail from that time. I also learned a lot about human nature, and the myriad ways in which people will never fail to suprise you, even when you think you've seen it all. And I'm not just talking about the users there... &lt;br /&gt;&lt;br /&gt;But ultimately, it was a failure to evolve and keep up with the competition (e.g. MySpace) that gradually put the nails in its coffin, and after about four years of knowing full well that its days were numbered, SG has finally been taken out the back and given a nice sunny wall to stand against, and a roll-up to smoke. And a blindfold.&lt;br /&gt;&lt;br /&gt;Farewell Smutgropes - you will live on in the memories of all those who worked on you. (Despite some quite determined scrubbing with Mind Bleach in some cases) And if anyone manages to track down the new homes of the &lt;strong&gt;Stereo Stimming&lt;/strong&gt; group, the &lt;strong&gt;Brent Spiner Data Lovers&lt;/strong&gt; group, or my own personal favourite - &lt;strong&gt;Hairy Bearded Scotsmen In Kilts&lt;/strong&gt; ("ONLY pictures of hairy bearded scotsmen in kilts will be accepted - any pictures of hairy bearded men in kilts who are NOT Scottish will be deleted...") then be sure to let me know. Such gems of the longest of long tails are surely too fine to be lost forever :)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116290605772828804?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116290605772828804/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116290605772828804' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116290605772828804'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116290605772828804'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/11/alas-poor-smartgroups-i-knew-it-well.html' title='Alas, poor Smartgroups, I knew it well....'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116194334610366154</id><published>2006-10-27T10:39:00.000+01:00</published><updated>2006-10-27T11:31:53.326+01:00</updated><title type='text'>Mutually Exclusive Interfaces</title><content type='html'>&lt;a href="http://blog.jasonnussbaum.com"&gt;Jason Nussbaum&lt;/a&gt; has posed a nice juicy thought experiment - &lt;a href="http://blog.jasonnussbaum.com/?p=228"&gt;how do you handle mutually exclusive interfaces?&lt;/a&gt; I started typing a comment, but it got too long, so here's my take on it : &lt;br /&gt;&lt;br /&gt;The question was:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Had a thought: how do people handle mutually exclusive interfaces?&lt;br /&gt;&lt;br /&gt;I suppose there's no such thing as mutually exclusive interfaces from a code perspective, but from a logic perspective, there may be. For example, let's say you do one of two things based on whether a class implements one of two interfaces. What do you do if something implements both?&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;It's a conceptual problem - as Jason says, there's not really a technical way that interfaces could be mutually exclusive. You can think of it this way - if an object X implements interface Y, then what you're really saying is "X can act like a Y". So there's no reason why X can't also act like a Z, depending on the situation. &lt;br /&gt;&lt;br /&gt;There are so many ways you could tackle this problem. You could introduce a super-interface:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;public interface DeadOrAlive{ public boolean isAlive();  }&lt;br /&gt;&lt;br /&gt;public interface Dead extends DeadOrAlive{&lt;br /&gt;    public void startToSmell();&lt;br /&gt;}&lt;br /&gt;public interface Alive extends DeadOrAlive{&lt;br /&gt;    public void breathe();&lt;br /&gt;}&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;...and thus force implementers of Dead or Alive to provide a method which indicates if they are alive or not. This feels like a bit of a kludge, but to my mind, less so than without.&lt;br /&gt;&lt;br /&gt;You could add Exceptions onto the signature methods of Dead and Alive, which might throw NotDeadException and NotAliveException respectively - but this actually feels more kludgey to me than the first case.&lt;br /&gt;&lt;br /&gt;I think the "proper" way to think of this is maybe the biggest kludge of all - as your object model is meant to reflect the real-world problem that you're modelling, if it produces this kind of conflict then maybe you need to re-think your model! &lt;br /&gt;&lt;br /&gt;- unless, of course, your name happens to be &lt;a href="http://en.wikipedia.org/wiki/Schrödinger's_cat"&gt;Schrödinger&lt;/a&gt;...&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;public interface Alive{ public void breathe();  }&lt;br /&gt;public interface Dead{ public void startToSmell(); }&lt;br /&gt;&lt;br /&gt;public class Cat implements Dead, Alive&lt;br /&gt;{&lt;br /&gt;    public void startToSmell();&lt;br /&gt;    public void breathe();&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class Box&lt;br /&gt;{&lt;br /&gt;    private Cat cat;&lt;br /&gt;    &lt;br /&gt;    public void putCatInBox();&lt;br /&gt;}&lt;br /&gt;&lt;/div&gt; &lt;br /&gt;&lt;br /&gt;...eep, lame quantum physics jokes on a Friday?! I think it's time for coffee...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116194334610366154?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116194334610366154/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116194334610366154' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116194334610366154'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116194334610366154'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/10/mutually-exclusive-interfaces.html' title='Mutually Exclusive Interfaces'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116177484472532928</id><published>2006-10-25T12:12:00.000+01:00</published><updated>2006-10-25T12:14:24.680+01:00</updated><title type='text'>Enron Explorer Going Viral</title><content type='html'>Since we got &lt;a href="http://www.boingboing.net/2006/10/24/enron_explorer_mines.html"&gt;Boing Boing'ed&lt;/a&gt; yesterday, Google Analytics shows that we've had over 21,000 page views so far - and that's just to the front page. Unfortunately the GA javascript can't be included in the main app, as it's pretty much all AJAX requests, so we'll have to wait for analysis of the Apache logs for full statistics.&lt;br /&gt;&lt;br /&gt;I also put the &lt;a href="http://del.icio.us/url/d49cab969d6d74af13a9042eb70f92e3?all"&gt;Enron Explorer on del.icio.us&lt;/a&gt; 4 days ago - it's now been echoed by 112 people, including one who speaks Russian, by the looks of it. Google Analytics confirms that we've had visitors from 71 different countries, insterestingly enough including two from the Cayman Islands and one from an anonymising proxy - Kenny Boy, that's not you is it....?&lt;br /&gt;&lt;br /&gt;Some of the comments are fantastic :&lt;br /&gt;&lt;br /&gt;&lt;a href="http://del.icio.us/fpaulus"&gt;fpaulus&lt;/a&gt;&lt;br /&gt;"Interesting (both technologically and economically) walk through the Enron e-mail archives"&lt;br /&gt;&lt;br /&gt;&lt;a href="http://del.icio.us/markwithasee"&gt;markwithasee&lt;/a&gt;&lt;br /&gt;"searchable database of all of enron's internal email from 99-02. WOW."&lt;br /&gt;&lt;br /&gt;&lt;a href="http://del.icio.us/RStacy"&gt;RStacy&lt;/a&gt;&lt;br /&gt;"A look into what the future could look like in corporate transparency and analysis."&lt;br /&gt;&lt;br /&gt;&lt;a href="http://del.icio.us/slightlyfleury"&gt;slightlyfleury&lt;/a&gt;&lt;br /&gt;&lt;em&gt;"Enron emails. Gawd, did I just come?"&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;some of the tags that people have applied to it  (under the posting history on the right) are also interesting - I always find it intriguing to see how &lt;em&gt;other people&lt;/em&gt; see what we've done.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116177484472532928?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116177484472532928/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116177484472532928' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116177484472532928'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116177484472532928'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/10/enron-explorer-going-viral.html' title='Enron Explorer Going Viral'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116171093699152876</id><published>2006-10-24T18:21:00.000+01:00</published><updated>2006-10-24T18:28:57.010+01:00</updated><title type='text'>We got Boing Boing'ed!</title><content type='html'>Another way to hammer a webserver - the &lt;a href="http://enron.trampolinesystems.com"&gt;Enron Explorer&lt;/a&gt; got highlighted on &lt;a href="http://www.boingboing.net/2006/10/24/enron_explorer_mines.html"&gt;Boing Boing&lt;/a&gt;, "The Most Popular Blog In The World" according to Technorati. And our traffic has gone through the roof, but the app is holding up thanks to judicious planning for heavy usage, and some hefty &lt;a href="http://www.squid-cache.org/"&gt;Squid proxying&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;Remember kids, "Proper Planning Prevents Piss-Poor Performance"&lt;br /&gt;&lt;br /&gt;&lt;em&gt;....and Corny Cliches Create Copious Cringes... &lt;br /&gt;and Aggravated Alliteration Attains Acute Annoyance. &lt;br /&gt;etc. &lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116171093699152876?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.boingboing.net/2006/10/24/enron_explorer_mines.html' title='We got Boing Boing&apos;ed!'/><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116171093699152876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116171093699152876' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116171093699152876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116171093699152876'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/10/we-got-boing-boinged.html' title='We got Boing Boing&apos;ed!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116116360925856639</id><published>2006-10-18T09:42:00.000+01:00</published><updated>2006-10-18T10:34:17.976+01:00</updated><title type='text'>The Enron Explorer!</title><content type='html'>Phew - at long last we've gone live, and I can finally blog about what we've been working on here! &lt;br /&gt;&lt;br /&gt;&lt;a href="#enron"&gt;&lt;em&gt;( yeah, yeah, skip all that, what about Enron? )&lt;/em&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Our next major product is aimed squarely at the enterprise market, and it's a tool to analyse and extract meaning from corporate data stores and email traffic. Based on this information extraction, we can then map social networks and analyse information flow throughout the organisation, and use this information to give a shot in the arm to communications effectiveness - forwarding emails to you that are especially relevant to you but that you might otherwise have missed, and letting you set "volume level" for each of your interests. &lt;br /&gt;&lt;br /&gt;There's no end of applications for this kind of tech - everything from expertise analysis to Sarbannes-Oxley compliance. The hardest question of all, though, was what to call it. After a tortuous voting and lobbying process second only to the &lt;acronym title="Internation Olympic Committee"&gt;IOC&lt;/acronym&gt; in labyrinthine complexity, we finally settled on the name &lt;a href="http://www.trampolinesystems.com/products/sonar-platform-social-networks-and-relevance/"&gt;SONAR&lt;/a&gt;, as it kind of implies what the product does (scans things and identifies things that you need to know about) and it's also a merely-mildly-icky acronym - &lt;strong&gt;SO&lt;/strong&gt;cial &lt;strong&gt;N&lt;/strong&gt;etworks &lt;strong&gt;A&lt;/strong&gt;nd &lt;strong&gt;R&lt;/strong&gt;elevance.&lt;br /&gt;&lt;br /&gt;Anyway, while we were working on the early stages of SONAR, we needed a large set of plausible test data to test the &lt;acronym title="Natural Language Processing"&gt;NLP&lt;/acronym&gt; algorithms - sadly, my usual plethora of furry animals and pitiful puns just wasn't enough in this case - and &lt;a href="http://www.trampolinesystems.com/people/jan"&gt;Jan&lt;/a&gt; came up with a stroke of genius - the Enron email archive.&lt;br /&gt;&lt;br /&gt;&lt;a name="enron"&gt;&lt;/a&gt;In Oct. 2003, the &lt;acronym title="Federal Energy Regulation Committee"&gt;FERC&lt;/acronym&gt; released ~200,000 Enron emails into the public domain, as part of the inquiry into the Enron fraud. So we grabbed the database, imported it into a very early version of SONAR, and set it chugging away. The results were so absorbing that we can't stop fiddling with it! We decided it was a great way to demo our system, even though it's a very early version, so laydeeeez an' gennulmen, &lt;em&gt;(drum roll)&lt;/em&gt; allow me to present....&lt;br /&gt;&lt;br /&gt;&lt;A href="http://enron.trampolinesystems.com/"&gt;THE ENRON EXPLORER!&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Just click on a name or a theme to get in, and off you go. There's some real gems in the archive, ranging from the hilariously obscene :&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;a href="http://enron.trampolinesystems.com/focus/2016#focus=/focus/57447c2249209343ab488c055da76368f04a0lightbox=/show_email/38534c2249209343ab488c055da76368f04a0unique=45"&gt;Jeff Skilling to Andy Zipper&lt;/a&gt;:&lt;br /&gt;&lt;q&gt;Fuck you, you piece of shit. I can't wait to see you go down with the ship like all the other vermin. Smug, paranoid, unhappy mother fucker. Eat shit.&lt;/q&gt;&lt;br /&gt;&lt;em&gt;...to which I think Andy Zipper responded with &lt;a href="http://enron.trampolinesystems.com/focus/2016#focus=/focus/57447&amp;lightbox=/show_email/214031&amp;unique=20"&gt;admirable restraint!&lt;/a&gt;&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;- to the heartbreaking:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;a href="http://enron.trampolinesystems.com/focus/2016#focus=/focus/57447c2249209343ab488c055da76368f04a0lightbox=/show_email/38534c2249209343ab488c055da76368f04a0unique=45&amp;lightbox=/show_email/31998&amp;unique=2"&gt;Sato (Enron Japan) to Andy Fastow&lt;/a&gt;:&lt;br /&gt;&lt;q&gt;Please don't fire Enron Japan staff, we do nothing wrong! Please!!! [....] We know how serious is the situation but please don't fire us now. Our families are waiting for a happy chrismas and new year!!&lt;/q&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;We can (and often do) lose ourselves in this for ages - have an explore and enjoy finding out what they were saying to each other as the house of cards crumbled down around them.&lt;br /&gt;&lt;br /&gt;On a technical note, the interface is written in RubyOnRails, with a Java backend. It's AJAX'ed up to the eyeballs - hey, we even have rounded corners and gradient fills &lt;em&gt;on the AJAX loading indicators!&lt;/em&gt; - and although there are still a couple of issues to sort out, we've worked hard on maintaining the usual expectations of browser behaviour.&lt;br /&gt;&lt;br /&gt;The back button works (with a couple of minor niggles), and you can bookmark and email the url of your current view, and it should (nearly!) always bring you back to the same point. There's some smooth integration between the Java applet visualiser and the AJAX calls too, although again there's still a couple of niggles to deal with. &lt;br /&gt;&lt;br /&gt;There's a whole load of other niftiness going on behind the scenes, but I'll blog about that later. In the meantime, have fun with it, and if you find anything particularly juicy, leave a comment (either here or &lt;a href="http://www.trampolinesystems.com/enron-explorer/"&gt;on the app itself&lt;/a&gt;) and share it with the world!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116116360925856639?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://enron.trampolinesystems.com/' title='The Enron Explorer!'/><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116116360925856639/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116116360925856639' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116116360925856639'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116116360925856639'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/10/enron-explorer.html' title='The Enron Explorer!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-116056308995916537</id><published>2006-10-11T11:26:00.000+01:00</published><updated>2006-10-11T11:38:09.976+01:00</updated><title type='text'>Styled Checkboxes and Radio Buttons</title><content type='html'>I'm sure you've experienced the problem - most form elements can be styled pretty easily, but checkboxes and radio buttons? Forget it. If you're anything like me, you probably gave up by now and accepted that it's just one of those things you have to put up with. However, &lt;a href="http://www.dxdec.com/wod/formstyle/index.html"&gt;Philip Howard has released a nice CSS and JS solution&lt;/a&gt; that allows you to wrap a span of a certain class around the elements you want styled, and the magic pixies will do the rest. &lt;br /&gt;&lt;br /&gt;I've seen this done before, but this solution is worth highlighting because :&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It degrades gracefully back to the standard form elements if your browser does not support JS, CSS or images, and&lt;/li&gt;&lt;li&gt;He's put the extra bit of effort in to support the standard keyboard controls for form elements - space bar toggles status, left and right move the focus along a group of radio buttons, etc.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Nice one Philip.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-116056308995916537?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.dxdec.com/wod/formstyle/index.html' title='Styled Checkboxes and Radio Buttons'/><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/116056308995916537/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=116056308995916537' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116056308995916537'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/116056308995916537'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/10/styled-checkboxes-and-radio-buttons.html' title='Styled Checkboxes and Radio Buttons'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115995860915242846</id><published>2006-10-04T10:36:00.000+01:00</published><updated>2006-10-04T11:49:03.960+01:00</updated><title type='text'>Breaking Dependencies With Interfaces</title><content type='html'>I just picked up on Mike Dinowitz's post &lt;a href="http://www.blogoffusion.com/index.cfm/2006/10/3/Interfaces--Why-bother"&gt;Interfaces - Why Bother?&lt;/a&gt; asking for benefits that interfaces offer. I started typing a comment, but realised that my two-penn'orth was way too sprawling for a comment and needed a whole post of its own. So here goes - I'm sure this won't be telling someone as experienced as Mike anything he doesn't know, but there's also been a call for more introductory-level blog posts in the last couple of days, so hopefully someone out there will find this useful as a concrete example of where I'd be lost without interfaces.&lt;br /&gt;&lt;br /&gt;I'm currently working on a large J2EE platform with over 2000 classes. The only sensible way to manage such complexity is to split it into several distinct modules, each of which is conceptually self-contained and compiled and unit-tested separately, in a strict order. The ANT build script builds the core framework module first, then the email server, then the groups and user modules, etc etc, finally finishing up with the Tapestry-based web interface.&lt;br /&gt;&lt;br /&gt;This all works great, &lt;em&gt;except&lt;/em&gt; that you're still left with cross-module dependencies. The email server needs to know that group emails should be forwarded to group members, the groups module needs to know about its users, and so on.&lt;br /&gt;&lt;br /&gt;The way these dependencies are resolved, is through interfaces. In the email module, we create an interface that represents the bit of group- or member- related functionality that the email server needs to know about - in this case, the ability to accept and propagate an email - and make the group and member objects (which come &lt;em&gt;after&lt;/em&gt; the email server) implement that interface. &lt;br /&gt;&lt;br /&gt;So we make an interface called &lt;strong&gt;EmailPropagator&lt;/strong&gt; with one method - propagate() - in the email server module, and make groups and members implement EmailPropagator. &lt;br /&gt;&lt;br /&gt;This way, the email server doesn't need to know anything else about the "thing" it's sending email to - so long as it implements &lt;strong&gt;EmailPropagator&lt;/strong&gt;, the email server can ask it to propagate email to whoever or wherever it feels like, and that's &lt;em&gt;all&lt;/em&gt; that the email server needs to know about it in order to do its job. &lt;br /&gt;&lt;br /&gt;We can make &lt;em&gt;anything&lt;/em&gt; be accepted by the email server, so long as it implements that one method. It could be a group / mailing list, an individual person, a spam demon that forwards ten thousand copies to random addresses, a black hole that just swallows it up, a file store.... anything, so long as it implements that one method.&lt;br /&gt;&lt;br /&gt;This is part of the power of OO, and particularly the power of interfaces. It's also different from inheritance - in Java, and hence CF too, a class can only extend one base class. However, it can implement as many different interfaces as you like. You've heard of "If it looks like a duck and quacks like a duck, it must be a duck...." - well, this extends to "If it quacks like a duck, I don't care &lt;em&gt;what&lt;/em&gt; it is, so long as I can ask it to quack". &lt;br /&gt;&lt;br /&gt;It's one of the guiding mantras of OO that you should "design to interfaces, not implementations" and this is exactly why - we can make a million different things plug into our email server and do a million different actions with an email, without ever needing to change the email server code. &lt;em&gt;That's&lt;/em&gt; power, and &lt;em&gt;that's&lt;/em&gt; reusability. And &lt;em&gt;that's&lt;/em&gt; why I love interfaces.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115995860915242846?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115995860915242846/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115995860915242846' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115995860915242846'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115995860915242846'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/10/breaking-dependencies-with-interfaces.html' title='Breaking Dependencies With Interfaces'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115995428413306710</id><published>2006-10-04T10:12:00.000+01:00</published><updated>2006-10-04T10:33:41.396+01:00</updated><title type='text'>How Enterprise 2.0 Products Can Succeed</title><content type='html'>Much food for thought on Andrew McAfee's blog discussing &lt;a href="http://dor.hbs.edu/fi_redirect.jhtml?facInfo=bio&amp;facEmId=jgourville"&gt;John Gourville&lt;/a&gt;'s concept of the &lt;a href="http://blog.hbs.edu/faculty/amcafee/index.php/faculty_amcafee_v3/the_9x_email_problem/"&gt;"9x Email" problem&lt;/a&gt; which new technologies must overcome - "a mismatch of 9 to 1 between what innovators think consumers want and what consumers actually want."&lt;br /&gt;&lt;br /&gt;People - real people, not tech people, who sometimes seem to be rabidly adopting the newest, most obscure technology for no other reason than to claim "geekier than thou" bragging rights amongst their peers - have an inbuilt tendency to stick with what they know. Gourville suggests that in order for new technology to go viral, it must offer a &lt;em&gt;tenfold&lt;/em&gt; improvement over what's already out there. The problem is that in order to overcome the inertia associated with the status quo, en &lt;em&gt;evolutionary, not revolutionary&lt;/em&gt; approach is called for:&lt;br /&gt;&lt;br /&gt;&lt;q&gt;Gourville's research suggests that the average person will underweight the prospective benefits of a replacement technology for it by about a factor of three, and overweight by the same factor everything they're being asked to give up&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;McAfee takes the example of email versus "groupware", and cites the intuitive nature of email interfaces as a point of comparison for Enterprise 2.0 apps. Just about everyone "gets" email as a concept, and the critical problem for Enterprise 2.0 technologies is one of interfaces. Rather than adding more and more bells and whistles to an interface, we should concentrate on making the interfaces clean, elegant, and instantly-comprehensible. This is a viewpoint I wholeheartedly share, but McAfee puts it very succinctly:&lt;br /&gt;&lt;br /&gt;&lt;q&gt;A great UI not only heightens the perceived benefits of a proposed collaboration technology, it also lowers the perceived costs.&lt;br /&gt;...&lt;br /&gt;The greatest challenge here, I think, doesn't have to do with making the browser sufficiently application-like ... It has to do with making technologists sufficiently user-like --  getting them to stop thinking in terms of bells and whistles and elaborate functionality, and to start thinking instead about busy users with short attention spans who need to get something done, and who can always reach for email&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;Insightful stuff.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115995428413306710?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115995428413306710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115995428413306710' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115995428413306710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115995428413306710'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/10/how-enterprise-20-products-can-succeed.html' title='How Enterprise 2.0 Products Can Succeed'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115952020262620328</id><published>2006-09-29T09:38:00.000+01:00</published><updated>2006-09-29T09:56:43.203+01:00</updated><title type='text'>How To Architect Your CSS</title><content type='html'>We all have our own favourite strategies for architecting our application code, but CSS is often one of the aspects of a site that gets copmletely overlooked. As CSS - along with its browser support - matures and grows more powerful, how we structure our CSS becomes more and more important for maintainability and expansibility&lt;sup&gt;&lt;a href="#footnote1"&gt;1&lt;/a&gt;&lt;/sup&gt; of our applications.&lt;br /&gt;&lt;br /&gt;I'm sure we've all experienced it - you start out with a prototype with a few simple CSS rules and the best of intentions, but as your application grows and changes, and more and more people have their input into the design process, the CSS grows and morphs and accumulates more and more quick fixes and cheesey hacks. Months later, you realise you've got to the point where something as simple as "can we make the comments link appear in red?" can take hours of navigating the jungle of interfering specificities and ids. Eventually you admit defeat, and put in a cheesey hack "just this once", and mentally promise to go back and fix it properly later - but somehow you never get round to it, and the process continues. &lt;br /&gt;&lt;br /&gt;Digital Web has an interesting, if short, article on &lt;A href="http://www.digital-web.com/articles/architecting_css/"&gt;Architecting CSS&lt;/a&gt;, with some useful tips. If I had to pick two simple things that you should do &lt;em&gt;RIGHT NOW, DAMMIT&lt;/em&gt; if you're not already, it would be :&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;Commenting&lt;/strong&gt; your rules just as much as your code, and &lt;/li&gt;&lt;li&gt;&lt;strong&gt;Alphabetically sorting&lt;/strong&gt; your attributes.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Oh, and avoiding the &lt;span class="code"&gt;!important&lt;/span&gt; hack if at all possible. &lt;br /&gt;&lt;br /&gt;Alright, &lt;em&gt;three&lt;/em&gt; simple things....&lt;sup&gt;&lt;a href="#footnote2"&gt;2&lt;/a&gt;&lt;/sup&gt; You get the idea :)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;br /&gt;&lt;a name="footnote1"&gt;1&lt;/a&gt; - apologies for grimace-inducing linguistic contortions. It's Friday, it's early, and I haven't had coffee yet. Bleh.&lt;br /&gt;&lt;a name="footnote2"&gt;2&lt;/a&gt; - No-one expects the Spanish blog post!&lt;br /&gt;&lt;br /&gt;&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115952020262620328?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115952020262620328/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115952020262620328' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115952020262620328'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115952020262620328'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/09/how-to-architect-your-css.html' title='How To Architect Your CSS'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115886080207226030</id><published>2006-09-21T16:57:00.000+01:00</published><updated>2006-09-22T09:11:28.443+01:00</updated><title type='text'>Unfortunate Comedy Typos part 1</title><content type='html'>Ever have one of those days where no matter what you do, you just can't type properly? Today, I'm finding it impossible to type StringBuffer - it keeps coming out as &lt;strong&gt;StringBugger&lt;/strong&gt;.&lt;br /&gt;&lt;br /&gt;Calling Dr. Freud, calling Dr. Freud.....&lt;br /&gt;&lt;br /&gt;A couple of other Freudian &lt;strike&gt;slaps&lt;/strike&gt; (ahem) &lt;em&gt;SLIPS&lt;/em&gt; I keep having, mean that I keep declaring &lt;em&gt;functino's&lt;/em&gt; (maybe a new fundamental particle for the &lt;a href="http://en.wikipedia.org/wiki/Standard_Model"&gt;Standard Model&lt;/a&gt;?) and &lt;em&gt;functoni's&lt;/em&gt; - and I don't know if it's just me, but &lt;em&gt;Func Toni&lt;/em&gt; conjures up a disturbing image of &lt;a href="http://en.wikipedia.org/wiki/Swiss_Toni"&gt;Swiss Toni&lt;/a&gt; in a gold lam&amp;eacute; shirt with eighteen-inch collars, star-shaped shades, and a big-ass medallion, strutting his funky stuff to Graham Central Station - like &lt;em&gt;this:&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;&lt;object width="425" height="350"&gt;&lt;param name="movie" value="http://www.youtube.com/v/Z1IuD6F3R5I"&gt;&lt;/param&gt;&lt;param name="wmode" value="transparent"&gt;&lt;/param&gt;&lt;embed src="http://www.youtube.com/v/Z1IuD6F3R5I" type="application/x-shockwave-flash" wmode="transparent" width="425" height="350"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;br /&gt;&lt;br /&gt;&lt;strong&gt;Damn,&lt;/strong&gt; that's a funky bassline!&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(sigh)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;Thank crunchie it's Friday...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115886080207226030?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115886080207226030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115886080207226030' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115886080207226030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115886080207226030'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/09/unfortunate-comedy-typos-part-1.html' title='Unfortunate Comedy Typos part 1'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115883451954942351</id><published>2006-09-21T11:18:00.000+01:00</published><updated>2006-09-21T11:28:39.583+01:00</updated><title type='text'>Demos wins award</title><content type='html'>Congratulations to m'erstwhile colleagues at &lt;a href="http://headshift.com"&gt;Headshift&lt;/a&gt; on scooping &lt;a href="http://www.demos.co.uk/projects/demoswebsite/blog/bestwebsite"&gt;another award&lt;/a&gt; for the &lt;a href="http://instantbadger.blogspot.com/2006/08/demos-is-live.html"&gt;recently launched&lt;/a&gt; &lt;a href="http://www.demos.co.uk/"&gt;Demos&lt;/a&gt; site. I, along with the rest of the crew, put a huge amount of thought and care in to that site, and it gives me a nice warm fuzzy feeling to know that it's not just the client who appreciates it. Special kudos to &lt;a href="http://coldflush.blogspot.com/"&gt;Neil Roberts&lt;/a&gt; for doing the always-tricky task of picking up the reins on it after I left and teasing it lovingly into deployment.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115883451954942351?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115883451954942351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115883451954942351' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115883451954942351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115883451954942351'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/09/demos-wins-award.html' title='Demos wins award'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115815526535902133</id><published>2006-09-13T13:42:00.000+01:00</published><updated>2006-09-14T11:03:12.303+01:00</updated><title type='text'>Svn over ssh (svn+ssh://) on Windows via cygwin and PuTTy</title><content type='html'>One of the more obvious things lacking from Windows is an ssh (secure shell) client, and this has caused me no end of grief trying to check out code from &lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; repositories on servers that require ssh access - such as our own.  &lt;br /&gt;&lt;br /&gt;I'd previously managed to get access by forcing my username in the URL of the repository along with a saved &lt;a href="http://www.chiark.greenend.org.uk/~sgtatham/putty/"&gt;PuTTY&lt;/a&gt; session name:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;svn+ssh://alistair@&lt;em&gt;(saved PuTTY session name)&lt;/em&gt;/&lt;em&gt;etc...&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;...where the saved PuTTY session used my imported private key from the live host. &lt;br /&gt;&lt;br /&gt;&lt;em&gt;(Note: you *must* use PuTTYGen to convert your &lt;a href="http://openssh.org/"&gt;OpenSSH&lt;/a&gt; keys to PuTTY-compatible keys)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;This caused a certain amount of sneering from the Debian users in the office, who didn't have to jump through any of these hoops, but I could live with that...&lt;br /&gt;&lt;br /&gt;The latest problem to have me gesticulating at the screen like a south-american-footballer-in-front-of-a-referee-who's-reaching-for-his-pocket was the use of &lt;em&gt;&lt;strong&gt;externals&lt;/strong&gt;&lt;/em&gt;. &lt;br /&gt;&lt;br /&gt;An external in a Subversion repository is a URL reference to another resource - for instance, in a Java app that requires a certain library to build, you might add an external pointing to the download url of the particular jar file that you need. &lt;br /&gt;&lt;br /&gt;All well and good, but you can also add externals pointing to a different location in the &lt;em&gt;same&lt;/em&gt; repository, for instance, to share config files between back end and front end modules. &lt;br /&gt;&lt;br /&gt;Now here was the problem - you can't have relative urls in externals, they have to be absolute. This particular external was pointing to a different lcoation in the same repository in the standard URL way, which works fine on UNIX (Debian) clients because they have a native ssh client : &lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;svn+ssh//&lt;em&gt;(host)&lt;/em&gt;/&lt;em&gt;(path)&lt;/em&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;- but there was no way this was going to work on Windows via PuTTY - the only way I'd got svn+ssh:// urls to work previously was via the username@savedsessionname method. Every time I tried to update the working copy, it would get as far as that external, then give me this error message:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;svn: Connection closed unexpectedly&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So how do you get round this? Enter the cavalry - &lt;a href="http://www.cygwin.com/"&gt;Cygwin&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Cygwin is a command shell for Windows that gives you a &lt;a href="http://www.gnu.org/software/bash/"&gt;Bash&lt;/a&gt;-style environment, including....&lt;em&gt;(drum roll)&lt;/em&gt;..... an ssh client!&lt;br /&gt;&lt;br /&gt;With this, you can configure SVN (and the rather wonderful &lt;a href="http://tortoisesvn.tigris.org"&gt;TortoiseSVN&lt;/a&gt;) to use this ssh client and - crucially - pass it a command line parameter that tells it to use your private key file for authentication.&lt;br /&gt;&lt;br /&gt;If you open your SVN config file in a text editor, (you'll find this in C:\Documents and Settings\&lt;em&gt;(username)&lt;/em&gt;\Application Data\Subversion\config) you'll see instructions on how to configure ssh. You can pretty much ignore it :-) and just use the following line: &lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;ssh = "C:/&lt;em&gt;(cygwin root path)&lt;/em&gt;/bin/ssh.exe" -i "C:/&lt;em&gt;(path to your private key)&lt;/em&gt;"&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;...and that's it! It's one of those really simple solutions that took me a couple of minutes to implement, but hours of desk-headbanging frustration to find. You can also use the same command line in TortoiseSVN -&gt; settings -&gt; network -&gt; ssh client.&lt;br /&gt;&lt;br /&gt;One other note: make sure that you point ssh.exe at your &lt;em&gt;openssh-compatible&lt;/em&gt; private key. If you've set up your key with PuTTYGen, you can load it in and export an OpenSSH-compatible key from there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115815526535902133?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115815526535902133/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115815526535902133' title='14 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115815526535902133'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115815526535902133'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/09/svn-over-ssh-svnssh-on-windows-via.html' title='Svn over ssh (svn+ssh://) on Windows via cygwin and PuTTy'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>14</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115762093566750902</id><published>2006-09-07T10:19:00.000+01:00</published><updated>2006-09-07T10:22:15.683+01:00</updated><title type='text'>Lessons Learned From Kiko</title><content type='html'>Richard White has posted an insightful write-up of his &lt;a href="http://www.height1percent.com/articles/2006/08/18/actual-lessons-from-kiko"&gt;Actual Lessons From Kiko&lt;/a&gt;. Well worth a few minutes of anybody who is in the web app space.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115762093566750902?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115762093566750902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115762093566750902' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115762093566750902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115762093566750902'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/09/lessons-learned-from-kiko.html' title='Lessons Learned From Kiko'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115695195803000781</id><published>2006-08-30T16:18:00.000+01:00</published><updated>2006-08-30T16:38:40.576+01:00</updated><title type='text'>RegEx to fully validate RFC822 email addresses</title><content type='html'>I &lt;em&gt;love&lt;/em&gt; Perl. No, I really do - I can sit and look at the really good examples for hours, and still have no clue what they're doing. And here is a &lt;em&gt;fantastic&lt;/em&gt; example....&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.ex-parrot.com/~pdw/Mail-RFC822-Address.html"&gt;Mail::RFC822::Address&lt;/a&gt; - a module that tells you whether the given string is a valid email address or not. &lt;br /&gt;&lt;br /&gt;Most email validators only check the absolute basics - e.g. some characters followed by an at-sign, followed by some more characters and at least one dot. But have you ever actually read &lt;a href="http://www.ietf.org/rfc/rfc0822.txt"&gt;RFC822&lt;/a&gt;, the critical RFC that defines the standard of what's acceptable in an email and what isn't ? It's surprisingly loose on what's an acceptable address. &lt;br /&gt;&lt;br /&gt;&lt;em&gt;(go on, read chapter 6, and try to summarise it - you know you want to...!)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;But no more, thanks to Paul Warren, who has solved all our email-address-validation woes with one almighty mother of a reg ex - and here it is. You'll like this. No, you will.... here we go:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]&lt;br /&gt;)+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:&lt;br /&gt;\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(&lt;br /&gt;?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[&lt;br /&gt;\t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\0&lt;br /&gt;31]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+&lt;br /&gt;(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:&lt;br /&gt;(?:\r\n)?[ \t])*))*|(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z&lt;br /&gt;|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n) ?[&lt;br /&gt;\t])*)*\&lt;(?:(?:\r\n)?[ \t])*(?:@(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\ r\n)?[&lt;br /&gt;\t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[&lt;br /&gt;\t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n) ?[&lt;br /&gt;\t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t]&lt;br /&gt;)*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[&lt;br /&gt;\t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*&lt;br /&gt;)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t]&lt;br /&gt;)+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)&lt;br /&gt;*:(?:(?:\r\n)?[ \t])*)?(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+&lt;br /&gt;|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r \n)?[&lt;br /&gt;\t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?: \r\n)?[&lt;br /&gt;\t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t&lt;br /&gt;]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031&lt;br /&gt;]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](&lt;br /&gt;?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?&lt;br /&gt;:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?&lt;br /&gt;:\r\n)?[ \t])*))*\&gt;(?:(?:\r\n)?[ \t])*)|(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?&lt;br /&gt;:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)? [&lt;br /&gt;\t]))*"(?:(?:\r\n)?[ \t])*)*:(?:(?:\r\n)?[ \t])*(?:(?:(?:[^()&lt;&gt;@,;:\\".\[\]&lt;br /&gt;\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|&lt;br /&gt;\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;&lt;br /&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"&lt;br /&gt;(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t]&lt;br /&gt;)*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?&lt;br /&gt;:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[&lt;br /&gt;\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:[^()&lt;&gt;@,;:\\".\[\] \000-&lt;br /&gt;\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(&lt;br /&gt;?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\&lt;(?:(?:\r\n)?[ \t])*(?:@(?:[^()&lt;&gt;@,;&lt;br /&gt;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([&lt;br /&gt;^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\" .\[\]&lt;br /&gt;\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\ [\]&lt;br /&gt;\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\]&lt;br /&gt;\000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]&lt;br /&gt;|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?(?:[^()&lt;&gt;@,;:\\".\[\] \0&lt;br /&gt;00-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,&lt;br /&gt;;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|"(?&lt;br /&gt;:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*))*@(?:(?:\r\n)?[ \t])*&lt;br /&gt;(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".&lt;br /&gt;\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t])*(?:[&lt;br /&gt;^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]&lt;br /&gt;]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\&gt;(?:(?:\r\n)?[ \t])*)(?:,\s*(&lt;br /&gt;?:(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(&lt;br /&gt;?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[&lt;br /&gt;\["()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t&lt;br /&gt;])*))*@(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?&lt;br /&gt;:\.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|&lt;br /&gt;\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*|(?:&lt;br /&gt;[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)*\&lt;(?:(?:\r\n) ?[&lt;br /&gt;\t])*(?:@(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["&lt;br /&gt;()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n) ?[&lt;br /&gt;\t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;&lt;br /&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*(?:,@(?:(?:\r\n)?[&lt;br /&gt;\t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,&lt;br /&gt;;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:\.(?:(?:\r\n)?[ \t]&lt;br /&gt;)*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*)*:(?:(?:\r\n)?[ \t])*)?&lt;br /&gt;(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\["()&lt;&gt;@,;:\\".&lt;br /&gt;\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])*)(?:\.(?:(?:&lt;br /&gt;\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z|(?=[\[&lt;br /&gt;"()&lt;&gt;@,;:\\".\[\]]))|"(?:[^\"\r\\]|\\.|(?:(?:\r\n)?[ \t]))*"(?:(?:\r\n)?[ \t])&lt;br /&gt;*))*@(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])&lt;br /&gt;+|\Z|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*)(?:.(?:(?:\r\n)?[ \t])*(?:[^()&lt;&gt;@,;:\\".\[\] \000-\031]+(?:(?:(?:\r\n)?[ \t])+|\Z&lt;br /&gt;|(?=[\["()&lt;&gt;@,;:\\".\[\]]))|\[([^\[\]\r\\]|\\.)*\](?:(?:\r\n)?[ \t])*))*\&gt;(?:(&lt;br /&gt;?:\r\n)?[ \t])*))*)?;\s*)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;q&gt;After a while, all you see is ....blonde......brunette.....&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;PS - hope Paul doesn't mind me posting it here. It's truly a thing of beauty, but I'll take it down if requested.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115695195803000781?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115695195803000781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115695195803000781' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115695195803000781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115695195803000781'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/regex-to-fully-validate-rfc822-email.html' title='RegEx to fully validate RFC822 email addresses'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115693643784548300</id><published>2006-08-30T12:05:00.000+01:00</published><updated>2006-08-30T12:13:57.876+01:00</updated><title type='text'>URGENT html prototype designer/coder needed today and tomorrow!</title><content type='html'>Bit of a long shot this one, but Lise has been left high-and-dry by a contract developer taken ill. The job is mocking up flat HTML interfaces, and will probably require a bit of a late night tonight as it must be finished by close-of-play tomorrow for user testing on Friday. Ideally you'd be able to make it to Islington THIS AFTERNOON for a briefing and handover. &lt;br /&gt;&lt;br /&gt;Very short notice, but they're a bit stuck - so if anyone finds themselves at a loose end for a couple of days, add a comment on this post and i'll pass your details on.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115693643784548300?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115693643784548300/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115693643784548300' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115693643784548300'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115693643784548300'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/urgent-html-prototype-designercoder.html' title='URGENT html prototype designer/coder needed today and tomorrow!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115685113784412905</id><published>2006-08-29T11:18:00.000+01:00</published><updated>2006-08-29T12:32:18.480+01:00</updated><title type='text'>Flickr's Geotagging Let Down By Location Search</title><content type='html'>&lt;a href="http://thomashawk.com/"&gt;Thomas Hawk&lt;/a&gt; of Geo-tagging mashup &lt;a href="http://beta.zooomr.com/tagmap"&gt;Zoomr&lt;/a&gt; does a good job of keeping his objectivity in his review of Flickr's new Geotagging facility. &lt;br /&gt;&lt;br /&gt;I just had a play with it myself, and I have to agree with Thomas - the nicest aspect is the way that it's integrated with the Flickr Organiser (I still keep spelling that Organizr...) and the by-now-expected Ajax drag-and-drop wizardry. But the most disappointing aspect of it all is the underlying Yahoo! maps data. It's fine if you want to geotag photos down to city block level in most major US cities, but stray off the beaten path to tag photos of &lt;a href="http://flickr.com/photos/drsnooks/sets/72157594147892513/"&gt;mountaineering treks&lt;/a&gt; or rock climbing venues - even &lt;a href="http://flickr.com/photos/drsnooks/sets/72157594169877190/"&gt;world-famous rock climbing venues&lt;/a&gt; - and the map detail just isn't there. &lt;br /&gt;&lt;br /&gt;To be fair, this isn't just a limitation of Yahoo!'s map data, it's similarly limited in Google Maps aswell - and I guess it's a reflection of the underlying business drivers behind the map data. Constructing an index of the whole globe is a massive undertaking, and it has to be funded somehow. The obvious channel is advertising, but only businesses are willing to pay for advertising, and most businesses tend to be centred around urban areas, therefore it's more important to the map provider that the advertisers are kept happy.&lt;br /&gt;&lt;br /&gt;It just seems to take on an added dimension of disappointment when this limitation applies to photos. To be honest, one U.S. city block looks pretty much the same as any other U.S. city block, in the grand scheme of things, and part of the whole joy of Flickr comes from discovering fascinating, beautiful images that you may never otherwise have seen. Almost by its very nature, this is going to involve out-of-the-way places like the &lt;a href="http://flickr.com/photos/35663537@N00/154017488/"&gt;Sim Gang Glacier&lt;/a&gt; in Pakistan, &lt;a href="http://flickr.com/photos/cassio/204160765/"&gt;La Dibona&lt;/a&gt; of Les Ecrins in the Alps, or &lt;a href="http://flickr.com/photos/heilemann/30220469/"&gt;K2&lt;/a&gt;, which requires 14 days of &lt;em&gt;hard&lt;/em&gt; trekking from the nearest road before you even reach the mountain - often cited as the hardest trek in the world, but surely one of the most beautiful. Try to find any of these places in the Flickr map, and you'll have trouble. Even closer to home, in the &lt;a href="http://flickr.com/photos/drsnooks/130255163/in/photostream/"&gt;rugged mountain landscape of North Wales around Tryfan&lt;/a&gt;, the map engine draws a blank.&lt;br /&gt;&lt;br /&gt;To take the next step in geotagging, and in mapping as a whole, requires the next step to be taken in the search technology that takes a user-supplied string and works out what the hell you actually meant. This is a mammoth task in itself, considering how many different ways people can refer to a particular langitude and latitude, across languages and character sets, let alone local names vs. standard names. But whoever cracks &lt;em&gt;that&lt;/em&gt; problem can look forward to a very bright future indeed, and that's just one reason why Natural Language Processing - clearing away the cobwebs of context and language and working with raw chunks of &lt;em&gt;meaning&lt;/em&gt; - is becoming such a hot topic right now. Stay tuned...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115685113784412905?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115685113784412905/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115685113784412905' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115685113784412905'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115685113784412905'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/flickrs-geotagging-let-down-by.html' title='Flickr&apos;s Geotagging Let Down By Location Search'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115641066471592320</id><published>2006-08-24T09:56:00.000+01:00</published><updated>2006-08-24T10:11:04.736+01:00</updated><title type='text'>So many parties, so little time...</title><content type='html'>Well, I had the best intentions of making it to &lt;a href="http://ukcfug.org"&gt;tonight's London CFUG&lt;/a&gt;, but once again my path is strewn with cowpats from the Devil's own Satanic herd** as it once again conflicts with something else that I just can't miss. This time, it's the &lt;a href="http://www.trampolinesystems.com"&gt;Trampoline Systems&lt;/a&gt; Summer Bash, graced by the intriguing &lt;q&gt;Czechoslovakian Alternative Folktronica&lt;/q&gt; of the most delectable Miss &lt;a href="http://www.myspace.com/evaeden"&gt;Eva Eden&lt;/a&gt;. Should be a larf - I've never had to mike-up a &lt;a href="http://images.google.co.uk/images?svnum=10&amp;hl=en&amp;lr=&amp;q=bontempi+organ&amp;btnG=Search"&gt;Bontempi organ&lt;/a&gt; before.... I'll whack the more-salacious photos onto Flickr tomorrow, plus any videos I happen to get of any embarrassingly drunk bigwigs saying spectacularly inappropriate things.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So have a pint and a whinge for me at the CFUG tonight, and hopefully I'll make it to the next one. Unless I'm halfway up some mountain somewhere or something.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;**&lt;em&gt;series and episode, anyone?&lt;/em&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115641066471592320?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115641066471592320/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115641066471592320' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115641066471592320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115641066471592320'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/so-many-parties-so-little-time.html' title='So many parties, so little time...'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115583444802191530</id><published>2006-08-17T18:01:00.000+01:00</published><updated>2006-08-17T18:07:28.066+01:00</updated><title type='text'>Who Wants To Buy An Ajax Calendar App?</title><content type='html'>In a move that's not-at-all-designed-to-create-an-internet-buzz, AJAX calendar app Kiko is &lt;a href="http://cgi.ebay.com/ws/eBayISAPI.dll?ViewItem&amp;item=120021374185"&gt;for sale on eBay&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Starting price US $49,999.99. No Bids yet.&lt;br /&gt;&lt;br /&gt;In the interests of completeness, I should point out that they're not the first, by a long way - which market sector do you think got there first? Of course - &lt;a href="http://www.boingboing.net/2005/12/06/porn_company_for_sal.html"&gt;porn!&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;The listing states &lt;br /&gt;&lt;br /&gt;&lt;q&gt;We are selling Kiko because we want to have time to work on other projects as a development team.&lt;/q&gt;&lt;br /&gt;&lt;br /&gt;...and not because they are now in direct competition with Google, or anything...&lt;br /&gt;&lt;br /&gt;best of luck Kiko guys&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115583444802191530?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115583444802191530/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115583444802191530' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115583444802191530'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115583444802191530'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/who-wants-to-buy-ajax-calendar-app.html' title='Who Wants To Buy An Ajax Calendar App?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115582205789212066</id><published>2006-08-17T11:38:00.000+01:00</published><updated>2006-08-17T14:40:58.053+01:00</updated><title type='text'>A Pox Upon AppleMail!</title><content type='html'>A lot of my time since joining &lt;a href="http://www.trampolinesystems.com"&gt;Trampoline&lt;/a&gt; 6 weeks ago has been spent reaquainting myself with the black art of parsing and dismembering MIME emails with the JavaMail API. There's much I could say about the MIME format and particularly the JavaMail API, but those apopleptic rants deserve to be written up and nailed to church doors all of their own. &lt;br /&gt;&lt;br /&gt;This post is about a problem I've been having with a mail generated by Apple Mail, that has been driving me nuts. It's not the first issue I've had with Apple Mail and it's funky attachment formatting, and I'm sure it won't be the last. However, it's the most maddening to date! Here's the problem:&lt;br /&gt;&lt;br /&gt;In a multipart email, you separate each part with a unique string that must not occur in any of the parts. This is generated by the email client, and declared in the Content-Type header. RFC 2045 states that the boundary declaration is required for any multipart subtype. The header should look like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;Content-Type: multipart/&lt;em&gt;(whatever)&lt;/em&gt;; boundary=&amp;quot;----=&lt;em&gt;(unique string)&lt;/em&gt;&amp;quot; &lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;You will then get a set of Parts, each separated by an occurence of the boundary string, and each declaring what type of content it is by means of its own Content-Type header -&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;br /&gt;Content-Type: multipart/&lt;em&gt;(whatever)&lt;/em&gt;; boundary=&amp;quot;----=&lt;em&gt;(unique string)&lt;/em&gt;&amp;quot; &lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;An example might be:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;br /&gt;Content-Type: multipart/mixed; boundary=&amp;quot;----=ABCDEFGHIJKLMNOP&amp;quot; &lt;br /&gt;&lt;br /&gt;----=ABCDEFGHIJKLMNOP&lt;br /&gt;Content-Type: text/plain; charset=US-ASCII&lt;br /&gt;&lt;br /&gt;Hi Al, &lt;br /&gt;&lt;br /&gt;Here's the schematic for the secret base under the island volcano. Note the new layout of the shark pools, and the trapdoor is now triggered from the pressure pad under your desk as requested. Will give the engineers a kick about the frickin' lasers and see what's taking them so long.&lt;br /&gt;&lt;br /&gt;Cheers,&lt;br /&gt;&lt;br /&gt;Dave&lt;br /&gt;&lt;br /&gt;----=ABCDEFGHIJKLMNOP&lt;br /&gt;Content-Type: image/png; name="plans.png"&lt;br /&gt;Content-Transfer-Encoding: base64&lt;br /&gt;Content-Disposition: inline; filename="plans.png"&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(lots of data encoded into base 64 so that it can be transferred as text)&lt;/em&gt;&lt;br /&gt;----=ABCDEFGHIJKLMNOP&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;All well and good so far.&lt;br /&gt;&lt;br /&gt;It gets a bit more complicated when you introduce the fact that any part of the mail body can also be a multipart type, which must declare its &lt;strong&gt;own&lt;/strong&gt; boundary string, but still, it should be parseable into a coherent tree structure, right?&lt;br /&gt;&lt;br /&gt;Well yes - as long as you play by the rules. &lt;br /&gt;&lt;br /&gt;Apple Mac files consist of two forks:&lt;br /&gt;&lt;br /&gt;1) an apple-specific part called the RESOURCE fork which contains arbitrary information such as icon bitmaps and file info parameters, &lt;br /&gt;2) a DATA fork which contains the actual file data. &lt;br /&gt;&lt;br /&gt;This translates logically into a MIME multipart format - &lt;a href="http://www.iana.org/assignments/media-types/multipart/appledouble"&gt;multipart/appledouble &lt;/a&gt; - with one part for each fork. &lt;br /&gt;&lt;br /&gt;So, if we were to to send the example message above from a Mac using AppleMail, you would get something like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;br /&gt;Content-Type: multipart/mixed; boundary=&amp;quot;----=TOPLEVELBOUNDARY&amp;quot; &lt;br /&gt;&lt;br /&gt;----=TOPLEVELBOUNDARY&lt;br /&gt;Content-Type: text/plain; charset=US-ASCII&lt;br /&gt;&lt;br /&gt;Hi Al, &lt;br /&gt;&lt;br /&gt;Here's the schematic for the secret base under the island volcano. Note the new layout of the shark pools, and the trapdoor is now triggered from the pressure pad under your desk as requested. Will give the engineers a kick about the frickin' lasers and see what's taking them so long.&lt;br /&gt;&lt;br /&gt;Cheers,&lt;br /&gt;&lt;br /&gt;Dave&lt;br /&gt;&lt;br /&gt;----=TOPLEVELBOUNDARY&lt;br /&gt;Content-Type: multipart/appledouble; boundary="----=HEYIMTHEAPPLEDOUBLEBOUNDARY"&lt;br /&gt;&lt;br /&gt;----=HEYIMTHEAPPLEDOUBLEBOUNDARY&lt;br /&gt;Content-Type: application/applefile; name=plans.png&lt;br /&gt;Content-Disposition: inline; filename="plans.png"&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(apple-specific file information encoded into base 64)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;----=HEYIMTHEAPPLEDOUBLEBOUNDARY&lt;br /&gt;Content-Type: image/png; name=plans.png&lt;br /&gt;Content-Transfer-Encoding: base64&lt;br /&gt;Content-Disposition: inline; filename="plans.png"&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(actual file data encoded into base 64)&lt;/em&gt;&lt;br /&gt;----=HEYIMTHEAPPLEDOUBLEBOUNDARY&lt;br /&gt;&lt;br /&gt;----=TOPLEVELBOUNDARY&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Again, this is all well and good so far - apart from one or two minor irritations like that lack of quotes around the name of the file in the Content-Type header - which can cause some grief if the filename has spaces in it.... but that can be got round without much trouble using a bit of regex in pre-processing.&lt;br /&gt;&lt;br /&gt;The problem comes when you have &lt;strong&gt;multiple&lt;/strong&gt; appledouble-encoded attachments. What you would expect is something like this:&lt;br /&gt;&lt;br /&gt;&lt;div class="code"&gt;&lt;pre&gt;&lt;br /&gt;Content-Type: multipart/mixed; boundary=&amp;quot;----=TOPLEVELBOUNDARY&amp;quot; &lt;br /&gt;&lt;br /&gt;----=TOPLEVELBOUNDARY&lt;br /&gt;Content-Type: text/plain; charset=US-ASCII&lt;br /&gt;&lt;br /&gt;blah - message text&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;ATTACHMENT 1:&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;----=TOPLEVELBOUNDARY&lt;br /&gt;Content-Type: multipart/appledouble; boundary="----=HEYIMTHEAPPLEDOUBLEBOUNDARY"&lt;br /&gt;&lt;br /&gt;----=HEYIMTHEAPPLEDOUBLEBOUNDARY&lt;br /&gt;Content-Type: application/applefile; name=plans.png&lt;br /&gt;Content-Disposition: inline; filename="plans.png"&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(apple-specific file information encoded into base 64)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;----=HEYIMTHEAPPLEDOUBLEBOUNDARY&lt;br /&gt;Content-Type: image/png; name=plans.png&lt;br /&gt;Content-Transfer-Encoding: base64&lt;br /&gt;Content-Disposition: inline; filename="plans.png"&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(actual file data encoded into base 64)&lt;/em&gt;&lt;br /&gt;----=HEYIMTHEAPPLEDOUBLEBOUNDARY&lt;br /&gt;&lt;br /&gt;&lt;em&gt;&lt;strong&gt;ATTACHMENT 2:&lt;/strong&gt;&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;----=TOPLEVELBOUNDARY&lt;br /&gt;Content-Type: multipart/appledouble; boundary="----=DIFFERENTAPPLEDOUBLEBOUNDARY"&lt;br /&gt;&lt;br /&gt;----=DIFFERENTAPPLEDOUBLEBOUNDARY&lt;br /&gt;Content-Type: application/applefile; name=plans.png&lt;br /&gt;Content-Disposition: inline; filename="plans.png"&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(apple-specific file information encoded into base 64)&lt;/em&gt;&lt;br /&gt;&lt;br /&gt;----=DIFFERENTAPPLEDOUBLEBOUNDARY&lt;br /&gt;Content-Type: image/png; name=plans.png&lt;br /&gt;Content-Transfer-Encoding: base64&lt;br /&gt;Content-Disposition: inline; filename="plans.png"&lt;br /&gt;&lt;br /&gt;&lt;em&gt;(actual file data encoded into base 64)&lt;/em&gt;&lt;br /&gt;----=DIFFERENTAPPLEDOUBLEBOUNDARY&lt;br /&gt;&lt;br /&gt;----=TOPLEVELBOUNDARY&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;But what's actually happening is that in the second attachment, the all-important Content-Type: declaration - &lt;br /&gt;&lt;br /&gt;Content-Type: multipart/appledouble; boundary="----=DIFFERENTAPPLEDOUBLEBOUNDARY"&lt;br /&gt;&lt;br /&gt;- is missing!&lt;br /&gt;&lt;br /&gt;This line is absolutely vital, as it not only declares that this part is in appledouble format, but more fundamentally it declares that this part is itself a multipart and is split with THIS boundary marker rather than any other. &lt;br /&gt;&lt;br /&gt;If this line is missing, then ONLY the FIRST attachment gets recognised. Any subsequent attachments which don't get the content-type header are then considered to be text/plain by default, so you get an email which has the first attachment properly parsed as an image, but everything after that appears inline as text. So anyone reading the email gets a big long string of base64 encoded image data. Not nice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115582205789212066?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115582205789212066/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115582205789212066' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115582205789212066'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115582205789212066'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/pox-upon-applemail.html' title='A Pox Upon AppleMail!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115573544722440025</id><published>2006-08-16T14:28:00.000+01:00</published><updated>2006-08-16T15:25:55.130+01:00</updated><title type='text'>Slovenia - The Game!</title><content type='html'>At last, the Flash app you've all been waiting for - &lt;a href="http://www.carniola.org/2006/08/slovenia-the-game-this-time-for-real.htm"&gt;Slovenia, The Game!&lt;/a&gt; It's a very cute, virtual community based around various beauty spots in Slovenia. &lt;a href="http://flickr.com/photos/drsnooks/sets/72057594139931538/"&gt;We went there last summer for a week&lt;/a&gt;, and completely fell in love with the place. It's just stupidly beautiful - everywhere you look, your jaw drops, and this quirky little game just brought it all back.... I'm sitting here with a silly grin on my face and a wistful look in my eye. Ah, memories...&lt;br /&gt;&lt;br /&gt;by the way - you seem to get called &amp;quot;pedr&amp;quot; a lot - &lt;em&gt;pedr&lt;/em&gt; loosely translates as &amp;quot;fag&amp;quot;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115573544722440025?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115573544722440025/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115573544722440025' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115573544722440025'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115573544722440025'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/slovenia-game.html' title='Slovenia - The Game!'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115554644475744443</id><published>2006-08-14T09:58:00.000+01:00</published><updated>2006-08-14T10:07:24.770+01:00</updated><title type='text'>How to get a job in Silicon Valley</title><content type='html'>Good-old Guy Kawasaki has an endearingly cynical but still-so-true guide to &lt;a href="http://blog.guykawasaki.com/2006/08/the_inside_scoo.html"&gt;How To Get Hired In Silicon Valley&lt;/a&gt;. Well worth a read, and just as applicable outside California. Not that I've ever &lt;em&gt;applied&lt;/em&gt; for a job in Silicon Valley (yet), but it certainly brought a wry smile to my face.&lt;br /&gt;&lt;br /&gt;I've read many CVs and conducted many interviews, and it's amazing just how easy some people make it for you to put their CV on the "no" pile. I know we're now living in the txt-spk generation, but seriously - if you can't spell or punctuate &lt;em&gt;on your CV&lt;/em&gt; when you're promoting yourself, how well are you going to promote the company? Interviewers are busy people, and will be looking for any reason to say no - spell well, communicate well, keep it short, and if you get an interview, read and learn from Mr Kawasaki.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115554644475744443?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115554644475744443/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115554644475744443' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115554644475744443'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115554644475744443'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/how-to-get-job-in-silicon-valley.html' title='How to get a job in Silicon Valley'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115529341933069041</id><published>2006-08-11T11:12:00.000+01:00</published><updated>2006-08-11T11:50:19.386+01:00</updated><title type='text'>What do atoms look like?</title><content type='html'>....they look like &lt;a href="http://www.aip.org/png/2006/264.htm"&gt;THIS&lt;/a&gt; - Physics News Graphics have published a field-ion microscope image of the &lt;a href="http://www.sciencedaily.com/releases/2006/07/060711131527.htm"&gt;sharpest man-made object ever produced&lt;/a&gt; - a needle with a tip that's just a single tungsten atom. The thing I love about this image is the way some of the atoms look like blobs of mercury -  because they moved during the 1-second-long imaging process.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115529341933069041?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115529341933069041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115529341933069041' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115529341933069041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115529341933069041'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/what-do-atoms-look-like.html' title='What do atoms look like?'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115469920889826323</id><published>2006-08-04T14:41:00.000+01:00</published><updated>2006-08-04T14:46:48.910+01:00</updated><title type='text'>Java Programmers are the Erotic Furries of Programming</title><content type='html'>Nice chart of smugness here on &lt;a href="http://lukewelling.com/2006/08/03/java-programmers-are-the-erotic-furries-of-programming/"&gt;Some Guy Ranting&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;....but where would CF programmers sit on this chart?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115469920889826323?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115469920889826323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115469920889826323' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115469920889826323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115469920889826323'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/java-programmers-are-erotic-furries-of.html' title='Java Programmers are the Erotic Furries of Programming'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6799088.post-115459670734782921</id><published>2006-08-03T09:49:00.000+01:00</published><updated>2006-08-03T10:18:27.363+01:00</updated><title type='text'>Demos is live</title><content type='html'>Nice to see that &lt;a href="http://www.headshift.com"&gt;Headshift&lt;/a&gt; have got the new &lt;a href="http://www.demos.co.uk"&gt;Demos&lt;/a&gt; site live. This was the last site I worked on at Headshift, and there's quite a few aspecs to it of which I - and m'former colleagues Neil Roberts and Andy Birchall - feel quite proud.&lt;br /&gt;&lt;br /&gt;It's architected with the "Everything-is-a-Item" approach that I learned from my Headshift predecessor, erstwhile colleague and all-round-good-egg &lt;a href="http://associativetrails.co.uk/index.cfm/about"&gt;Matt Perdeaux&lt;/a&gt;, which opens a lot of doors in terms of code reuse and making the most of your content. &lt;br /&gt;&lt;br /&gt;Put simply, everything on the site - blog posts, comments, people, users, CMS pages - has the basic common data such as title, summary, and full description abstracted to an Items table, to which you can join as required in your SQL. All links between items and other entities (e.g. tags) are done at the Items level. This makes things like a global search across all content trivially easy (at least in theory - doesn't always work out that way of course... ) and means that once you've implemented tagging for, say, blog posts, you can have tagging on everything else as well with virtually zero extra effort - because everything is-a Item.&lt;br /&gt;&lt;br /&gt;As with a lot of systems I've written over the years, it gets a little frustrating that some of the nicest bits are hidden away from public view in the admin interfaces. But you'll just have to take my word for it that there are lots of self-contained AJAX-driven custom tags that do things like tag administration and item-to-item linkages, which only had to be written once, and could be brought into any form for any kind of item and would work straight away - because everything is-a Item. &lt;br /&gt;&lt;br /&gt;Other aspects of the site which give me that warm fluffy feeling inside include the friendly url scheme. The site is written with a Fusebox front-end, but there's a very thin layer above that which translates human-friendly urls into fusebox-friendly urls. This is done with Apache mod_rewrite, translating a url like &lt;br /&gt;&lt;br /&gt;http://www.demos.co.uk/projects/demoswebsite/blog/artistaudience&lt;br /&gt;&lt;br /&gt;into :&lt;br /&gt;&lt;br /&gt;http://www.demos.co.uk/&lt;strong&gt;projects.cfm?sParams=&lt;/strong&gt;/demoswebsite/blog/artistaudience&lt;br /&gt;&lt;br /&gt;projects.cfm then parses the sParams string into its expected parameters, retrieves any referenced items into request scope, checks permissions, works out the appropriate fuseaction, and passes on to the FB framework by including index.cfm. &lt;br /&gt;&lt;br /&gt;It would make a good CFUG case study, I reckon. (Hey Matt - fancy another vaudevillian double-act? Gaw blimey, roll out the barrel guvnor...) Mind you, I'm not sure if it's appropriate for me to reveal the technical innards of a system when I don't work for the company anymore... hmm... over to you then Neil ;)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6799088-115459670734782921?l=instantbadger.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://instantbadger.blogspot.com/feeds/115459670734782921/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6799088&amp;postID=115459670734782921' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115459670734782921'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6799088/posts/default/115459670734782921'/><link rel='alternate' type='text/html' href='http://instantbadger.blogspot.com/2006/08/demos-is-live.html' title='Demos is live'/><author><name>Alistair Davidson</name><uri>http://www.blogger.com/profile/07415960315873865766</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='22' height='32' src='http://photos1.blogger.com/blogger/4032/386/320/paris_thumb.jpg'/></author><thr:total>1</thr:total></entry></feed>
