THE BLOG

09
Feb

Clock of Clocks

Clock of Clocks

On a recent trip to London my girlfriend and I visited the Saatchi Gallery (superb gallery, you must go if you’re in the area), where I was mesmerised by a piece in their store called “The Clock Clock” by humans since 1982. Check out this video of it, it’s amazing. I thought that it would make a really cool little HTML5 experiment, so I gave it a go.You can view it here, but you’d do well to fire up the latest version of Chrome, Safari or Firefox first.

I’ve reached out to humans since 1982 to see what they think of it, but haven’t heard anything back yet.

Enjoy!

http://clock.imaginaryroots.co.uk/

05
Feb

Joining the idio team

idioIt was announced last week that the company I work for, thrudigital, had been acquired by idio, a company we’d had a close relationship with for some time. As a result, I, along with the rest of thrudigital, will be joining the idio development team as their “London office”. Primarily, my work for idio will focus on their platform, which deals with content aggregation, analytics, personalisation and distribution, and making improvements to help it scale. Very exciting stuff, so look out for some scalability related topics on here in the near future!

20
Jan

CakePHP routing with slugs and actions

Here’s a quick link to a post I put up on the thrudigital blog the other day, regarding a little technique I came up with to allow CakePHP to disambiguate between slugs and controller actions in routes. You can check the post out here. Enjoy!

07
Jan

Getting the most of PHP’s date() and strftime() functions

I posted on our company blog today about a CakePHP helper I wrote which allows you to make the most of the PHP date() and strftime() methods in one function. You can check out the blog post here, or grab the source code on Github.

06
Dec

Total Adsense Mastery

James CaanFor a period of over a month during October and November 2010, I was stalked across the internet by none other than James Caan, the multi-millionaire businessman and Dragon. He was advertising a one-day seminar entitled “James Caan’s Total Business Mastery” through a company called EBA which took place on November 27th. During the stalking period, almost any site I browsed which served up Google Ads (of which there are one or two) would display James’ smiling face, inviting me to purchase a ticket so I could learn to be more entrepreneurial. Sometimes there were as many as 3 of these adverts on a single page.

I noticed the trend sometime in early October, and made many comments about it in the office. It became something of an internal meme, and I assumed it would pass soon. In late October, it was apparent that the ad was sticking with me for a while, so I began to keep a tally of how many I saw in a day. Obviously, this tally is dependent on a number of factors, mostly how much I browsed the net that day (which isn’t necessarily a direct correlation to the amount of work I got done that day, of course…), and days I wasn’t in the office since I only saw them on my work computer, but it still makes for (vaguely) interesting statistics. A more interesting statistic would have been the ratio of pages which served Google Ads to the number of those which served James Caan adverts, but hey, I’m not made of tally charts, am I?

Stop, number time!

Between October 21st and November 26th 2010, I recorded 127 individual sightings of adverts for James Caan’s Total Business Mastery, averaging 4 a day, or 5 per work day. The most I saw in a single day was 16, and the minimum (not counting the rare days that I saw none) was 1. The mode of the dataset was 3, and the median was 3.5.

Enough stats already, give me a freakin’ graph! OK.

All this has made me wonder – is this normal? I can’t help but think that it is. Perhaps normally I simply ignore it, and that it was only James Caan’s happy smiling face that made me notice this particular ad campaign. I haven’t noticed any more Adsense stalkings since this one, but, as I say, I may be immune to them unless they have pictures of James Caan on them. Which leads me to wonder – is it only James Caan’s face I’ll respond to? Google, in the interests of science, can you serve me up some ads with Tina Fey on them? Thanks muchly.

As an aside, did anyone actually go to the seminar? Was it any good? I can’t help but think that it undersold, since the ads proclaimed ‘limited space available!’ yet they served me ads up until the day before the event.

Update: Turns out he’s doing another one in January, and I’m seeing adverts again. I’m taking tallies, so expect another update soon.

21
Nov

Words are funny, right?

Today I’ve hacked together and launched my own Twitter-bot, @chinesetwispers. The basic premise is that it finds tweets with certain words in them, replaces those words for other words which either sound like the word or are generally hilarious replacements, and then retweets it at the original author. I’m looking forward to seeing what it comes out with – hopefully no-one will get too offended!

As well as that, I’ve done some work on Twipestry, so it’s now a load more stable and produces more relevant results. As well as that it now uses the search API, meaning instead of just searching for users you can also do keyword-based searches as well. Lovely, no?

So that’s what I’ve been up to today. Other than that, I’ve been doing some work on DoodleDesk, trying to get it ready for release sometime soon, and also been working on Fretless on and off. Hopefully I’ll find a weekend or two in the near future to get both of these ready for a proper release. We’ll see…

31
Oct

Using CakePHP’s finderQuery Attribute For Complex Associations

I had some fun (“fun”) on a project the other day when I had to write a rather complex has-and-belongs-to-many association for a model in CakePHP. The basic scenario was this:

  • There are ‘Meetings’, which are associated with many People (and many People can be associated with many Meetings, but that’s not important here)
  • Each meeting has many ‘Conversations’, which currently are assumed to involve everyone in the Meeting

My task was to alter this model so as to cater for Conversations that only involve a subset of the People involved in the Meeting. For backwards compatibility, and to save on space (although admittedly the join table wouldn’t have exactly been huge), we decided to go with the following model:

  • We would create a ‘ConversationPerson’ model, which would obviously store the people involved in a conversation, but…
  • If a Conversation has no records in the join table, then the People involved in the Conversation is assumed to be all the people in the Meeting.

(Does that make sense?)

This is a (relatively) easy query to construct, so we decided to use a single query to define the association and then use the finderQuery parameter to tell Cake how to get the data. Our Conversation model then looked a bit like this:

<?php
class Conversation extends AppModel {
	...
	$hasAndBelongsToMany = array(
		'Person' => array(
			'finderQuery' =>
				'SELECT `ConversationPerson`.`id` AS `conversation_id`, 
						`Person`.* 
				 FROM 	`people` AS `Person`, 
						`meeting_people` AS `MeetingPerson`, 
						// Left join conversations with conversation_person
						// so we don't lose any conversations if there are 
						// no rows in conversation_people, and so we don't
						// get extra rows if there are
						`conversations` AS `Conversation` 
						LEFT JOIN 
						`conversation_people` AS `ConversationPerson` 
						ON 
						`Conversation`.`id` = `ConversationPerson`.`conversation_id`,
						// Count the number of associations in the
						// conversation_people table
						(SELECT COUNT(*) AS count 
						FROM `conversation_people` AS `CountConversationPeople` 
						WHERE `CountConversationPeople`.`conversation_id` = {$__cakeID__$}) 
						AS `Count`
						// This mimics the operation of an
						// if(count == 0){...}else{...}
						// If there are no associations in the conversation_person
						// table, grab the people from the meeting_person table
				 WHERE (`Count`.`count` = 0 AND 
						`Conversation`.`id` = {$__cakeID__$} AND 
						`Person`.`id` = `MeetingPerson`.`person_id` AND 
						`Conversation`.`meeting_id` = `MeetingPerson`.`meeting_id`)
						// If there are associations in the conversation_person
						// table, use those to define the people in the conversation
					OR (`Count`.`count` > 0 AND 
						`Conversation`.`id` = {$__cakeID__$} AND 
						`MeetingPerson`.`meeting_id` = `Conversation`.`meeting_id` AND 
						`MeetingPerson`.`person_id` = `ConversationPerson`.`person_id` AND 
						`ConversationPerson`.`person_id` = `Person`.`id`)'
		)
	);	
	...
}
?>	

This seemed alright, but Cake was throwing me error after error, and it took me a while to figure out what was going on. It turns out that when you perform a HABTM find in Cake, it expects to always see a join table. This is especially apparent when you select a group of rows, since it then uses the values in the join table to determine which row is associated with which model.

In the example above, Cake was expecting to always see two fields, one called ConversationPerson.conversation_id and another called ConversationPerson.person_id. Obviously, in the cases where the association exists in the join table, this is fine. However, no associations exist and we grab the data from the MeetingPerson table, Cake is unhappy. Unfortunately, Cake isn’t clever enough to look for those ids (conversation_id and person_id) in the other fields it returns, it explicitly wants them to come from a table called ConversationPeople.

Obviously, the solution is to use the query we created above to construct the join table, and then select from that. It is a bit frustrating that there isn’t a neater solution to this, but there we go. So, our Conversation model looked like this:

<?php
class Conversation extends AppModel {
	...
	$hasAndBelongsToMany = array(
		'Person' => array(
			'finderQuery' =>
				'SELECT `Person`.*, `ConversationPerson`.*
				FROM `people` AS `Person`,
					(SELECT `ConversationPerson`.`id` AS `conversation_id`, 
							`Person`.* 
					 FROM 	`people` AS `Person`, 
							`meeting_people` AS `MeetingPerson`, 
							`conversations` AS `Conversation` 
							LEFT JOIN 
							`conversation_people` AS `ConversationPerson` 
							ON 
							`Conversation`.`id` = `ConversationPerson`.`conversation_id`,
							(SELECT COUNT(*) AS count 
							FROM `conversation_people` AS `CountConversationPeople` 
							WHERE 
							`CountConversationPeople`.`conversation_id` = {$__cakeID__$}) 
							AS `Count`
					 WHERE (`Count`.`count` = 0 AND 
							`Conversation`.`id` = {$__cakeID__$} AND 
							`Person`.`id` = `MeetingPerson`.`person_id` AND 
							`Conversation`.`meeting_id` = `MeetingPerson`.`meeting_id`)
						OR (`Count`.`count` > 0 AND 
							`Conversation`.`id` = {$__cakeID__$} AND 
							`MeetingPerson`.`meeting_id` = `Conversation`.`meeting_id` AND 
							`MeetingPerson`.`person_id` = `ConversationPerson`.`person_id` 
							AND `ConversationPerson`.`person_id` = `Person`.`id`))
					AS `ConversationUser`
				WHERE `Person`.`id` = `ConversationUser`.`person_id`'
		)
	);	
	...
}
?>

In retrospect, I should have just bitten the bullet and populated the ConversationPerson table with the values from MeetingPerson (or perhaps gone one step further and inferred the people in the meeting from the people involved in Conversations in that meeting, but whatever), but in this case I think this was probably the nicest solution.

The example in this post is so incredibly edge-case that it will probably help absolutely no-one, but I thought I’d put it up anyway, since the Cake docs don’t mention requiring those particular fields when specifying a finderQuery.

24
Oct

An Interesting IE6 Bug (bleurgh)

I came across an interesting bug in IE6 the other day, and couldn’t find any documentation of it on the internet, so thought I’d share it with you all here (aren’t you lucky?).

I was working on a distributable widget, which has the functionality to grab some embed code so you can place it with pride on your own website. This embed code was generated dynamically by JavaScript based on some parameters the user entered, and was then placed in a textfield using the jQuery $().val(value) function. This worked fine in most browsers, but IE6 would throw a wobbly at this point in the form of an “Unspecified Error”. Incredibly helpful.

I found that the bug didn’t occur if I escape()‘d the value before I passed it into the textfield. It seems, therefore, that IE6 has an issue with strings that contain HTML content – presumably it was trying to parse the HTML when it was put in the textfield. The same behaviour was observed when using document.getElementById(…).innerHTML = value; as well.

The solution was to use the jQuery function $().text() – and this works cross browser. Presumably this only works for textfields – if you are setting the contents of normal text inputs then I’m not quite sure what you will observe. But then, you never are with IE6…

25
Aug

Setting up Snow Leopard for Web Development

My trusty old 1st-gen MacBook Pro died on me last week, so I’ve got myself a shiny new one, which is exciting! One thing I was looking forward to was a clean install of the OS, so I could re-appraise the way I used my computer. One of the things I decided to do was to move away from using XAMPP and have a more traditional set up using the built-in Web Sharing. Here, I’m going to talk about what I’ve done and how I propose to use my new laptop for Web Development to smooth my workflow.

Apache and PHP

Mac OS X Snow Leopard comes with Apache and PHP, although PHP is disabled by default. I didn’t know this to begin with, so I started off by installing Marc Liyanage’s PHP bundle. However, after installing this I found that I was getting 301 “Permanently Moved” errors on all my pages, so that was soon uninstalled.

To enable PHP on Snow Leopard, you need to open the config file in your favourite text editor (mine’s TextMate, but you can use vim, nano, or any other). In a terminal, open the file for editing (replacing ‘mate’ with the command for your text editor) entering your password when prompted.
sudo mate /etc/apache2/httpd.conf
You’re looking for a line like this:
#LoadModule php5_module        libexec/apache2/libphp5.so
If it exists, remove the # symbol, otherwise add the line in, minus the #. Save the file and exit the editor. Now you need to restart the Apache server, so type the following in the terminal:
sudo apachectl restart
You can test whether it’s worked by creating a file in your Sites directory that looks like this:
<?php phpinfo(); ?>
Call this file info.php, and then in your browser navigate to http://localhost/~yourusername/info.php – if it’s worked then you should see a nice table which has all the details of the PHP installation.

MySQL

If, like me, you’re going to be using MySQL for the database side of your web applications, then you’re in luck! It’s really easy to install, since MySQL provide some lovely .dmgs. Go to http://dev.mysql.com/downloads/mysql/ and choose the DMG archive most appropriate for you (remember to check your operating system version, and whether your computer is 32 or 64 bit). Note that all of these downloads are for Intel-based Macs only. When you’ve got the file, open it up and you’ll see 4 files. Start by installing the main MySQL application which will be called something like mysql-5.1.50-osx10.6-x86_64.pkg. When that’s done, I installed the MySQLStartupItem.pkg and the MySQL.prefPane files (both done by double clicking on them), which starts the MySQL server when I login and lets me control it through the System Preferences application.

You can manage the database in a number of ways, but I choose to use SequelPro. It’s a free application that’s got all the features you need in a lovely Cocoa-based UI. If you prefer, you could install phpMyAdmin or one of a number of alternatives, but I haven’t tried those so I shan’t give you any advice on them.

Starting Development

Now you’ve got all the components set up, it’s time to create some web apps! All the code will go in the Sites folder that’s located in your home directory. I’m going to be following Remy Sharp’s guide, which sets up domain names that point to your local machine (so, for example, I could develop and view on my local machine at the url http://imaginaryroots.dev). There’s quite a lot of work involved in that, so I intend to write a shell script to do the majority for me, but I haven’t gotten around to it yet…

Now all’s that’s left is to knuckle down and write some code – and let’s face it, isn’t that why we’re all here?

09
Aug

On Brevity

Lots happened in my blogging hiatus, and I intend to talk more thoroughly on those topics in the near future, but I thought I’d give you a quick bullet-pointed list of things that I’ve done and am interested in at the moment. Enjoy!

  • I graduated with a 1st class Honours in Computer Science from Warwick.
  • I learnt Objective-C and Cocoa to make my first desktop app, DoodleDesk.
  • I’ve re-written the Mondrian generator in using JavaScript and the Raphael library as a result of my growing dislike of all things Flash.
  • I’ve been working on Fretless quite a lot. It’s a big project that’s taking a while to come together, but it is, slowly…
  • As of September I will be an employee of thrudigital – exciting!
  • I’ve been recording guitar parts for the debut release from my band, Drongo Sealion Magic.
  • I’ve been playing a lot of Gran Tourismo 5 Prologue, and have been holding my breath far too long for the release of GT5…
  • I’ve designed and launched a website for my brother’s company, Energy Regeneration.
  • I’m following with interest the recent developments in the P vs. NP problem.
  • I’ve held off on getting an iPad so far… Will be getting an iPhone 4 soon though.