<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.rowtheboat.com/~d/styles/itemcontent.css"?><rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:posterous="http://posterous.com/help/rss/1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/" xmlns:feedburner="http://rssnamespace.org/feedburner/ext/1.0" version="2.0">
  <channel>
    <title>rowtheboat.com</title>
    <link>http://rowtheboat.com</link>
    <description>The online home of George Palmer</description>
    <generator>posterous.com</generator>
    <link xmlns="http://www.w3.org/2005/Atom" href="http://posterous.com/api/sup_update#41f6bfaab" type="application/json" rel="http://api.friendfeed.com/2008/03#sup" />
    
    
    <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" type="application/rss+xml" href="http://feeds.rowtheboat.com/Rowtheboat" /><feedburner:info uri="rowtheboat" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com/" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://posterous.superfeedr.com/" /><item>
      <pubDate>Fri, 11 Feb 2011 02:18:35 -0800</pubDate>
      <title>The down side of test first, code second</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/jvu0RgKJ8dY/the-down-side-of-testing-computer-code</link>
      <guid isPermaLink="false">http://rowtheboat.com/the-down-side-of-testing-computer-code</guid>
      <description>
        <![CDATA[<p>
	<blockquote>
<p><span style="border-collapse: collapse; font-family: arial, sans-serif;">One interesting quirk of Pinboard is a complete absence of unit tests. I used to be a die-hard believer in testing, but in Pinboard tried a different approach, as an experiment. Instead of writng tests I try to be extremely careful in coding, and keep the code size small so I continue to understand it. I&rsquo;ve found my defect rate to be pretty comparable to earlier projects that included extensive test suites and fixtures, but I am much more productive on Pinboard.</span></p>
</blockquote>
<p><span style="border-collapse: collapse; font-family: arial, sans-serif;">This quote is by </span><span style="border-collapse: collapse; font-family: arial, sans-serif;"><a href="http://www.readwriteweb.com/hack/2011/02/pinboard-creator-maciej-ceglow.php" target="_blank" style="color: #2244bb;">Maciej Ceglowski</a>&nbsp;and I can't agree enough. &nbsp;When writing code I focus on understanding the problem and breaking it down in my mind (and the code) to manageable parts. &nbsp;I then prototype the code, mess around with naming, strucutre it logically and only when I'm happy do I write tests at the end. &nbsp;This is the exact opposite of what most test first die-hards would recommend.</span></p>
<p><span style="border-collapse: collapse; font-family: arial, sans-serif;">I've found several issues with the traditional test first approach that have led me to write code this way:</span></p>
<ul>
<li><span style="font-family: arial, sans-serif;"><span style="border-collapse: collapse;">At the start when writing tests you don't understand the problem and must think it through. You invariably forget stuff in the same way you forget to spec out stuff when writing a 20 page spec if developing by the waterfall method. &nbsp;By writing code first you actually get into the problem and work through exactly what is needed.</span></span></li>
<li><span style="font-family: arial, sans-serif;"><span style="border-collapse: collapse;">You don't write code to make the tests pass. &nbsp;This is dangerous as you lose the big picture of the problem you're trying to solve. &nbsp;Instead you write code to make a test pass and there's no guarantee that the test suite you write is complete. &nbsp;In fact nearly every time I've been pairing using test ping-pong the developer finds missing tests. &nbsp;This highlights the downfall of such an approach.</span></span></li>
<li><span style="font-family: arial, sans-serif;"><span style="border-collapse: collapse;">Tests can't be used as an excuse for defects. &nbsp;I've worked on several client projects with other companies where the party will respond to an issue on a live site by writing a test and fixing it, passing responsability to the missing test (which they didn't write to start with). This is good practice at a technical level but infuriating from the clients point of view. &nbsp;Why did this happen? &nbsp;Because you forgot to write a test and missed out some functionality. &nbsp;By writing the code first you get into the problem and understand it better so are less likely to miss areas of functionality. &nbsp;As a second benefit it stops you shifting the blame to tests and makes you face up to the client. &nbsp;This makes you really think things through when writing critical code.</span></span></li>
</ul>
<p>To be clear I do value tests and for two reasons - as a protection against introducing regressions and as a sanity check at the end of writing a feature. &nbsp;These tests can be easily written by somebody else against your final API with them working through the logic of the problem as well.</p>
<p>How well does it work? &nbsp;Last years ago I wrote <a href="http://www.5ftshelf.com">5ft Shelf</a>&nbsp;which has had 150,000 page views to date, runs on 10,000 lines of code and has had 3 bug reports. &nbsp;<a href="http://www.digitaldeliveryapp.com">Digital Delivery App</a>&nbsp;has only been live a few months, runs on 4000 lines of code, and has had 1 bug report so far. &nbsp;These rates are hugely lower than the other 10 or so rails projects I've consulted on (note most of these, but not all, are multi-developer projects which leads to greater bug rates due to developers incorrectly internalising how the application works)</p>
<p>Strongly linked to keeping my defect row low has been a constant drive for simple code above everything else. &nbsp;This means not using the latest gem because it's fresh off the shelf &amp; cool and not using some eleborate mega encapsulation system because you understand everything better than anyone else could possibly ever. &nbsp;In the city (financial district of London) they call this "mental masturbation". &nbsp;Less crudely&nbsp;<a href="http://po-ru.com/diary/a-website-is-not-a-ship/">Paul Battley</a>&nbsp;sums this up perfectly with&nbsp;<span style="font-family: Helvetica; font-size: 12px;">"narcissism is one of the biggest causes of technical problems". &nbsp;Simplicty wins above everything else.</span></p>
	
</p>

<p><a href="http://rowtheboat.com/the-down-side-of-testing-computer-code">Permalink</a> 

	| <a href="http://rowtheboat.com/the-down-side-of-testing-computer-code#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/the-down-side-of-testing-computer-code</feedburner:origLink></item>
    <item>
      <pubDate>Mon, 17 Jan 2011 00:55:00 -0800</pubDate>
      <title>Website Migration</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/fVmRT5WRl3c/website-migration</link>
      <guid isPermaLink="false">http://rowtheboat.com/website-migration</guid>
      <description>
        <![CDATA[<p>
	<p>Just a quick note to say I've migrated my blog to Posterous. This post will hopefully trigger feedburner to pickup the new feed as well.</p>
	
</p>

<p><a href="http://rowtheboat.com/website-migration">Permalink</a> 

	| <a href="http://rowtheboat.com/website-migration#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/website-migration</feedburner:origLink></item>
    <item>
      <pubDate>Mon, 16 Aug 2010 17:09:00 -0700</pubDate>
      <title>Making Gmail Leaner</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/ISBRzcJNUhE/128</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/128</guid>
      <description>
        <![CDATA[<p>
	<p>Recently Gmail rolled out a series of user interface improvements that I didn't really like.  I <a href="http://twitter.com/Georgio_1999/status/20974885802">tweeted about the changes</a> and seemed to get many replies in agreement.  Top of my annoyance list was adding a Mail/Contacts/Tasks context switch as the top most item on the left hand side.  My annoyance was that the context switch seemed unnecessary for my usage of Gmail - I rarely go into contacts and I don't use the tasks area.  Whilst I've no doubt both features are useful to other people, I like to keep my inbox lean and minimalist.</p>
<p>After a few days of usage I decided I hadn't adapted to the change and wrote a script to hide the links.  I also took the opportunity to make a few other improvements to the standard GMail interface - most notably:</p>
<ul>
<li>Removes invite a friend and messenger panels</li>
<li>Removes the select All/None tickbox </li>
<li>Remove tips in footer</li>
<li>Remove search the web button</li>
<li>Remove adverts when viewing emails</li>
<li>Makes the inbox area border complete rather than head off edge of the right edge of the screen</li>
</ul>
<p>For those wondering how you access the contacts once the link has been removed you can do a keyboard shortcut of g then c.  To get back to the inbox do g then i.</p>
<p>The difference can be seen in these two screenshots: <div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_wfxqf" height="189" src="http://getfile2.posterous.com/getfile/files.posterous.com/import-ehvm/hkBepwDgzeExhgmGIdEgrFfcIFateEcvDvmCDhdouxtBzHHzChxuciBjuGhC/media_httpwwwrowthebo_wfxqF.png.scaled500.png" width="300" />
</div>
 <div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_xjcgf" height="153" src="http://getfile7.posterous.com/getfile/files.posterous.com/import-ehvm/cfDxqtHmGtvdvwFskoswjmxeAvdcukovhvtbuDmkdxJcueADvruDmoejJlna/media_httpwwwrowthebo_xJCgF.png.scaled500.png" width="300" />
</div>
 With the changes above I definitely prefer the interface when compared to the old one.  The script was produced purely for personal preference though and my changes won't suit everyone.  It is released as is but if you want just some of the features then the code is commented and really easy to change.  Install or grab the code over at <a href="http://userscripts.org/scripts/show/83798">userscripts</a></p>
	
</p>

<p><a href="http://rowtheboat.com/archives/128">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/128#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="189" width="300" url="http://getfile9.posterous.com/getfile/files.posterous.com/import-ehvm/hkBepwDgzeExhgmGIdEgrFfcIFateEcvDvmCDhdouxtBzHHzChxuciBjuGhC/media_httpwwwrowthebo_wfxqF.png">
        <media:thumbnail height="189" width="300" url="http://getfile2.posterous.com/getfile/files.posterous.com/import-ehvm/hkBepwDgzeExhgmGIdEgrFfcIFateEcvDvmCDhdouxtBzHHzChxuciBjuGhC/media_httpwwwrowthebo_wfxqF.png.scaled500.png" />
      </media:content>
      <media:content type="image/png" height="153" width="300" url="http://getfile4.posterous.com/getfile/files.posterous.com/import-ehvm/cfDxqtHmGtvdvwFskoswjmxeAvdcukovhvtbuDmkdxJcueADvruDmoejJlna/media_httpwwwrowthebo_xJCgF.png">
        <media:thumbnail height="153" width="300" url="http://getfile7.posterous.com/getfile/files.posterous.com/import-ehvm/cfDxqtHmGtvdvwFskoswjmxeAvdcukovhvtbuDmkdxJcueADvruDmoejJlna/media_httpwwwrowthebo_xJCgF.png.scaled500.png" />
      </media:content>
    <feedburner:origLink>http://rowtheboat.com/archives/128</feedburner:origLink></item>
    <item>
      <pubDate>Mon, 05 Jul 2010 20:48:00 -0700</pubDate>
      <title>My first marathon</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/4DczgZmGGFQ/72</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/72</guid>
      <description>
        <![CDATA[<p>
	<p><div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_excjh" height="269" src="http://getfile3.posterous.com/getfile/files.posterous.com/import-ehvm/dcBwoxEeEiEyqpvrmcDqoqhblIzGEjlqwBqiBfuxjpggChcpniygvcFvkFzm/media_httpwwwrowthebo_ExCjh.jpeg.scaled500.jpg" width="179" />
</div>
As those of you who <a href="http://twitter.com/georgio_1999">follow me on twitter</a> will have probably saw, I recently completed my first marathon.  It was actually 6 weeks ago now but I've only just got round to writing the blog post but for once I can blame the British weather for this - it has been beautiful here lately and I've been enjoying it to the max. The post is quite long so for those wanting the short version, I couldn't get to Vienna due to the ash cloud so had to readjust and run Edinburgh a month later.  In a rather hot 25c I ran a very pleasing 3:03:01, came 142nd and got myself an automatic place for London next year.  For those wanting the longer version of what happened, a series of training tips I picked up and the highs and lows of marathon training read on.</p>
<p>I'm not sure where the dream first came from but as long as I can remember I've always wanted to run a marathon.  I'm not sure what exactly appealed about it since I spent my time as a kid playing football and tennis, not running.  In fact I only really got into running when I left school although arguably I was pretty good at it whilst forced to do it in PE.  Fast forwarding a few (or rather quite a few) years I joined <a href="http://www.claphamchasers.co.uk">an excellent running club</a> when I moved to London, and taking inspiration from the fellow runners who had just done Paris marathon, I decided this year was going to be the one to do it.  Running sub 3 was something I'd always dreamed of too although I was unsure if that was a realistic target for my first marathon or not.  Talking to friends in the club it seems quite a few had tried and failed at what is for most club runners the most symbolic of marathon times (it's quite a nice round number ok?).  Undaunted in late October I rocked up to a half marathon to get a gauge of how fit I was and whether this crazy sub 3 idea was going to be achievable.  I finished the race in a surprising 1:27:02 and thoroughly enjoyed my first long race, getting lots of support from the locals, a few high 5s round the course and a top 50 finish.  Not a bad gauge race and fast enough that I considered a 3 hour marathon a possibility.</p>
<p>Through November and December I concentrated on two main things: building my weekly milage trying to run upto 17 miles comfortably and improving my running technique.  The first was quite simple in theory - run 13 miles one weekend, 15 the next, 17 the next and then repeat again.  In reality the appalling winter weather, running considerably further than I've ever run before and the logistics of not getting lost around London made this somewhat more difficult than I imagined it first would be.  These challenges were fairly insignificant compared to improving my running form though.</p>
<p>There was lots of talk on the running circuit about a book called <a href="http://www.amazon.co.uk/Born-Run-Ultra-running-Super-athlete-Tribe/dp/1861978235">Born to Run</a> in which the story of an ultra-running tribe called the <a href="http://en.wikipedia.org/wiki/Tarahumara">Tarahumara</a>, the 30-100+ miles they compete in, and the benefits of running barefoot rather than in trainers are told.  Although it was documented a lot of the fastest africans ran barefoot as children, nobody was really sure why it had such an effect on running speed.  The fact the Tarahumara could run such long distances in flat sandals challenged the concept that buying the most expensive cushioned trainers possible was the best thing to do.  In fact the reported injury rate in athletes was much lower before the cushioned trainer came along and the reason is somewhat obvious when all the facts become clear.  Given the extra cushioning in modern trainers people start running in a style where they land on their heel rather than mid or forefoot.  This is because the trainers heel cushioning encourages such a strike.  Landing on your heel exerts much more force through your body though and thus increases the chances of injury.  Hopping on your heel rather than forefoot, or trying to run down the street barefoot and landing on your heel, provide a great example of this.</p>
<p>I had spent the previous few years struggling with injuries and ended up buying more and more expensive trainers with additional cushioning to try and combat my injury woes.  So reading the Born to Run book seemed like a revolution and convinced me of the need to alter my style.  In the end I opted for the <a href="http://www.chirunning.com/shop/home.php">ChiRunning</a> technique but the POSE method is fairly similar.  Both teach you to run in a way much closer to how we run barefoot and land on either the mid or forefoot.  Reducing impact force not only reduces injury risk but makes you more energy efficient over the course of a run and whilst I did spend some time injured adapting to the technique, I have been running much smoother since and spending much less time injured.  In fact I've barely been injured at all since switching styles, I use cheaper less cushioned trainers and the trainers last a lot longer too.</p>
<p>By christmas my style was making big improvements and my fitness was starting to increase to the extent that going out for a 15 mile run seemed like no different than a 6 mile jog.  Then somewhat inevitably, disaster struck.  Over the christmas break I went for a 13 mile run whilst back visiting my parents and jogging along an old route that I used to cycle along, I faced an icy patch.  I was only a few miles from home and the backtrack option was a good 6 miles, so I decided to run on with the hope the ice cleared.  After a mile or so it eased and I managed to get home fairly unscathed, or so I thought.  The next day the tendons in my left ankle were really sore and I was unable to run further than 2 miles.  How could I have been so stupid?  Right as the critical marathon training was about to kick off I was out injured.  And not only that tendons are notoriously slow to heal.</p>
<p>Anger quickly turns to practicality and once back in London I went to see both a Physiotherapist and a Sports Massage person.  In the end I found the sports massage to be much better as they actually worked on the problem, forcing blood to the area via massage, ultrasound and acupuncture, which inturn speeds up the healing.  Phyios on the other hand spend a lot of time diagnosing the problem and then offering advice on strengthening weak muscles - useful, but not much help for overcoming your current injury.  Of course this was my experience and your milage may vary (no pun intended).</p>
<p>In times of injury it's important to readjust your training schedule and try to keep fitness levels high by cross-training.  I found swimming and cycling to be best for this although as your heart works at a lower level you need to spend more time doing these activities than you would running - eg 2 hours on the bike is equivalent to 1-1:30 of running.  2 hours on a bike in a gym, if the mental image hasn't already flashed across your mind, is boring as hell and motivating yourself to get to the session can be quite hard.  Some intelligent scheduling can normally find a big sporting event on at the same time which helps speed away the time.  This year I watched a lot of the Australian Open and really got into F1 - not bad things to do whilst cycling for 2 hours by any stretch of the imagination.</p>
<p>Luckily I had planned some time in to my training schedule to be lost to injury and/or illness (I highly recommend doing this), and as I got back running in early February my dreams of a 3 hour marathon were just about still alive.   As the month progressed we moved on to doing a 20 miler every other weekend and a 13-15 miler on the other weekend.  This was in addition to 1 interval, 1 medium (8-10 miles) and a couple of recovery runs.  In a few weeks I topped over 50 miles per week although my milage was low compared to many (more on this later).  I was also learning to pace myself well - or more accurately I bought a <a href="https://buy.garmin.com/shop/shop.do?cID=142&amp;pID=349">Garmin Forerunner 305</a> which has a GPS built in so you can see how far/fast you're running.  It's extremely useful information to know if you're not racing as getting the pace wrong in a 20 mile run is not nice.  I remember one training session with a few club mates where 2 of us ended up out front.  At 18 miles my friend Dave was struggling and our pace was slipping back to 7:30s, so I agreed to meet him at the end and picked up the pace.  I banged out a 6:30 mile and then starting struggling fast.  Moving my legs was becoming difficult and as I crossed the finish line going slower than 8 minute miles, Dave shouted my name finishing less than 10s behind me.  He was looking fresh and ready for a cool down, I was lying on the floor barely able to move!  The lesson had been learnt - 26 miles is a long way and pacing it correctly is critical.</p>
<p>I also learnt the perils of stopping.  Trying to even walk after 20 miles feels like somebody is stabbing you with a knife in the back of the hamstrings every step you take.  To combat this I settled into a fairly nice routine after long runs.  Firstly 5 minutes stretch off and put on compression tights.  <a href="http://www.2xu.com/compression/">2XU</a> are the favourites among my club mates and my experience with them has been nothing sort of fantastic.  They work by pumping blood into your muscles helping to clear out the lactic acid and consequently reduce soreness the next day.  They also keep you nice and warm which becomes a big issue 10 minutes after finishing.  As your body has been hot for so long stopping quickly results in a loss of body temperature, so this was something else that had to be dealt with.  I'd put on compression gear, joggers, clean t-shirt, jumper and get somewhere warm if I wasn't finishing at home.</p>
<p>Next most important is to get some kind of fuel back into your body.  Running 20 miles burns around 2000 calories depleting your glucose stores and ripping thousands of your muscles fibres to bits.  There's a 20 minute window after exercise in which your body repsonds extremely well to any kind of nutrition you give it.  The optimum blend (for running) is a 4-1 carb to protein formula.  Although best to have this as proper solid food in reality it's not normally possible and a recovery drink is the best option.  Also as your stomach shrinks the longer you exercise, it's unlikely you're going to want to drink it.  It took me a few weeks to adapt to this but eventually my body caved and started taking it down.  Finally you can then get home, shower and crash.  And you will need to do crash - some prefer to go to bed for a few hours, personally I struggle to sleep after the post run high so just lied on the sofa for 3 hours under a blanket to keep warm.  After that my body started to come round and I could eat and go do stuff.    To be honest, the days I did a long run I didn't really do much else although as I adapted over the months it become easier.  Oh and one more thing your immune system is weakened for a few days after such a long run so if there's a bug going round expect to get it :(  That time crashed out on the sofa helps combat the start of this.</p>
<p>As I trained through February, perfected the recovery strategy and adapted to the increased milage recovering became easier, but the legs were always sore and I was always laid out for a few hours post training.  As March rolled round, training was going well and I nailed a 21.5 mile run with an average pace of 6:42 a mile, exactly 10s faster than I'd need to do for the marathon.  Surely that made me on target?  Indeed it did, the excellent <a href="http://www.mcmillanrunning.com/mcmillanrunningcalculator.htm">McMillan Running Calculator</a> providing the calculations.  Training was also taking its toll on me though and I spent a lot of February and March really quite tired.  Luckily the sports massage guy I went to see noticed I was looking slightly anemic.</p>
<p>Training hard puts extra demands on your iron stores as your body increases the amount of red blood cells it needs.  It's a fairly well documented phenomena in people training for long distances and its important to get lots of iron in your diet.  Once I became aware of this I altered my diet to include more read meat and spinach and took a few teaspoons of <a href="http://en.wikipedia.org/wiki/Molasses">Blackstrap Molasses</a> each day.  Blackstrap Molasses is produced from the left overs of sugar cane boiled up three times.  It sounds, looks and tastes fairly disgusting but it's great for you, containing large amounts of iron, zinc, magnesium and even something that helps your body break down sugar and deal with it more effectively (needless to say they refine this out in for sugar you buy in supermarkets).  After just 1 week I was amazed how much difference this made and to this day I still take Blackstrap Molasses a few times a week.</p>
<p>As race day dawned my thoughts turned to race strategy.  The usual rules apply here - get there early, drop off your bags, go to the toilet, find your starting area, warm up and go to the toilet again.  All the big marathons have "starting pens" which essentially put people of a similar pace together.  It's not worth lying on your entry form here as you'll find yourself frustrated by slower runners in front of you or getting knocked over by faster ones coming from behind.  If you think you're going to be on a nice boundary though (eg 4 hours) it's worth writing 3:59 as your predicted time.  This will ensure you're in the 3:45-3:59 pace group rather than the 4:00 - 4:15 pace group.  When there's 30,000 people crossing the starting line, that makes a lot of difference.</p>
<p>Once the race has started I like to make friends with people around me, it's much easier to run with people at the same pace as you.  Throughout any race you'll hit highs and lows and running with other people means you drag them along when they're struggling and you get dragged along when you're struggling.  My better than expected performance in the forementioned 21.5 miler was because I was running with 2 club mates at the same speed (sadly they both got injured before the marathon so I was running the main event alone).  If you're pacing yourself correctly then you shouldn't be out of breath for the first half of the race so don't worry about any performance implications of talking.  The other reason it's good to talk to the other runners is it's great for finding out information about the course - runners who have done it before and will point out hard miles, runners who drove the course the day before, or runners who know where there are lots of supporters.  It's all great information to know and makes the earlier miles much more fun.</p>
<p>Water and energy strategy is really important.  When training I carried a half litre bottle of water for 20 miles.  This taught me to operate on less water than I'd get in a race whilst giving me enough to stop dehydration effecting my performance.  Energy gels are important for giving your body instantly usable energy too as there isn't enough glucose stores in most people to run 26 miles.  They taste pretty rubbish so experiment and find your favourite but I quite liked the <a href="http://www.torqfitness.co.uk/acatalog/torq_energy_gels.html">Torq</a> ones with Guarana.  Some require you to have them with water whereas others don't.  The Torq ones definitely require water but I favoured having the water about 4-5 mins after the gel or it made me feel sick. Experiment during training not the actual race!</p>
<p>I mentioned earlier that I did less miles than most other runners my speed.  The reason was I took a less is more approach and ran all my long runs (except 1) at or within 15s per mile of marathon pace.  On the downside this meant it took longer to recover and I couldn't train very well the following week.  On the up side as it was my first marathon it meant I was fairly confident of finishing in the time I expected, knew my pacings well and knew how my body would react.  It isn't the most efficient way to train but it gave me a lot of confidence and that was extremely valuable.  Consequently going into the race I was confident but not arrogant.</p>
<p>As the last 3 weeks before the main event rolled round I started tapering.  Tapering is a period where you reduce your weekly milage by 30%, 50% and then 70% in the final week.  This lets your body recover from all your training whilst not losing too much fitness, leaving you in optimum shape for the big day.  It's also important to adjust your diet to reflect the reduced amount of training or you will feel stuffed.  This can be quite hard to do as pounding out so many miles increases your daily calorie intake by anything from 20-50%.  At times during my training I would find myself eating a huge meal and then becoming hungry again 2 hours later, so in the tapering period it's important not to eat for the sake of it or rapid weight gain will ensue!  In the final few days you need to build up the carb stores in your body and the best way to do this is to eat lots of slow release carbohydrates.  Like many my favourite source become pasta and I'd make pasta bake for the few nights before the race.  If you're travelling to the race becareful on where you eat out - anything to creamy or spicy is a bad idea.  I had a spicy sausage pasta before my race and very nearly regretted it on race day.</p>
<p>As I mentioned we were due to run in Vienna but unfortunately that was the same time the Icelandic Volcano decided to erupt, grounding all european air traffic.  This was gutting, 4 months training and I couldn't get to the start line.  Luckily the club is well connected and after a few hours of panic, we managed to get offers of places at both Brighton and Sussex marathons although both had officially closed many weeks earlier.  I opted for the Sussex one along with the rest of the club as it provided a half marathon option (which Vienna also did and some had signed up to, whereas Brighton didn't).  As the gun went and I raced off I knew something wasn't right.  At 3 miles I was struggling to hold pace and by 7 I was stupidly out of breath.  I realised something was wrong and there was no point continuing so pulled out.  Obviously this was hugely gutting and a massive disappointment after so much training but the next day I came down ill for a few days so I guess I was just unlucky.  Thinking back there were a few warning signs the day before but I just put these down to pre-race nerves.</p>
<p>The next week I faced a choice - carry on moping and call it quits till the Autumn marathons or try and get a place in Edinburgh 4 weeks away.  I refocused and opted for the later, got a quick weeks worth of training in and started the taper period again.  Clearly this wasn't ideal as I had just spent 3 weeks resting so loss of fitness would become an issue.  But if anything my pre-marathon races had suggested I'll be a few minutes under 3 hours, so I figured it was still on.</p>
<p>I arrived in Edinburgh the day before the big race to a rather hot 21c.  I bought a running cap and prayed for much cooler weather the next day.  On waking the sky had clouded over and it was cool walking to the start.  Perfect conditions for marathon running in fact.  As I entered the starting pen though the sun creaped out from behind a cloud - damn, but not to worry still plenty of cloud cover.  By the time the gun went I noticed the cloud cover clearing but I wasn't paying too much attention, the crowds were cheering, the adrenaline was flowing, I started my watch and my second marathon began.  Hopefully I'd finish this one.</p>
<p>In the first few miles you have to be really disaplined not to go too fast and keep on target pace.  In Edinburgh this is especially true as the first few miles are down hill.  I stuck to my race plan and didn't let myself exceed 6:40s.  After the first 4 or 5 miles the sky was completely clear and it was clear this whole race would be in the sun.  The race start at 10am was less than ideal too and it meant the later parts of the race would be in the midday sun.  Bugger.  Finally the course in Edinburgh wasn't helping either - it was basically a 3 mile run out the city and then a 13 miles out and 10 miles back to a finish (down the same sparsely populated road).  By the time we got on the road at mile 3 it became clear there was going to be absolutely no shade.  None of this had to matter though, I could let it matter but I wasn't going to, it was just things to be dealt with.</p>
<p>At mile 6 I started to feel a slight niggle in my left hamstring.  Pounding out so many miles in training puts a big stress on your body as you adapt to the increased milage and the rather excellent <a href="http://www.amazon.co.uk/Trigger-Point-Therapy-Workbook-Self-Treatment/dp/1572243759/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1278274158&amp;sr=8-1">Trigger Point Therapy Workbook</a> had saved me many times through the months of training.  In a race there's nothing you can do though - well actually you can stop but that's not ideal.  At this point it was just a niggle so I switched my focus to something else, in this case the other hamstring.  This one was fine and by thinking about that I was able to shift the doubts about my left one.  It worked well and a mile later I heard no more from my left hamstring all race.</p>
<p>As I came through half way things were going really well.  I ran through in 1:28:30, not out of breath and exactly where I wanted to be time wise.  Within a few miles though things started to get difficult though.  Through the water stations I was grabbing 2 bottles, one to drink and one to pour over myself.  This has kept me good to 17 miles but now the sun was really intense.  There were more warning signs too - the guy I passed collapsed under a tree, the guy dressed as a superhero stopped and the amount of people I was overtaking who were struggling was rapidly increasing.</p>
<p>Around 20 miles you normally evaluate how you're feeling and if good, pick up speed a bit for the last few miles.  This time I knew how I was feeling and it wasn't good.  My pace was slowing, I was dehydrating and, as I'd later find out, it was 25c.  I knew at this point I had to refocus and look to finish the marathon not get a good time.  I'd lost count of the amount of people I'd seen being sick or dropped out, hell even the eventual winner stopped for a short while to cool down.  Although I was struggling I was overtaking lots of people who were struggling more and I had to use this to my advantage.  This is the best thing about pacing yourself right from the beginning - not only is it a more efficient way to run but in the last quarter of the race it catches up with the runners who started too fast and you can pull them back in.  This is motivating at the best of times.  This is really motivating when you're running in 25c and your throat feels like you've just woken up after a stag night.  At this point in the race it becomes as much a mind game as a physical one - keep going is hard physically, convincing yourself you can keep going is much harder.</p>
<p>The last 4 miles hurt a lot.  From 18 to 22 I'd managed to keep running at 7:10, meaning from the dodgy calculations I was attempting to do whilst running, that I would have been within a minute of a 3 hours finish time.  Very impressive given the conditions.  But then my pace started to slip - 7:30, 7:40, 8:00, 8:20.  Dehydration had caught up with me, my body could no longer cool itself and even the hose pipe of a local resident who sprayed me wasn't helping.  At this point it's all about taking it one mile at a time.  Thinking you've got 4 more left is enough to make you stop.  Thinking you've got 1 mile and doing it 4 times is much more manageable.  I first heard of this strategy watching the fantastic film <a href="http://www.imdb.com/title/tt0379557/">Touching the Void</a> and use it a lot in races.</p>
<p>By 24 I was starting to get a little worried.  My legs and breathing were fine but my body was getting really hot.  I made the decision to keep going although I knew I might not be too far off doing myself some serious damage.  If I'd felt like this 4 miles from the end I would have stopped.  As you exercise a lot you learn when you can push yourself and when something isn't right.  I was definitely moved from the former to the later and I didn't want to take any silly risks.  In the last two miles I become the person being overtaken but my focus was just on finishing and making sure I didn't deteriorate anymore.</p>
<p>As I finally crossed the line I was apparently looking very pale.  As I collected my medal I turned to see some shade and a large collection of runners downing water looking very un-well.  By the time I hydrated and cooled down I was fine but sadly many weren't.  1 in 40 runners needed medical attention and 1 person sadly died.  I even heard (unverified) rumours they ran out of ambulances.  It was pretty mean out there.  <div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_srudk" height="300" src="http://getfile1.posterous.com/getfile/files.posterous.com/import-ehvm/CBjDHflycbDHzHsukhAsjFDwBvlIdafFJmcfbtxiJCijFootnBcDjjtipCCg/media_httpwwwrowthebo_sruDk.jpg.scaled500.jpg" width="225" />
</div>
</p>
<p>Finishing a marathon is a feeling that's very hard to describe.  As I collapsed on the shady grass, medal in hand it was hard not to get emotional about the journey to this point.  5 months of training, countless early mornings, several major social events sacrificed for sunday races and endless training miles run, made finishing the race a relief comparable with finishing your finals at university.  The cocktail of emotions pumping round my body were like nothing I've ever experienced.  Once I rehydrated and the reality sunk in, I picked myself up and headed to try and see my friend finish.  She should have been 30 minutes behind me but I was in such a bad way 40 minutes had passed by the time I got back to the start.  I cut my losses and heading to the baggage collection area a hobbling mess.  To be honest the ending to Edinburgh marathon was a joke, a 10 minute walk to pickup your bag and a further 15 minute walk to the bus (after running a f**king marathon).  It didn't really matter though - I was just happy to have finished.</p>
<p>So bad race conditions aside, would I do one again?  Yes I'd like to.  The challenge of the learning about when to push your body and when to hold back, the nutrition and the discipline of training have all been worthwhile things to experience and practice.  Above everything else the atmosphere of the race is unrivaled though - the spirit of the other racers, the people calling out your name and the cheer as you cross the line are really like nothing else.  It's very easy to see why people get addicted to marathon running.  I took a few weeks off running after the event to let my body recover but it wasn't long before I was craving the open road again.  And it wasn't long before there was talk of another marathon.  In October I'll be doing Amsterdam - I won't train hard like I did last time as I still feel a little tired from the long miles - but I will keep my long run fitness ticking over and hopefully go sub 3.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/72">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/72#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
      <media:content type="image/jpeg" height="269" width="179" url="http://getfile1.posterous.com/getfile/files.posterous.com/import-ehvm/dcBwoxEeEiEyqpvrmcDqoqhblIzGEjlqwBqiBfuxjpggChcpniygvcFvkFzm/media_httpwwwrowthebo_ExCjh.jpeg">
        <media:thumbnail height="269" width="179" url="http://getfile3.posterous.com/getfile/files.posterous.com/import-ehvm/dcBwoxEeEiEyqpvrmcDqoqhblIzGEjlqwBqiBfuxjpggChcpniygvcFvkFzm/media_httpwwwrowthebo_ExCjh.jpeg.scaled500.jpg" />
      </media:content>
      <media:content type="image/jpeg" height="300" width="225" url="http://getfile8.posterous.com/getfile/files.posterous.com/import-ehvm/CBjDHflycbDHzHsukhAsjFDwBvlIdafFJmcfbtxiJCijFootnBcDjjtipCCg/media_httpwwwrowthebo_sruDk.jpg">
        <media:thumbnail height="300" width="225" url="http://getfile1.posterous.com/getfile/files.posterous.com/import-ehvm/CBjDHflycbDHzHsukhAsjFDwBvlIdafFJmcfbtxiJCijFootnBcDjjtipCCg/media_httpwwwrowthebo_sruDk.jpg.scaled500.jpg" />
      </media:content>
    <feedburner:origLink>http://rowtheboat.com/archives/72</feedburner:origLink></item>
    <item>
      <pubDate>Tue, 12 Jan 2010 14:16:00 -0800</pubDate>
      <title>AB Testing with Google Analytics</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/SUOx6pC5YLY/39</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/39</guid>
      <description>
        <![CDATA[<p>
	<p>I love AB testing. I think it is either related to the fact I did a year of Maths at university before switching to Computer Science or because human psychology fascinates me.  Either way when I launched <a href="http://www.5ftshelf.com">5ft Shelf</a> I was keen to test lots.  First on the agenda was the default shelf view.   For those not familiar with the site you can view a shelf of books in one of two ways - cover view or spine view as we call it on the site (see screenshots - click to enlarge).  Whilst my designer was sure that the cover view would be better I knew the target audience wasn't necessarily technical, and besides the cool factor of a 3D book shelf would capture attention.  It was going to be an interesting AB test.</p>
<p><div class='p_embed p_image_embed'>
<a href="http://getfile2.posterous.com/getfile/files.posterous.com/temp-2011-02-03/GrCygBIHcEAHtECvwBprFEHcoaFDGeIdgiilCGJouEJhFipxvsuiDspFgqFx/Screen-shot-2010-01-12-at-13.21.07.png.scaled1000.png"><img alt="Screen-shot-2010-01-12-at-13" height="117" src="http://getfile8.posterous.com/getfile/files.posterous.com/temp-2011-02-03/GrCygBIHcEAHtECvwBprFEHcoaFDGeIdgiilCGJouEJhFipxvsuiDspFgqFx/Screen-shot-2010-01-12-at-13.21.07.png.scaled500.png" width="500" /></a>
</div>
<div class='p_embed p_image_embed'>
<a href="http://getfile6.posterous.com/getfile/files.posterous.com/temp-2011-02-03/sGtkxnzzxcrklJonvxzGEpHGretfdpCjEqskEaajofItiCIJraBzioAngvJi/Screen-shot-2010-01-12-at-13.21.20.png.scaled1000.png"><img alt="Screen-shot-2010-01-12-at-13" height="111" src="http://getfile2.posterous.com/getfile/files.posterous.com/temp-2011-02-03/sGtkxnzzxcrklJonvxzGEpHGretfdpCjEqskEaajofItiCIJraBzioAngvJi/Screen-shot-2010-01-12-at-13.21.20.png.scaled500.png" width="500" /></a>
</div>
</p>
<p><a href="http://www.rowtheboat.com/wp-content/uploads/2010/01/Screen-shot-2010-01-12-at-13.21.07.png"></a> Traditionally when doing AB testing I've added the logic to the rails model (it doesn't really matter if you're not a rails developer, the point is it was in the application database).  This had a few disadvantages.  First up it didn't keep code or the database as clean as it could be in places.  Secondly tools had to be built to then analyse the data and thirdly it couldn't be easily plotted against relevant variables such as those stored in analytics software.</p>
<p>So when Google <a href="http://analytics.blogspot.com/2009/12/new-google-analytics-api-features.html">announced custom variable support in Analytics</a> I sat up and paid attention.  By passing through my AB test number to Google Analytics I can do all the reporting associated with AB testing outside my application database and report against more complex metrics that I don't store such as bounce rate.  Perfect, so let's get started:</p>
<p>The first step is to assign each user to your site a unique AB test number.  I went for a random number in the range 1 to 120.  Why so large?  Well most AB test I perform are just 2 or 3 options.  Depending on traffic you can go much larger but to be honest it becomes harder to understood the factors that lead to the users choice if you do&nbsp;that.  Anyway 120 is 2*3*4*5 which means you can have 2, 3, 4 or 5 options (or any product of these numbers - eg 6,8...).  This gives plenty of options for the future.  So for every visitor that comes to 5ft Shelf they get a random number and this remains with them (unless they clear out their cookies).</p>
<p>I then pass this number through to Google Analytics via a custom variable.  For example:</p>
<div class="CodeRay">
  <div class="code"><pre>var gaJsHost = ((&quot;https:&quot; == document.location.protocol) ? &quot;https://ssl.&quot; : &quot;http://www.&quot;);
document.write(unescape(&quot;%3Cscript src='&quot; + gaJsHost + &quot;google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E&quot;));

try {
    var pageTracker = _gat._getTracker(&quot;UA-11028671-1&quot;);
    pageTracker._setCustomVar(1, &quot;shelf_view&quot;, &quot;USERS_AB_NUMER&quot;, 1);
    pageTracker._trackPageview();
} catch(err) {}</pre></div>
</div>

<p>The USERS_AB_NUMBER needs replacing with the users actual ab number.  In ruby I used:</p>
<div class="CodeRay">
  <div class="code"><pre>@site_preferences.ab_test_number.modulo(2)</pre></div>
</div>

<p>as I was only testing two options. &nbsp;You can substitute this code as appropriate for the number of AB choices you have (and to your language of choice if not using Ruby).</p>
<p>A few notable things:</p>
<ul>
<li>The first argument in the custom variable index number.  Google analytics provides you with 5 of these so you can feasibly track upto five AB tests at once.</li>
<li>The second argument is the name.  I use this to identify the name of the AB test for easy identification in Analytics at a later date</li>
<li>The third argument is the value we want to store against the name.  I used the ab number modulo 2 as that is what is used to display the different options in the view of the application.  If you use more complex logic you should substitute it here.  You're not limited to numbers though - you can also store strings</li>
<li>The last argument is the the scope of the variable.  For AB testing you'll nearly always want 1 for visitor level.  More on this can be found in the <a href="http://code.google.com/apis/analytics/docs/tracking/gaTrackingCustomVariables.html">Analytics help centre</a> if you need it</li>
</ul>
<p>Note: you can just pass through the AB test number but you will have to combine each number's entries into 2 sets in this example.  This is a real pain as one set will be 1,3,5,..119 the next 2,4,6,..120, so unless you like maths best let your application do the logic and keep the data in analytics simple.</p>
<p>Now we've passed the data through to Google Analytics we need to make application specific logic in your views to render the different AB tests (again substitute for your test setup and language):</p>
<div class="CodeRay">
  <div class="code"><pre>if @site_preferences.ab_test_number.modulo(2) == 0
  # Render option 1
else
  # Render option 2
end</pre></div>
</div>

<p>Finally we need to create a custom report in Google Analytics.  Login and click 'Custom Reporting' down the left hand side.  Then click 'Manage Custom Reports' underneath and then 'Create new report'.  You can then drag the metrics which you wish to measure along the top (think what would constitute as a success in your test - longer page views, lower bounce rate etc) and the dimensions down below (you'll probably only want the Custom Variables here).  Make sure you get the Custom Variable values not the keys.  The screenshot below should show you how it looks (hover to increase size).</p>
<p><div class='p_embed p_image_embed'>
<a href="http://getfile6.posterous.com/getfile/files.posterous.com/temp-2011-02-03/FfhrEvbCkHiFxChxuElasqsEEjtkppAtjzqhEyjHzfAxtbChahmaJzdIJEFG/Screen-shot-2010-01-12-at-14.00.43.png.scaled1000.png"><img alt="Screen-shot-2010-01-12-at-14" height="259" src="http://getfile7.posterous.com/getfile/files.posterous.com/temp-2011-02-03/FfhrEvbCkHiFxChxuElasqsEEjtkppAtjzqhEyjHzfAxtbChahmaJzdIJEFG/Screen-shot-2010-01-12-at-14.00.43.png.scaled500.png" width="500" /></a>
</div>
</p>
<p>Then it's just a case of waiting however long you want to run your test case for  and logging into analytics to see the results.  You'll hopefully end up with a chart looking something like the one shown below (hover to increase size).  Note here the real data you're after is not in the graph but the table below.</p>
<p><div class='p_embed p_image_embed'>
<a href="http://getfile1.posterous.com/getfile/files.posterous.com/temp-2011-02-03/ueDFIEhEgFIbFjiivsyaIyzeovxzzBzCgzumjfuhEIABCyciIEAJHpofsFpr/Screen-shot-2010-01-12-at-14.00.14.png.scaled1000.png"><img alt="Screen-shot-2010-01-12-at-14" height="347" src="http://getfile4.posterous.com/getfile/files.posterous.com/temp-2011-02-03/ueDFIEhEgFIbFjiivsyaIyzeovxzzBzCgzumjfuhEIABCyciIEAJHpofsFpr/Screen-shot-2010-01-12-at-14.00.14.png.scaled500.png" width="500" /></a>
</div>
</p>
<p>So for those of you that asked that's why 5ft Shelf switched to spine view as the default, there's your answer :D  Happy AB testing folks.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/39">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/39#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="128" width="580" url="http://getfile4.posterous.com/getfile/files.posterous.com/import-ehvm/apCedzHJaIDDHjpfytoIsnnqqfxIHEGxrnAagBbsmuegJIEJrJegrojxtaBw/media_httpwwwrowthebo_xBCcF.png">
        <media:thumbnail height="110" width="500" url="http://getfile8.posterous.com/getfile/files.posterous.com/import-ehvm/apCedzHJaIDDHjpfytoIsnnqqfxIHEGxrnAagBbsmuegJIEJrJegrojxtaBw/media_httpwwwrowthebo_xBCcF.png.scaled500.png" />
      </media:content>
      <media:content type="image/png" height="136" width="580" url="http://getfile6.posterous.com/getfile/files.posterous.com/import-ehvm/AwbisnuqHaGleugekjzAmzhoyDnasfjjfkJJacfCwabIrGiqgnHrjHcwefiJ/media_httpwwwrowthebo_AaxaC.png">
        <media:thumbnail height="117" width="500" url="http://getfile9.posterous.com/getfile/files.posterous.com/import-ehvm/AwbisnuqHaGleugekjzAmzhoyDnasfjjfkJJacfCwabIrGiqgnHrjHcwefiJ/media_httpwwwrowthebo_AaxaC.png.scaled500.png" />
      </media:content>
      <media:content type="image/png" height="155" width="300" url="http://getfile2.posterous.com/getfile/files.posterous.com/import-ehvm/ktIeAxzDqskgxvmrxkEcnpJCcbcGwfhnzEHrwIDixaIfFulcHiyqDvEJplwH/media_httpwwwrowthebo_etGct.png">
        <media:thumbnail height="155" width="300" url="http://getfile5.posterous.com/getfile/files.posterous.com/import-ehvm/ktIeAxzDqskgxvmrxkEcnpJCcbcGwfhnzEHrwIDixaIfFulcHiyqDvEJplwH/media_httpwwwrowthebo_etGct.png.scaled500.png" />
      </media:content>
      <media:content type="image/png" height="208" width="300" url="http://getfile3.posterous.com/getfile/files.posterous.com/import-ehvm/lHzwBtnikApFgucdpmcqDAEcIfnzjJJaufgCyyxHChsdECkyBAIjeddfpdGI/media_httpwwwrowthebo_irskj.png">
        <media:thumbnail height="208" width="300" url="http://getfile8.posterous.com/getfile/files.posterous.com/import-ehvm/lHzwBtnikApFgucdpmcqDAEcIfnzjJJaufgCyyxHChsdECkyBAIjeddfpdGI/media_httpwwwrowthebo_irskj.png.scaled500.png" />
      </media:content>
      <media:content type="image/png" height="220" width="939" url="http://getfile4.posterous.com/getfile/files.posterous.com/temp-2011-02-03/GrCygBIHcEAHtECvwBprFEHcoaFDGeIdgiilCGJouEJhFipxvsuiDspFgqFx/Screen-shot-2010-01-12-at-13.21.07.png">
        <media:thumbnail height="117" width="500" url="http://getfile8.posterous.com/getfile/files.posterous.com/temp-2011-02-03/GrCygBIHcEAHtECvwBprFEHcoaFDGeIdgiilCGJouEJhFipxvsuiDspFgqFx/Screen-shot-2010-01-12-at-13.21.07.png.scaled500.png" />
      </media:content>
      <media:content type="image/png" height="210" width="948" url="http://getfile7.posterous.com/getfile/files.posterous.com/temp-2011-02-03/sGtkxnzzxcrklJonvxzGEpHGretfdpCjEqskEaajofItiCIJraBzioAngvJi/Screen-shot-2010-01-12-at-13.21.20.png">
        <media:thumbnail height="111" width="500" url="http://getfile2.posterous.com/getfile/files.posterous.com/temp-2011-02-03/sGtkxnzzxcrklJonvxzGEpHGretfdpCjEqskEaajofItiCIJraBzioAngvJi/Screen-shot-2010-01-12-at-13.21.20.png.scaled500.png" />
      </media:content>
      <media:content type="image/png" height="549" width="790" url="http://getfile4.posterous.com/getfile/files.posterous.com/temp-2011-02-03/ueDFIEhEgFIbFjiivsyaIyzeovxzzBzCgzumjfuhEIABCyciIEAJHpofsFpr/Screen-shot-2010-01-12-at-14.00.14.png">
        <media:thumbnail height="347" width="500" url="http://getfile4.posterous.com/getfile/files.posterous.com/temp-2011-02-03/ueDFIEhEgFIbFjiivsyaIyzeovxzzBzCgzumjfuhEIABCyciIEAJHpofsFpr/Screen-shot-2010-01-12-at-14.00.14.png.scaled500.png" />
      </media:content>
      <media:content type="image/png" height="404" width="779" url="http://getfile1.posterous.com/getfile/files.posterous.com/temp-2011-02-03/FfhrEvbCkHiFxChxuElasqsEEjtkppAtjzqhEyjHzfAxtbChahmaJzdIJEFG/Screen-shot-2010-01-12-at-14.00.43.png">
        <media:thumbnail height="259" width="500" url="http://getfile7.posterous.com/getfile/files.posterous.com/temp-2011-02-03/FfhrEvbCkHiFxChxuElasqsEEjtkppAtjzqhEyjHzfAxtbChahmaJzdIJEFG/Screen-shot-2010-01-12-at-14.00.43.png.scaled500.png" />
      </media:content>
    <feedburner:origLink>http://rowtheboat.com/archives/39</feedburner:origLink></item>
    <item>
      <pubDate>Wed, 28 Oct 2009 09:47:00 -0700</pubDate>
      <title>5ft Shelf</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/rgNuTv6jpYA/38</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/38</guid>
      <description>
        <![CDATA[<p>
	<p><div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_hrsem" height="104" src="http://getfile5.posterous.com/getfile/files.posterous.com/import-ehvm/lJFyaCFqlfHBFBaihpImibDtkBvialqGfrEjjjbgwtFtoqyujyjGpFopHjkz/media_httpwwwrowthebo_Hrsem.png.scaled500.png" width="310" />
</div>
For the last six months I've been alternating between freelancing and working on my own personal projects.&nbsp; It's one of the main reasons I went freelance, so it was great to finally make the time to do it.&nbsp; In particular though, there was a itch I really wanted to scratch.&nbsp; I first learnt of the <a href="http://en.wikipedia.org/wiki/Harvard_Classics">Harvard Classics</a> a few years ago and since then the idea has really struck a chord with me - a sort of popular list of books that one should read to have a liberal education.&nbsp; As I read more about the collection of books I became  more interested in what the modern equivalent would be - what are the quintessential books one should read 100 years later?</p>
<p>Today I can announce  <a href="http://www.5ftshelf.com">5ft Shelf</a> - a site to do just that:</p>
<blockquote>In 1909 Dr Eliot, then President of Harvard University, claimed a liberal education could be achieved by reading a collection of books that would total no more than 5ft in width. A local publisher challenged him to name them and he responded with what became known as the Harvard Classics.  Shifting forward 100 years and into the age of the internet, we're trying to find out what the modern equivalent would be. &nbsp;A lot has happened in the last century and we felt it would be impossible to get a fair representation of people&rsquo;s interests without introducing two quintessential formats for modern living&mdash;music albums and movies.  Rather than use a single authoritative source for the modern shelf, we've taken the opposite approach by allowing users to create their own shelf.&nbsp; We then combine all the users' shelves to find the most popular items and create our top 5ft shelf ("the ultimate shelf").</blockquote>
<p>The sites offers a little more than the introduction eludes to.&nbsp; Amongst other features, mini-shelves contain the most popular items in a given subject area and the recommendation algorithm makes personalised recommendations based on the items on your shelf.&nbsp; For more details read the <a href="http://blog.5ftshelf.com">5ft Shelf blog</a> <a href="http://blog.5ftshelf.com/2009/10/28/going-live/">Going Live post</a></p>
<p>As with all projects there has been big highs and hard lows but overall it has been great fun and scratching a personal itch was particularly satisfying.&nbsp; On a more technical / agile development note I intend to publish two separate blog posts on the results of my endeavors.&nbsp; The first will be on <a href="http://www.5ftshelf.com/books/B001CJUHSI/Getting-Real-The-smarter-faster-easier-way-to-build-a-successful-web-Application">Getting Real</a> and my experiences using the 37 signals development approach, along with some productivity experiments I performed myself during the time.&nbsp; The second will be on how long it takes to get a startup to market.&nbsp; I'm used to tracking time spent on projects for clients so I did the same for this project and found some interesting results.</p>
<p>Look for both blog posts in the coming weeks and in the meantime please do check out <a href="http://www.5ftshelf.com">5ft Shelf</a> and pass it along to anyone who you think might be interested.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/38">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/38#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="104" width="310" url="http://getfile3.posterous.com/getfile/files.posterous.com/import-ehvm/lJFyaCFqlfHBFBaihpImibDtkBvialqGfrEjjjbgwtFtoqyujyjGpFopHjkz/media_httpwwwrowthebo_Hrsem.png">
        <media:thumbnail height="104" width="310" url="http://getfile5.posterous.com/getfile/files.posterous.com/import-ehvm/lJFyaCFqlfHBFBaihpImibDtkBvialqGfrEjjjbgwtFtoqyujyjGpFopHjkz/media_httpwwwrowthebo_Hrsem.png.scaled500.png" />
      </media:content>
    <feedburner:origLink>http://rowtheboat.com/archives/38</feedburner:origLink></item>
    <item>
      <pubDate>Sun, 29 Mar 2009 14:39:00 -0700</pubDate>
      <title>CouchDB and ORMs</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/1VymTda0BTE/37</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/37</guid>
      <description>
        <![CDATA[<p>
	<p><a href="http://twitter.com/langalex">Alex</a> did a good introduction talk to CouchDB at <a href="http://scotlandonrails.com/">Scotland on Rails</a>.  Towards the end of the talk he did an overview of the current ruby plugins/gems available for interfacing with CouchDB, one of which was my own <a href="http://github.com/georgepalmer/couch_foo/tree/master">CouchFoo</a>.  Alex's opinion was that any ORM for CouchDB should be as thin as possible just wrapping the Ruby to JSON object translation.  I raised my opinion in the question section at the end by saying that I didn't agree and thought the ORM should map the level of functionality available in ActiveRecord.  This sparked a debate both in the talk and via Twitter of the best approach for an ORM for CouchDB to take.  As a result I agreed to write this blog post to outline my views.</p>
<p>CouchDB is a document orientated database with a HTTP interface amongst other features.  When I first started using it I played with the database a lot via simple interactions through CURL.  In the same way I feel it is important to know SQL before using any higher level API to store and retrieve objects in a relational database, I feel it is important to understand how CouchDB works before using a library to interact with it.  As with most areas of computing you will find a range of opinions over what level you interact with the database - there are the purists who like to write SQL queries for each database query performed and those who are willing to sacrifice a bit of performance (maybe not having the optimum query run each time) for the time efficiencies realized whilst developing.  I align quite well with the Rails mantra on this one - I'm willing to sacrifice perfect SQL each time for the efficiency gains made whilst developing.  Part of Alex's argument was that you should be as close to the database as possible because the Ruby to JSON conversion is much less than the Ruby to SQL conversion.  Whilst I don't disagree that it's important to know how CouchDB works, I do disagree on the level at which any Ruby library should sit.  I'm happy to pay a small price in terms of extra ruby code executed because I want as clean as DSL as possible.</p>
<p>Whilst developing CouchDB I tried all the existing ruby libraries and as I worked through them I ran into several issues.  After using ActiveRecord's save and find methods it was particularly annoying to use a library that used different method names for the same conceptual operations (eg get instead of find).  This wasn't a major issue of course I just forked the library and made changes.  But as time went on there were features that I missed from ActiveRecord.  Validations, callbacks, finders and associations were the prime contenders.  Then dynamic finders and named scopes got added to the list.  In the end changing the existing libraries became so much work I decided to start with ActiveRecord and work from there.</p>
<p>Of the features in ActiveRecord Associations are perhaps the most controversial on whether they should apply to Document orientated databases or not.  The argument goes that if you're trying to use associations you don't understand how CouchDB should be used.  I disagree on this point - a simple counter argument is presented by having a document that allows comments.  Those comments could be stored inline in the document itself or in separate documents that have a reference to their parent.  This is association whichever way you look at it.  Which approach you decide to use will depend on your application and the characteristics of it.  Incidentally Alex's gem did a great job of this letting the user specify in the association whether they wanted the object stored inline or not.  This has since been removed from his gem but is something that's definitely on the TODO list for CouchFoo.</p>
<p>For me CouchDB lends itself well to two distinct domains.  Firstly domains where documents are used - that is an object where the fields that are stored to the database change depending on the object.  Secondly domains where you wish to take advantage of some of CouchDB's features not present (or poorly implemented) in relational databases - a HTTP interface, fantastic scaling ability due to bi-directional replication, and schema free nature (<a href="http://bret.appspot.com/entry/how-friendfeed-uses-mysql">see this excellent article on friendfeed experience with MySQL</a>) are just a few that spring to mind.  People may use CouchDB for the second set of criteria even though their database design could be considered quite structured, and I fully expect this group of people to rise as CouchDB reaches 1.0.  However that wasn't why I wrote CouchFoo, my project fell into the first domain.  Whilst I provided a way to use ActiveRecord's higher level API I also provided access to a database object that allows simple storage and retrieval of documents by id.  If that is all the functionality you require then I would expect CouchREST would be a better choice.  However I believe in reality you will quickly find you need to add validations to a field, or maybe add an association or two.  And as soon as you start on that slope I believe CouchFoo to be a better choice.</p>
<p>Ultimately I created CouchFoo as I missed the richness of the ActiveRecord API.  Whilst I don't believe my library will be perfect for everyone it has received a lot of good feedback.  To paraphrase DHH I didn't create the perfect framework for everyone else, I created it for me.  I only hope that other people find it useful.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/37">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/37#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/37</feedburner:origLink></item>
    <item>
      <pubDate>Sat, 21 Mar 2009 23:41:00 -0700</pubDate>
      <title>SXSW</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/zMs2LpoyfNg/36</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/36</guid>
      <description>
        <![CDATA[<p>
	<p>Thanks to a lucky draw at <a href="http://dconstruct.org/">dConstruct</a> last year I bagged two free tickets to this years <a href="http://sxsw.com/">SXSW</a>.  I decided to invite <a href="http://jamesbrady.wordpress.com">Jim</a> along for no other reason that he was likely to be the closest to the event.  I'd never been to Texas before and despite hearing many bad reports, word was Austin really wasn't quite as bad.</p>
<p>And what a surprise it was - a laid back city with fairly liberal attitudes.  Once I got over the English-American language barrier (swap line for queue, register for till and give me for can I have) things seemed to go well.  The line up of talks was amazing - Gary Vaynerchuk was awesome although sadly I only caught the last 20 minutes (<a href="http://events.carsonified.com/fowa/2009/miami/videos/gary-vaynerchuk">good video of him here at FOWA</a>), Brian Brushwood did an excellent talk based on his <a href="http://revision3.com/scamschool/">scam school series</a>, <a href="http://en.wikipedia.org/wiki/James_Powderly">James Powderly</a> gave a fascinating talk of his grafitti art and getting detained in China, and there was an extremely useful panel on how to give good presentations.  That's one of the parts I enjoyed the most - the sheer diversity of talks.  In addition there were more informal talks where the presenter started off for 10 minutes before opening up to the room - going freelance and becoming productive were two of my favourites in this format.  Of course due to the sheer volume of talks many good ones were missed - <a href="http://en.wikipedia.org/wiki/Larry_Lessig">Larry Lessig</a> seems the prime candidate here.  They're going to make all the talks available for download so I'm looking forward to catching what I missed.</p>
<p>The talks are only half of Southby though.  The night life is great and there's loads of parties with free drink and beer flowing.  These seemed quite hit and miss with the Digg party being awful and the <a href="http://twitter.com/goodgravy/status/1330461672">queue to get a signature from Kevin Rose</a> a really quite distressing sight.  But for every flop there were some good ones that provided entertainment as wide ranging as Burlesque and live photoshop drawing.</p>
<p>I met plenty of new interesting people, and bumped into quite a few from England although I know of least two out there who I didn't bump into all week.  Other highlights included the weather, free wifi everywhere, a film called Burma VJ we randomly caught and England destroy France in the Rugby.  More random things included drive through banks and a gig featuring a hip-hop group I strangely enjoyed.  And the downers?  Well I can't finish without digging just how awful the all american diet is (suprisingly I didn't want my meal in a sea of melted cheese but gee thanks).  Overall though a great experience and well worth it if you've never made the trip.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/36">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/36#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/36</feedburner:origLink></item>
    <item>
      <pubDate>Sat, 21 Mar 2009 22:36:00 -0700</pubDate>
      <title>Using objects in models (with CouchFoo)</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/2uerOYtlAzo/35</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/35</guid>
      <description>
        <![CDATA[<p>
	<p>ActiveRecord allows you to serialize objects into text columns through YAML.  This seems useful but in my experience is under-used.  One of the primary reasons for this is it's not possible to use the data that the object encapsulates without the ruby model.  For example it's not possible to find on the contents of that object or for that matter, modify the object with languages that lack YAML support.  With CouchDB all data is stored in JSON so this is not an issue.</p>
<p>The project I wrote CouchFoo for used complex ACLs and I wanted to encapsulate this all in an object rather than use several many-many relationships and construct an ACL object based on their contents.  So how do you this with CouchFoo?  Simple, any object can be assigned as a property in a CouchFoo model as long as it has a .to_json method and a class .from_json method.  The methods do what you'd expect, for example:</p>
<div class="CodeRay">
  <div class="code"><pre>class DataObjectAttributeList

  attr_accessor :attributes

  # Constructs the object from JSON
  def self.from_json(json)
    DataObjectAttributeList.new(json)
  end

  # Converts the object to JSON
  def to_json
    @attributes
  end

  def initialize(initials = {}, *args)
    @attributes = initials
  end
end</pre></div>
</div>

<p>This is just a simple example storing a hash but the structure could be as complex as you'd like.  In the future I plan to add inline associations to CouchFoo, so rather than have a one-to-many association where the many are accessed via a second database query you could have the objects stored as part of the parent contents.  Performance wise, this is normally much more efficient (although not in all situations - eg heavy write and low read).</p>
<p>Overall, this becomes a very addictive way of developing and in the same way <a href="http://push.cx/2009/rules-of-database-app-aging">you start to question whether you need a relational database</a>, you start to question whether you should store associated objects inline or separately.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/35">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/35#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/35</feedburner:origLink></item>
    <item>
      <pubDate>Wed, 04 Feb 2009 11:25:00 -0800</pubDate>
      <title>CouchFoo: ActiveRecord styled API for CouchDB </title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/TPws7yFKLQo/33</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/33</guid>
      <description>
        <![CDATA[<p>
	<p>CouchDB is an excellent database, designed especially for distributed applications.  To quote the <a href="http://couchdb.apache.org/">official site</a> site:</p>
<blockquote class="posterous_medium_quote">Apache CouchDB is a distributed, fault-tolerant and schema-free document-oriented database accessible via a RESTful HTTP/JSON API. Among other features, it provides robust, incremental replication with bi-directional conflict detection and resolution, and is queryable and indexable using a table-oriented view engine with JavaScript acting as the default view definition language.</blockquote>
<p>along with the knowledge it's written in Erlang, you know it's going to go be a winner in the future.  <div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_wagsy" height="110" src="http://getfile9.posterous.com/getfile/files.posterous.com/import-ehvm/nqbrygrzCahhuIezeihyooFzDHcFEfmJtAoBllJgIdfDltlanFpBcojoafvw/media_httpwwwrowthebo_wAgsy.png.scaled500.png" width="183" />
</div>
</p>
<p>For one of my current freelance projects I needed to store data in a document fashion - ie unstructured.  This made CouchDB an ideal candidate.  There were several ruby gems available: CouchPotato, CouchREST, ActiveCouch and RelaxDB gems. Each offered its own benefits and own challenges. After hacking with each I couldn&rsquo;t get a library was happy with. So I started with ActiveRecord and modified it to work with CouchDB. And so <a href="http://github.com/georgepalmer/couch_foo">CouchFoo</a> was born.</p>
<p>In the end I ended up with a gem that mirrors ActiveRecord in all but a few minor places.  In particular:</p>
<ul>
<li>CouchDB is schema free so property defintions for the document are defined in the model (like DataMapper)</li>
<li>:select, :joins, :having, :group, :from and :lock are not available on find or associations as they don&rsquo;t apply (locking is handled as conflict resolution at insertion time)</li>
<li>:conditions can only accept a hash and not an array or SQL. For example :conditions =&gt; {:user_name =&gt; "Georgio_1999"}</li>
<li>:offset is less efficient in CouchDB - there&rsquo;s more on this in the rdoc</li>
<li>:order is applied after results are retrieved from the database. Therefore :order cannot be used with :limit without a new option :use_key. This is explained fully in the quick start guide and CouchFoo#find documentation</li>
<li>:include isn&rsquo;t implemented yet but the finders and associations still accept the option so you won&rsquo;t need to make any code changes</li>
<li>By default results are ordered by document key. The key uses a UUID scheme so these don&rsquo;t auto-increment and are likely to come out in a different order to insertion. default_sort can be used on a model to sort by create date by default and overcome this</li>
<li>validates_uniqueness_of has had the :case_sensitive option removed</li>
<li>Because there&rsquo;s no SQL there&rsquo;s no SQL finder methods</li>
<li>Timezones, aggregations and fixtures are not yet implemented</li>
<li>The price of index updating is paid when next accessing the index rather than the point of insertion. This can be more efficient or less depending on your application. It may make sense to use an external process to do the updating for you - see CouchFoo#find for more on this</li>
<li>On that note, occasional compacting of CouchDB is required to recover space from old versions of documents and keep performance high. This can be kicked off in several ways (see quick start guide)</li>
</ul>
<p>The RDoc for the gem contains more details on each of these differences, new features that I added, a quick start guide and additional areas of responsibility to think about when using CouchDB (in particular performance).</p>
<p>As a quick overview, basic operations are the same as ActiveRecord:</p>
<div class="CodeRay">
  <div class="code"><pre>class Address &lt; CouchFoo::Base
property :number, Integer
property :street, String
property :postcode # Any generic type is fine as long as .to_json can be called on it
end</pre></div>
</div>

<div class="CodeRay">
  <div class="code"><pre>address1 = Address.create(:number =&gt; 3, :street =&gt; &quot;My Street&quot;, :postcode =&gt; &quot;secret&quot;) # Create address
address2 = Address.create(:number =&gt; 27, :street =&gt; &quot;Another Street&quot;, :postcode =&gt; &quot;secret&quot;)
Address.all # = [address1, address2] or maybe [address2, address2] depending on key generation
Address.first    # = address1 or address2 depending on keys so probably isn't as expected
Address.find_by_street(&quot;My Street&quot;) # = address1</pre></div>
</div>

<p>As key generation is through a UUID scheme, the order can't be predicted. However you can order the results by default:</p>
<div class="CodeRay">
  <div class="code"><pre>class Address &lt; CouchFoo::Base
property :number, Integer
property :street, String
property :postcode # Any generic type is fine as long as .to_json can be called on it
property :created_at, DateTime

default_sort :created_at
end</pre></div>
</div>

<div class="CodeRay">
  <div class="code"><pre>Address.all # = [address1, address2]
Address.first    # = address1 or address2, sorting is applied after results
Address.first(:use_key =&gt; :created_at) # = address1 but at the price of creating a new index</pre></div>
</div>

<p>Note that there's an optimisation that will order results by created_at if there are no conditions so in the above case, the default_sort wasn't required.  However when using with conditions it will be required so it makes sense to use at all times.</p>
<p>Conditions work slightly differently:</p>
<div class="CodeRay">
  <div class="code"><pre>Address.find(:all, :conditions {:street =&gt; &quot;My Street&quot;}) # = address1, creates index on :street
Address.find(:all, :conditions {:created_at =&gt; &quot;sometime&quot;}) # Uses same index as :use_key =&gt; :created_at
Address.find(:all, :use_key =&gt; :street, :startkey =&gt; 'p') # All streets from p in alphabet, reuses the index created 2 lines up</pre></div>
</div>

<p>As well as providing support for people using relational databases, CouchFoo attempts to provide a library for those wanting to use CouchDB as a document-orientated database:</p>
<div class="CodeRay">
  <div class="code"><pre>class Document &lt; CouchFoo::Base
property :number, Integer
property :street, String

view :number_ordered, &quot;function(doc) {emit([doc.number , doc.street], doc); }&quot;, nil, :descending =&gt; true
end</pre></div>
</div>

<div class="CodeRay">
  <div class="code"><pre>Document.number_ordered(:limit =&gt; 75) # Will get the last 75 documents in the database ordered by number, street attributes</pre></div>
</div>

<p>Associations work as expected but you must to remember to add the properties required for an association (we&rsquo;ll make this automatic soon):</p>
<div class="CodeRay">
  <div class="code"><pre>class House &lt; CouchFoo::Base
has_many :windows
end

class Window &lt; CouchFoo::Base
property :house_id, String
belongs_to :house
end</pre></div>
</div>

<p>There's a few bits left to tidy up (as noted in the readme) but generally speaking it's now ready for use by others. Grab it on <a href="http://github.com/georgepalmer/couch_foo">github</a> and feel free to fork and send me pull requests.</p>
<p>And now to do something I've not being doing a lot of lately, spend some more time on the Couch...</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/33">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/33#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="110" width="183" url="http://getfile4.posterous.com/getfile/files.posterous.com/import-ehvm/nqbrygrzCahhuIezeihyooFzDHcFEfmJtAoBllJgIdfDltlanFpBcojoafvw/media_httpwwwrowthebo_wAgsy.png">
        <media:thumbnail height="110" width="183" url="http://getfile9.posterous.com/getfile/files.posterous.com/import-ehvm/nqbrygrzCahhuIezeihyooFzDHcFEfmJtAoBllJgIdfDltlanFpBcojoafvw/media_httpwwwrowthebo_wAgsy.png.scaled500.png" />
      </media:content>
    <feedburner:origLink>http://rowtheboat.com/archives/33</feedburner:origLink></item>
    <item>
      <pubDate>Thu, 08 Jan 2009 20:34:00 -0800</pubDate>
      <title>Rails with Datamapper</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/H_0ind8eRvY/32</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/32</guid>
      <description>
        <![CDATA[<p>
	<p>With the recent announcement that <a href="http://weblog.rubyonrails.org/2008/12/23/merb-gets-merged-into-rails-3">Rails and MERB will merge</a> and my preference for <a href="http://datamapper.org">DataMapper</a> I decided to plug datamapper into rails for my next freelance project.  The theory goes this should make the upgrade path to Rails 3 a lot simpler!</p>
<p>It's currently possible to use Datamapper with Rails, heck even <a href="http://loudthinking.com/">DHH</a> himself commented so, but it's not quite easy as using ActiveRecord.  After a quick Google I only ran into question of how to do it, no howto guide.  So I set out to make mine own - it really was quite simple in the end:</p>
<div class="CodeRay">
  <div class="code"><pre>sudo gem install addressable data_objects do_mysql # do_mysql can be changed for do_postgres or do_sqlite3 as appropriate
sudo gem install dm-core dm-more</pre></div>
</div>

<p>In the <a href="http://github.com/datamapper/dm-more/tree/master">dm-more github repos</a> there's a folder called rails_datamapper which is a plugin for rails to add datamapper support.  This doesn't install with the dm-more gem so it's a case of cloning the git repository and copy the folder to your rails project:</p>
<div class="CodeRay">
  <div class="code"><pre>git clone git://github.com/sam/dm-more.git 
cp -R dm-more/rails_datamapper /vendor/plugins</pre></div>
</div>

<p>Then edit your project environment.rb file and add the following lines:</p>
<div class="CodeRay">
  <div class="code"><pre># Load the required gems in the correct order 
config.gem &quot;addressable&quot;, :lib =&gt; &quot;addressable/uri&quot; 
config.gem &quot;data_objects&quot; 
config.gem &quot;do_mysql&quot; 
config.gem &quot;dm-core&quot;  

# Make datamapper load first as some plugins have dependencies on it
config.plugins = [ :rails_datamapper, :all ]

# Remove ActiveRecord if you no longer need it
config.frameworks -= [ :active_record ]</pre></div>
</div>

<p>The connection to the database will be made by the rails_datamapper plugin using your database.yml configuration file.  You'll need to use a slightly different format for datamapper:</p>
<div class="CodeRay">
  <div class="code"><pre>development: 
:repositories: 
:adapter: mysql 
:database: opnli_dev</pre></div>
</div>

<p>Or alternately you can specify your own initializer and forgo the rails plugin:</p>
<div class="CodeRay">
  <div class="code"><pre>hash = YAML.load(File.new(RAILS_ROOT + &quot;/config/database.yml&quot;)) DataMapper.setup(:default, hash[RAILS_ENV])</pre></div>
</div>

<p>The only real gotcha in using datamapper is some rails plugins assume you're using ActiveRecord.  Hopefully this won't be the case in the future, but for now you'll need to get forking!</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/32">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/32#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/32</feedburner:origLink></item>
    <item>
      <pubDate>Sun, 21 Dec 2008 17:46:00 -0800</pubDate>
      <title>Git</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/k12_HrDtBcI/31</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/31</guid>
      <description>
        <![CDATA[<p>
	<p>For those readers of my blog who don't live in the rails world I highly recommend checking out <a href="http://git.or.cz/">Git</a>, a distributed version control system.  It has been big in the rails world since early this year for several good reasons:</p>
<ul>
<li>It has distributed and offline functionality</li>
<li>Making and merging branches is a breeze - encouraging you to try experiments in branches</li>
<li>It uses much less space than alternatives, such as Subversion, and only has one .git folder at the base of your project</li>
<li>It's in active development with constant releases of new features (but stable enough to be used for the linux kernel)</li>
</ul>
<p>The terminology is slightly different from subversion and friends but once you've got used to it you never look back!</p>
<p><a href="http://merbivore.com/">Merb</a> was very quick to jump on the git bandwagon and <a href="http://rubyonrails.org/">rails</a> followed not much later.  Practically this made distributed development a hell of a lot easier, but it also had some nice knock on effects.  Patching is now a lot quicker too - you simply fork the project, make a fix and inform the admin who can then choose to merge back into the master (if they see fit).  It's made the process for fixing bugs a hell of a lot quicker.</p>
<p>Soon after git came along the fantastic <a href="http://github.com">github.com</a> followed making it easy to host remote repositories.  And so to the reason for me writing this post - github <a href="http://github.com/blog/272-github-pages">just launched git pages</a> where you can upload your own page to front your repositories.  It's a neat idea and naturally is all managed through a git repository.  You simply create your site in a repository, push to github and the deployment is automatic.  Although it's only simple HTML pages, it's a great proof of concept of other things that could be possible.  <a href="http://georgepalmer.github.com/">My effort can be found here</a> which following the git ethos I just forked from <a href="http://schacon.github.com/">somebody else</a></p>
	
</p>

<p><a href="http://rowtheboat.com/archives/31">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/31#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/31</feedburner:origLink></item>
    <item>
      <pubDate>Sun, 23 Nov 2008 15:38:00 -0800</pubDate>
      <title>Ruby Manor</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/rFYRjg-ipG8/28</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/28</guid>
      <description>
        <![CDATA[<p>
	<p><div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_qkfij" height="150" src="http://getfile1.posterous.com/getfile/files.posterous.com/import-ehvm/DwpwnDDAFnctvxkInFplnjmauzDlxkzoDGoqcxmahdxgwjDonurvxarhwiqf/media_httpwwwrowthebo_qkFIj.png.scaled500.png" width="150" />
</div>
 Yesterday I spoke at and attended <a href="http://rubymanor.org/">Ruby Manor</a>.  It was a grass route conference with the attendees determining the <a href="http://rubymanor.org/schedule/">agenda</a> and the <a href="http://www.h-lame.com">organising</a> <a href="http://interblah.net/">duo</a> aiming to keep costs to an absolute minimum.  So successful were they infact, and as one twitterer aptly <a href="http://twitter.com/kerryb/status/1018588591">points out</a>, that for an amazing &pound;12 you got a series of excellent talks, no annoying sponsorship and &pound;500 behind the bar at the end of the night.  Given even the <a href="http://2008.dconstruct.org/">cheapest of conferences</a> aimed at freelancers clocks in well over the &pound;100 mark, this really was a fantastic achievement.</p>
<p>On a personal note I presented on <a href="http://github.com/ezmobius/nanite/tree/master">nanite</a> which is a background processing solution for rails and merb.  It was quite a technical talk and I was trying to get a lot into the 30 minutes timeslot, so it felt a little rushed.  Nevertheless most people seemed to grab the concept and the live demo at the end went really well.  For those that missed the event I'm sure the videos will be online soon, but in the meantime there's an excellent coverage on this <a href="http://effectif.com/">blog</a> and the slides from my presentation are available on <a href="http://www.slideshare.net/Georgio_1999/rubymanor-presentation/">slideshare</a> or below:</p>
<div style="">
<strong style="display: block; margin: 12px 0 4px;"><a href="http://www.slideshare.net/Georgio_1999/rubymanor-presentation" title="Rubymanor - Nanite talk">Rubymanor - Nanite talk</a></strong>
<object height="355" width="425">
<param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=rubymanor-1227366017327827-9&amp;stripped_title=rubymanor-presentation&amp;userName=Georgio_1999" />
<param name="allowFullScreen" value="true" />
<param name="allowScriptAccess" value="always" />
<embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=rubymanor-1227366017327827-9&amp;stripped_title=rubymanor-presentation&amp;userName=Georgio_1999" type="application/x-shockwave-flash" height="355" width="425"></embed>
</object>
<div style="padding: 5px 0 12px;">View more <a href="http://www.slideshare.net/">presentations</a> from <a href="http://www.slideshare.net/Georgio_1999">Georgio_1999</a>.</div>
</div>
	
</p>

<p><a href="http://rowtheboat.com/archives/28">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/28#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="150" width="150" url="http://getfile1.posterous.com/getfile/files.posterous.com/import-ehvm/DwpwnDDAFnctvxkInFplnjmauzDlxkzoDGoqcxmahdxgwjDonurvxarhwiqf/media_httpwwwrowthebo_qkFIj.png">
        <media:thumbnail height="150" width="150" url="http://getfile1.posterous.com/getfile/files.posterous.com/import-ehvm/DwpwnDDAFnctvxkInFplnjmauzDlxkzoDGoqcxmahdxgwjDonurvxarhwiqf/media_httpwwwrowthebo_qkFIj.png.scaled500.png" />
      </media:content>
    <feedburner:origLink>http://rowtheboat.com/archives/28</feedburner:origLink></item>
    <item>
      <pubDate>Fri, 26 Sep 2008 17:21:00 -0700</pubDate>
      <title>New plugins</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/h5yGAM2oSKE/27</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/27</guid>
      <description>
        <![CDATA[<p>
	<p>I've just pushed two plugins to github.  The <a href="https://github.com/GeorgePalmer/defensio/tree">first</a> is an improvement on the standard <a href="http://defensio.com/">Defensio</a> plugin that only checks the validity of your API key when posting articles or comments.  This is better than checking each time a model that uses the plugin is instantiated as it doesn't require contact with the Defensio API (so is faster) and also won't bring your site to a standstill if someone is just viewing a page and the Defensio service is down.</p>
<p>The <a href="https://github.com/GeorgePalmer/timed_fragment_cache/tree">second</a> updates the highly useful <a href="http://livsey.org/">timed fragment cache plugin</a> by Richard Livsey to support Rails 2.1</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/27">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/27#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/27</feedburner:origLink></item>
    <item>
      <pubDate>Fri, 26 Sep 2008 16:30:00 -0700</pubDate>
      <title>Wordia - an online dictionary</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/MYUxbZQ3CIQ/25</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/25</guid>
      <description>
        <![CDATA[<p>
	<p>For the last 6 weeks I've been head deep coding for a new startup <a href="http://www.worida.com">wordia.com</a> The idea is rather fresh - to allow people to learn the meaning of words by watching videos of people saying what the word means to them.  So far my favourites include <a href="http://www.wordia.com/words/invention/videos/174">invention</a> by Nikki Grahame of big brother fame, <a href="http://www.wordia.com/words/banana/videos/21">bannana</a> by two randoms at Edinburgh festival and, as everybody looked up rude words when they first got access to a real dictionary, <a href="http://www.wordia.com/words/incest/videos/39">incest</a>.  <div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_xhqws" height="150" src="http://getfile3.posterous.com/getfile/files.posterous.com/import-ehvm/uhsiaajHzajgcvBzilgimlyvndrtlFmgmnGhyewItboIygafGAxthBfwohpu/media_httpwwwrowthebo_xHqws.jpg.scaled500.jpg" width="150" />
</div>
</p>
<p>It's been a really hard few work so was very rewarding on launch day (after 4.5 hours of sleep) to see London's press arrive at our launch event (see photo left).  The site went down well and we featured in most the <a href="http://www.guardian.co.uk/global/2008/sep/20/samueljohnson">major</a> <a href="http://blogs.telegraph.co.uk/richard_tyler/blog/2008/09/18/bebo_to_bottom_feeder_michael_birch_and_the_video_dictionary">newspapers</a> in the following days.  It was also great to meet our backer <a href="http://en.wikipedia.org/wiki/Michael_Birch">Michael Birch</a> in person having recently exited from <a href="http://www.bebo.com">Bebo</a></p>
<p>In the coming months we've lot of new features planned and exciting people lined up to film with.  I'll also be dropping back down to 3 days per week so I can get working on my own projects again.  It should be an exciting 3 months.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/25">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/25#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
      <media:content type="image/jpeg" height="150" width="150" url="http://getfile8.posterous.com/getfile/files.posterous.com/import-ehvm/uhsiaajHzajgcvBzilgimlyvndrtlFmgmnGhyewItboIygafGAxthBfwohpu/media_httpwwwrowthebo_xHqws.jpg">
        <media:thumbnail height="150" width="150" url="http://getfile3.posterous.com/getfile/files.posterous.com/import-ehvm/uhsiaajHzajgcvBzilgimlyvndrtlFmgmnGhyewItboIygafGAxthBfwohpu/media_httpwwwrowthebo_xHqws.jpg.scaled500.jpg" />
      </media:content>
    <feedburner:origLink>http://rowtheboat.com/archives/25</feedburner:origLink></item>
    <item>
      <pubDate>Wed, 27 Aug 2008 20:24:00 -0700</pubDate>
      <title>Who really won the olympics?</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/l3ccQmq0rK4/22</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/22</guid>
      <description>
        <![CDATA[<p>
	<p><div class='p_embed p_image_embed'>
<img alt="Media_httpwwwrowthebo_azpel" height="145" src="http://getfile4.posterous.com/getfile/files.posterous.com/import-ehvm/owBwiJashyqomlnfwgbgxGDtDoyddgudlIrfmGDBhikkFpevpFrnJGuHCaGB/media_httpwwwrowthebo_azpEl.png.scaled500.png" width="300" />
</div>
 I've just finished the excellent <a href="http://www.amazon.co.uk/Freakonomics-Economist-Explores-Everything-Celebrations/dp/0141035307/ref=sr_1_2?ie=UTF8&amp;s=books&amp;qid=1219867334&amp;sr=8-2">Freakonomics book</a>.  If you've not heard about the book the authors, an economist who dislikes Maths and a Journalist, ask unusual questions about everyday situations - does abortion effect crime rates, are estate agents telling you the truth about your house price and what really makes a good parent.  On the back of such an enjoyable read I also subscribed to their <a href="http://freakonomics.blogs.nytimes.com/">blog</a>, which although a little post heavy, does offer some good insights.  One that I found of particular interest was regarding <a href="http://freakonomics.blogs.nytimes.com/2008/08/22/media-bias-olympics-edition/">Olympic medals</a>.  Comically the American media used the total number of the medals as the medal table ranking system, and not the official number of golds, then silver, then bronze system.  This of course ensured they *beat* the Chinese.</p>
<p>Of more interest though was this <a href="http://freakonomics.blogs.nytimes.com/2008/08/27/olympic-wrap-up-jamaica-wins-aussies-are-5th-us-ranks-33rd-china-is-47th/">later post</a> about calculating the medal table by country population (I have long used this argument to explain why America gets more medals than us).  Pretty interesting I thought, but of course this goes deeper.  There are other factors that would truly measure a nations athletic talent - national expenditure on sport and maybe even climate are two that spring to mind.</p>
<p>Australia, one of the best sporting nations, is still <a href="http://www.canberratimes.com.au/news/local/news/general/lottery-for-sports-draws-interest/1254923.aspx">considering a review of the way they fund sport</a> after such a dismal performance though.  Using the freakonomics chart they shouldn't be so disappointed (even if they did get beaten by New Zealand)!</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/22">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/22#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
      <media:content type="image/png" height="145" width="300" url="http://getfile8.posterous.com/getfile/files.posterous.com/import-ehvm/owBwiJashyqomlnfwgbgxGDtDoyddgudlIrfmGDBhikkFpevpFrnJGuHCaGB/media_httpwwwrowthebo_azpEl.png">
        <media:thumbnail height="145" width="300" url="http://getfile4.posterous.com/getfile/files.posterous.com/import-ehvm/owBwiJashyqomlnfwgbgxGDtDoyddgudlIrfmGDBhikkFpevpFrnJGuHCaGB/media_httpwwwrowthebo_azpEl.png.scaled500.png" />
      </media:content>
    <feedburner:origLink>http://rowtheboat.com/archives/22</feedburner:origLink></item>
    <item>
      <pubDate>Wed, 16 Jul 2008 19:14:00 -0700</pubDate>
      <title>ActiveRecord outside of Rails (and YAML)</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/wECnWE07I2Q/21</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/21</guid>
      <description>
        <![CDATA[<p>
	<p>Lately I've been working on a stock trading program that automatically trades and saves the results to a database (more details to follow in the not too distant future...)&nbsp; Naturally the choice of language was ruby, and for the front end, a rails application.&nbsp; Once coded, I bundled the stock trading program into the rails lib folder and reused the rails model for saving to the database.</p>
<p>I ran into a slight problem doing this though as ActiveRecord needs configuring manually outside the rails framework but I wanted to follow best practices and keep my code <a href="http://en.wikipedia.org/wiki/DRY">DRY</a>.  After a quick search I found a <a href="http://rails.aizatto.com/2007/05/21/activerecord-without-rails/">clean solution</a> that allows you to re-use the database.yml configuration file from the rails application:</p>
<div class="CodeRay">
  <div class="code"><pre>dbconfig = YAML::load(File.open('config/database.yml'))
ActiveRecord::Base.establish_connection(dbconfig[&quot;development&quot;])</pre></div>
</div>

<p>This snippet got me thinking about <a href="http://en.wikipedia.org/wiki/YAML">YAML</a> which I use a lot but have never played with in Ruby.  It turns out the YAML class is basically a hash that uses strings (gotcha not keys) to access a simple hierarchy:</p>
<div class="CodeRay">
  <div class="code"><pre>dbconfig = YAML::load(File.open('config/database.yml')) 
dbconfig[&quot;development&quot;][&quot;adapter&quot;] # = mysql [/sourcecode]</pre></div>
</div>

<p>All very simple stuff but if its never crossed your mind before, you yet again appreciate the elegance of ruby.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/21">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/21#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/21</feedburner:origLink></item>
    <item>
      <pubDate>Sat, 28 Jun 2008 15:56:00 -0700</pubDate>
      <title>Testing your rake tasks</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/vPg9jElj4gc/20</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/20</guid>
      <description>
        <![CDATA[<p>
	<p>This week I've been working on a client project where I needed to import a massive XML dataset into a database.&nbsp; The XML was non-standard and broke its own rules in several places.&nbsp; Consequently my rake task quickly become very complicated and I needed some tests to ensure I wasn't breaking previous work.&nbsp; This leads to a rather interesting question: how do you test rake tasks?&nbsp; After a bit of googling I found a <a href="http://blog.jayfields.com/2006/11/ruby-testing-rake-tasks.html">rather neat solution</a> of simply pulling out the rake code to a class.  For example, rather than:</p>
<div class="CodeRay">
  <div class="code"><pre>desc &quot;Import the XML&quot; 
task :import =&gt; :cleanup do   
  File.open(XML_FILE) do |file|     
  # Do importing here...   
end 
end</pre></div>
</div>

<p>do something more like</p>
<div class="CodeRay">
  <div class="code"><pre>desc &quot;Import the XML&quot; 
task :import =&gt; :cleanup do
   File.open(XML_FILE) do |file|
     XMLImporter.parse_lines(file)
   end 
end</pre></div>
</div>

<p>XMLImporter can then be tested in the normal way. Good ruby - clean and effective.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/20">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/20#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/20</feedburner:origLink></item>
    <item>
      <pubDate>Sat, 07 Jun 2008 17:36:00 -0700</pubDate>
      <title>Capistrano 2.3, Git and frozen MERB</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/oZu_SIBcCU8/19</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/19</guid>
      <description>
        <![CDATA[<p>
	<p>I had a few issues getting a <a href="http://capify.org/">Capistrano</a> file to work with <a href="http://git.or.cz/">Git</a> and frozen MERB the last few days.  The script below is how I finally cracked it and also has starters and stoppers for Memcached and my <a href="http://github.com/GeorgePalmer/backgroundrb_merb/tree/master">forked backgroundrb</a> for MERB.</p>
<div class="CodeRay">
  <div class="code"><pre># Basic deploy details
set :application, &quot;myapp&quot;
set :deploy_to, &quot;/var/www/apps/#{application}&quot;
set :mongrel_conf, &quot;#{deploy_to}/current/config/mongrel_cluster.yml&quot;

# Repository details
set :repository,  &quot;git@ipaddress:reposname.git&quot;
set :scm, &quot;git&quot;
set :scm_passphrase, &quot;&quot;
set :set_branch, &quot;origin/master&quot;
set :git_enable_submodules, 1

# Required to get git password prompt
default_run_options[:pty] = true

# Set the user account to use for deployment and running
set :user, &quot;deploy&quot;
set :use_sudo, false

# Server details
role :app, &quot;mysite.com&quot;
role :web, &quot;mysite.com&quot;
role :db,  &quot;mysite.com&quot;, :primary =&gt; true

desc &quot;Deploy to the production server&quot;
task :production do
role :app, &quot;mysite.com&quot;
role :web, &quot;mysite.com&quot;
role :db,  &quot;mysite.com&quot;, :primary =&gt; true
end

desc &quot;Change the database configuration file&quot;
task :after_update_code do
run &quot;mv #{release_path}/config/database.yml.production #{release_path}/config/database.yml&quot;

# Remove code we don't want on the server
run &quot;rm -rf #{release_path}/autotest&quot;
run &quot;rm #{release_path}/config/deploy.rb&quot;
run &quot;rm -rf #{release_path}/coverage&quot;
run &quot;rm -rf #{release_path}/spec&quot;
run &quot;rm -rf #{release_path}/stories&quot;

# Make config directory just readable
run &quot;chmod 700 #{release_path}/config&quot;
run &quot;chmod 400 #{release_path}/config/*&quot;
run &quot;chmod 700 #{release_path}/config/environments&quot;
run &quot;chmod 700 #{release_path}/config/initializers&quot;
run &quot;chmod 400 #{release_path}/config/environments/*&quot;

# Make frozen-merb bin file executable
run &quot;chmod 755 #{release_path}/framework/merb-more/merb-freezer/bin/frozen-merb&quot;
end

# Override db:migrate as it calls rails specific stuff
namespace :deploy do
desc &quot;Migrate the database&quot;
task :migrate, :roles =&gt; :db do
run &quot;cd #{release_path}; rake db:migrate MERB_ENV=production&quot;
end
end

# Override the default deploy options to use backgroundrb, memcached and mongrel
namespace :deploy do
namespace :backgroundrb do
desc &quot;Start backgroundrb&quot;
task :start, :roles =&gt; :app do
invoke_command &quot;cd /var/www/apps/bablo/current &amp;&amp; script/backgroundrb start -- -r production&quot;, :via =&gt; run_method
end
task :stop, :roles =&gt; :app do
invoke_command &quot;cd /var/www/apps/bablo/current &amp;&amp; script/backgroundrb stop&quot;, :via =&gt; run_method
end
end

namespace :memcached do
desc &quot;Start memcached&quot;
task :start, :roles =&gt; :app do
run &quot;memcached -l 127.0.0.1 -d -m 96 -p 17898&quot;
end
task :stop, :roles =&gt; :app do
run &quot;killall -s TERM memcached&quot;
end
end

namespace :mongrel do
desc &quot;Start mongrel&quot;
task :start, :roles =&gt; :app do
invoke_command &quot;cd /var/www/apps/bablo/current &amp;&amp; #{release_path}/framework/merb-more/merb-freezer/bin/frozen-merb -d -e production -c 5&quot;, :via =&gt; run_method
end
desc &quot;Stop mongrel&quot;
task :stop, :roles =&gt; :app do
invoke_command &quot;cd /var/www/apps/bablo/current &amp;&amp; #{release_path}/framework/merb-more/merb-freezer/bin/frozen-merb -K all&quot;, :via =&gt; run_method
end
end

desc &quot;Custom restart task for mongrel cluster&quot;
task :restart, :roles =&gt; :app, :except =&gt; { :no_release =&gt; true } do
deploy.backgroundrb.stop
deploy.memcached.stop
deploy.memcached.start
deploy.mongrel.stop
deploy.mongrel.start
deploy.backgroundrb.start # Doesn't work if straight after the stop
end

desc &quot;Custom start task for mongrel cluster&quot;
task :start, :roles =&gt; :app do
deploy.backgroundrb.start
deploy.memcached.start
deploy.mongrel.start
end

desc &quot;Custom stop task for mongrel cluster&quot;
task :stop, :roles =&gt; :app do
deploy.backgroundrb.stop
deploy.memcached.stop
deploy.mongrel.stop
end
end</pre></div>
</div>

<p>I've still not managed to get Git caching working with submodules, so any input on this would be appreciated (it seems to work fine without submodules).&nbsp;</p>
<p><strong>Updated:</strong> Fixed in Capistrano 2.4</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/19">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/19#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/19</feedburner:origLink></item>
    <item>
      <pubDate>Sat, 07 Jun 2008 17:23:00 -0700</pubDate>
      <title>MERB: The new rails?</title>
      <link>http://feeds.rowtheboat.com/~r/Rowtheboat/~3/Y-Nez-XEqYA/18</link>
      <guid isPermaLink="false">http://rowtheboat.com/archives/18</guid>
      <description>
        <![CDATA[<p>
	<p>I presented on <a href="http://merbivore.com/">MERB</a> last weekend at <a href="http://barcamp.org/BarCampLondon4">Barcamp London 4</a> I've been using the technology the last two months at Bablo so it was good to share my experience with other users.  With it being quite a bleeding edge technology there was just a small gang of us there but there was plenty of good discussion.  The talk I gave had a purposely provocative title but ended with a great quote by <a href="http://brainspl.at/">Ezra</a></p>
<blockquote class="posterous_medium_quote">more choices make the Ruby ecosystem a better place. So let&rsquo;s just stop with the Rails VS Merb stuff. How about people choose what framework they want to use based on the frameworks merits and features rather then religious arguments about how my framework can beat up your framework.</blockquote>
<p>I was then <a href="http://merbunity.com/news/24">flicking through some slides</a> from the recent RubyConf and was mightily impressed by a slide from the end of the MERB talk:</p>
<blockquote class="posterous_short_quote">It's consider a bug if:<br />* It's not documented <br />* MERB gets slower <br />* There's a public API change without prior deprecation in a timely manner</blockquote>
<p>It seems the guys at <a href="http://www.engineyard.com/">EngineYard</a> have a great attitude, not to mention a really well thought out framework.  I'm looking forward to using it fulltime.</p>
	
</p>

<p><a href="http://rowtheboat.com/archives/18">Permalink</a> 

	| <a href="http://rowtheboat.com/archives/18#comment">Leave a comment&nbsp;&nbsp;&raquo;</a>

</p>]]>
      </description>
      <posterous:author>
        <posterous:userImage>http://files.posterous.com/user_profile_pics/961500/katieWedding-me.jpg</posterous:userImage>
        <posterous:profileUrl>http://posterous.com/users/1lrZs0uLPFVn</posterous:profileUrl>
        <posterous:firstName>George</posterous:firstName>
        <posterous:lastName>Palmer</posterous:lastName>
        <posterous:nickName>GeorgeP</posterous:nickName>
        <posterous:displayName>George Palmer</posterous:displayName>
      </posterous:author>
    <feedburner:origLink>http://rowtheboat.com/archives/18</feedburner:origLink></item>
  </channel>
</rss>

