<?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.feedburner.com/~d/styles/itemcontent.css"?><rss version="2.0">
  <channel>
    <title>Bamboo Blog</title>
    <link>http://blog.new-bamboo.co.uk</link>
    <description>...</description>
    <language>en-us</language>
    <pubDate>Mon, 15 Jun 2009 18:10:36 +0000</pubDate>
    <lastBuildDate>Mon, 15 Jun 2009 18:10:36 +0000</lastBuildDate>
    <managingEditor>info@new-bamboo.co.uk (Damien Tanner)</managingEditor>
    <webMaster>info@new-bamboo.co.uk (Damien Tanner)</webMaster>
    
      <atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/newbamboo" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
        <title>We've moved!</title>
        <link>http://blog.new-bamboo.co.uk/2009/6/15/new-offices</link>
        <author>max@new-bamboo.co.uk (max)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/6/15/new-offices#disqus_thread</comments>
        <description><![CDATA[<img src='/assets/new_offices.jpg' alt='Our new offices' />
<p>After a long period of searching and deliberating, we have finally moved offices!</p>

<p>We have been looking for the right home over the last couple of years, and feel the new place is well worth the wait. We are now based on Exmouth Market, a lovely buzzing area close to Farringdon. There are plenty of reasons to swing past if you are near - not least excellent food at the stalls in the street. If you are around, feel free to pop up and say hi.</p>

<p>The full address is:</p>

<p>3rd Floor, Gensurco House, 46A Rosebery Avenue, London, EC1R 4RP (<a href='http://maps.google.co.uk/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=46A+Rosebery+Ave,+Islington,+London+EC1R,+UK&amp;sll=51.525861,-0.110207&amp;sspn=0.007877,0.019848&amp;ie=UTF8&amp;ll=51.525861,-0.110207&amp;spn=0.007877,0.019848&amp;z=16&amp;iwloc=A'>map</a>)</p>

<p>There are some photos of the place, and us moving in <a href='http://www.flickr.com/photos/ismasan/sets/72157619286836406/'>here</a>. Plus we&#8217;ve also make a few tweaks to <a href='http://new-bamboo.co.uk'>our website</a> which includes adding recent clients and some more information about how we work.</p>]]></description>
        <pubDate>Mon, 15 Jun 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/6/15/new-offices</guid>
      </item>
    
      <item>
        <title>Rails technology briefing event - London April 22nd</title>
        <link>http://blog.new-bamboo.co.uk/2009/4/16/rails-tech-briefing-event</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/4/16/rails-tech-briefing-event#disqus_thread</comments>
        <description><![CDATA[<p>Over the past year we have been working in collaboration with <a href='http://madebymany.co.uk'>Made by Many</a> on numerous projects. Most recently this has included the <a href='http://fwd.five.tv'>FiveFWD</a> site for <a href='http://www.five.tv'>Channel 5 Broadcasting</a>, which was deployed in partnership with <a href='http://www.uk.clara.net'>Claranet</a>.</p>

<p>On Wednesday 22nd of April we&#8217;re going to be holding a Ruby on Rails Technology Briefing session at Claranet&#8217;s offices (address below) in London.</p>

<p>The session will be an informal event designed for web developers, online publishers, technology strategists and business managers to come together to learn more about Ruby on Rails as a rapidly evolving framework and explore its relevance to doing business online.</p>

<p><strong>Key speeches for the evening include:</strong></p>

<ul>
<li>
<p>Rails Inside Claranet: Chris Cooke - Claranet&#8217;s Rails developer &amp; managed application hosting guru - talks about his experiences in developing Claranet&#8217;s (very large) Rails application – an internal system which is being used across all of our European offices.</p>
</li>

<li>
<p>Making the Business Case for Rails: Made by Many co-founder talks about his experiences in selling Rails as a strategic asset to both internal stakeholders and clients.</p>
</li>

<li>
<p>Best Practice for deploying and managing Rails: Laurie Young from New Bamboo will talk about their experiences in deploying and managing one of Channel Five&#8217;s largest websites – and recommends best practice for managing Rails in hosted environments.</p>
</li>
</ul>

<p>&#8230;and importantly, drinks will follow the speeches in a bar around the corner.</p>

<p><strong>Event Details:</strong></p>

<p>Claranet Ltd, 21 Southampton Row, London WC1B 5HA Wednesday 22nd April</p>

<ul>
<li>6:30 Arrive, registration &amp; refreshments</li>

<li>7:00 Welcome &amp; Briefing</li>

<li>8:30 Q&amp;A</li>

<li>8:45 Drinks</li>
</ul>

<p>Seats are limited, register here: <a href='http://www.uk.clara.net/rails-tech-briefing'>http://www.uk.clara.net/rails-tech-briefing</a></p>]]></description>
        <pubDate>Thu, 16 Apr 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/4/16/rails-tech-briefing-event</guid>
      </item>
    
      <item>
        <title>Upgrading to Rails 2.3 with Git</title>
        <link>http://blog.new-bamboo.co.uk/2009/3/17/upgrading-rails-with-git</link>
        <author>olly@new-bamboo.co.uk (olly)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/3/17/upgrading-rails-with-git#disqus_thread</comments>
        <description><![CDATA[<p>So with the <a href='http://weblog.rubyonrails.org/2009/3/16/rails-2-3-templates-engines-rack-metal-much-more'>release of Rails 2.3</a> hot off the presses, it is time to start upgrading some of our rails applications. While the process has got much easier since the 0.x days, some files still need to be updated in the application itself.</p>
<img class='picture' src='/assets/rails-git-upgrade-terminal.png' />
<p>To help with the upgrade path the <tt>rails</tt> command prompts you to overwrite, keep or diff with the latest files. However this isn&#8217;t always what you want - quite often you want to merge changes instead of just overwriting them.</p>

<p>So I thought I would experiment with a new method, using the power of git to handle the merging of the old and new. First I created a branch to do the upgrade in, so that I can commit and make sure everything is working before I merge back into master.</p>

<pre><code>git branch rails_upgrade</code></pre>

<p>Secondly, I create a <a href='http://tim.dysinger.net/2008/03/31/creating-blank-git-branches/'>blank branch</a> imaginatively named <tt>fresh_rails_2.3.2</tt>. Then we remove all of the files from this branch to give us a blank slate.</p>

<pre><code>git symbolic-ref HEAD refs/heads/fresh_rails_2.3.2
git rm -r --cached .
rm -rf *
rm .*</code></pre>

<p>The next step is to generate a blank rails application (assuming you have the latest gems installed) and commit it to our detached branch.</p>

<pre><code>rails .
git commit -m &#39;Generate Rails v.2.3.2 skeleton.&#39;</code></pre>

<p>Now we have the latest and greatest files we can merge back into our <tt>rails_upgrade</tt> branch.</p>

<pre><code>git co rails_upgrade
git merge fresh_rails_2.3.2</code></pre>

<p>Unless you have barely touched your application since it was previously generated, there should now be a number of conflicts that you need to resolve. Fix each one in turn and add it to the staging area. For example:</p>

<pre><code>mate config/environment.rb
# Fix the conflicts and save
git add config/environment.rb</code></pre>

<p><strong>Note:</strong> If you have previously ignored any files from you application (<tt>config/database.yml</tt> for example) you will need to remove them from the staging area otherwise they will be added back into your project.</p>

<pre><code>git reset config/database.yml</code></pre>

<p>With the merge complete you can run your tests to make sure the merge was a success. If everything passes you can now merge the upgrade back into the master branch.</p>

<pre><code>rake spec
rake features
git commit
git co master
git merge rails_upgrade</code></pre>
<img class='picture' src='/assets/rails-git-upgrade-gitx.png' />
<p><a href='http://gitx.frim.nl/'>GitX</a> shows my history looking like this. Overall I think the experiment worked quite well and it was defiantly useful to be able to manually pick and choose which bits of the new files I wanted.</p>]]></description>
        <pubDate>Tue, 17 Mar 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/3/17/upgrading-rails-with-git</guid>
      </item>
    
      <item>
        <title>Fixing The Web With Stylish</title>
        <link>http://blog.new-bamboo.co.uk/2009/2/27/fixing-the-web-with-stylish</link>
        <author>gwyn@new-bamboo.co.uk (gwyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/2/27/fixing-the-web-with-stylish#disqus_thread</comments>
        <description><![CDATA[<img class='picture' src='/assets/stylish_before_250.png' />
<p>Design becomes beautiful when there&#8217;s nothing left to take away. Google understands this; too many other web applications don&#8217;t.</p>

<p>Many apps grow new features simply to differentiate themselves from the competition, or to increase their target market by supporting multiple conflicting workflows. But every single thing I don&#8217;t need detracts from the usability of the things I do. In the past I&#8217;ve had to abandon apps that do more than I need, but not any more.</p>

<p>Now I can fix them.</p>

<p>Stylish is a Firefox plugin that lets you write your own css per-site, overriding the existing css. This means you can set display:none on everything unnecessary.</p>

<p>It&#8217;s a <a href='https://addons.mozilla.org/en-US/firefox/addon/2108'>one-click install</a>. You&#8217;ll also need <a href='https://addons.mozilla.org/en-US/firefox/addon/1843'>Firebug</a> to assist in identifying elements you want to block.</p>

<p>Once it&#8217;s installed, open firebug and point to the offending feature. Figure out a way to identify it with a CSS selector. In some cases this&#8217;ll be easy - it might have a classname like AnnoyingFeature. In others you&#8217;ll have to use CSS3 selectors. For example, you can do:</p>
<div class='gist' id='gist-71410'>                    <div class='gist-file'>        <div class='gist-data gist-syntax'>                                          <div class='gist-highlight'><pre><div class='line' id='LC1'><span class='nt'>label</span><span class='o'>[</span><span class='nt'>id</span><span class='o'>*=</span><span class='s2'>&quot;requested_by_id&quot;</span><span class='o'>]</span> <span class='p'>{</span><span class='k'>display</span><span class='o'>:</span><span class='k'>none</span><span class='p'>}</span></div></pre></div>                  </div>        <div class='gist-meta'>          <a href='http://gist.github.com/raw/71410/f3a57a5f33340f146802b9265539b45eddfab6ad/gistfile1.css' style='float:right;'>view raw</a>          <a href='http://gist.github.com/71410'>This Gist</a> brought to you by <a href='http://github.com'>GitHub</a>.        </div>      </div>          </div>
<p>This matches every Label element with an &#8216;id&#8217; attribute <em>containing</em> &#8216;requested_by_id&#8217;. This is helpful if the site&#8217;s using ids like &#8216;story_34524_requested_by_id&#8217;.</p>

<p>But what about if you had this?</p>
<div class='gist' id='gist-71411'>                    <div class='gist-file'>        <div class='gist-data gist-syntax'>                                          <div class='gist-highlight'><pre><div class='line' id='LC1'><span class='nt'>&lt;div&gt;</span></div><div class='line' id='LC2'>&nbsp;&nbsp;<span class='nt'>&lt;span</span> <span class='na'>class=</span><span class='s'>&#39;annoying&#39;</span><span class='nt'>&gt;</span>annoyance 1<span class='nt'>&lt;/span&gt;</span></div><div class='line' id='LC3'>&nbsp;&nbsp;<span class='nt'>&lt;span&gt;</span>annoyance 2<span class='nt'>&lt;/span&gt;</span></div><div class='line' id='LC4'><span class='nt'>&lt;/div&gt;</span></div></pre></div>                  </div>        <div class='gist-meta'>          <a href='http://gist.github.com/raw/71411/178b85f5b82980376382e18907456febe3ad2f9a/gistfile1.htm' style='float:right;'>view raw</a>          <a href='http://gist.github.com/71411'>This Gist</a> brought to you by <a href='http://github.com'>GitHub</a>.        </div>      </div>          </div>
<p>It&#8217;d be nice to block the parent div, thereby taking out &#8216;annoyance 2&#8217;, but you can&#8217;t; <a href='http://adityamukherjee.com/geekaholic/archives/513'>there's no "parent selector"</a> even in CSS3. Greasemonkey could fix this; Stylish can&#8217;t.</p>

<p>While you&#8217;re there, you can also fix other usability issues like textboxes being too small - it&#8217;s as simple as &#8216;textarea {height:300px}&#8217;.</p>
<img class='picture' src='/assets/stylish_after_250.png' />
<p>Stylish has a live preview, and it&#8217;s only one click to turn off custom styles, so you can afford to be both aggressive and experimental.</p>

<p>You can share custom styles. I&#8217;d like to suggest that styles solely designed to remove distractions, rather than &#8216;re-skin&#8217; sites, include &#8216;Simplify&#8217; in the name to make them easy to find. Over the next few days I&#8217;ll be redesigning some of the web tools I use frequently to make them more of a joy to work with.</p>]]></description>
        <pubDate>Fri, 27 Feb 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/2/27/fixing-the-web-with-stylish</guid>
      </item>
    
      <item>
        <title>Working the Mic</title>
        <link>http://blog.new-bamboo.co.uk/2009/2/23/working-the-mic</link>
        <author>gwyn@new-bamboo.co.uk (gwyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/2/23/working-the-mic#disqus_thread</comments>
        <description><![CDATA[<img class='picture' src='/assets/gwyn_mic.jpg' />
<p>Remote scrums are hard work. The first clue that something was wrong was when our sole remote coder volunteered to stop attending the scrum because he didn&#8217;t find it useful.</p>

<p>This is a danger sign. Further probing determined that it wasn&#8217;t that the format was wrong; it was just that he couldn&#8217;t hear anyone. We&#8217;re using Skype video calls, with the laptop sitting on a chair in its place in the circle, using the Macbook&#8217;s built-in mic and speakers. While the mic&#8217;s okay for 1:1 conferencing, it&#8217;s not sensitive enough for people standing a couple of metres away.</p>

<p>No problem; I bought a £15 <a href='http://www.amazon.co.uk/Skytronic-952-194-BOUNDARY-MICROPHONE/dp/B000LAX44I/ref=sr_1_1?ie=UTF8&amp;s=electronics&amp;qid=1235391132&amp;sr=8-1'>omnidirectional mic</a> from Amazon. But now he couldn&#8217;t hear us at all.</p>

<p>It turns out that Macbooks - even the new ones - don&#8217;t have a mic-in port. They have a line-in port. Microphones work at much lower voltage, and can&#8217;t be used there.</p>

<p>Okay. We have a Plantronics headset adapter that inputs a stereo jack and outputs USB. It doesn&#8217;t work with the new mic, though.</p>

<p>So we needed a preamp. I bought an iMic; £30 of glossy white plastic that also inputs a stereo jack and outputs USB. It even has a &#8220;mic mode&#8221; switch that activates the internal preamp. Should be perfect, right?</p>

<p>It doesn&#8217;t work. This omnidirectional conference mic is not the same as the 1:1 headsets that the iMic is designed for; it&#8217;s a &#8216;boundary microphone&#8217; that provides a weak signal even for a microphone. It turns out that microphones are described in terms of impedance, measured in ohms, and divided into &#8216;low-impedance&#8217; (±1000Ω) and &#8216;high-impedance&#8217; (±10000Ω). A high-impedance mic, like ours, won&#8217;t work with the iMic and requires a more powerful preamp.</p>

<p>I&#8217;m still chasing that down. I&#8217;m not looking for production-quality here, just something good enough for intelligible speech.</p>

<p>In the meantime, we&#8217;re passing the mic around - it works well enough if we speak directly into it, just not in &#8216;omnidirectional&#8217; mode. This actually has some benefits, since it acts as a token - only the person with the mic speaks - and makes us all feel like radio announcers. We&#8217;re hoping to get a USB speakerphone, which ought to be fully integrated, soon.</p>

<p>In 2009, setting up a video conference should not require an audio engineer. But it does.</p>]]></description>
        <pubDate>Mon, 23 Feb 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/2/23/working-the-mic</guid>
      </item>
    
      <item>
        <title>Migrating our blog from Mephisto to git and Jekyll</title>
        <link>http://blog.new-bamboo.co.uk/2009/2/20/migrating-from-mephisto-to-jekyll</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/2/20/migrating-from-mephisto-to-jekyll#disqus_thread</comments>
        <description><![CDATA[<p>Since we started this blog it has been running on the same <a href='http://mephistoblog.com/'>Mephisto</a> release (on Rails 1.2). Aside from being far from easy to upgrade and migrate the data to the latest version of of Mephisto, we were getting pretty bad comment spam and syntax highlighting was frustrating.</p>

<p>Classic blogging apps were never quite up to the job in my opinion. The editing interface is just a textarea or a broken wysiwyg editor and versioning is shoehorned into a database. Since <a href='http://blog.new-bamboo.co.uk/2008/3/11/github-textmate-wiki-bundle-giki'>moving our wiki</a> from an app to simply markdown files on <a href='http://github.com/'>Github</a> we haven&#8217;t looked back, so why not the blog?</p>

<p>The <a href='http://github.com/mojombo/jekyll'>Jekyll</a> is a neat little static site generator which can also help with maintaining a blog. Migrating over required the use of a few scripts. Firstly to dump the old blog posts in Mephisto into markdown files, and secondly for comments. Having a statically generated site also means comments need to be hosted somewhere else. We went with Disqus which provides a pretty good js commenting service, and used a script to migrate the Mephisto comments over there.</p>

<p>I also thought it would be nice to have all the code snippets hosted at <a href='http://gists.github.com'>gists.github.com</a>, but didn&#8217;t want to include them all using Javascript, so I wrote a little liquid filter to pull them directly into a page when the site is generated. These tweaks are all in the <a href='http://github.com/newbamboo/jekyll'>newbamboo jekyll branch</a>.</p>

<p>Our Jekyll has the following tweaks</p>

<ul>
<li>Date format in urls to match Mephisto so the urls didn&#8217;t break</li>

<li>Some extra lists of posts for use in the templates (our front page has 3 full text posts, followed by 5 titles and finally an archive page with the titles of all posts)</li>

<li><code>date_to_rfc2822</code> for use in the rss feed</li>
</ul>

<h2 id='jekyll'>Jekyll</h2>

<p>First step is to setup <a href='http://github.com/mojombo/jekyll'>Jekyll</a> locally, which is all covered by the readme in the repo. You&#8217;ll see that posts are simply .markdown files in the _posts directory, so if you&#8217;re migrating an old blog you&#8217;ll need to dump all your data there.</p>

<p>When you run the jekyll command inside your blog&#8217;s directly it&#8217;ll then convert all the markdown files in the _posts dir into html, using the little bit of yaml at the top to set the layout and provide variables for the liquid layout (e.g. we have put author in the yaml of all of our articles). If you then put a .html file in the root of your blog dir it will get run through the liquid generator after the posts have been generated, so it has access to the list of all posts etc.. This is how the <a href='http://gist.github.com/67168'>index.html</a> and <a href='http://gist.github.com/67170'>archive.html</a> pages are done.</p>

<p>Layouts live in the _layouts dir. Our <a href='http://gist.github.com/67171'>default.html</a> and <a href='http://gist.github.com/67174'>post.html</a> layouts were left pretty much untouched from our Mephisto theme.</p>

<h2 id='migrate_articles_from_mephisto'>Migrate articles from Mephisto</h2>

<p>The following script is a quick hack to write all of the articles from our old MySQL Mephisto database. It&#8217;s going to need tweaking if you&#8217;re going to use it with anything else.</p>
<div class='gist' id='gist-65418'>                    <div class='gist-file'>        <div class='gist-data gist-syntax'>                                          <div class='gist-highlight'><pre><div class='line' id='LC1'><span class='n'>articles</span> <span class='o'>=</span> <span class='no'>Article</span><span class='o'>.</span><span class='n'>find</span><span class='p'>(</span><span class='ss'>:all</span><span class='p'>)</span></div><div class='line' id='LC2'>&nbsp;</div><div class='line' id='LC3'><span class='n'>articles</span><span class='o'>.</span><span class='n'>each</span> <span class='k'>do</span> <span class='o'>|</span><span class='n'>article</span><span class='o'>|</span></div><div class='line' id='LC4'>&nbsp;&nbsp;<span class='n'>published_at</span> <span class='o'>=</span> <span class='s2'>&quot;</span><span class='si'>#{</span><span class='n'>published_at</span><span class='o'>.</span><span class='n'>year</span><span class='si'>}</span><span class='s2'>-</span><span class='si'>#{</span><span class='n'>published_at</span><span class='o'>.</span><span class='n'>month</span><span class='si'>}</span><span class='s2'>-</span><span class='si'>#{</span><span class='n'>published_at</span><span class='o'>.</span><span class='n'>day</span><span class='si'>}</span><span class='s2'>&quot;</span></div><div class='line' id='LC5'>&nbsp;&nbsp;<span class='n'>author</span> <span class='o'>=</span> <span class='n'>article</span><span class='o'>.</span><span class='n'>user</span><span class='o'>.</span><span class='n'>login</span></div><div class='line' id='LC6'>&nbsp;&nbsp;<span class='n'>permalink</span> <span class='o'>=</span> <span class='n'>article</span><span class='o'>.</span><span class='n'>permalink</span></div><div class='line' id='LC7'>&nbsp;&nbsp;<span class='n'>title</span> <span class='o'>=</span> <span class='n'>article</span><span class='o'>.</span><span class='n'>title</span></div><div class='line' id='LC8'>&nbsp;&nbsp;<span class='n'>excerpt</span> <span class='o'>=</span> <span class='n'>article</span><span class='o'>.</span><span class='n'>excerpt</span></div><div class='line' id='LC9'>&nbsp;&nbsp;<span class='n'>body</span> <span class='o'>=</span> <span class='n'>article</span><span class='o'>.</span><span class='n'>body</span></div><div class='line' id='LC10'>&nbsp;&nbsp;<span class='n'>fp</span> <span class='o'>=</span> <span class='no'>File</span><span class='o'>.</span><span class='n'>open</span><span class='p'>(</span><span class='s2'>&quot;_posts/</span><span class='si'>#{</span><span class='s1'>&#39;draft-&#39;</span> <span class='k'>if</span> <span class='n'>article</span><span class='o'>.</span><span class='n'>published_at</span><span class='o'>.</span><span class='n'>nil?</span><span class='si'>}#{</span><span class='n'>published_at</span><span class='si'>}</span><span class='s2'>-</span><span class='si'>#{</span><span class='n'>permalink</span><span class='si'>}</span><span class='s2'>.markdown&quot;</span><span class='p'>,</span> <span class='s1'>&#39;w&#39;</span><span class='p'>)</span></div><div class='line' id='LC11'>&nbsp;&nbsp;<span class='n'>fp</span><span class='o'>.</span><span class='n'>write</span><span class='p'>(</span><span class='sx'>%&quot;---</span></div><div class='line' id='LC12'><span class='sx'>layout: post</span></div><div class='line' id='LC13'><span class='sx'>title: \\&quot;</span><span class='c1'>#{title}\\&quot;</span></div><div class='line' id='LC14'><span class='n'>author</span><span class='p'>:</span> <span class='c1'>#{author}</span></div><div class='line' id='LC15'><span class='o'>---</span></div><div class='line' id='LC16'><span class='c1'>#{excerpt}</span></div><div class='line' id='LC17'>&nbsp;</div><div class='line' id='LC18'><span class='c1'>#{body}&quot;)</span></div><div class='line' id='LC19'>&nbsp;&nbsp;<span class='n'>fp</span><span class='o'>.</span><span class='n'>close</span></div><div class='line' id='LC20'><span class='k'>end</span></div></pre></div>                  </div>        <div class='gist-meta'>          <a href='http://gist.github.com/raw/65418/f8a82e3091a77e42c4c0035ddfc796ad6e29b64d/migrate_articles_from_mephisto_markdown_files.rb' style='float:right;'>view raw</a>          <a href='http://gist.github.com/65418'>This Gist</a> brought to you by <a href='http://github.com'>GitHub</a>.        </div>      </div>          </div>
<h2 id='migrate_comments_from_mephisto_to_disqus'>Migrate comments from Mephisto to Disqus</h2>

<p>This script was modified a bit from the original. It&#8217;s a bit of a kludge because Disqus doesn&#8217;t allow you to delete comments via the API, and they only just added a feature in the admin to remove a site from your account. You may find yourself running this several times before you get that single clean import.</p>
<div class='gist' id='gist-65380'>                    <div class='gist-file'>        <div class='gist-data gist-syntax'>                                          <div class='gist-highlight'><pre><div class='line' id='LC1'><span class='c1'># Copyright 2009 Michael Ivey, released to public domain</span></div><div class='line' id='LC2'><span class='c1'># Disqus guts lifted from http://github.com/squeejee/disqus-sinatra-importer/tree/master</span></div><div class='line' id='LC3'><span class='c1'># I wanted it to run from MySQL and command line, instead of a Sinatra app</span></div><div class='line' id='LC4'>&nbsp;</div><div class='line' id='LC5'><span class='nb'>require</span> <span class='s1'>&#39;rubygems&#39;</span></div><div class='line' id='LC6'><span class='nb'>require</span> <span class='s1'>&#39;rest_client&#39;</span></div><div class='line' id='LC7'><span class='nb'>require</span> <span class='s1'>&#39;json&#39;</span></div><div class='line' id='LC8'><span class='nb'>require</span> <span class='s1'>&#39;sequel&#39;</span></div><div class='line' id='LC9'>&nbsp;</div><div class='line' id='LC10'><span class='n'>disqus_url</span> <span class='o'>=</span> <span class='s1'>&#39;http://disqus.com/api&#39;</span></div><div class='line' id='LC11'><span class='n'>user_api_key</span> <span class='o'>=</span> <span class='s1'>&#39;&#39;</span></div><div class='line' id='LC12'><span class='n'>forum_shortname</span> <span class='o'>=</span> <span class='s1'>&#39;myblog&#39;</span></div><div class='line' id='LC13'><span class='n'>current_blog_base_url</span> <span class='o'>=</span> <span class='s1'>&#39;http://myblog&#39;</span></div><div class='line' id='LC14'><span class='n'>db</span> <span class='o'>=</span> <span class='s1'>&#39;mephisto_production&#39;</span></div><div class='line' id='LC15'><span class='n'>db_user</span> <span class='o'>=</span> <span class='s1'>&#39;root&#39;</span></div><div class='line' id='LC16'>&nbsp;</div><div class='line' id='LC17'><span class='n'>resource</span> <span class='o'>=</span> <span class='no'>RestClient</span><span class='o'>::</span><span class='no'>Resource</span><span class='o'>.</span><span class='n'>new</span> <span class='n'>disqus_url</span></div><div class='line' id='LC18'><span class='n'>forums</span> <span class='o'>=</span> <span class='no'>JSON</span><span class='o'>.</span><span class='n'>parse</span><span class='p'>(</span><span class='n'>resource</span><span class='o'>[</span><span class='s1'>&#39;/get_forum_list?user_api_key=&#39;</span><span class='o'>+</span><span class='n'>user_api_key</span><span class='o'>].</span><span class='n'>get</span><span class='p'>)</span></div><div class='line' id='LC19'><span class='n'>forum_id</span> <span class='o'>=</span> <span class='n'>forums</span><span class='o'>[</span><span class='s2'>&quot;message&quot;</span><span class='o'>].</span><span class='n'>select</span> <span class='p'>{</span><span class='o'>|</span><span class='n'>forum</span><span class='o'>|</span> <span class='n'>forum</span><span class='o'>[</span><span class='s2'>&quot;shortname&quot;</span><span class='o'>]==</span><span class='n'>forum_shortname</span><span class='p'>}</span><span class='o'>[</span><span class='mi'>0</span><span class='o'>][</span><span class='s2'>&quot;id&quot;</span><span class='o'>]</span></div><div class='line' id='LC20'><span class='n'>forum_api_key</span> <span class='o'>=</span> <span class='no'>JSON</span><span class='o'>.</span><span class='n'>parse</span><span class='p'>(</span><span class='n'>resource</span><span class='o'>[</span><span class='s1'>&#39;/get_forum_api_key?user_api_key=&#39;</span><span class='o'>+</span><span class='n'>user_api_key</span><span class='o'>+</span><span class='s1'>&#39;&amp;forum_id=&#39;</span><span class='o'>+</span><span class='n'>forum_id</span><span class='o'>].</span><span class='n'>get</span><span class='p'>)</span><span class='o'>[</span><span class='s2'>&quot;message&quot;</span><span class='o'>]</span></div><div class='line' id='LC21'>&nbsp;</div><div class='line' id='LC22'><span class='n'>db</span> <span class='o'>=</span> <span class='no'>Sequel</span><span class='o'>.</span><span class='n'>mysql</span><span class='p'>(</span><span class='n'>db</span><span class='p'>,</span> <span class='ss'>:user</span> <span class='o'>=&gt;</span> <span class='n'>db_user</span><span class='p'>,</span> <span class='ss'>:host</span> <span class='o'>=&gt;</span> <span class='s1'>&#39;localhost&#39;</span><span class='p'>)</span></div><div class='line' id='LC23'><span class='n'>query</span> <span class='o'>=</span> <span class='s2'>&quot;SELECT comments.body, comments.author, comments.author_email, comments.created_at comment_created_at, articles.title, articles.permalink, articles.published_at article_published_at FROM contents AS comments LEFT JOIN contents AS articles ON comments.article_id = articles.id WHERE comments.type = &#39;Comment&#39; AND comments.approved = 1 AND articles.published_at IS NOT NULL ORDER BY comments.created_at DESC&quot;</span></div><div class='line' id='LC24'>&nbsp;</div><div class='line' id='LC25'><span class='n'>db</span><span class='o'>[</span><span class='n'>query</span><span class='o'>].</span><span class='n'>each</span> <span class='k'>do</span> <span class='o'>|</span><span class='n'>comment</span><span class='o'>|</span></div><div class='line' id='LC26'>&nbsp;&nbsp;<span class='n'>article_url</span> <span class='o'>=</span> <span class='s2'>&quot;</span><span class='si'>#{</span><span class='n'>current_blog_base_url</span><span class='si'>}</span><span class='s2'>/</span><span class='si'>#{</span><span class='n'>comment</span><span class='o'>[</span><span class='ss'>:article_published_at</span><span class='o'>].</span><span class='n'>year</span><span class='si'>}</span><span class='s2'>/</span><span class='si'>#{</span><span class='n'>comment</span><span class='o'>[</span><span class='ss'>:article_published_at</span><span class='o'>].</span><span class='n'>month</span><span class='si'>}</span><span class='s2'>/</span><span class='si'>#{</span><span class='n'>comment</span><span class='o'>[</span><span class='ss'>:article_published_at</span><span class='o'>].</span><span class='n'>day</span><span class='si'>}</span><span class='s2'>/</span><span class='si'>#{</span><span class='n'>comment</span><span class='o'>[</span><span class='ss'>:permalink</span><span class='o'>]</span><span class='si'>}</span><span class='s2'>&quot;</span></div><div class='line' id='LC27'>&nbsp;&nbsp;</div><div class='line' id='LC28'>&nbsp;&nbsp;<span class='n'>thread</span> <span class='o'>=</span> <span class='no'>JSON</span><span class='o'>.</span><span class='n'>parse</span><span class='p'>(</span><span class='n'>resource</span><span class='o'>[</span><span class='s1'>&#39;/get_thread_by_url?forum_api_key=&#39;</span><span class='o'>+</span><span class='n'>forum_api_key</span><span class='o'>+</span><span class='s1'>&#39;&amp;url=&#39;</span><span class='o'>+</span><span class='n'>article_url</span><span class='o'>].</span><span class='n'>get</span><span class='p'>)</span><span class='o'>[</span><span class='s2'>&quot;message&quot;</span><span class='o'>]</span></div><div class='line' id='LC29'>&nbsp;&nbsp;</div><div class='line' id='LC30'>&nbsp;&nbsp;<span class='c1'># If a Disqus thread is not found with the current url, create a new thread and add the url.</span></div><div class='line' id='LC31'>&nbsp;&nbsp;<span class='k'>if</span> <span class='n'>thread</span><span class='o'>.</span><span class='n'>nil?</span>  </div><div class='line' id='LC32'>&nbsp;&nbsp;&nbsp;&nbsp;<span class='n'>thread</span> <span class='o'>=</span> <span class='no'>JSON</span><span class='o'>.</span><span class='n'>parse</span><span class='p'>(</span><span class='n'>resource</span><span class='o'>[</span><span class='s1'>&#39;/thread_by_identifier&#39;</span><span class='o'>].</span><span class='n'>post</span><span class='p'>(</span><span class='ss'>:forum_api_key</span> <span class='o'>=&gt;</span> <span class='n'>forum_api_key</span><span class='p'>,</span> <span class='ss'>:identifier</span> <span class='o'>=&gt;</span> <span class='n'>comment</span><span class='o'>[</span><span class='ss'>:title</span><span class='o'>]</span><span class='p'>,</span> <span class='ss'>:title</span> <span class='o'>=&gt;</span> <span class='n'>comment</span><span class='o'>[</span><span class='ss'>:title</span><span class='o'>]</span><span class='p'>))</span><span class='o'>[</span><span class='s2'>&quot;message&quot;</span><span class='o'>][</span><span class='s2'>&quot;thread&quot;</span><span class='o'>]</span></div><div class='line' id='LC33'>&nbsp;&nbsp;&nbsp;&nbsp;</div><div class='line' id='LC34'>&nbsp;&nbsp;&nbsp;&nbsp;<span class='c1'># Update the Disqus thread with the current article url</span></div><div class='line' id='LC35'>&nbsp;&nbsp;&nbsp;&nbsp;<span class='n'>resource</span><span class='o'>[</span><span class='s1'>&#39;/update_thread&#39;</span><span class='o'>].</span><span class='n'>post</span><span class='p'>(</span><span class='ss'>:forum_api_key</span> <span class='o'>=&gt;</span> <span class='n'>forum_api_key</span><span class='p'>,</span> <span class='ss'>:thread_id</span> <span class='o'>=&gt;</span> <span class='n'>thread</span><span class='o'>[</span><span class='s2'>&quot;id&quot;</span><span class='o'>]</span><span class='p'>,</span> <span class='ss'>:url</span> <span class='o'>=&gt;</span> <span class='n'>article_url</span><span class='p'>)</span> </div><div class='line' id='LC36'>&nbsp;&nbsp;<span class='k'>end</span></div><div class='line' id='LC37'>&nbsp;&nbsp;</div><div class='line' id='LC38'>&nbsp;&nbsp;<span class='c1'># Import posts here</span></div><div class='line' id='LC39'>&nbsp;&nbsp;<span class='k'>if</span> <span class='n'>resource</span><span class='o'>[</span><span class='s1'>&#39;/create_post&#39;</span><span class='o'>].</span><span class='n'>post</span><span class='p'>(</span><span class='ss'>:forum_api_key</span> <span class='o'>=&gt;</span> <span class='n'>forum_api_key</span><span class='p'>,</span> <span class='ss'>:thread_id</span> <span class='o'>=&gt;</span> <span class='n'>thread</span><span class='o'>[</span><span class='s2'>&quot;id&quot;</span><span class='o'>]</span><span class='p'>,</span> <span class='ss'>:message</span> <span class='o'>=&gt;</span> <span class='n'>comment</span><span class='o'>[</span><span class='ss'>:body</span><span class='o'>]</span><span class='p'>,</span> <span class='ss'>:author_name</span> <span class='o'>=&gt;</span> <span class='n'>comment</span><span class='o'>[</span><span class='ss'>:author</span><span class='o'>]</span><span class='p'>,</span> <span class='ss'>:author_email</span> <span class='o'>=&gt;</span> <span class='n'>comment</span><span class='o'>[</span><span class='ss'>:author_email</span><span class='o'>]</span><span class='p'>,</span> <span class='ss'>:created_at</span> <span class='o'>=&gt;</span> <span class='n'>comment</span><span class='o'>[</span><span class='ss'>:comment_created_at</span><span class='o'>].</span><span class='n'>strftime</span><span class='p'>(</span><span class='s2'>&quot;%Y-%m-%dT%H:%M&quot;</span><span class='p'>))</span></div><div class='line' id='LC40'>&nbsp;&nbsp;&nbsp;&nbsp;<span class='nb'>puts</span> <span class='s2'>&quot;Success: </span><span class='si'>#{</span><span class='n'>comment</span><span class='o'>[</span><span class='ss'>:author</span><span class='o'>]</span><span class='si'>}</span><span class='s2'> on </span><span class='si'>#{</span><span class='n'>comment</span><span class='o'>[</span><span class='ss'>:title</span><span class='o'>]</span><span class='si'>}</span><span class='s2'>&quot;</span></div><div class='line' id='LC41'>&nbsp;&nbsp;<span class='k'>else</span></div><div class='line' id='LC42'>&nbsp;&nbsp;&nbsp;&nbsp;<span class='nb'>puts</span> <span class='s2'>&quot;FAIL: </span><span class='si'>#{</span><span class='n'>comment</span><span class='o'>[</span><span class='ss'>:author</span><span class='o'>]</span><span class='si'>}</span><span class='s2'> on </span><span class='si'>#{</span><span class='n'>comment</span><span class='o'>[</span><span class='ss'>:title</span><span class='o'>]</span><span class='si'>}</span><span class='s2'>&quot;</span></div><div class='line' id='LC43'>&nbsp;&nbsp;<span class='k'>end</span></div><div class='line' id='LC44'><span class='k'>end</span></div><div class='line' id='LC45'>&nbsp;</div><div class='line' id='LC46'>&nbsp;</div></pre></div>                  </div>        <div class='gist-meta'>          <a href='http://gist.github.com/raw/65380/7f301f04f787e14b70688b4a8e8a916efa135bbb/mephisto_comments_to_disqus.rb' style='float:right;'>view raw</a>          <a href='http://gist.github.com/65380'>This Gist</a> brought to you by <a href='http://github.com'>GitHub</a>.        </div>      </div>          </div>
<h2 id='use_google_for_search'>Use Google for search</h2>

<p>With no database we need to handle search elsewhere as well. Google does a pretty good job, so we changed our search form to submit to site search, and then setup an nginx redirect (see next section) so any indexed searches would end up in the right place. See the search form in our <a href='http://gist.github.com/67168'>index.html</a> for how it&#8217;s done.</p>

<h2 id='nginx_redirects'>Nginx redirects</h2>

<p>Nginx is used to serve up the static site. We had to setup a several redirects to indexed pages from the Mephisto days still worked.</p>
<div class='gist' id='gist-65423'>                    <div class='gist-file'>        <div class='gist-data gist-syntax'>                                          <div class='gist-highlight'><pre><div class='line' id='LC1'>server {</div><div class='line' id='LC2'>&nbsp;&nbsp;listen 80;</div><div class='line' id='LC3'>&nbsp;&nbsp;server_name  blog.new-bamboo.co.uk;</div><div class='line' id='LC4'>&nbsp;</div><div class='line' id='LC5'>&nbsp;&nbsp;client_max_body_size 10M;</div><div class='line' id='LC6'>&nbsp;</div><div class='line' id='LC7'>&nbsp;&nbsp;root /usr/local/www/nb_blog_jekyll/_site;</div><div class='line' id='LC8'>&nbsp;</div><div class='line' id='LC9'>&nbsp;&nbsp;rewrite ^/archives/.* /archive permanent;</div><div class='line' id='LC10'>&nbsp;</div><div class='line' id='LC11'>&nbsp;&nbsp;location /search {</div><div class='line' id='LC12'>&nbsp;&nbsp;if ( $args ~ &quot;q=(.*)&quot; ) {</div><div class='line' id='LC13'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;set $args &quot;site=&amp;hl=en&amp;q=site%3Ablog.new-bamboo.co.uk+$1&quot;;    </div><div class='line' id='LC14'>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;rewrite ^ http://www.google.com/search permanent;</div><div class='line' id='LC15'>&nbsp;&nbsp;&nbsp;&nbsp;}</div><div class='line' id='LC16'>&nbsp;&nbsp;}</div><div class='line' id='LC17'>&nbsp;</div><div class='line' id='LC18'>&nbsp;&nbsp;location / {</div><div class='line' id='LC19'>&nbsp;&nbsp;&nbsp;&nbsp;if (-f $request_filename.html)       { rewrite (.*) $1.html break; }</div><div class='line' id='LC20'>&nbsp;&nbsp;}</div><div class='line' id='LC21'>&nbsp;</div><div class='line' id='LC22'>&nbsp;&nbsp;default_type  text/html;</div><div class='line' id='LC23'>}</div></pre></div>                </div>        <div class='gist-meta'>          <a href='http://gist.github.com/raw/65423/060f120448524b55b66b381a9e6af77fcbd273e9/nginx_jekyll.conf' style='float:right;'>view raw</a>          <a href='http://gist.github.com/65423'>This Gist</a> brought to you by <a href='http://github.com'>GitHub</a>.        </div>      </div>          </div>
<p>The search redirect is pretty interesting and took some fiddling to get right. It takes an old search url like &#8220;http://blog.new-bamboo.co.uk/search?q=rails&#8221; and redirects it to Google site search with the same query string.</p>]]></description>
        <pubDate>Fri, 20 Feb 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/2/20/migrating-from-mephisto-to-jekyll</guid>
      </item>
    
      <item>
        <title>Sixty-Second Scrum</title>
        <link>http://blog.new-bamboo.co.uk/2009/2/6/sixty-second-scrum</link>
        <author>gwyn@new-bamboo.co.uk (gwyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/2/6/sixty-second-scrum#disqus_thread</comments>
        <description><![CDATA[<p>Our Daily Scrums were taking over fifteen minutes. Even for eight people, that&#8217;s excessive; it wasted two whole developer-hours and meant that the day started at low energy.</p>

<p>We tried rephrasing the questions - &#8220;Of what you did yesterday, what gotchas do the rest of the team need to know about?&#8221;.</p>

<p>We tried aggressively intercepting conversations that should be taken offline, using postit notes as placeholders.</p>

<p>We tried &#8216;just trying to hurry&#8217;.</p>

<p>It turns out that all we needed was a ticking bomb. As the team&#8217;s scrum master, I start <a href='http://gwynmorfey.com/1min.mp3'>this mp3</a> playing through my phone&#8217;s speakers when each person starts speaking. It ticks - a reminder to <i>hustle</i>, plays a warning at 45 seconds, and sounds an alarm at 60 seconds. Being constantly aware of the time means that most people don&#8217;t hit the alarm.</p>

<p>It doesn&#8217;t make people speak much faster; it just makes them filter what they&#8217;re saying more effectively. More conversations are taken out of the daily scrum - sometimes even whole-team conversations, and sometimes they start immediately after the scrum - but this is preferable to extending the whole-team, mandatory standup.</p>

<p>So far this is working well; in the future, I hope that I can &#8216;forget&#8217; the timer and find that nothing changes.</p>]]></description>
        <pubDate>Fri, 06 Feb 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/2/6/sixty-second-scrum</guid>
      </item>
    
      <item>
        <title>Tourdust launches - stealthily</title>
        <link>http://blog.new-bamboo.co.uk/2009/1/28/tourdust-launches-stealthily</link>
        <author>max
@new-bamboo.co.uk (max
)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/1/28/tourdust-launches-stealthily#disqus_thread</comments>
        <description><![CDATA[<p>One of our latest client products has slipped stealthily from private beta to being open to the public, so I thought I&#8217;d let people know about its existence.</p>
<a href='http://tourdust.com' target='_blank'><img class='picture' src='http://blog.new-bamboo.co.uk/assets/2009/1/28/tourdust.jpg' alt='Tourdust' /></a>
<p><a href='http://tourdust.com'>Tourdust</a> is a website for independent tour operators to advertise their diverse and interesting experiences for free. It is centred around the kind of activities which the packaged holidays don&#8217;t offer; the real gems which lie slightly off the beaten track. Many of the people who run these tours have little access to a global audience, and building and promoting their own websites is often a problem.</p>

<p>I hope people will agree that the design is fantastic, it was produced by our long term Brighton pals <a href='http://clearleft.com'>Clearleft</a>.</p>

<p>The project was run in an Agile manner, and I think that it illustrates many of the advantages of working in this way. The feature set is refreshingly minimal, and this produces a much better user experience overall. We were constantly re-evaluating the priorities throughout, and anything which wasn&#8217;t critical to the site&#8217;s function was cut out.</p>

<p>I think this is a win-win situation for both our client, and for us. They have got the most concentrated features to meet the requirements of their mission statement, and we have been spared the pain (which so often occurs in software development) of building things you are sure are never going to be used.</p>

<p>So check it out, you might even find a good reason to go off on holiday!</p>]]></description>
        <pubDate>Wed, 28 Jan 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/1/28/tourdust-launches-stealthily</guid>
      </item>
    
      <item>
        <title>Writing On The Wall</title>
        <link>http://blog.new-bamboo.co.uk/2009/1/26/writing-on-the-wall</link>
        <author>gwyn@new-bamboo.co.uk (gwyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/1/26/writing-on-the-wall#disqus_thread</comments>
        <description><![CDATA[<p>The best CMS I have ever used is a thick black sharpie and a wall.</p>

<p>&#8220;When I walk in here, I don&#8217;t have to ask how it&#8217;s going&#8221;.</p>

<p>Our tech lead can walk in, scan the room, and see in seconds that we&#8217;re slightly ahead of schedule, that the performance data is good but needs checking, and that we won&#8217;t be deploying today until 4pm.</p>

<p>And because it&#8217;s easy, and it&#8217;s interesting, he <em>does</em>. &#8220;Write on the walls&#8221; is immune to the &#8220;ghost town&#8221; effect that afflicts most corporate intranets. Everyone likes drawing, and a bright red &#8220;WALL OF SPEED&#8221; is a much more fun way of communicating benchmark results than an Excel spreadsheet buried somewhere in source control.</p>

<p>It does need maintenance. If I walk past something that I know is out-of-date, I&#8217;ll tear it down. Every week we clean up the office more formally. We go through a fortune in stationery.</p>

<p>This only works, of course, because we&#8217;re all in the same room. For a while our team spent four days a week at the client site, and Fridays in the company office, but we&#8217;re so crippled without our War Room that we hardly move any more.</p>

<p>But we have to be anyway. Any web project needs constant, <em>constant</em> communication between graphic designers and developers, and instant messaging doesn&#8217;t cut it. There are few other areas in IT where the required specialties need such different mindsets, but are so dependent on each other. Sitting side-by-side takes all the heat out of a suggestion of web-safe fonts where an email could be taken as a challenge to the designer&#8217;s authority.</p>

<p>Version control is a developer skill, and with sixteen different product<em>home</em>page.psd files floating around, it was hard to keep track of the definitive version. We solved the problem by printing out the designs - all of them - and sticking them on the wall. Areas that need discussion are marked with yellow post-it notes. While I can find and open a photoshop file in thirty seconds, I can turn my chair in two. And visitors to the work room can see exactly what, and how, we&#8217;re doing.</p>

<p>With talk of distributed teams, drag-and-drop task management and ubiquitous video conferencing, I can&#8217;t help but feel that the industry is moving in the wrong direction. So if your team isn&#8217;t really using their internal communication tools, try turning them off. Move everyone into one room. Clear the walls. Bring in a big box of pens, paper, and bluetack. See what happens.</p>]]></description>
        <pubDate>Mon, 26 Jan 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/1/26/writing-on-the-wall</guid>
      </item>
    
      <item>
        <title>Story Workshops and the Brain Dump</title>
        <link>http://blog.new-bamboo.co.uk/2009/1/19/story-workshops-and-the-brain-dump</link>
        <author>gwyn@new-bamboo.co.uk (gwyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/1/19/story-workshops-and-the-brain-dump#disqus_thread</comments>
        <description><![CDATA[<p>It seemed wasteful to bring the entire development team of four into the whole-day User Stories Workshop, so we didn&#8217;t. I wanted to take half an hour to brain-dump the result into the two absent developers, but never got the chance. So we went into the estimation session with two developers who&#8217;d been at the USW, and two who were working only from the cards.</p>

<p>We tried Speed Poker without success; two developers were playing &#8217;?&#8217; cards or wildly varying estimates. The estimating took a half-day, using up all the time we&#8217;d saved at the workshop.</p>

<p>The User Stories Workshop isn&#8217;t just about writing story cards; it&#8217;s about loading a whole lot of <em>extra</em> data, that doesn&#8217;t make it on to the cards, into the brains of everyone there. I still think we can optimise the process, since the developer-developer interface is higher bandwidth than the client-developer interface (and <em>much</em> higher than the meeting-developer interface). But in cases where we do exclude some people from the workshop, we need to rigorously schedule a developer brain-dump immediately afterwards.</p>]]></description>
        <pubDate>Mon, 19 Jan 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/1/19/story-workshops-and-the-brain-dump</guid>
      </item>
    
      <item>
        <title>Advanced Poker</title>
        <link>http://blog.new-bamboo.co.uk/2009/1/12/our-agile-toolbox-part-1</link>
        <author>gwyn@new-bamboo.co.uk (gwyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/1/12/our-agile-toolbox-part-1#disqus_thread</comments>
        <description><![CDATA[<p>Planning Poker is a basic part of the Agile process. We&#8217;ve optimised it.</p>

<p>We can play without cards. For short sessions, or when we need to re-estimate on the fly, fingers behind the back work perfectly. Big stories need both hands. Hands behind the back means &#8221;I&#8217;m ready to estimate&#8221;, a crooked finger means &#8220;half a point&#8221;, open palms means &#8220;I don&#8217;t know&#8221;. This works well in release planning when we&#8217;re discussing descoping a card; the client agrees to a change, and we do a new estimate on the spot.</p>

<p>When we use cards, we use good ones - presented to us by a client, our Planning Poker decks have been custom-printed by moo.com and feature a bonus &#8220;WTF?&#8221; card as well as the usuals.</p>

<p>We play fast. The &#8216;classic&#8217; approach is to talk about a story, explore all the options and implications, then finally estimate. When we do this, we nearly always get matching estimates. But it&#8217;s time-consuming, and if we&#8217;re not planning to implement the story straight away (in which case we needed to have the conversation anyway), it&#8217;s wasteful.</p>

<p>So when there&#8217;s a big stack of cards to get through - like after a User Stories Workshop on a new project - we play Speed Poker. We select a card, read it verbatim, and estimate. If the estimates all match up, we move to the next card - we were all thinking the same thing, and we&#8217;ve saved the time needed to have the conversation. If they don&#8217;t match up, we use the classic approach for that card.</p>

<p>We play commitment poker. For new projects, or those without a stable velocity, we use commitment-based planning rather than velocity-based planning. That is, after each story is added to the plan, we check that each member of the team is still comfortable committing. This suffers from the same &#8216;peer pressure&#8217; effect as normal estimation, though, and we solve it the same way: on three, thumbs-up means &#8221;I&#8217;m sure we can do this&#8221;, thumbs-down means &#8221;I&#8217;m no longer sure&#8221;.</p>]]></description>
        <pubDate>Mon, 12 Jan 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/1/12/our-agile-toolbox-part-1</guid>
      </item>
    
      <item>
        <title>Blackberry Break</title>
        <link>http://blog.new-bamboo.co.uk/2009/1/8/blackberry-break</link>
        <author>gwyn
@new-bamboo.co.uk (gwyn
)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/1/8/blackberry-break#disqus_thread</comments>
        <description><![CDATA[<p>Stakeholder availability is a problem for any methodology, but Agile gets hit particularly hard. The User Stories Workshop is most vulnerable; although the rest of the process uses a single Product Owner, the USW needs representatives from all over the client organisation. This, combined with a typical half-day duration, makes it very hard to schedule.</p>

<p>Above a certain level in the management hierarchy, nobody&#8217;s ever really 100% present in any case. At our last workshop, both high-level stakeholders had mobiles out, dealing with incoming emails that are more polite, but not much less disruptive, than incoming calls.</p>

<p>So we extended the coffee break to a fifteen-minute Blackberry Break. Those with urgent issues dealt with them. Everyone else had an out-of-band chat about how the process was going, and dealt with other todo items like external dependencies and scheduling.</p>

<p>We returned to the meeting with everybody focused. Going forward, we might even make this explicit: &#8220;there&#8217;ll be a Blackberry Break every half hour&#8221;. For product demos, we could go as far as &#8220;this is a fifteen-minute demo. Phones off, please&#8221;.</p>

<p>Singletasking: it&#8217;s the future..</p>]]></description>
        <pubDate>Thu, 08 Jan 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/1/8/blackberry-break</guid>
      </item>
    
      <item>
        <title>How-to setup your own virtualized Rails hosting environment with Xen</title>
        <link>http://blog.new-bamboo.co.uk/2009/1/6/how-to-setup-your-own-virtualized-rails-</link>
        <author>damien
@new-bamboo.co.uk (damien
)</author>
        <comments>http://blog.new-bamboo.co.uk/2009/1/6/how-to-setup-your-own-virtualized-rails-#disqus_thread</comments>
        <description><![CDATA[<a href='http://www.flickr.com/photos/kky/704056791/'><img class='picture' src='/assets/2009/1/6/704056791_63f1e492d8_m.jpg' /></a>
<p>A few months ago we transitioned from hosting all of our apps in development on two <a href='http://slicehost.com'>Slicehost</a> slices to individual Amazon EC2 instances for each. As much you want vendor frozen Rails and Merb to work (including all your app&#8217;s dependencies) it&#8217;s always plagued with issues. Having completely separate environment for each app clears up a lot of issues, including the all to often mistake of upgrading Rails only to find some old project isn&#8217;t able to co-exist with it. Our EC2 setup involved creating a base image with the basic Rails environment, and whenever a new staging server was needed we would clone the Base image and modify it specifically for the app. Being able to startup new environments with a single command is wonderfully easy, but a dozen staging servers later, the cost certainly starts to add up.</p>

<p>So for the cost of 1 month of our EC2 instances, we purchased a Dell box with 8Gb to run as an internal staging server and host all of our virtual servers. We chose Ubuntu as both the host and guest operating system. We started out trying <a href='https://help.ubuntu.com/community/KVM'>KVM</a> which is officially supported by Ubuntu. Everything went to plan and we had a great setup except for one major problem: slices would randomly lock up. After no success finding a remedy we decided to give <a href='http://www.xen.org/'>Xen</a> a go and with a little work got it setup and running along nicely.</p>

<p>Most of the instructions here have come from a combination of the <a href='https://help.ubuntu.com/community/'>Ubuntu</a> and <a href='http://www.virtuatopia.com/index.php/Xen_Virtualization_Essentials'>Virtuatopia</a> wikis.</p>

<p>The first step was to install <a href='http://www.ubuntu.com/getubuntu/download'>Ubuntu 8.10 (Intrepid) Server Edition</a> (remember to select the &#8220;Server Edition&#8221; tab before downloading).</p>

<h1 id='setup_xen'>Setup Xen</h1>

<p>Detailed instructions are on the <a href='https://help.ubuntu.com/community/Xen'>Ubuntu wiki Xen page</a></p>

<pre><code>sudo apt-get install ubuntu-xen-server</code></pre>

<h2 id='install_xen_kernel_from_hardy'>Install Xen kernel from Hardy</h2>

<p>Grab the .deb from here: <a href='http://packages.ubuntu.com/hardy-updates/linux-image-2.6.24-21-xen'>http://packages.ubuntu.com/hardy-updates/linux-image-2.6.24-21-xen</a></p>

<pre><code>sudo dpkg -i linux-image-2.6.24-21-xen_2.6.24-21.43_amd64.deb</code></pre>

<p>Make sure your eth0 is configured.</p>

<p>Reboot.</p>

<h1 id='using_xentools_to_create_images'>Using xen-tools to create images</h1>

<p>We should now have an Ubuntu server running the Xen kernel with all the standard Xen tools installed.</p>

<p>Next we&#8217;ll create a base Xen image running Debian Etch. In an ideal world it would be running Ubuntu 8.10 (Intrepid), but there isn&#8217;t yet a xen-tools template for Ubuntu, so Debian has to do.</p>

<p>Configure xen-tools:</p>

<pre><code>vim /etc/xen-tools/xen-tools.conf</code></pre>

<p>This is our config (installing Debian Etch):</p>

<pre><code>dir = ~/slices
install-method = debootstrap

size   = 4Gb      # Disk image size.
memory = 256Mb    # Memory size
swap   = 512Mb    # Swap size
fs     = ext3     # use the EXT3 filesystem for the disk image.
dist   = etch     # Default distribution to install.
image  = sparse   # Specify sparse vs. full disk images.

dhcp = 1

passwd = 1

accounts = 1

kernel      = /boot/vmlinuz-`uname -r`
initrd      = /boot/initrd.img-`uname -r`

arch = amd64

mirror = http://ftp.us.debian.org/debian/

ext3_options   = noatime,nodiratime,errors=remount-ro
ext2_options   = noatime,nodiratime,errors=remount-ro
xfs_options    = defaults
reiser_options = defaults</code></pre>

<p>If you want to include anything extra in the image, you can put files in the /etc/xen-tools/skel directory. We&#8217;ve written a script to configure the slice for Rails with Apache running Phusion Passenger.</p>

<p>Grab the <a href='http://gist.github.com/43765'>boot_xen_passenger.sh</a> script, and copy it to /etc/xen-tools/skel if you plan on using it.</p>

<pre><code>sudo vim /etc/xen-tools/skel/boot_xen_passenger.sh 
sudo chmod +x /etc/xen-tools/skel/boot_xen_passenger.sh </code></pre>

<p>Create your first slice:</p>

<pre><code>mkdir ~/slices
sudo xen-create-image --hostname=xentest1</code></pre>

<p>Start image:</p>

<pre><code>sudo xm create /etc/xen/xentest1.cfg</code></pre>

<p>View slices with:</p>

<pre><code>sudo xm list</code></pre>

<p>Attach to the console with:</p>

<pre><code>sudo xm console xentest1</code></pre>

<p>SSH in and run the setup script:</p>

<pre><code>sudo ./boot_xen_passenger.sh</code></pre>

<h1 id='networking'>Networking</h1>

<h2 id='hostname_resloving'>Hostname resloving</h2>

<p>Dnsmasq is a nice little dns server which will allow us to reference our slices by their hostnames.</p>

<pre><code>sudo apt-get install dnsmasq</code></pre>

<p>Dnsmasq can also act as a dhcp server. When you slices ask for a dhcp lease they will also give dnsmasq their hostname. This way you won&#8217;t need to reserve ip addresses for slices. However we&#8217;ve had some issues with the dhcp client on Debian when used in conjunction dnsmasq. In some cases you will want to forward certain ports from the outside world to a slice, and if have a dumb router it will only be able to forward to a specific ip.</p>

<p>If you wish you setup the dhcp server in dnsmasq:</p>

<pre><code>sudo vim /etc/dnsmasq.conf</code></pre>

<p>Uncommend the line <code>#dhcp-range=192.168.0.50,192.168.0.150,12h</code> and edit the ip range if desired.</p>

<p>If you use a separate router for your internet, you&#8217;ll want to set the gateway options as well by uncommenting the following line <code>dhcp-option=option:router,192.168.1.1</code> and replacing <code>1.2.3.4</code> with your router&#8217;s ip.</p>

<p>Now restart dnsmasq:</p>

<pre><code>sudo /etc/init.d/dnsmasq restart</code></pre>

<p>If you have an existing dhcp server on the network you will need to change it to forward requests to the new dnsmasq dhcp server. This is to ensure that new slices ask dnsmasq for an ip address, and when doing so it will also broadcast its hostname. On the local network this will allow you do to nice things like http://myslice and <code>ssh myslice</code>.</p>

<p>However if you&#8217;ve opted not use dnsmasq as your dhcp server, you will need to add a line to the host&#8217;s /etc/hosts file with the hostname of the slice and its ip address (every time you edit /etc/hosts you will also need to restart dnsmasq). You will also need to specify the ip address of your slices when they are created. See the <a href='http://www.virtuatopia.com/index.php/Building_a_Xen_Guest_Domain_using_Xen-Tools#Configuring_Xen_Guest_Network_Options'>Configuring Xen Guest Network Options wiki page</a> for more information about configuring xen-tools.</p>

<p>On your router or for individual clients you will also need to add the ip of the server with dnsmasq setup as a DNS server.</p>

<h2 id='magic_subdomain_forwarding_with_nginx'>Magic subdomain forwarding with nginx</h2>

<p>Any slice we startup should now be accessible by its hostname. To reduce the configuration required for each slice, we setup Nginx on the host to automatically forward http requests from any subdomain under our staging domain to the slice with a hostname the same as the subdomain. So that if you visit http://myslice.mystagingdomain.com from anywhere, it will be internally redirected to http://myslice and allow all your slices to be behind one ip.</p>

<h3 id='setup_nginx'>Setup nginx</h3>

<p>Grab nginx:</p>

<pre><code>sudo apt-get install nginx</code></pre>

<p>Then add a new site config for the slice forwarding:</p>

<pre><code>sudo vim /etc/nginx/sites-available/slices_proxy.conf</code></pre>

<p>Here is the config we have:</p>

<pre><code>map $host $backend {
    hostnames;
}

server {
  resolver 127.0.0.1;
  listen   80;
  server_name  _;

  access_log  /var/log/nginx/localhost.access.log;

  location / {
    if ($host ~* &quot;^(.*)\.mystagingdomain\.co\.uk&quot;) {
      set $host_without_www $1;
      proxy_pass http://$host_without_www$request_uri;
    }
  }
}</code></pre>

<p>Enable the config:</p>

<pre><code>sudo ln -s /etc/nginx/sites-available/slices_proxy.conf /etc/nginx/sites-enabled/slices_proxy.conf</code></pre>

<p>Best to also remove the default config:</p>

<pre><code>sudo rm /etc/nginx/sites-enabled/default</code></pre>

<p>Then restart nginx:</p>

<pre><code>sudo /etc/init.d/nginx restart</code></pre>

<h3 id='setup_dns'>Setup DNS</h3>

<p>Next add an wildcard A record to your mystagingdomain.com domain to point to your host&#8217;s external ip, and all should be good to go.</p>

<h1 id='next_steps'>Next steps</h1>

<p>The instructions here should get you started with a local Xen setup. However there&#8217;s still a few of the finer points to perfect. The Rails setup script could be integrated with Xen better so it&#8217;s automatically run when a slice is created. The script itself is also very brittle and likely to fail on another environment. Using <a href='http://www.deprec.org/'>deprec</a> would probably be a better solution in the long term.</p>]]></description>
        <pubDate>Tue, 06 Jan 2009 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2009/1/6/how-to-setup-your-own-virtualized-rails-</guid>
      </item>
    
      <item>
        <title>Amazon's CDN is coming soon</title>
        <link>http://blog.new-bamboo.co.uk/2008/9/18/amazon-s-cdn-is-coming-soon</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/9/18/amazon-s-cdn-is-coming-soon#disqus_thread</comments>
        <description><![CDATA[<p>It couldn&#8217;t have been better timed. Amazon has announced their forthcoming CDN service on the <a href='http://aws.typepad.com/aws/2008/09/were-never-cont.html'>AWS Blog</a>. Once released this service will be a perfect complement to <a href='http://pandastream.com'>Panda</a>, allowing videos to be streamed to users even more efficiently!</p>

<blockquote>
<p>You will start by storing your content in an Amazon S3 bucket and then marking the content as publicly readable. Next you&#8217;ll make a single API call to register the bucket. The call will return a domain name that you&#8217;ll use to refer to your content in your web page or application. When clients request the object via the returned domain name they&#8217;ll be routed to the nearest edge location, for high performance delivery.</p>
</blockquote>]]></description>
        <pubDate>Thu, 18 Sep 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/9/18/amazon-s-cdn-is-coming-soon</guid>
      </item>
    
      <item>
        <title>Panda - open source video encoding powered by Merb, EC2, S3 and SimpleDB</title>
        <link>http://blog.new-bamboo.co.uk/2008/9/11/panda-open-source-video-encoding-</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/9/11/panda-open-source-video-encoding-#disqus_thread</comments>
        <description><![CDATA[<a href='http://pandastream.com/'><img class='picture' src='http://blog.new-bamboo.co.uk/assets/2008/9/11/panda_logo_arch_1.gif' /></a>
<p>Earlier in the year, we found many of our clients were looking for a solution to allow users to easily upload their own videos. We had a good look into the <a href='http://sesamevault.com/'>numerous</a> <a href='http://heywatch.com'>existing</a> <a href='http://gomediaplug.com/'>services</a> <a href='http://zencoder.tv/'>already</a> <a href='http://www.filepipes.com/'>available</a>, but ultimately found none quite fit the bill; either being too expensive, closed source or overly complex. <a href='http://pandastream.com/'>Panda</a> was conceived to be a simple open source solution for handling online video. Essentially, <a href='http://pandastream.com/'>Panda</a> allows you seamlessly integrate video uploading, encoding and streaming into your web application, while utilizing the power of the cloud.</p>

<p>We took inspiration from what was already out there and distilled it down to its core. <a href='http://pandastream.com/'>Panda</a> runs entirely on <a href='http://aws.amazon.com/'>Amazon&#8217;s Web Services</a> utilising <a href='http://aws.amazon.com/ec2'>EC2</a>, <a href='http://aws.amazon.com/s3'>S3</a> and <a href='http://aws.amazon.com/simpledb'>SimpleDB</a> and the <a href='http://merbivore.com'>Merb</a> framework. Once you&#8217;ve started up the application on an EC2 instance you can access the API and administration backend. The REST API allow painless integration with your own web application. Videos are uploaded directly to <a href='http://pandastream.com/'>Panda</a> and transcoded using <a href='http://ffmpeg.mplayerhq.hu/'>FFmpeg</a> to any format you desire. Video can be streamed using any Flash video player such as the <a href='http://www.jeroenwijering.com/?item=JW_FLV_Player'>JW FLV Media Player</a>, and with the addition of h264 support in Flash 0.9.115 users can enjoy full HD quality.</p>

<p>So, visit the <a href='http://pandastream.com/'>Panda site</a>, grab the code from <a href='http://github.com/newbamboo/panda'>Github</a>, read the <a href='http://pandastream.com/docs/getting_started'>Getting Started Guide</a> and don&#8217;t forget to join the <a href='http://groups.google.com/group/pandastream'>Google Group</a> if you have any questions or success stories to talk about!</p>]]></description>
        <pubDate>Thu, 11 Sep 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/9/11/panda-open-source-video-encoding-</guid>
      </item>
    
      <item>
        <title>Protect them humans</title>
        <link>http://blog.new-bamboo.co.uk/2008/9/4/protect-them-humans</link>
        <author>max@new-bamboo.co.uk (max)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/9/4/protect-them-humans#disqus_thread</comments>
        <description><![CDATA[<a href='http://protectthehuman.com'><img class='picture' src='http://blog.new-bamboo.co.uk/assets/2008/9/4/pth.jpg' /></a>
<p>We don&#8217;t normally use the blog to announce the launch of client websites, but it might interest some people to check out some of the work we actually do. With this in mind, I&#8217;d like to introduce you to <a href='http://protectthehuman.com'>Protect The Human</a>, a recently launched project for <a href='http://www.amnesty.org.uk/'>Amnesty International UK</a> that we have been working on for the past few months in collaboration with our design peeps up in London <a href='http://madebymany.co.uk'>Made by Many</a>. Though it has been publicly available for a couple of weeks now, I wanted to let the dust settle a bit before posting.</p>

<p>So what is it? It is different things depending on who you talk to, but for me it is an assortment of different parts, playing a key role in Amnesty&#8217;s online strategy to increasing activism (catchy, eh?). It is firstly a content publishing platform; populated by fantastic Amnesty videos, galleries of images about the work they do, and masses of bookmarks to fascinating and current content all over the interweb. But this is not purely a top down publishing platform, users of the site are actively encouraged to create their own content, and rate comment and discuss any item that is on there.</p>

<p>This is a site about activism: the small things people can do which collectively create interest for others and effect change on important issues. Users are prompted to take various actions at every opportunity, depending on the time they have available. Examples of actions can be signing online petitions for the victims of Human rights abuses, attending protests, or simply sending great content to friends to help spread the word. This aspect of the site has been a real eye opener, I have never before seen so many opportunities to take proactive action on such important issues in such an accessible way.</p>

<p>There are other parts to this site, such as hubs for the various campaigns Amnesty UK are engaged in, and rather than me describe it all, the best thing to do is to have a poke around. Because we worked in an Agile way, there are still more features which are going to be rolling out soon, and we hope to be iteratively improving the site over the next few months.</p>

<p>We will probably be talking about how the Agile process worked in practice on this at some point, but that will have to wait a bit. For the moment, have a look, sign up, and take some actions. There really isn&#8217;t any excuse not to, the site makes it so easy to make a contribution, whatever that might be.</p>]]></description>
        <pubDate>Thu, 04 Sep 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/9/4/protect-them-humans</guid>
      </item>
    
      <item>
        <title>APML</title>
        <link>http://blog.new-bamboo.co.uk/2008/8/29/apml</link>
        <author>paul
@new-bamboo.co.uk (paul
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/8/29/apml#disqus_thread</comments>
        <description><![CDATA[<p><a href='http://apml.org'>APML</a> is an XML file format used to store the attention profiles of users, and it&#8217;s got the potential to become a revenue stream for web 2.0.</p>

<p>From my time at various web 2.0 start-ups, one of my gripes with the industry was that too many start-ups were focussing on advertising as their sole revenue stream, gunning to grow big and sell to GYM (Google Yahoo Microsoft), and hope that whoever bought them would figure out how to make them profitable. This strategy has worked for some, but I doubt that it will work for all. There are start-ups out there who may have to find other ways to make money, rather than just chase VCs and old media conglomerates, and shovel the cash on a bonfire.</p>

<p>My topic today is about an xml file format called <a href='http://apml.org'>APML</a>, used to store attention profiles of users. The technology is not complex, and there are plenty of places where it could be implemented. What&#8217;s more, the interesting thing about APML is that is has the potential to turn a lot of the data that web 2.0 startups have into valuable information that can be sold as a commodity. In other words, a revenue stream that isn&#8217;t &#8220;AdBucks saves the day!&#8221;</p>

<h2 id='introducing_apml'>Introducing APML</h2>

<p><a href='http://apml.org'>APML</a> is an xml file format for Attention Profiles, and Attention Profiles are lists of things that you are interested in, with a value signalling how interested you are in that thing. Examples of attention profiles include:</p>

<ul>
<li>A list of your favourite music artists collected by Last.fm, ranked by how often you listen to them.</li>

<li>A list of topics that you like to Twitter about, ranked by a count of how many times you mention each topic.</li>

<li>A list of products that I looked at and purchased / bid on from Amazon or Ebay, ranked by clicks.</li>

<li>A list of urls I have bookmarked via del.icio.us, ranked by the number of visits to each link.</li>

<li>A list of articles I have read on BusinessWeek, ranked by the number of visits to each article.</li>
</ul>

<p>Let&#8217;s grab a real example so you can see what I mean. Here&#8217;s my <a href='http://last.fm/user/paulbjensen'>my last.fm profile</a>. Notice the list of top artists, ranked by the number of times I&#8217;ve played songs/albums by those artists. And here is <a href='http://aura.darkstar.sunlabs.com/AttentionProfile/apml/last.fm/paulbjensen'>the APML file of the same data</a>. There we go, an APML file of my favourite musicians/music categories, courtesy of SunLabs and Last.fm.</p>

<p>Comparing the two sources, you get a better idea of what APML does to Attention Profile data. Here&#8217;s a diagram of the schema for APML:</p>

<p><img src='http://farm4.static.flickr.com/3152/2636182051_2beb83f36a_o_d.png' alt='APML schema' /></p>

<p>If you like all the juicy details, <a href='http://apml.pbwiki.com/'>here you go</a>. In the meantime, I&#8217;ll give you a quick runthrough.</p>

<p>The head element contains some basic descriptive data about the Attention Data, who it belongs to (the email), what application / site generated it, and when. Then in the Body element, we have 2 sub-elements; profile, and applications. Profile is where the real meat is, it&#8217;s essentially a grouping of the Attention Profile data, in my example Last.fm provided a list of all the music I like, and the music I have listened to in the last week.</p>

<p>The Profile element contains 2 sub-elements; ExplicitData, ImplicitData. Explicit and Implicit Data are similar in their schema, meaning that they store the same kind of data, but the difference is this; Explicit data is data that the user generated and gave to the application/site (in my last.fm example, that is the list of musicians I listened to), and Implicit data is data that the application generated (the list of music categories that I liked). I tell last.fm what artists I like (Explicit), and last.fm tells me what music categories I listen to (Implicit).</p>

<p>The Explicit/Implicit data element contains 2 sub-elements; concepts, and sources. Concepts are a collection of elements representing the thing that you are interested in, such as a music artist, and a value representing your level of interest in that item. The value is ranked from 1 to 0, with 1 being the thing which you are most interested in, and everything else being compared against that top concept. Sources are a collection of the sources of the data stored in concepts, such as blog sites, web applications, etc.</p>

<p>Jumping up a couple of levels, the Application element is used to store information about the application which generated the APML file, as well as any meta-data that the application carries with it. I have to put my hands up here and admit that I don&#8217;t fully understand this element, so you&#8217;re best bet is to flick through the juicy details rather than take my vague description as gospel.</p>

<h2 id='why_its_good'>Why its good</h2>

<p>All APML does essentially is add structure to information about what we as people are interested in, which for its own sake seems a rather pointless venture. However, as Orwell once said, knowledge is power, and what APML offers is a number of opportunities to turn an excess product of web 2.0 into a commodity of value.</p>

<p>Finding out what stuff people are interested in and organising that information has proven to be useful information to businesses, just ask Tesco why they place beer next to nappies*. Web 2.0 start-ups are collecting a massive amount of this raw data about their users, but not necessarily putting it to full use.</p>

<p>But who&#8217;s buying the data? That&#8217;s where I get creative and illustrate to you a business idea that I think could work for an APML analytics service&#8230;</p>

<p><img src='http://farm4.static.flickr.com/3238/2809084336_5bb2ee96c0_o_d.png' alt='APML business idea' /></p>

<p>This diagram might be worth 1000 words, but I&#8217;ll explain it anyway (accessibility ; ) ). Various web services which collect data about their users convert that data to APML, and post it to us. We take it, store it in a big database alongside data from other providers, and then do 3 things with that data:</p>

<p>1 - Provide an analytics service to interested 3rd parties, advertisers and publishers to analyse a large data-set and spot interest patterns across a whole range of consumer products (nappies and beer?).</p>

<p>2 - Provide a recommendation engine service to interested web services looking to pool a large data-set to get highly-accuracy suggestions to users.</p>

<p>3 - Provide (and this is where I shoot myself in the foot) an Advertising Management service that suggests the most relevant ads based on the user&#8217;s APML data across various services.</p>

<p>The general idea for the revenue stream here would be to charge for these 3 individual services, and apportion a percentage of that revenue to the APML data providers, based on how much of their data was used by the customer. That&#8217;s really all there is to it.</p>

<h2 id='summary'>Summary</h2>

<p>APML helps to standardise data about what users are interested in, and in doing this, an opportunity might be open to build a web-based service that can sort all of that data and spit it out into a neat little analytics service for 3rd parties to analyse to bits, alongside the recommendation engine to serve to interested web services, as well as the (choke) Ad management service to suggest relevant ads. All in all, this could be a way to provide those web 2.0 services with a revenue stream where they provide a web service with APML, and get money back in return.</p>

<p>To explain the nappies and beer reference, Tesco&#8217;s loyalty card scheme provides a wealth of information about consumer purchase patterns, one of which was that men in their 30&#8217;s would be visiting their stores on a Friday night to buy nappies of all things (as my sister&#8217;s partner must be finding out). They strategically placed beer next to nappies, and boosted their sales.</p>]]></description>
        <pubDate>Fri, 29 Aug 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/8/29/apml</guid>
      </item>
    
      <item>
        <title>Panda Google Group</title>
        <link>http://blog.new-bamboo.co.uk/2008/8/22/panda-google-group</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/8/22/panda-google-group#disqus_thread</comments>
        <description><![CDATA[<p>http://groups.google.com/group/pandastream</p>]]></description>
        <pubDate>Fri, 22 Aug 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/8/22/panda-google-group</guid>
      </item>
    
      <item>
        <title>Bambinos: our tumblog</title>
        <link>http://blog.new-bamboo.co.uk/2008/8/10/bambinos-our-tumblog</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/8/10/bambinos-our-tumblog#disqus_thread</comments>
        <description><![CDATA[<p>A lot goes on here, believe me. Not everything is worthy of a full post though, so we&#8217;ve been running a tumblog internally for a little while. <a href='http://bambinos.tumblr.com/'>Bambinos</a> is where all those useful code snippets and links go. If you enjoy the posts here I&#8217;d certainly recommend you <a href='http://feeds.feedburner.com/bambinos'>subscribe</a>.</p>

<p>Some recent picks from the archive:</p>

<ul>
<li><a href='http://bambinos.tumblr.com/post/44320406/hangup-meetings-an-improvement-on-xps-stand-up'>HangUp Meetings</a> - An improvement on XP’s stand-up meeting&#8230;</li>

<li><a href='http://bambinos.tumblr.com/post/43059473/picture-bong'>Picture Bong</a> - Graphs for the Bong benchmarking tool&#8230;</li>

<li><a href='http://bambinos.tumblr.com/post/42774103/manec2'>ManEC2</a> - Keep tracks of all those EC2 instances you have&#8230;</li>
</ul>]]></description>
        <pubDate>Sun, 10 Aug 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/8/10/bambinos-our-tumblog</guid>
      </item>
    
      <item>
        <title>Sexy Forms for Merb</title>
        <link>http://blog.new-bamboo.co.uk/2008/7/4/supersexy-forms-for-merb</link>
        <author>matt
@new-bamboo.co.uk (matt
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/7/4/supersexy-forms-for-merb#disqus_thread</comments>
        <description><![CDATA[<p>I&#8217;ve written a little lib which piggy backs on the standard merb form helpers to give you nicer looking forms with help from Andy and Martyn.</p>

<p>We weren&#8217;t happy with our forms and we were writing too much code. So we wrapped some goodies around the merb form helpers. We&#8217;ve got notes, inline errors, required / not required formatting, and cancel buttons. The API is so minimal you won&#8217;t be able to resist.</p>

<p>Here&#8217;s an example of the ruby code, the generated HTML, and what it looks like with some sexy CSS:</p>

<h2 id='example_code'>Example Code</h2>

<h3 id='ruby'>Ruby</h3>
<pre><code class='ruby'>
&lt;%= field(:text, :nickname, :required =&gt; true, :note =&gt; &quot;You&#x27;ll be known by this on the site. Don&#x27;t use your real name. It has to be unique, and you can&#x27;t change it later.&quot;)%&gt;
&lt;%= field(:password, :password, :required =&gt; true)%&gt;
&lt;%= field(:password, :password_confirmation, :required =&gt; true, :note =&gt; &quot;Type the same password again, just to make sure we&#x27;ve got it right&quot; )%&gt;
&lt;%= field(:checkbox, :terms_and_conditions, :required =&gt; true, :value =&gt; true, :label=&gt;&quot;I accept the terms and conditions&quot;)%&gt;
&lt;%= form_submit &quot;Sign Up!&quot; %&gt; 
</code></pre>
<h3 id='html_generated'>Html Generated</h3>
<pre><code class='html'>
&lt;div class=&quot;field password required&quot;&gt;
&lt;label for=&quot;user_password&quot;&gt;Password&lt;/label&gt;
&lt;input type=&quot;password&quot; class=&quot;password&quot; name=&quot;user[password]&quot; id=&quot;user_password&quot; /&gt;
&lt;/div&gt;

&lt;div class=&quot;field password required&quot;&gt;
&lt;label for=&quot;user_password_confirmation&quot;&gt;Password confirmation&lt;/label&gt;
&lt;input type=&quot;password&quot; class=&quot;password&quot; name=&quot;user[password_confirmation]&quot; id=
&quot;user_password_confirmation&quot; /&gt;

&lt;p class=&quot;note&quot;&gt;Type the same password again, just to make sure we&#x27;ve got it right
&lt;abbr title=&quot;smile&quot;&gt;: )&lt;/abbr&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class=&quot;field checkbox required&quot;&gt;
&lt;input type=&quot;hidden&quot; class=&quot;hidden&quot; name=&quot;user[terms_and_conditions]&quot; value=&quot;0&quot; /&gt;
&lt;input type=&quot;checkbox&quot; class=&quot;checkbox&quot; name=&quot;user[terms_and_conditions]&quot; value=&quot;1&quot;
id=&quot;user_terms_and_conditions&quot; /&gt;
&lt;label for=&quot;user_terms_and_conditions&quot;&gt;I accept the terms and conditions&lt;/label&gt;
&lt;/div&gt;

&lt;div class=&quot;field controls&quot;&gt;
&lt;button type=&quot;submit&quot; class=&quot;positive&quot;&gt;Sign Up!&lt;/button&gt;
&lt;/div&gt;
</code></pre>
<h3 id='html_with_errors'>Html with Errors</h3>
<pre><code class='html'>
&lt;div class=&quot;field error password required&quot;&gt;
&lt;label for=&quot;user_password&quot;&gt;Password&lt;/label&gt;
&lt;input type=&quot;password&quot; class=&quot;error password&quot; name=&quot;user[password]&quot; id=&quot;user_password&quot; /&gt;

&lt;p class=&quot;error&quot;&gt;can&#x27;t be blank and is too short (minimum is 4 characters)&lt;/p&gt;
&lt;/div&gt;

&lt;div class=&quot;field error password required&quot;&gt;
&lt;label for=&quot;user_password_confirmation&quot;&gt;Password confirmation&lt;/label&gt;
&lt;input type=&quot;password&quot; class=&quot;error password&quot; name=&quot;user[password_confirmation]&quot; id=&quot;user_password_confirmation&quot; /&gt;

&lt;p class=&quot;note&quot;&gt;Type the same password again, just to make sure we&#x27;ve got it right
&lt;abbr title=&quot;smile&quot;&gt;: )&lt;/abbr&gt;&lt;/p&gt;

&lt;p class=&quot;error&quot;&gt;can&#x27;t be blank&lt;/p&gt;
&lt;/div&gt;

&lt;div class=&quot;field error checkbox required&quot;&gt;
&lt;input type=&quot;hidden&quot; class=&quot;hidden&quot; name=&quot;user[terms_and_conditions]&quot; value=&quot;0&quot; /&gt;
&lt;input type=&quot;checkbox&quot; class=&quot;error checkbox&quot; name=&quot;user[terms_and_conditions]&quot; value=&quot;1&quot; id=&quot;user_terms_and_conditions&quot; /&gt;
&lt;label for=&quot;user_terms_and_conditions&quot;&gt;I accept the terms and conditions&lt;/label&gt;

&lt;p class=&quot;error&quot;&gt;must be accepted&lt;/p&gt;
&lt;/div&gt;
</code></pre>
<h3 id='sexy_form'>Sexy Form</h3>

<p><img src='http://blog.new-bamboo.co.uk/assets/2008/7/4/sexyform.png' alt='sexyform' /></p>

<p>You can be the judge of the sexyness of these forms, but they are much nicer than the default error message in my opinion.</p>

<h2 id='codecss'>Code/CSS</h2>

<p>Code is licensed under the MIT License, and under no guarantee that it will not break if they change the external API for form helpers.</p>

<ul>
<li>
<p><a href='http://www.friendpaste.com/F4j3Q3VM'>For Merb and AR</a></p>
</li>

<li>
<p><a href='http://www.friendpaste.com/NZrQcCjb'>For Merb and DM</a></p>
</li>

<li>
<p><a href='http://www.friendpaste.com/9aFfN4ce'>The CSS</a></p>
</li>
</ul>

<h3 id='install'>Install</h3>

<p>Add the file in your lib folder, add the dependency in your init.rb and then include in your global helpers.</p>

<p>Enjoy!</p>]]></description>
        <pubDate>Fri, 04 Jul 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/7/4/supersexy-forms-for-merb</guid>
      </item>
    
      <item>
        <title>APICache</title>
        <link>http://blog.new-bamboo.co.uk/2008/6/6/api_cache</link>
        <author>martyn
@new-bamboo.co.uk (martyn
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/6/6/api_cache#disqus_thread</comments>
        <description><![CDATA[<p>You want to use the Twitter API but you don&#8217;t want to die? I have the solution to API caching:</p>

<pre><code>APICache.get(&quot;http://twitter.com/statuses/public_timeline.rss&quot;)</code></pre>

<p>You get the following functionality for free:</p>

<ul>
<li>New data every 10 minutes</li>

<li>If the twitter API dies then keep using the last data received for a day. Then assume it&#8217;s invalid and announce that Twitter has FAILED (optional).</li>

<li>Don&#8217;t hit the rate limit (70 requests per 60 minutes)</li>
</ul>

<p>So what exactly does <code>APICache</code> do? Given cached data less than 10 minutes old, it returns that. Otherwise, assuming it didn&#8217;t try to request the URL within the last minute (to avoid the rate limit), it makes a get request to the Twitter API. If the Twitter API timeouts or doesn&#8217;t return a 2xx code (very likely) we&#8217;re still fine: it just returns the last data fetched (as long as it&#8217;s less than a day old). In the exceptional case that all is lost and no data can be returned, it raises an <code>APICache::NotAvailableError</code> exception. You&#8217;re responsible for catching this exception and complaining bitterly to the internet.</p>

<p>All very simple. What if you need to do something more complicated? Say you need authentication or the silly API you&#8217;re using doesn&#8217;t follow a nice convention of returning 2xx for success. Then you need a block:</p>

<pre><code>APICache.get(&#39;twitter_replies&#39;, :cache =&gt; 3600) do
  Net::HTTP.start(&#39;twitter.com&#39;) do |http|
    req = Net::HTTP::Get.new(&#39;/statuses/replies.xml&#39;)
    req.basic_auth &#39;username&#39;, &#39;password&#39;
    response = http.request(req)
    case response
    when Net::HTTPSuccess
      # 2xx response code
      response.body
    else
      raise APICache::Invalid
    end
  end
end</code></pre>

<p>All the caching is still handled for you. If you supply a block then the first argument to <code>APICache.get</code> is assumed to be a unique key rather than a URL. Throwing <code>APICache::Invalid</code> signals to <code>APICache</code> that the request was not successful.</p>

<p>You can send any of the following options to <code>APICache.get(url, options = {}, &amp;block)</code>. These are the default values (times are all in seconds):</p>

<pre><code>{
  :cache =&gt; 600,    # 10 minutes  After this time fetch new data
  :valid =&gt; 86400,  # 1 day       Maximum time to use old data
                    #             :forever is a valid option
  :period =&gt; 60,    # 1 minute    Maximum frequency to call API
  :timeout =&gt; 5     # 5 seconds   API response timeout
}</code></pre>

<p>Before using the APICache you need to initialize the caches. In merb, for example, put this in your <code>init.rb</code>:</p>

<pre><code>APICache.start</code></pre>

<p>Currently there are two stores available: <code>MemcacheStore</code> and <code>MemoryStore</code>. <code>MemcacheStore</code> is the default but if you&#8217;d like to use <code>MemoryStore</code>, or another store - see <code>AbstractStore</code>, just supply it to the start method:</p>

<pre><code>APICache.start(APICache::MemoryStore)</code></pre>

<p>I suppose you&#8217;ll want to get your hands on this magic. For now just grab the source from <a href='http://github.com/mloughran/api_cache/tree/master'>http://github.com/mloughran/api_cache/tree/master</a> and <code>rake install</code>. I&#8217;ll get a gem sorted soon.</p>

<p>This is the irb quickstart so that you don&#8217;t have to re-parse the above:</p>

<pre><code>require &#39;rubygems&#39;
require &#39;api_cache&#39;
APICache.start(APICache::MemoryStore)
APICache.get(&quot;http://twitter.com/statuses/public_timeline.rss&quot;)</code></pre>

<p>Please send feedback if you think of any other functionality that would be handy.</p>]]></description>
        <pubDate>Fri, 06 Jun 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/6/6/api_cache</guid>
      </item>
    
      <item>
        <title>Integration testing Merb with Webrat</title>
        <link>http://blog.new-bamboo.co.uk/2008/4/23/integration-testing-merb-with-webrat</link>
        <author>gwyn
@new-bamboo.co.uk (gwyn
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/4/23/integration-testing-merb-with-webrat#disqus_thread</comments>
        <description><![CDATA[<p><a href='http://rspec.info/'>RSpec stories</a> are a way of doing integration and acceptance testing using plaintext executable tests. You can use them in Merb as well as Rails. Here&#8217;s how.&lt;/p&gt;</p>

<h2 id='setting_up'>Setting up</h2>

<p><a href='http://merbivore.com/get_merb.html#trunk'>Install edge Merb</a>; the latest gem (0.9.2) will not work. You need merb-core, merb-more, and merb-plugins.</p>

<p>Merb-plugins gives you the merb_stories gem, so you don&#8217;t need to install that separately.</p>

<p>Add this line to your app&#8217;s config/environments/test.rb:</p>

<pre><code>dependencies &quot;merb_stories&quot;, &quot;webrat&quot;</code></pre>

<p>(Note that merb_stories&#8217; README file is wrong about this, for now - and the generator will create a dependency on merb-rspec, which no longer exists. <a href='http://github.com/gwynm/merb-plugins/tree/master'>My fork</a> fixes this.)</p>

<p>Now generate your story:</p>

<pre><code>merb-gen story mystory</code></pre>

<p>Now run your story:</p>

<pre><code>rake story\\[mystory\\]</code></pre>

<p>Yes, you must include the square brackets, and you have to escape them.</p>

<h2 id='writing_stories'>Writing stories</h2>

<p>Now fill out your story. There are some differences to Rails&#8217; versions. The best places to look for help are in the Merb code itself:</p>

<ul>
<li>spec/public/test/controller <em>matchers</em>spec.rb</li>

<li>lib/merb-core/test/helpers</li>

<li>lib/merb-core/test/matchers</li>
</ul>

<p>To start you off, here are the steps for a simple integration test:</p>

<pre><code>steps_for(:homepage) do
  When(&quot;I visit the root&quot;) do
    @mycontroller = get(&quot;/&quot;)
  end
  Then(&quot;I should see the home page&quot;) do
    @mycontroller.should respond_successfully
    @mycontroller.body.should contain(&quot;Hello&quot;) 
  end    
end</code></pre>

<p>As you write your tests, don&#8217;t trust Merb absolutely. Some things are wrong, don&#8217;t work, or aren&#8217;t meant to work [yet]. As part of debugging, look at the Merb source, and <a href='http://gweezlebur.com/2008/2/9/contributing-to-merb-part-2'>fork it and fix it</a> if needed.</p>

<h2 id='adding_webrat'>Adding Webrat</h2>

<p>Webrat lets you write integration tests that are even closer to natural language. You can say things like:</p>

<pre><code>visits &#39;/auth/login&#39;
fills_in &#39;username&#39;,:with=&gt;&quot;bob&quot;
fills_in &#39;password&#39;,:with=&gt;&quot;hunter2&quot;
clicks_button &quot;login&quot;
response.should be_successful
...</code></pre>

<p>I&#8217;ve <a href='http://github.com/gwynm/webrat/tree/master'>forked Webrat to add Merb support</a>.</p>

<p>To get started, <a href='http://gwynmorfey.com/categories/1-on-the-job/articles/128-git-github-and-rubygems-quickstart'>clone it, build the gem and install it</a>.</p>

<p>You&#8217;ll also need to make sure you&#8217;re using the memory session store (for testing), or your sessions won&#8217;t be preserved:</p>

<pre><code>Merb::Config.use do |c|
  c[:session_store] = &#39;memory&#39; 
end  </code></pre>

<h2 id='continuous_integration'>Continuous integration</h2>

<p>Autotest will run your specs continuously, but won&#8217;t run your stories. You can fix this, but stories run very slowly, since they use the full stack. At the very least, though, you want to run your stories after you deploy, in case you have &#8220;works on dev machine, dies on production&#8221; problems.</p>

<p>Add this to deploy.rb:</p>

<pre><code>namespace :deploy do
  task :after_deploy do
    run_remote_tests
  end
  desc &quot;Run tests on remote server&quot;
  task :run_remote_tests do
    run &quot;cd #{deploy_to}/current &amp;&amp; rake spec&quot;
    run &quot;cd #{deploy_to}/current &amp;&amp; rake story\[all\] MERB_ENV=test&quot;
  end 
end</code></pre>

<p>This will fail if you&#8217;re using <a href='http://blog.new-bamboo.co.uk/2008/3/14/freeze-gems-in-a-merb-application'>Vendor Everything</a> like us - it won&#8217;t be able to find merb-core. To fix it, replace stories/stories/all.rb:</p>

<pre><code>env = ENV[&#39;MERB_ENV&#39;] || &#39;test&#39;
require &#39;rubygems&#39;
Gem.clear_paths
Gem.path.unshift(File.join(File.dirname(__FILE__), &quot;..&quot;,&quot;..&quot;,&quot;gems&quot;))

require &#39;merb-core&#39;
Merb.load_dependencies(:environment =&gt; env)

require &#39;spec&#39;
Merb.start_environment(:testing =&gt; true, :adapter =&gt; &#39;runner&#39;, :environment =&gt; env)

dir = File.dirname(__FILE__)
Dir[File.expand_path(&quot;#{dir}/**/*.rb&quot;)].uniq.each do |file|
  require file
end</code></pre>

<p>You&#8217;ll have the same problem with specs. Edit <tt>spec/spec_helper.rb</tt>:</p>

<pre><code>Gem.clear_paths
Gem.path.unshift(File.join(File.dirname(__FILE__), &quot;..&quot;,&quot;gems&quot;))</code></pre>

<p>Now you have natural-language integration tests that run automatically on deployment. Sweet.</p>]]></description>
        <pubDate>Wed, 23 Apr 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/4/23/integration-testing-merb-with-webrat</guid>
      </item>
    
      <item>
        <title>Merb Book (Part 3)</title>
        <link>http://blog.new-bamboo.co.uk/2008/4/18/merb-book-part-3</link>
        <author>matt
@new-bamboo.co.uk (matt
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/4/18/merb-book-part-3#disqus_thread</comments>
        <description><![CDATA[<p>Here we are going cover the install instructions for Merb, RSpec, and DataMapper (0.9) and how to create a bare application. The next post will cover aspects of the framework and introduce the example application we will be building.</p>

<h2 id='getting_started'>Getting Started</h2>

<p>Before we get started I&#8217;m going to assume you have the following installed:</p>

<ul>
<li><a href='http://www.ruby-lang.org/'>Ruby</a></li>

<li>A DBMS (we&#8217;ll use <a href='http://mysql.org/'>MySQL</a>)</li>

<li><a href='http://subversion.tigris.org/'>SVN</a> and <a href='http://git.or.cz/'>git</a> (on OSX, port install <code>git-core</code> worked for me)</li>
</ul>

<h3 id='installing_merb'>Installing Merb</h3>
<hr />
<p>If you have an older version of Merb (&#60;0.9.2) you should remove the all the gems before continuing. Use <code>gem list</code> to see your installed gems.</p>
<hr />
<p>Installing the <code>merb</code> gems should be as simple as:</p>

<pre><code>sudo gem install merb --source http://merbivore.org </code></pre>

<p><strong>Unfortunately</strong> we are living right on the edge of development so we&#8217;ll need to get down and dirty with building our own gems from source. Luckiliy this is much easier than it sounds&#8230;</p>

<p>Start by installing the <code>gem</code> dependancies:</p>

<pre><code>sudo gem install rack mongrel json_pure erubis mime-types rspec hpricot \
    mocha rubigen haml markaby mailfactory ruby2ruby</code></pre>

<p>Then download the <code>merb</code> source:</p>

<pre><code>git clone git://github.com/wycats/merb-core.git
git clone git://github.com/wycats/merb-plugins.git
git clone git://github.com/wycats/merb-more.git</code></pre>

<p>Then install the gems via rake:</p>

<pre><code>cd merb-core ; rake install ; cd ..    
cd merb-more ; rake install ; cd ..
cd merb-plugins; rake install ; cd ..</code></pre>

<p>The <code>json_pure</code> gem is needed for merb to install on <a href='http://jruby.codehaus.org/'>JRuby</a> (Java implementation of a Ruby Interpreter), otherwise use the <code>json</code> gem as it&#8217;s faster.</p>

<p>Merb is ORM agnostic, but as the title of this book suggests we&#8217;ll be using DataMapper. Should you want to stick with ActiveRecord or play with Sequel, check the <a href='http://merb.rubyforge.org/files/README.html'>merb documentation</a> for install instructions.</p>

<h3 id='installing_datamapper'>Installing DataMapper</h3>
<hr />
<p>DataMapper is splitting into <code>dm-core</code> and <code>dm-more</code> so <code>datamapper 0.3</code> will be outdated soon. If you have an older version of <code>datamapper</code>, <code>data_objects</code>, or <code>do_mysql</code>, <code>merb_datamapper</code> (&lt; 0.9) you should remove them first. Remove the <code>merb_datamapper</code> gem before installing <code>dm-merb</code> within <code>dm-more</code>.</p>
<hr />
<p>We will use MySQL in the following example, but you can use either sqlite3 or PostgreSQL, just install the appropriate gem. You will also need to ensure that MySQL is on your system path for the gem to install correctly.</p>

<p>(TODO) - gem instructions for DM 0.9 once they are released</p>

<p>To get the gems from source:</p>

<pre><code>git clone git://github.com/sam/do.git

cd do
cd data_objects
rake install ; cd ..
cd do_mysql  # || do_postgres || do_sqlite3
rake install

git clone git://github.com/sam/dm-core.git
git clone git://github.com/sam/dm-more.git

cd dm-core ; rake install ; cd ..
cd dm-more
cd dm-merb ; rake install ; cd ..    
cd dm-validations ; rake install ; cd ..</code></pre>

<p>To update a gem from source, run <code>git pull</code> and <code>rake install</code> again.</p>

<h3 id='install_rspec'>Install RSpec</h3>

<p>The <code>rspec</code> gem was installed in the Merb section above. However, if for some reason you didn&#8217;t install it there, or want to grab the it from source, run the following commands:</p>

<pre><code>gem install rspec
svn checkout http://rspec.rubyforge.org/svn/trunk rspec_trunk</code></pre>

<h2 id='creating_an_app'>Creating an App</h2>

<p>Now that we&#8217;ve got all of that installed, time to create a test Merb application. Merb follows the same naming convention for projects as rails, so &#8216;my_test_app&#8217; and &#8216;Test2&#8217; are valid names but &#8216;T 3&#8217; is not (they need to be valid SQL table names).</p>

<pre><code>merb-gen app test</code></pre>

<p>This will generate an empty Merb app, so lets go in and take a look. You&#8217;ll notice that the directory structure is similar to Rails, with a few differences.</p>

<pre><code># expected output
RubiGen::Scripts::Generate
  create  app
  create  app/controllers
  create  app/helpers
  create  app/views
  create  app/views/exceptions
  create  app/views/layout
  create  autotest
  create  config
  create  config/environments
  create  public
  create  public/images
  create  public/stylesheets
  create  spec
  create  app/controllers/application.rb
  create  app/controllers/exceptions.rb
  create  app/helpers/global_helpers.rb
  create  app/views/exceptions/internal_server_error.html.erb
  create  app/views/exceptions/not_acceptable.html.erb
  create  app/views/exceptions/not_found.html.erb
  create  app/views/layout/application.html.erb
  create  autotest/discover.rb
  create  autotest/merb.rb
  create  autotest/merb_rspec.rb
  create  config/rack.rb
  create  config/router.rb
  create  config/init.rb
  create  config/environments/development.rb
  create  config/environments/production.rb
  create  config/environments/rake.rb
  create  config/environments/test.rb
  create  public/merb.fcgi
  create  public/images/merb.jpg
  create  public/stylesheets/master.css
  create  spec/spec.opts
  create  spec/spec_helper.rb
  create  /Rakefile</code></pre>

<h3 id='configuring_merb'>Configuring Merb</h3>

<p>Before we get the server running, you&#8217;ll need to edit the init.rb file and un-comment the following lines (this is only necessary if you need to connect to a database, which we do in our case):</p>

<p>config/init.rb</p>

<pre><code>use_orm :dm_core

use_test :rspec</code></pre>

<p>Typing <code>merb</code> now in your command line will try and start the server.</p>

<pre><code>Started merb_init.rb ...
No database.yml file found in /Users/work/merb/example_one/config.
A sample file was created called database.sample.yml for you to copy and edit.</code></pre>

<p>As you can see, we forgot to set up the database. A sample file has helpfully been generated for us. Edit this and rename it to database.yml:</p>

<pre><code># This is a sample database file for the DataMapper ORM
development:
   adapter: mysql
   database: test
   username: root
   password: 
   host: localhost
   socket: /tmp/mysql.sock</code></pre>

<p>Don&#8217;t forget to specify your socket, if you do not know it&#8217;s location, you can find it by typing:</p>

<pre><code>mysql_config --socket</code></pre>

<p>Starting Merb again shows that everything is running okay.</p>

<p>The following command will give you access to the Merb console:</p>

<pre><code>merb -i</code></pre>

<p>You&#8217;ll notice Merb runs on port 4000, but this can be changed with flag <code>-p [port number]</code>. More options can be found by typing:</p>

<pre><code>merb --help</code></pre>

<p>You can even run Merb with any application server that supports rack (thin, evented_mongrel, fcgi, mongrel, and webrick):</p>

<pre><code>merb -a thin</code></pre>]]></description>
        <pubDate>Fri, 18 Apr 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/4/18/merb-book-part-3</guid>
      </item>
    
      <item>
        <title>Merb and DataMapper Book (Part 2)</title>
        <link>http://blog.new-bamboo.co.uk/2008/4/17/merb-and-datamapper-book-part-2</link>
        <author>matt
@new-bamboo.co.uk (matt
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/4/17/merb-and-datamapper-book-part-2#disqus_thread</comments>
        <description><![CDATA[<p>Here is the next part of the <a href='http://blog.new-bamboo.co.uk/2008/4/9/life-on-the-edge-with-merb-datamapper-rspec-part-1'>Merb, DataMapper, RSpec book</a>. I was planning on releasing more today but I am rewriting the examples for DataMapper 0.9. There is a example app for DataMapper 0.3 in the <a href='http://github.com/deimos1986/book_mdar/tree/master'>git repository of the book</a>, if you can&#8217;t wait to play with it.</p>

<h2 id='communities'>Communities</h2>

<blockquote>
<p>i&#8217;m going to become rich and famous after i invent a device that allows you to stab people in the face over the internet - &#60;[SA]HatfulOfHollow&#62;</p>
</blockquote>

<p>The internet is a scary place, but fortunately the Ruby community is very friendly. All these open source projects rely on the contributions from the community, if something needs fixing consider helping out.</p>

<h3 id='websites'>Websites</h3>

<p>These are the first places to go for help. Check out the API documentation and see if you can find your answer there.</p>

<ul>
<li><a href='http://merbivore.com/'>http://merbivore.com/</a></li>

<li><a href='http://datamapper.org/'>http://datamapper.org/</a></li>

<li><a href='http://rspec.info/'>http://rspec.info/</a></li>
</ul>

<h3 id='irc_channels__freenodenet'>IRC Channels - freenode.net</h3>

<p>If you can&#8217;t find what you were looking for in the API docs then you could join the respective IRC channel on FreeNode and ask your question in there, you may need to wait for a response.</p>

<ul>
<li><a href='irc://irc.freenode.net/merb'>#merb</a></li>

<li><a href='irc://irc.freenode.net/datamapper'>#datamapper</a></li>

<li><a href='irc://irc.freenode.net/rspec'>#rspec</a></li>
</ul>

<h3 id='mailing_lists'>Mailing Lists</h3>

<p>The mailing lists are another good way to get help, the response time isn&#8217;t as fast as asking in an irc channel but it can be useful to do a search to see if someone else has had your problem before.</p>

<ul>
<li><a href='http://groups.google.com/group/merb/'>http://groups.google.com/group/merb/</a></li>

<li><a href='http://groups.google.com/group/datamapper/'>http://groups.google.com/group/datamapper/</a></li>

<li><a href='http://rubyforge.org/pipermail/rspec-users/'>http://rubyforge.org/pipermail/rspec-users/</a></li>
</ul>

<h3 id='bug_trackers'>Bug Trackers</h3>

<p>Your problem may or may not be a known bug. Search the bug trackers and submit a ticket if its not there already (don&#8217;t forget to include a description and test cases, or better yet: a patch!). You may find the ticket is solved in the edge version.</p>

<ul>
<li><a href='http://merb.lighthouseapp.com/'>http://merb.lighthouseapp.com/</a></li>

<li><a href='http://wm.lighthouseapp.com/projects/4819-datamapper/overview'>http://wm.lighthouseapp.com/projects/4819-datamapper/overview</a></li>

<li><a href='http://wm.lighthouseapp.com/projects/4819-datamapper/overview'>http://rspec.lighthouseapp.com/</a></li>
</ul>

<h3 id='wikis'>Wikis</h3>

<ul>
<li><a href='http://wiki.merbivore.com/'>http://wiki.merbivore.com/</a></li>

<li><a href='http://wiki.datamapper.org/doku.php?id=start'>http://wiki.datamapper.org/</a></li>
</ul>
<hr />
<p>Next we&#8217;ll cover installing the gems, and generating a bare project.</p>]]></description>
        <pubDate>Thu, 17 Apr 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/4/17/merb-and-datamapper-book-part-2</guid>
      </item>
    
      <item>
        <title>Merb and DataMapper Book (Part 1)</title>
        <link>http://blog.new-bamboo.co.uk/2008/4/9/life-on-the-edge-with-merb-datamapper-</link>
        <author>matt
@new-bamboo.co.uk (matt
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/4/9/life-on-the-edge-with-merb-datamapper-#disqus_thread</comments>
        <description><![CDATA[<p>When I started learning Merb and DataMapper I kept a collection of notes to help me keep up with these projects. These grew to the point that they couldn&#8217;t fit nicely into a single text file. With contributions from others I started to put together a small <a href='http://github.com/deimos1986/book_mdar/tree/master'>book</a> on developing web apps with Merb and DataMapper. I have decided to release it as we go. There is also a project in GitHub if you want to check it out and contribute.</p>

<p>So here we go (corrections and comments welcome):</p>

<h2 id='life_on_the_edge_with_merb_datamapper__rspec'>Life On The Edge With Merb, DataMapper &amp; RSpec</h2>

<h2 id='whats_merb_datamapper__rspec'>What&#8217;s Merb, DataMapper &amp; RSpec?</h2>

<blockquote>
<p>If you&#8217;re not living on the edge, you&#8217;re taking up too much room. - Alice Bartlett</p>
</blockquote>

<p>Merb, DataMapper and RSpec are all open source projects, which can be used for building kick-ass web applications. They are all in active development and it can be hard to keep up, but we&#8217;ll try our best to keep up to date.</p>

<h3 id='merb'><a href='http://merbivore.com/'>Merb</a></h3>

<p>It&#8217;s a relatively new framework (a bit like Ruby on Rails) and was created by <a href='http://brainspl.at/'>Ezra Zygmuntowicz</a>. Merb stands for Mongrel + ERB although now it supports the <a href='http://rack.rubyforge.org/'>rack webserver interface</a> so it can user any web server that has rack support (Mongrel, Thin, ebb, etc).</p>

<p>If you know Ruby and have used Rails you&#8217;re likely to get the hang of Merb quite easily. Noticeable differences from Merb to Rails are, it&#8217;s stance on being less opinionated and it&#8217;s approach to modularisation.</p>

<p>Merb is actually made up of a number of gems: <code>merb-core</code>, <code>merb-more</code> and <code>merb-plugins</code>. This is so you can pick and choose the functionality you need. The <code>merb</code> gem installs both <code>merb-core</code> and <code>merb-more</code> so you can get started straight away. The benefit of modularity is it&#8217;s kept simple with additional functionality provided by gems.</p>

<p><code>merb-core</code> alone can be used as an upload server, to provide an API or to create a simple web app (a la <a href='http://code.whytheluckystiff.net/camping/'>camping</a>) where the functionality of a fully fledged framework isn&#8217;t necessary.</p>

<h3 id='datamapper'><a href='http://datamapper.org/'>DataMapper</a></h3>

<p>DataMapper is an Object-Relational Mapper (ORM) written in Ruby, by Sam Smoot. It&#8217;s what we&#8217;ll be using with Merb. It&#8217;s possible to use the same ORM as Rails (ActiveRecord), but as there are plenty of examples of using ActiveRecord already I&#8217;ve chosen to use DataMapper.</p>

<p>It has some nice features which makes it faster than ActiveRecord in some cases. What really stands out for me is the way it handles database attributes. The schema, migrations and attributes are all defined in one place: your model. So you no longer have to look around in your database or other files to see what is defined.</p>

<p>DataMapper has some similarities with ActiveRecord. We will be highlighting the differences as we go along.</p>

<h3 id='rspec'><a href='http://rspec.info/'>RSpec</a></h3>

<p>RSpec is a Behaviour Driven Development framework for Ruby. Merb currently supports the Test::Unit and RSpec testing frameworks. As the specs for Merb and Datamapper are written in RSpec, we will be covering some aspects of RSpec but it will not be our main focus.</p>

<h2 id='what_about_ruby_on_rails'>What About Ruby On Rails?</h2>

<blockquote>
<p>Merb is Harder, Better, Faster, Stronger, to quote Daft Punk - Max Williams</p>
</blockquote>

<p>So what&#8217;s the big deal? We have Ruby on Rails and that&#8217;s enough, isn&#8217;t it? There is little doubt that Ruby on Rails has rocked the web application development world. You have to give credit where credit&#8217;s due, but it can be unforgiving if you don&#8217;t want to do things &#8216;the Rails way&#8217;.</p>

<p>Where Rails is opinionated, Merb is agnostic. You can easily use your favourite ORM (ActiveRecord, DataMapper, Sequel) or none at all, Javascript Library and template language.</p>

<p>Merb also has super-fast routing and is thread-safe (If performant were a word, Merb would be it). The core functionality is kept separate from the other plugins and it uses less Ruby &#8216;magic&#8217;, which makes it easy to understand and hack.</p>

<p>Rails (and consequently Ruby) has received a lot of criticism for not being suitable for large scale web applications, which isn&#8217;t necessarily true and Merb has been built from the outset to prove that Ruby is a viable language for building fast and scaleable web applications.</p>

<p>At the end of the day it&#8217;s about choice. There are many new Ruby frameworks springing up, undoubtedly helped by the success of Rails, but in my opinion Merb shows the most promise as a Rails competitor.</p>

<p>If you&#8217;d like to take a look at some other frameworks these links should get you started:</p>

<ul>
<li><a href='http://camping.rubyforge.org/files/README.html'>http://camping.rubyforge.org/files/README.html</a></li>

<li><a href='http://www.nitroproject.org/'>http://www.nitroproject.org/</a></li>

<li><a href='http://ramaze.rubyforge.org/'>http://ramaze.rubyforge.org/</a></li>

<li><a href='http://sinatra.rubyforge.org/'>http://sinatra.rubyforge.org/</a></li>

<li><a href='http://halcyon.rubyforge.org/'>http://halcyon.rubyforge.org/</a></li>

<li><a href='http://wisteria.swiftcore.org/'>http://wisteria.swiftcore.org/</a></li>
</ul>
<hr />
<p>So that&#8217;s it for now, stay tuned for how to install/set-up merb and datamapper next&#8230;</p>]]></description>
        <pubDate>Wed, 09 Apr 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/4/9/life-on-the-edge-with-merb-datamapper-</guid>
      </item>
    
      <item>
        <title>Euruko 2008</title>
        <link>http://blog.new-bamboo.co.uk/2008/3/30/euruko-2008</link>
        <author>matt@new-bamboo.co.uk (matt)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/3/30/euruko-2008#disqus_thread</comments>
        <description><![CDATA[<p>We&#8217;re at Euroko in Prague, with some of the guys at New Bamboo. It&#8217;s been pretty good so far (crippling flu aside) and Prague is a lovely city.</p>

<p>I gave a talk today on Aspect Orientated Programming in Ruby using Aquarium, it quickly covered what I mentioned in my last post on <a href='http://blog.new-bamboo.co.uk/2007/10/9/aspects-of-aop'>AOP</a>, and went into more detail with actual code examples this time!</p>

<p>The slides are on slideshare, the demo code is currently in a <a href='http://github.com/deimos1986/merb-meet-aop/tree/master'>git repo</a> . The demo app is Merb/Datamapper and I&#8217;m currently trying think of more examples of cool aspects to put in there, feel free to add some.</p>

<p>If anyone saw my talk, I&#8217;d love to hear what you thought of it so drop me a line matt@new-bamboo.co.uk or leave a comment on this post.</p>
<div id='__ss_327739' style='width:425px;text-align:left'><object height='355' style='margin:0px' width='425'><param name='movie' value='http://static.slideshare.net/swf/ssplayer2.swf?doc=aspect-orientated-programming-in-ruby-1206886984742805-4' /><param name='allowFullScreen' value='true' /><param name='allowScriptAccess' value='always' /><embed src='http://static.slideshare.net/swf/ssplayer2.swf?doc=aspect-orientated-programming-in-ruby-1206886984742805-4' type='application/x-shockwave-flash' allowfullscreen='true' allowscriptaccess='always' height='355' width='425' /></object><div style='font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;'><a href='http://www.slideshare.net/?src=embed'><img src='http://static.slideshare.net/swf/logo_embd.png' alt='SlideShare' style='border:0px none;margin-bottom:-5px' /></a> | <a href='http://www.slideshare.net/deimos/aspect-orientated-programming-in-ruby?src=embed' title='View &apos;Aspect Orientated Programming in Ruby&apos; on SlideShare'>View</a> | <a href='http://www.slideshare.net/upload?src=embed'>Upload your own</a></div></div>]]></description>
        <pubDate>Sun, 30 Mar 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/3/30/euruko-2008</guid>
      </item>
    
      <item>
        <title>QCON London 2008</title>
        <link>http://blog.new-bamboo.co.uk/2008/3/16/qcon-london-2008</link>
        <author>matt@new-bamboo.co.uk (matt)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/3/16/qcon-london-2008#disqus_thread</comments>
        <description><![CDATA[<p>I&#8217;ve just spent the last week at &#8220;QCon&#8221;:http://jaoo.dk:80/london-2008/conference/ and I&#8217;ve just about fully recovered (it was pretty intense). Considering there was only one Ruby track I was a little worried that I wouldn&#8217;t find most of the talks interesting, however this wasn&#8217;t the case. The most interesting talks had very little to do with Ruby.</p>

<p>When I go to conferences, my main goal isn&#8217;t to learn lots of things it&#8217;s to be inspired and generate new ideas, consequently my &#8220;things to do in my spare time&#8221; list has grown exponentially.</p>

<p>The two underling themes that I detected from the conference was developers are people too and scaling is important. It was interesting to hear Java guys talk about how to make developing easier (more fun), because this is one area Ruby (in my opinion) excels over most other languages.</p>

<p>One of the main criticisms about the Ruby community is we don&#8217;t care about scaling, and scaling is something that the Java guys have been working on for years. So when &#8220;Ola Bini&#8221;:http://ola-bini.blogspot.com/ says that &#8220;JRuby&#8221;:http://jruby.codehaus.org/ will be the best stack/platform around, I&#8217;m starting to believe him.</p>

<p>Imagine having your Ruby app with objects persisting in something like &#8220;Terracotta&#8221;:http://www.terracotta.org/confluence/display/labs/Terracotta+for+JRuby?</p>

<p>Or being able to use existing &#8220;Java aspects&#8221;:http://www.eclipse.org/aspectj/ on Ruby code (and &#8220;visa versa&#8221;:http://blog.objectmentor.com/articles/2008/02/25/writing-java-aspects-with-jruby-and-aquarium).</p>

<p>There was talk about GemStone porting their &#8220;Smalltalk VM&#8221;:http://www.gemstone.com/products/smalltalk/ to Ruby so we may have an &#8216;enterprise&#8217; stack.</p>

<p>And where the hell are the Ruby Specs? I mean the tests for the language itself. There should be one central repository where YARV, Rubinius, JRuby, GemStone and every Ruby developer out there can contribute to Spec&#8217;in out the language.</p>

<p>They are just a handful of ideas that popped into my head. I had a great time at the &#8220;conference&#8221;:http://flickr.com/photos/tags/qconlondon2008/ and met many interesting people, hopefully I&#8217;ll be able to do it again next year!</p>

<p>The slides (videos soon) from the talks are going &#8220;online&#8221;:http://jaoo.dk/london-2008/schedule/thursday.jsp so take a look. Please excuse me while I go play with Erlang.</p>

<p><em>UPDATE</em> I&#8217;ve put some of the slides that are available on the QCon site onto slideshare tagged with &#8220;QCONLONDON2008&#8221;:http://www.slideshare.net/tag/qconlondon2008 , enjoy.</p>
<div id='__ss_309239' style='width:425px;text-align:left'><object height='355' style='margin:0px' width='425'><param name='movie' value='http://static.slideshare.net/swf/ssplayer2.swf?doc=l-inda-rising-born-to-cycle-1205704574243690-5' /><param name='allowFullScreen' value='true' /><param name='allowScriptAccess' value='always' /><embed src='http://static.slideshare.net/swf/ssplayer2.swf?doc=l-inda-rising-born-to-cycle-1205704574243690-5' type='application/x-shockwave-flash' allowfullscreen='true' allowscriptaccess='always' height='355' width='425' /></object><div style='font-size:11px;font-family:tahoma,arial;height:26px;padding-top:2px;'><a href='http://www.slideshare.net/?src=embed'><img src='http://static.slideshare.net/swf/logo_embd.png' alt='SlideShare' style='border:0px none;margin-bottom:-5px' /></a> | <a href='http://www.slideshare.net/deimos/l-inda-rising-born-to-cycle?src=embed' title='View &apos;Linda Rising Born To Cycle&apos; on SlideShare'>View</a> | <a href='http://www.slideshare.net/upload?src=embed'>Upload your own</a></div></div>]]></description>
        <pubDate>Sun, 16 Mar 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/3/16/qcon-london-2008</guid>
      </item>
    
      <item>
        <title>Nested layouts in Merb</title>
        <link>http://blog.new-bamboo.co.uk/2008/3/15/nested-layouts-in-merb</link>
        <author>martyn@new-bamboo.co.uk (martyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/3/15/nested-layouts-in-merb#disqus_thread</comments>
        <description><![CDATA[<p>First a simple solution using <code>throw_content</code> and <code>catch_content</code>.</p>

<p>This is what <code>sublayout.html.erb</code> looks like</p>

<pre><code>&lt;% throw_content :sublayout do %&gt;
  &lt;div class=&quot;header&quot;&gt;
    ...
  &lt;/div&gt;
  
  &lt;%= catch_content :for_layout %&gt;
  
  &lt;div class=&quot;footer&quot;&gt;
    ...
  &lt;/div&gt;
&lt;% end %&gt;

&lt;%= render catch_content(:sublayout), :layout =&gt; &quot;application&quot; %&gt;</code></pre>

<p>Your main layout contains <code>&lt;%= catch_content :for_layout %&gt;</code> as usual.</p>
<hr />
<p>You might find it handy to use catch and throw content in a few more places. For example, in your sublayout you could <code>&lt;%= catch_content :sublayout_title %&gt;</code> and throw it in the view with</p>

<pre><code>&lt;% throw_content :sublayout_title %&gt;
  Hippos
&lt;% end %&gt;</code></pre>

<p>As a little bonus I wrote a helper so that you can use the same syntax as you&#8217;re used to from the <a href='http://nested-layouts.rubyforge.org/'>rails plugin</a>. Just add this snippet into <code>Merb::GlobalHelpers</code></p>

<pre><code>def inside_layout(layout, &amp;block)
  content = render(capture(&amp;block), :layout =&gt; layout)
  concat(content, block.binding)
end</code></pre>

<p>Then your sublayout looks like</p>

<pre><code>&lt;% inside_layout :sublayout do %&gt;
  &lt;div class=&quot;header&quot;&gt;
    ...
  &lt;/div&gt;
  
  &lt;%= catch_content :for_layout %&gt;
  
  &lt;div class=&quot;footer&quot;&gt;
    ...
  &lt;/div&gt;
&lt;% end %&gt;</code></pre>
<hr />
<p>Here&#8217;s another idea which I think would be even better: What if you could say <code>layout [&#39;application&#39;, &#39;sublayout&#39;]</code> in your controller and have each layout rendered inside it&#8217;s parent all up the chain? Then <code>sublayout.html.erb</code> wouldn&#8217;t be constrained to always render inside <code>application.html.erb</code> and you could use it in a much more flexible way. However Damien things it&#8217;s a mad idea. What do you think?</p>]]></description>
        <pubDate>Sat, 15 Mar 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/3/15/nested-layouts-in-merb</guid>
      </item>
    
      <item>
        <title>Freeze gems in a Merb application</title>
        <link>http://blog.new-bamboo.co.uk/2008/3/14/freeze-gems-in-a-merb-application</link>
        <author>martyn@new-bamboo.co.uk (martyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/3/14/freeze-gems-in-a-merb-application#disqus_thread</comments>
        <description><![CDATA[<p>Frozen gems in Merb == gems installed inside the application.</p>

<p>That&#8217;s clever because now you can use the standard tools for installing and managing the installed gems. For example, you get dependency checking and so on for free. Add <code>-i gems</code> to the command line options to specify the install location.</p>

<pre><code>gem install merb-core -i gems</code></pre>

<p>That&#8217;s the simplest case (run it from the root of you merb app). It fetches the <code>merb-core</code> gem from the gem servers and installs it inside a <code>gems</code> directory.</p>

<p>However, in many cases that&#8217;s not what you want. Really you want to freeze the version you&#8217;re currently developing on. Maybe one you&#8217;ve installed from the merb-core git repository. That&#8217;s really easy because you have the gem package files on your system already. For me they&#8217;re in <code>/Library/Ruby/Gems/1.8/cache</code>. Get your location by running <code>gem environment gemdir</code> and look for a <code>cache</code> folder. Install the package with</p>

<pre><code>gem install /Library/Ruby/Gems/1.8/cache/merb-core-0.9.1.gem -i gems</code></pre>

<p>You should add <code>/gems/doc</code> to your .gitignore since you definitely don&#8217;t want ri and rdoc files in your repository. Alternatively add <code>--no-ri --no-rdoc</code> to the <code>gem install</code> command.</p>

<p>The final challenge is actually getting merb started. <strike>Right now I'm relying on a [pastie](http://pastie.caboo.se/pastes/164289) that google found for me (I'm sorry I have no idea who the author is). Make that executable and use it to run merb with the usual merb command line options. That's surely a bit of a hack though and I'm hoping that someone will point out a better way :)</strike> <strong>Update:</strong> Thanks Steve - I hadn&#8217;t spotted the generator. Run <code>merb-gen freezer frozen-merb</code> and then run Merb with <code>./script/frozen-merb</code> passing the usual merb command line options.</p>]]></description>
        <pubDate>Fri, 14 Mar 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/3/14/freeze-gems-in-a-merb-application</guid>
      </item>
    
      <item>
        <title>How to use git / github with capistrano</title>
        <link>http://blog.new-bamboo.co.uk/2008/3/12/github-with-capistrano</link>
        <author>martyn@new-bamboo.co.uk (martyn)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/3/12/github-with-capistrano#disqus_thread</comments>
        <description><![CDATA[<p>I&#8217;m assuming that you&#8217;re using private key authentication for git and that you&#8217;ve got an ssh agent set up (if you&#8217;re running Leopard then you automatically have this).</p>

<p>The trick is to use ssh agent forwarding, now supported in capistrano. It allows the server to pull the latest code from github using your local private key and ssh agent. To understand how this trickery works take a look at this <a href='http://www.unixwiz.net/techtips/ssh-agent-forwarding.html'>illustrated guide</a>.</p>

<pre><code>set :ssh_options, { :forward_agent =&gt; true }</code></pre>

<p>One of the great things about git is that it&#8217;s really fast, even if you&#8217;re cloning an entire repository. However you can go even faster by using the <code>repository_cache</code> option in capistrano. This essentially keeps a clone of your app on the server and then just does a <code>git pull</code> to fetch new changes and copies the directory across when you deploy.</p>

<pre><code>set :repository_cache, &quot;git_cache&quot;
set :deploy_via, :remote_cache</code></pre>

<p>Another recommended strategy is to specify a git branch. Otherwise you&#8217;ll default to deploying HEAD which might be some crazy experiment.</p>

<pre><code>set :branch, &quot;stable&quot;</code></pre>

<p>In case you haven&#8217;t used remote branches they&#8217;re really easy. Say you have a local stable branch. Push this to github using</p>

<pre><code>git push github stable</code></pre>

<p>So the complete capistrano file should look something like</p>

<pre><code>set :application, &quot;your cool app&quot;

# Source code
set :scm, :git
set :repository, &quot;git@github.com:githubusername/gitrepo.git&quot;
set :branch, &quot;stable&quot;
set :repository_cache, &quot;git_cache&quot;
set :deploy_via, :remote_cache
set :ssh_options, { :forward_agent =&gt; true }

# Deployment servers
role :app, &quot;your server&quot;
role :web, &quot;your server&quot;
role :db,  &quot;your server&quot;, :primary =&gt; true
set :deploy_to, &quot;/var/www/#{application}&quot;</code></pre>]]></description>
        <pubDate>Wed, 12 Mar 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/3/12/github-with-capistrano</guid>
      </item>
    
      <item>
        <title>Github + Textmate Wiki bundle = Giki</title>
        <link>http://blog.new-bamboo.co.uk/2008/3/11/github-textmate-wiki-bundle-giki</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/3/11/github-textmate-wiki-bundle-giki#disqus_thread</comments>
        <description><![CDATA[<p>In the past year we&#8217;ve probably gone through about 3 different wikis. Either they don&#8217;t have things like <a href='http://stikipad.com/'>email notifications with a diff</a>, <a href='http://twiki.org/'>have their own special markup language</a> or <a href='http://pbwiki.com/'>cost what is unwieldy amount for just a wiki</a>.</p>

<p>We&#8217;ve scrapped all of these and have now migrated to a private <a href='http://github.com'>Github</a> repo containing markdown files managed with the help of the <a href='http://interconnected.org/home/more/2007/05/textmate-wiki/'>Textmate Wiki Bundle</a>. The simplest solutions are the best.</p>]]></description>
        <pubDate>Tue, 11 Mar 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/3/11/github-textmate-wiki-bundle-giki</guid>
      </item>
    
      <item>
        <title>Using the Nginx Upload Progress Module with Safari</title>
        <link>http://blog.new-bamboo.co.uk/2008/2/29/using-the-nginx-upload-progress-module</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/2/29/using-the-nginx-upload-progress-module#disqus_thread</comments>
        <description><![CDATA[<p>When using the <a href='http://wiki.codemongers.com/NginxHttpUploadProgressModule'>nginx_uploadprogress_module</a> you need to set the X-Progress-ID header in your progress polls. Safari sanitises header names, so X-Progress-ID ends up becoming X-Progress-Id in your request. Unfortunately Firefox does just the oppose renaming X-Progress-Id to X-Progress-ID (but only for this particular header). The current release of nginx_uploadprogress_module is case sensitive, so I&#8217;ve patched it to be case insensitive.</p>

<p><a href='http://pastie.caboo.se/pastes/159308'>Nginx Upload Progress Module Safari patch</a></p>]]></description>
        <pubDate>Fri, 29 Feb 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/2/29/using-the-nginx-upload-progress-module</guid>
      </item>
    
      <item>
        <title>HTTP/AJAX debugging with Charles</title>
        <link>http://blog.new-bamboo.co.uk/2008/2/29/http-ajax-debugging-with-charles</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/2/29/http-ajax-debugging-with-charles#disqus_thread</comments>
        <description><![CDATA[<p>If you ever need to inspect an AJAX request from a browser such as Safari (which doesn&#8217;t quite have a Firebug equivalent], <a href='http://www.xk72.com/charles/'>Charles</a> is a useful debugging proxy.</p>]]></description>
        <pubDate>Fri, 29 Feb 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/2/29/http-ajax-debugging-with-charles</guid>
      </item>
    
      <item>
        <title>IrregularScience</title>
        <link>http://blog.new-bamboo.co.uk/2008/2/19/irregularscience</link>
        <author>martyn
@new-bamboo.co.uk (martyn
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/2/19/irregularscience#disqus_thread</comments>
        <description><![CDATA[<p><a href='http://seattlerb.rubyforge.org/ImageScience.html'>ImageScience</a> is great. Just look at the code! One beautiful class wraps up FreeImage with some jucy inline C. If you can install FreeImage then nothing can go wrong. Much better than a bazillion lines of RMagick (which sometimes still can&#8217;t do what you need)!</p>

<p>Simplicity is great but unfortunately ImageScience has this infatuation with squares. I&#8217;ll explain.</p>

<p>Image science has two low level methods which allow arbitrary cropping and resizing. They look like this:</p>

<pre><code>image.with_crop(left, top, right, bottom)

image.resize(width, height)</code></pre>

<p>However what you really want to do is usually a combination of cropping and resizing. ImageScience helpfully provides:</p>

<pre><code># Resizes to fit within a square
image.thumbnail(size)

# Resizes and crops to be exactly a square
image.cropped_thumbnail(size)</code></pre>

<p>This is great until you need an avatar that&#8217;s a fixed &#8230; rectangle. Or an image that will fit exactly inside a &#8230; rectangle.</p>

<p>In this case you need IrregularScience (aka irregular_science)!</p>

<p>You&#8217;ll get rectangular versions of thumbnail and cropped_thumbnail:</p>

<pre><code># Resizes to fit within a rectangle
image.resize_within(width, height)

# Resizes and crops to be exactly a rectangle
image.resize_exact(width, height)</code></pre>

<p>I gave my methods slightly different names because I&#8217;m like that ;)</p>

<p>There&#8217;s also the case where you want a exact height or an exact width regardless of the other dimension (ever seen facebook?). That turns out to be a really horrible hack with RMagick involving all sorts of squashing and stretching unless you go really low level. Just follow these incantations and know that no horrible hacks are going on:</p>

<pre><code>image.resize_to_width(width)

image.resize_to_height(height)</code></pre>

<p>To get started download <a href='http://gist.github.com/65818'>irregular_science.rb</a> and require it. Then adapt this example:</p>

<pre><code>ImageScience.with_image(upload_path) do |image|
  self.thumbnails.each do |name, size|
    image.resize_exact(*size) do |img|
      img.save(attachment_path(name))
    end
  end
end</code></pre>

<p>Enjoy!</p>

<p>P.S. This seems to be my first blog post here - so hi!</p>]]></description>
        <pubDate>Tue, 19 Feb 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/2/19/irregularscience</guid>
      </item>
    
      <item>
        <title>Micro-patterns in Ruby</title>
        <link>http://blog.new-bamboo.co.uk/2008/2/5/micro-patterns-in-ruby</link>
        <author>ismael
@new-bamboo.co.uk (ismael
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/2/5/micro-patterns-in-ruby#disqus_thread</comments>
        <description><![CDATA[<p>Yup. <em>Micro-patterns</em>. I've always wanted to begin a blog post with some highly sophisticated buzzword so it might as well be this one (sadly, I can't say <a href='http://portal.acm.org/citation.cfm?id=1094819' title='Micro Patterns in Java'>I'm the first to use it</a>).</p><p>
This is a tiny one. Let's say we have a <em>Factory</em> that gives me <em>Adapters</em><a href='#nota'>#1</a> to a series of hosted media services (Flickr, YouTube, Vimeo, PhotoBucket, etc.).
</p><p>
The user of my code provides a service prefix and a URL to a picture or photo page in a given service, and the factory returns an instance from which the user can fetch different media sizes through a standardized <abbr title='Application Programming Interface'>API</abbr>.
</p><pre><code class='ruby'>
flickr_pic = ServiceParser.instance( 
  'flickr', 'http://www.flickr.com/photos/new_bamboo_london/2158168775/'
)

puts flickr_pic.thumbnail  
# => http://farm3.static.flickr.com/2147/2158168775_01ae89cbfb_s.jpg

yt_video = ServiceParser.instance( 
  'youtube', 'http://www.youtube.com/watch?v=PCunD-_mOwQ' 
)

puts yt_video.thumbnail 
# => http://i.ytimg.com/vi/PCunD-_mOwQ/default.jpg
</code></pre><p>
Simple enough. But it seems a bit redundant to have the user declare both the service name and the page URL. Since URL's are unique anyway, those should be enough for our smart factory to know what <em>adapter</em> to hand us, kindly.
</p><p>
We could have a set of regular expressions in the factory, one for each URL/service.
</p><pre><code class='ruby'>
class ServiceParser
  SERVICES = {
    FlickrAdapter   =&gt; /flickr\.com/,
    YouTubeAdapter  =&gt; /youtube\.com/,
    VimeoAdapter  =&gt; /vimeo\.com/,
    PhotoBucketAdapter  =&gt; /photobucket\.com/
  }
  # Factory method
  #
  def self.instance( url )
    subclass = SERVICES.find(nil) {|klass, exp| url =~ exp}
    raise &#x27;not implemented&#x27; if subclass.nil?
    subclass.new url
  end
end
</code></pre><p>
We <em>could</em>. But if we did, all hell would break loose and a mob of angry Design Patterns advocates would ram our door and... reprimand us.
</p><p>
While they're at it, they would tell us that one of the many golden rules of Object Oriented software design is, blockquote please:
</p><blockquote>
The superclass should have no knowledge of the subclasses.
</blockquote><p>
But you already know that. We need to take those URL's out of the factory class and into where they belong, in each adapter class. Also, adapters should be able to register their URL's with the factory, so it knows where to look. Ruby to the rescue.
</p><pre><code class='ruby'>
# = The superclass / factory
class ServiceParser
  # We register adapters into this class variable
  #
  @@adapters = []
  
  # class reader
  #
  def self.adapters
    @@adapters
  end

  # Factory method here, se below...
  #
end

# = An example subclass / adapter
class FlickrAdapter &lt; ServiceParser
  def initialize( url)
  
  end

  # API methods
  #
  def thumbnail
    # do something clever here...
  end
  
  # register ourselves with the Factory
  #
  ServiceParser.adapters &lt;&lt; [self, /flickr\.com/]
end
</code></pre><p>
The fun thing about Ruby, class definitions being executable code and all, is that you can just have subclasses add themselves (and their corresponding regex.) to the factory's list of adapters in load time.
</p><pre><code class='ruby'>
 ServiceParser.adapters &lt;&lt; [self, /flickr\.com/]
</code></pre><p>
Then it is a matter of modifying the factory method to search for matching URL's in the dynamically populated list of subclasses / URL expressions.
</p><pre><code class='ruby'>
# Refactored factory. No pun intended.
#
def self.instance( url )
  match = @@adapters.find(nil) {|klass, exp| url =~ exp}
  raise 'not implemented' if match.nil?
  match.first.new url
end
</code></pre><p>
Finally, a little more elegance to make our adapter authors happy (and less prone to mess with the internals of our superclass!)
</p><pre><code class='ruby'>
class FlickrAdapter &lt; ServiceParser
  # Initializer, API and protected methods here...
  #
  # We register our URL with a neat class method on the superclass
  #
  register_url /flickr\.com/
end

class ServiceParser
  # Factory, abstract methods and cross-adapter utilities here...
  #
  # Class method for subclasses to register themselves.
  #
  @@adapters = [] #2 See note on class variables

  def self.register_url( exp )
    @@adapters &lt;&lt; [self, exp]
  end

end
</code></pre><p id='nota'>
<strong>#1</strong> <small>Two more buzzwords and just the second paragraph! And there's more...</small>
</p><p>
<strong>#2 </strong><small><strong>Class @@variables</strong>. These variables are not inherited but <em>shared</em> by all classes in the inheritance tree. That means that, whenever a subclass adds to the superclass' version of @@adapters, it'll actually have the new value in all subclasses. This is not an issue in our simple example, though.</small>
</p>]]></description>
        <pubDate>Tue, 05 Feb 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/2/5/micro-patterns-in-ruby</guid>
      </item>
    
      <item>
        <title>Code, glorious code...</title>
        <link>http://blog.new-bamboo.co.uk/2008/1/22/code-glorious-code</link>
        <author>andy@new-bamboo.co.uk (andy)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/1/22/code-glorious-code#disqus_thread</comments>
        <description><![CDATA[<p>&#8230;we live for it and treat it like our own child, nurturing it into maturity, but there comes a time when every child must flee the nest so&#8230;</p>

<h2 id='were_giving_it_away'>&#8230;we&#8217;re giving it away&#8230;</h2>

<p>&#8230;that&#8217;s right folks, I&#8217;d like to announce the introduction of <strong><a href='http://code.new-bamboo.co.uk'>code.new-bamboo.co.uk</a></strong>.</p>

<p>We hope that over time <a href='http://code.new-bamboo.co.uk'>code.new-bamboo.co.uk</a> will grow to become a great resource for open source projects and code snippets. To kick us off we have&#8230;</p>

<ul>
<li>PolyPage - A jQuery plugin for adding state to wireframes.</li>

<li>JSS - a jQuery plugin for adding CSS3 selector support for your stylesheets!</li>

<li>The Index Inspector - A rails specific rake task for probing your database indexes.</li>

<li>Dogger - A fun little log helper.</li>
</ul>

<p>I hope you&#8217;ll agree we are off to a great start but we&#8217;d love some help so if you have any ideas or suggestions then sound off in the comments for now. hopefully there will be a more formal issue tracker setup soon.</p>

<p><em>The jQuery plugins are released under the same licensing terms as jQuery itself, snippets aren&#8217;t formally licensed but feel free to use and distribute them as you please.</em></p>]]></description>
        <pubDate>Tue, 22 Jan 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/1/22/code-glorious-code</guid>
      </item>
    
      <item>
        <title>ActiveRecord != DataMapper</title>
        <link>http://blog.new-bamboo.co.uk/2008/1/16/activerecord-datamapper</link>
        <author>matt
@new-bamboo.co.uk (matt
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/1/16/activerecord-datamapper#disqus_thread</comments>
        <description><![CDATA[<p>As a follow-up to <a href='http://blog.new-bamboo.co.uk/2008/1/14/rails-merb'>Rails || Merb</a>, I&#8217;m going to highlight some of the differences between ActiveRecord (AR) and <a href='http://datamapper.org/'>Datamapper</a>. Merb is ORM agnostic so you can still use AR if you want to, but I thought I might as well try DataMapper out.</p>

<p>Datamapper (DM) isn&#8217;t really production ready at the moment as migrations are destructive, but hopefully that&#8217;ll change in the next few months.</p>

<p>The first thing you&#8217;ll notice is that in DM the properties of the database are in your model, where in AR they are in a migration.</p>

<p>Take a look at this example DM model:</p>
<pre><code class='ruby'>
class Post &lt; DataMapper::Base
  property :title,      :string
  property :textile,    :text
  property :created_at, :datetime
end

rake dm:db:automigrate || DataMapper::Base.auto_migrate!
</code></pre>
<p>With AR this would be in a migration, not to dissimilar to this:</p>
<pre><code class='ruby'>
class CreatePosts &lt; ActiveRecord::Migration
  def self.up
  create_table :posts do |t|
    t.string :title
    t.text   :textile
    t.timestamps
    end
  end

  def self.down
    drop_table :posts
  end
end

rake db:migrate
</code></pre>
<p>I should point out that just like AR the <tt>created_at</tt> field is automatically filled in by DataMapper.</p>

<p>DataMapper has a few ActiveRecord impersonations such as the follow methods: <tt>[], all, count, create, create!, delete_all, each, find, find_by_sql, find_or_create, first,  get, truncate!, destroy!, included, reload, reload!, save  </tt></p>

<p>Here is a list of some syntax comparisons:</p>
<table>
<tr><th>*The AR way*</th><th>*The DM way*</th></tr>
<tr><td>User.find(:all)</td><td>User.all</td></tr>
<tr><td>User.find(10)</td><td>User[ 10 ]</td></tr>
<tr><td>@user.update_attributes(:name => 'Matt')</td><td>@user.update_attributes(:name => 'Matt')</td></tr>
<tr><td>User.find(:all, :conditions => :sex = 'Female, :age > 21)</td><td>User.all(:sex => 'Female', :age.gt =>21)</td></tr>
<tr><td>User.find(:all, :conditions => ["id IN (?)", [1,3,37]]</td><td>User.all(:id.in => [1,3,37])</td></tr>
<tr><td>has_many :through</td><td> _not implemented yet_</td></tr>
</table>
<p>Something you might want to watch out for, <a href='http://www.jaikoo.com/2007/11/7/subtleness'>covered by Jon</a> is that DM will only persist changes that it manages, so if you&#8217;re using virtual attributes you&#8217;ll need to mark the model as dirty. Also there are currently bugs in finding with conditions on associations and count.</p>]]></description>
        <pubDate>Wed, 16 Jan 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/1/16/activerecord-datamapper</guid>
      </item>
    
      <item>
        <title>Rails || Merb</title>
        <link>http://blog.new-bamboo.co.uk/2008/1/14/rails-merb</link>
        <author>matt
@new-bamboo.co.uk (matt
)</author>
        <comments>http://blog.new-bamboo.co.uk/2008/1/14/rails-merb#disqus_thread</comments>
        <description><![CDATA[<p>After playing around with <a href='http://Merbivore.com/'>Merb</a> and <a href='http://datamapper.org/'>data mapper</a> over the holidays, I thought a post on some of the differences that I&#8217;ve found between rails and Merb might be helpful to someone thinking about trying Merb out.</p>

<p>If you haven&#8217;t already installed Merb, check out Jon&#8217;s <a href='http://www.jaikoo.com/2008/1/10/Merb-datamapper-and-Merbful_authenticaion-my-lovelies'>awesome screencast</a>, that should get you started.</p>

<p>Here&#8217;s my list<sup id='fnref:1'><a href='#fn:1' rel='footnote'>1</a></sup>:</p>
<table>
  <tr><th>The Rails way</th><th>The Merb way</th></tr>
  <tr><td>script/server</td><td>merb</td></tr>
  <tr><td>script/console</td><td>merb -i</td></tr>
  <tr><td>redirect_to blog_path(@blog)</td><td>redirect url(:blog, @blog)</td></tr>
  <tr><td>respond_to </td><td>provides :xml, :js, :yaml</td></tr>
  <tr><td>format</td><td>case content_type</td></tr>
  <tr><td>render :xml => @post</td><td>render @post</td></tr>
  <tr><td>render :file => 'public/404.html', :status => 404 </td><td>raise NotFound</td></tr>
  <tr><td>format.html</td><td>only_provides :html, :xml</td></tr>
  <tr><td>logger</td><td>Merb.logger</td></tr>
  <tr><td>before_filter</td><td>before</td></tr>
  <tr><td>render :partial</td><td>partial</td></tr>
  <tr><td>f.text_field :name</td><td>text_control :first_name, :label => 'First Name'</td></tr>
  <tr><td>RAILS_ENV</td><td>MERB_ENV post v0.5.0 Merb.environment</td></tr>
</table><div class='footnotes'><hr /><ol><li id='fn:1'>
<p>This list is by no means a complete list and I can&#8217;t guaranty that they won&#8217;t change as Merb is in active development, but feel free comment with anything I have missed.</p>
<a href='#fnref:1' rev='footnote'>&#8617;</a></li></ol></div>]]></description>
        <pubDate>Mon, 14 Jan 2008 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2008/1/14/rails-merb</guid>
      </item>
    
      <item>
        <title>Super simple image resizing</title>
        <link>http://blog.new-bamboo.co.uk/2007/12/3/super-f-simple-resizing</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/12/3/super-f-simple-resizing#disqus_thread</comments>
        <description><![CDATA[<p>Everyone knows <a href='http://www.imagemagick.org'>ImageMagick</a> is a goddam pain to install and a beast of a library to use for simple image resizing and cropping. <a href='http://seattlerb.rubyforge.org/ImageScience.html'>ImageScience</a> is on the way, but it's still easy to bump into issues compiling Freeimage. If all you want to do is resize (and crop) <a href='http://www.libgd.org'>libgd</a> is even more lightweight (and tiny enough to do away with DarwinPorts). With a bit of inline C we can do some really lean image resizing.</p><p>SfsResize (<a href='http://blog.new-bamboo.co.uk/assets/2007/12/3/sfs_resize.rb'>sfs_resize.rb</a>) is a super simple image resizer. Compile libgd in a few minutes (there is an <a href='http://www.libgd.org/DOC_INSTALL_OSX'>OS X how-to</a>) and then use it like this:</p><pre><code class='ruby'>
w = 100
h = 100
s = SfsResize.new("in.gif")
s.resize_and_write_to_file("out.gif", w, h)
</code></pre><p>Severely untested, but good fun nevertheless.</p>]]></description>
        <pubDate>Mon, 03 Dec 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/12/3/super-f-simple-resizing</guid>
      </item>
    
      <item>
        <title>Understanding Merb</title>
        <link>http://blog.new-bamboo.co.uk/2007/12/2/understanding-merb</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/12/2/understanding-merb#disqus_thread</comments>
        <description><![CDATA[<p>If you&#8217;re interested in learning more about innards of Merb, Ezra has written a <a href='http://brainspl.at/articles/2007/12/02/a-quick-jaunt-through-merbs-framework-code'>brilliant walkthrough of the framework code</a>. Merb&#8217;s lightweight nature mean&#8217;s this more of a Sunday stroll compared to the hike which Rails would involve.</p>

<p>Update: Shalev NessAiver has made a <a href='http://codingmind.wordpress.com/2007/12/03/merb-jaunt-graphic-pdf/'>pretty diagram</a>!</p>]]></description>
        <pubDate>Sun, 02 Dec 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/12/2/understanding-merb</guid>
      </item>
    
      <item>
        <title>Upload progress with Nginx</title>
        <link>http://blog.new-bamboo.co.uk/2007/11/23/upload-progress-with-nginx</link>
        <author>damien
@new-bamboo.co.uk (damien
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/11/23/upload-progress-with-nginx#disqus_thread</comments>
        <description><![CDATA[<p>Last month Brice Figureau released a little Nginx module which tracks the progress of uploads going through Nginx. Like Lighttpd's <a href='http://blog.lighttpd.net/articles/2006/08/01/mod_uploadprogress-is-back'>mod_uploadprogress</a> it's very nice to have the proxy take responsibility for reporting the progress.</p><img class='picture' src='http://blog.new-bamboo.co.uk/assets/2007/11/23/nginxupload.png' /><p>The Nginx upload progress module adds track_uploads and report_uploads directives which do what they say on the tin. When the user submits a form to the location supporting track_upload, we also send an extra X-Progress-ID parameter with a unique id for that upload. Requests made to the location supporting report_uploads (typically /uploads) will return JSON with the current progress for an upload. The X-Progress-ID header is added to the ajax requests to identify which upload we're wanting to know the status of.</p><p>Having Nginx track the progress gets around an issue with using the merb upload progress method. Nginx (un)fortunately does not pass along the upload to its destination until it has fully completed uploading. So when you upload a file, your Merb application won't know about it until it has been upload to Nginx and passed along. The only gripe with this whole process is the possibility of a small delay as Merb handles the request once the upload is complete. If you're using Rails however this can be an advantage, as it won't be tied up by the upload as it happens in realtime.</p><p>To get this all going you'll first want to download the <a href='http://wiki.codemongers.com/NginxHttpUploadProgressModule?action=AttachFile&amp;do=get&amp;target=nginx_uploadprogress_module-0.2.tar.gz'>Nginx upload progress module</a> and recompile Nginx with the following configure option.</p><pre>
--add-module=path/to/nginx_uploadprogress_module
</pre><p>Locate and open your nginx configuration file and add in the upload_progress, track_uploads and report_uploads directives.</p><p>If you're going to upload files of any considerable size you'll also want to the <a href='http://wiki.codemongers.com/NginxHttpCoreModule#client_max_body_size'>max_body_side</a> to something reasonable.</p><pre>
http {
	upload_progress proxied 1m;

	server {
		listen 80;
		server_name _ *;
		root /usr/local/www;

		location / {
			proxy_pass http://127.0.0.1:4000;
			track_uploads proxied 30s;
			client_max_body_size 500m;
		}

		location ^~ /progress {
			report_uploads proxied;
		}
	}
}
</pre><p>Nginx is now ready to track uploads! Generate a Merb project and add a new upload controller.</p><pre><code class='ruby'>
class Uploader &lt; Application
	def index
		render
	end

	def upload
		FileUtils.mv params[:file][:tempfile].path, \
MERB_ROOT+&quot;/files/#{params[:file][:filename]}&quot;
	end
end
</code></pre><p>Then add the upload view.</p><pre><code class='ruby'>
&lt;div id=&quot;pandaloader&quot;&gt;
	&lt;div id=&quot;uploader&quot;&gt;
		&lt;form id=&quot;upload&quot; enctype=&quot;multipart/form-data&quot; \
action=&quot;/uploader/upload&quot; method=&quot;post&quot;&gt;
			&lt;input name=&quot;file&quot; type=&quot;file&quot;&gt;
			&lt;input type=&quot;submit&quot; value=&quot;Upload&quot;&gt;
		&lt;/form&gt;
	&lt;/div&gt;

	&lt;div id=&quot;uploading&quot;&gt;
		&lt;div id=&quot;progress&quot; class=&quot;bar&quot;&gt;
			&lt;div id=&quot;progressbar&quot;&gt;&amp;nbsp;&lt;/div&gt;
		&lt;/div&gt;
	&lt;/div&gt;
&lt;/div&gt;
</code></pre><p>In the header of your application's layout you'll also need to include the javascript and css.</p><pre><code class='ruby'>
<script type='text/javascript' charset='utf-8'>
	$(function() {
		$('#upload').nginxUploadProgress();
	});
</script>
</code></pre><p>Aside from <a href='http://jquery.info'>jQuery</a>, you'll notice the jquery.nginxUploadProgress.js plugin which has been included. This is a little jQuery library which will query Nginx for the upload's progress and update the length of the progress bar accordingly. Grab the file here: <a href='http://blog.new-bamboo.co.uk/assets/2007/11/23/jquery.nginxUploadProgress.js'>jquery.nginxUploadProgress.js</a></p><p>Here is some styling for the progress bar.</p><pre><code class='css'>
.bar {
  width: 300px;
}

#progress {
  background: #eee;
  border: 1px solid #222;
}
#progressbar {
  width: 0px;
  height: 24px;
  background: #333;
}
</code></pre><p>Put all these together and you'll have a great little progress bar for uploads!</p>]]></description>
        <pubDate>Fri, 23 Nov 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/11/23/upload-progress-with-nginx</guid>
      </item>
    
      <item>
        <title>Database Optimization for Rails Apps</title>
        <link>http://blog.new-bamboo.co.uk/2007/11/20/database-optimization-for-rails-apps</link>
        <author>gwyn
@new-bamboo.co.uk (gwyn
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/11/20/database-optimization-for-rails-apps#disqus_thread</comments>
        <description><![CDATA[<h2>Summary</h2><ul>
<li>Use STRAIGHT_JOIN if MySQL is doing something silly</li>
<li>MySQL Datetimes are not slow</li>
<li> MyISAM is much, much faster than InnoDB</li>
<li> Consider MySQL partitioning; it's not hard</li>
<li> Watch out for background optimisation and query caching</li>
<li> Log everything <b>you</b> do, and <b>why</b></li>
</ul><p>The reporting application I'm working on does a date-range select against a table with ten million rows. It took over two minutes to execute. I cut it down to around two seconds without significantly changing the application. Here's how.</p><h2>Straight Joins</h2><p>The original SQL statement was 12 lines, featuring three joins, a group-by and a computed column. </p><p>To start with I used EXPLAIN SELECT to look at the execution plan. MySQL had made a bad guess, and was table-scanning a four-million-row related table that could have been joined by ID instead. </p><p>I added STRAIGHT_JOIN to the SQL statement and dropped the execution time to 20 seconds. </p><p><span class='pullme'>Use STRAIGHT_JOIN if MySQL is doing something silly.</span></p><h2>Finding the Weak Point</h2><p>Progress; MySQL was now table-scanning a 10 million row 'events' table to satisfy the WHERE clause, then joining the other three tables by ID, grouping and summing. I cut the query down progressively to a simple <b>"SELECT count(*) FROM events WHERE starttime &lt; &#x27;2006-12-30 08:30:00&#x27; AND endtime &gt; &#x27;2006-12-30 08:15:00'"</b> and found that performance didn't significantly change. </p><p>This isolated the slow part of the query. My original plan had been to denormalize the table structure, but there was no point.</p><p>At this point I built a test database of two million rows on my local machine with only the one table in question, and only the three columns that were needed.</p><h2>Indexes</h2><p>I added indexes on starttime, endtime, and (starttime,endtime), but the MySQL optimiser chose not to use them. When I forced it to use them, using FORCE INDEX, performance deteriorated (!). OPTIMIZE TABLE made no difference.</p><h2>Dead End: Int Conversion</h2><p>The application needs only 15-minute resolution on the date times, so I created start_int and finish_int columns and filled them:</p><pre>update events set start_int = floor(unix_timestamp(starttime)/900) 
update events set finish_int = floor(unix_timestamp(endtime)/900)</pre><p>Then I created indexes on the columns and tested again. The plan here was that, by reducing the resolution, there'd be fewer unique values and the indexes would be more effective.</p><p>It didn't work. Tablescanning against datetimes was <b>faster</b> than indexed lookup against ints.</p><p><span class='pullme'>MySQL DateTimes are not slow.</span></p><h2>Switching To MyISAM</h2><p>I'd noticed that my test database was performing much faster than the production database, even taking into account the smaller size. Running SHOW CREATE TABLE against the two revealed that I was using MyISAM, but the production database had InnoDB. I converted my local database to InnoDB and took a speed hit on the order of 6x.</p><p><span class='pullme'>MyISAM is <b>much</b> faster than InnoDB.</span> (but <a href='#notes'>see notes</a>)</p><p>Note that the conversion process is hideously slow; it's faster to create a new table and load it up with your test data yourself. If you do this, use extended inserts; they're about 25x faster than singular inserts.</p><p>You give up some goodies, including transactions, by making the switch.</p><h2>Partitioning</h2><p>The application never queries over a time range of greater than 15 minutes, and always aligns its requests on 15-minute boundaries. If we had to tablescan, I could use this to at least make sure we didn't have to tablescan all ten million rows.</p><p>I installed MySQL 5.1, which is the first release that supports partitioning, and created a new table using MyISAM, with a partition per 3 months, copying data from the old table in one hit:</p><pre>
CREATE TABLE `events2` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `starttime` datetime DEFAULT NULL,
 `endtime` datetime DEFAULT NULL,
 KEY `id` (`id`),
 KEY `starttime` (`starttime`),
 KEY `endtime` (`endtime`),
 KEY `starttime_and_endtime` (`starttime`,`endtime`),
) ENGINE=MyISAM DEFAULT CHARSET=utf8
PARTITION BY RANGE (TO_DAYS(starttime)) (
	PARTITION p0 VALUES LESS THAN (732312),
	PARTITION p1 VALUES LESS THAN (732677),
	PARTITION p2 VALUES LESS THAN (732767),
...
	PARTITION p17 VALUES LESS THAN MAXVALUE	
) SELECT * FROM events;
</pre><p>I'm using TO_DAYS because partitions by range can only be described in terms of a single integer (not a datetime).</p><p>Now I could adjust my query to address a single partition only:</p><pre>SELECT count(*) FROM events WHERE starttime &lt; &#x27;2006-12-30 08:30:00&#x27; 
AND endtime &gt; &#x27;2006-12-30 08:15:00&quot; AND starttime &gt; &#x27;2006-12-01 00:00:00&#x27;.</pre><p>I used EXPLAIN PARTITION SELECT ... to confirm that MySQL was accessing only a single partition.</p><p>The benefit of this particular partitioning strategy is that it's transparent to the application. More advanced approaches involve splitting records across database _servers_, rather than simply changing the 'create table' statement.</p><p><span class='pullme'>Consider MySQL partitioning; it's not hard.</span></p><h2>Results</h2><p>The full query originally took around 152 seconds to run. It's now down to around two seconds, or a 75x speed improvement. This is running essentially the same query, and returning data in the same format; the only change necessary at the application level was to reword the SQL slightly.</p><h2>General Tactics</h2><p>Those were the specific victories. Here are the overall tactics which are more generally applicable:</p><p>I found it helpful to think not about what my application needed to know, but what it <em>didn't</em> need to know. For example, rules like "we never run a query that does not align on a 15-minute boundary" can simplify the problem. </p><p>Nothing substitutes for benchmarks. Things that ought to be fast are sometimes slow. Things that ought to be slow are sometimes fast.</p><p>But don't trust the benchmarks. Sometimes I'd return to my machine to find the *same query* running in less than a second instead of over a minute. I was using SQL_NO_CACHE, so I think background optimisation is responsible here. Since the database was very large, it's possible that OSX's virtual memory management was also interfering.</p><p><span class='pullme'>Watch out for background optimisation and query caching.</span></p><p>The only reason I caught this was that I kept notes of every single query I ran, which machine I ran it on, the time it took to execute, and anything interesting about the results. It's a simple routine to create or edit the query in Textmate, then paste it into MySQL, instead of working in the MySQL window directly. </p><p><span class='pullme'>Log everything <b>you</b> do, and <b>why</b>.</span></p><p>I also kept notes about theories and ideas, inline with the sql, and later marked them with PROVED / DISPROVED and the evidence so that I could be sure I'd covered all the bases.</p><h2>Conclusion and Notes</h2><a name='notes' /><p>Tweaking the database lets you make huge gains in performance without modifying your complex application. The assertions above are only guidelines based on the evidence I collected on <i>one</i> project; ultimately it's your own benchmarks that will matter. Simplify the problem, record your investigations, and benchmark the results.</p><!-- From http://home.tampabay.rr.com/bmerkey/examples/css-pullquotes.html --><style type='text/css'>
blockquote {
	width: 24%;
	float: right;
	font-size: 125%;
	font-style: italic;
	font-family: "Palatino Linotype", Georgia, Caslon, serif;
	line-height: 140%;
	margin: 10px -20px 10px 10px;
	_margin-right: -35px; /* IE box model hack  */
	background: white url(http://dezyne.net/closequote.gif) no-repeat bottom right;
	padding: 2px 15px 2px 2px;
	border: dotted 3px #bcc;
	}

blockquote:first-letter {
	background: url(http://dezyne.net/openquote.gif) no-repeat left top;
	padding: 10px 2px 10px 25px;
	}
	
/* Print Styles*/
@media print {
blockquote {margin-right: -20pt;  border-width: 0 0 2pt 2pt}
blockquote:first-letter {padding: 0px;	font-size: 140%;}
}
</style><!-- <script type="text/javascript">
function makePullquote()
{
  var spans = document.getElementsByTagName('span');
  for (i = 0;  i < spans.length;  i++) 
  { 
    if(spans[i].className == "pullme")  
    {
      var pullquote = document.createElement('blockquote');
      pullquote.innerHTML = spans[i].innerHTML;  
      spans[i].parentNode.insertBefore(pullquote,null);
    } 
  }
}
window.onload = makePullquote;
</script> -->]]></description>
        <pubDate>Tue, 20 Nov 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/11/20/database-optimization-for-rails-apps</guid>
      </item>
    
      <item>
        <title>A little DSL on your Spaghetti (code)?</title>
        <link>http://blog.new-bamboo.co.uk/2007/11/15/a-little-dsl-on-your-spaghetti-code</link>
        <author>ismael
@new-bamboo.co.uk (ismael
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/11/15/a-little-dsl-on-your-spaghetti-code#disqus_thread</comments>
        <description><![CDATA[<p>
In one of our current projects, it is required that certain pages show different articles depending on the profile and network of contacts of the logged-in user.
</p><p>
In order to have a working prototype as soon -and Agile- as possible, we defined the logic for those viewing rules directly into our ActiveRecord models..
</p><pre><code class='ruby'>
def article_list( user )
  articles = []
  if user.is_a? Admin #Admin can see everything
    articles += find(:all)
  # public articles, written by the user or his contacts
  elsif user.is_a? User 
    articles += find(:all, :conditions=&gt;{:public=&gt;true})
    articles += find(:all, :conditions=&gt;{:author=&gt;user})
    articles += find(:all, 
       :conditions=&gt;[&quot;author_id IN (?)&quot;, user.friends.collect(&amp;:id)])
  else # anonymous user can only see public articles
    articles += find(:all, :conditions=&gt;{:public=&gt;true})
  end
 
  articles.uniq.sort_by(&amp;:published_at)
end
</code></pre><p>
It's a prototype. No one said it had to scale.
</p><p>
Even though we could optimize the method and reduce the amount of hits to the database, the real risk with this approach lies in that we're trying to declare our business logic (who sees what) and our data storage strategy in the same place. One block of code attempting to deal with two separate concerns at once. Should our viewing rules change, we would have to modify our conditional logic both in the <code>if / else</code> structures and in the arguments we pass to ActiveRecord finders. If, on the other hand, our app becomes a success and we decide to resort to <a href='http://www.danga.com/memcached/' title='A distributed memory object caching system'>Memcached</a> for quick data access, we must change our data storage strategy with extreme caution to avoid breaking the conditions that support our rules.
</p><p>
Expressing our rules in plain SQl is not a sensible option, as it probably would make the code even more intricate.
</p><p>
After a coffee (or ten) and a bit of fresh air things are clear. Viewing and privacy rules for the app's articles might change, or new user profiles can be added to the mix. The core concept of the project is sustained by those rules. The <em>how</em> and <em>where</em> we store those data is a detail of implementation that has little to do with the former.
</p><p>
We should be able to freely define rules and permissions in one place, and handle the heavy lifting elsewhere.
</p><p>
With a little refactoring and Ruby's transparency we came up with something like this:
</p><pre><code class='ruby'>
class RegisteredUser &lt; User
  ...
  def visible_articles
    set_rules do |articles|
      articles.add :from=&gt;self, :to=&gt;:anybody, :status =&gt; :all
      articles.add :from=&gt;:anybody, :to=&gt;self, :status =&gt; :public
      articles.add :from=&gt;contacts, :to=&gt;:anybody, :status =&gt; :all
    end
  end
 ...
end
</code></pre><p>
With this solution, the <code>set_rules</code> method can be reused in different contexts to define relevant rules. After processing a block of rules, it returns a simple array of options that we can pass to ActiveRecord finders or some other query-building utility that helps us minimize database hits. Yes, <a href='http://errtheblog.com/post/10722' title='Ambition gem'>Ambition</a>, I'm looking at you!
</p><p>
This simple <abbr title='Domain Specific Language'>DSL</abbr> separates the definition of our business rules from data access -which we handle in a different layer of the application.  As a side effect, this gives us much more readable code that is easy to maintain and test. Also, we get closer to the core domain of the project.
</p><p>
Where are the conditionals? With the proper use of inheritance -by class or modules- we can have every User profile declare their own set of straight-forward rules without the need of confusing conditions (you know it's a good time to re-think the architecture when those sneaky ifs and elses start creeping in all over the place).
</p><p>
Rails is in itself a DSL and suitable for most of web scenarios. Still, sometimes it makes sense to step back from your tools for a bit so you can get closer to the actual problem.
</p>]]></description>
        <pubDate>Thu, 15 Nov 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/11/15/a-little-dsl-on-your-spaghetti-code</guid>
      </item>
    
      <item>
        <title>Getting Things Done at New Bamboo</title>
        <link>http://blog.new-bamboo.co.uk/2007/11/8/getting-things-done-at-new-bamboo</link>
        <author>gwyn
@new-bamboo.co.uk (gwyn
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/11/8/getting-things-done-at-new-bamboo#disqus_thread</comments>
        <description><![CDATA[<p>Getting Things Done (GTD) is a way of choosing what to do next. It is more efficient and less stressful than other systems.</p><h2>Background</h2><p>Last year my web development company, consisting of myself and an ever-increasing number of subcontractors, began expanding very quickly. While I stayed mostly on top of the situation, the stress was overwhelming.</p><p>I made the switch to GTD during Christmas 06.</p><p>Now that I'm working full-time as a developer, instead of as a sole trader, my task lists are much shorter. I've therefore made some simplifications. GTD can be used for large-scale project management, but that isn't covered here.</p><h2>Benefits</h2><p>GTD is both a workflow and an attitude. The core concept is that <b>nothing</b> lives in your brain, except for what you're currently working on. You must be merciless about this.</p><p>The real benefit, and the driver for me, is this: You get a stronger personal focus, as well as a stronger work focus. </p><p>When I'm at work, I'm <i>really</i> at work. When I'm at home, I'm <i>really</i> at home. This means more effective recovery, and more energy at work. </p><p>David Allen's book <a href='http://davidco.com/store/product.php?productid=16182'>Getting Things Done</a>, which is where I started with GTD, says this:</p><p>"In order to hang out with friends or take a long aimless walk and truly have nothing on your mind, you need to know what all your actionable items are, where they are, and that they can wait. And you need to be able to do that in a few seconds, not days."</p><h2>Selecting Tasks</h2><p>I have <i>everything</i> that I <i>ever</i> need to do in <i>one</i> place. The <i>everything</i> bit is important.</p><p>These items are described in the form of "next actions", or "the very next thing I can do to move this forward". They are very specific. For example, I don't have "deal with tax return"; I have "phone ATO re: missing BAS for FY06-07, 13 28 66, 9-6pm Adelaide time".</p><p>Because of this precision, I can allocate tasks to "Contexts", which are sets of prerequisites. For example, I can't perform the "tidy room" task unless I'm at home. I can't call the ATO unless it's Really Early (because they're in Australia).</p><p>This means that tasks are invisible to me unless I can perform them <i>right now</i>.</p><p>The workflow is:</p><ol>
 <li>Complete current task.</li>
 <li>Select context.</li>
 <li>Select a new task from tasks in this context. There's no advance scheduling and prioritising; in the midst of constantly shifting requirements, I trust myself to make the selection just-in-time.</li>
</ol><h2>Adding Tasks</h2><p>That's how you work with tasks in the system. What about when a new task appears?</p><p>First, you stash it in one of your inboxes. You have the smallest number of these that you can get away with. </p><p>Your brain is absolutely not allowed to be an inbox. If you're filing things in your brain for even a second, you need another inbox.</p><p>I use two: my email account, and a notepad application on my phone, which is always with me.</p><p>Stashing an item in an inbox is very quick, and doesn't interrupt what you're doing. Whenever you have time - eg whenever you finish a task - "process" your inbox. For each item in it, you have four options:</p><h3>Do it</h3><p>If it's going to take less than two minutes, do it. Do it right now. Then forget about it.</p><p>The "two minutes" is a varying threshold. Anywhere between two and five works for me. I'll initiate system backups, answer quick emails, fill in my timesheets, and occasionally even make phone calls. If I spend ten minutes and kill off five two-minute tasks, I feel _good_.</p><p>I've taken this a step further, and will even break inbox queuing to do this: if a new task appears I'll sometimes stop what I'm doing to kill it off.</p><h3>Delete it</h3><p>GTD requires you to be realistic. Some things you'd like to do, you might have long-term plans to do, but realistically, you're never going to do them.</p><p>Don't feel bad about them. Don't lie to yourself, or to other people. Instead, say "I'm never going to do this", delete it from your inbox, and forget that it ever existed.</p><p>You can have a "Someday" context for items that will live in your brain if you don't put them somewhere else. I review mine periodically to weed out things that just aren't ever gonna happen.</p><h3>Delegate it</h3><p>If you give the job to someone else, the chances are you're still responsible for it. You need to add it to a "Waiting" context, with a description like "Waiting for ATO to send new form". Periodically scan this context, and see if any of these things:</p><ul>
<li>has happened, so you can remove it, or</li>
<li>hasn't happened, and you need to follow it up.</li>
</ul><p>If you don't put it in the "Waiting" context, it'll still live in your head. You don't want that.</p><h3>Defer it</h3><p>This is your option of last resort. It means "I can't do this now, but I do have to do it, and I can't give it to anyone else". Here's where you work out the very next action that you need to take in order to move this forward. <b>Do</b> take the time to calculate the next action; often, it's very small ("sort out my tax" is a huge job, but "email the ATO asking for a form 12" is not), and you will be able to do it now, after all. </p><p>Once you've got a next action, choose a context for it. Store it under that context in whatever system you're using.</p><p>You are now committed to doing this task. </p><h2>Equipment</h2><p>The Book tells you to buy a lot of things. I didn't have to. </p><p>You must, <i>must</i> have a small notebook that you carry with you all the time, particularly at first when you are dumping the contents of your brain. I use the built-in Notes application on my Symbian phone.</p><p>Until recently, I used <a href='http://www.kaboomerang.com'>Actiontastic</a>, relying on the fact that I always have my laptop with me. I'm now in the process of migrating to the Notes application on my phone, because I'm using the laptop much less at home. </p><p>I didn't get hung up on syncing. I always have my phone. Why would I need to sync it to anything else?</p><p>I have all my paperwork in a single box - yes, I use shoebox filing. This works because everything I care about is also in iPhoto. I use a digital camera rather than a scanner, because it's orders of magnitude faster and the quality is "good enough". iPhoto is crap for this, but it's "good enough" - I use the description field to insert keywords.</p><p>I have a single "reference" folder in my email account. I find that gmail's search is good enough that I don't even need to use labels. Anything that I'm not <i>sure</i> I'll need, I delete. </p><p>The Book covers some other areas, such as the "hard landscape" - tasks that must occur at certain times - and "tickler files" (tasks you'd like to forget about for a while, but do need to revisit). I use my phone's calendar for both. I sync with my partner, friends, and co-workers using the low-tech but highly-effective "what have we got on Tuesday?" method.</p><p>These are the tools you <b>do</b> need:</p><p><a href='http://davidco.com/store/product.php?productid=16182'>Getting Things Done</a>, by David Allen. It's an inspiring read, and packed full of details that are worth knowing about even if you don't use them. GTD is a style, not a religion. </p><p><a href='http://www.kaboomerang.com'>Actiontastic</a>, if you're going to keep your GTD lists on your Mac. The Omni Group is working on something called OmniFocus, but it's still in early beta.</p><p>As far as I know there are no GTD tools, or even suitable Todo-list tools, for Symbian phones. If there's enough demand, I'll write one - let me know ('gwyn' at this domain).</p><h2>Making The Switch</h2><p>One of the guys here at New Bamboo had previously tried to switch to GTD, but bailed out when The Book told him to take two days off to sort out his life. You don't <i>have</i> to do that.</p><p>But you should.</p><p>I read The Book and made the switch in the Christmas-New Year break last year. The timing worked well for me, and allowed me to go through <i>everything</i>, getting the full clear-head benefit. Yes, I had tasks like "charge my phone" and "tidy my desk". It's like tidying up; once you start, you go a little crazy. </p><p>Carrying a notebook is critical. I found that once I started the dump, I couldn't stop it. </p><p>Don't get sidetracked into software. Half of New Bamboo reacted to my initial presentation by planning to write software to track their tasks. This defeats the purpose, which is to <i>increase</i> productivity. This isn't about software, or three-colour stationery and ring binders. This is about habits.</p><h2>Summary</h2><p>To summarize:</p><ul>
<li>Get everything in one place.</li>
<li>Think of your tasks in terms of the Next Actions that are needed to complete them.</li>
<li>Classify your tasks into Contexts.</li>
</ul><p>GTD takes some effort to maintain, but you'll feel it when you start to slip: the stress will come back.</p><h2>Postscript: GTD at New Bamboo</h2><p>The material above is drawn from an internal presentation ("GTD: Task Management For Ninjas") that I gave for New Bamboo's Talk At Two program. A month later, I did a quick survey to see if it stuck. While there's only one new full-blown implementation, nearly everybody found something useful to incorporate into their existing systems. </p><p>Three people didn't attempt GTD. Two of those weren't here for the presentation.</p><p>Damien tried it, but found that my stripped-down version didn't track larger projects. He's using the "errands" context, though, with a separate list that he looks at on his way out the door.</p><p>Pablo's using a series of paper lists, and is enjoying lower stress and the sense of achievement that comes from crossing things out. He's also been inspired to collect old tasks and to evaluate them realistically.</p><p>Ismael's using GTD's aggressive single-task focus, but finds he can store his lists in his brain without stressing himself out.</p><p>Max is running a full implementation, using Actiontastic, but he's still using his brain as an inbox for when he's not near a computer.</p><p>Matt started using GTD, then found he no longer needed it.</p><p>He stopped because he Got Everything Done.</p>]]></description>
        <pubDate>Thu, 08 Nov 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/11/8/getting-things-done-at-new-bamboo</guid>
      </item>
    
      <item>
        <title>Google's OpenSocial with Ruby on Rails</title>
        <link>http://blog.new-bamboo.co.uk/2007/11/7/opensocial-with-ruby-on-rails</link>
        <author>olly@new-bamboo.co.uk (olly)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/11/7/opensocial-with-ruby-on-rails#disqus_thread</comments>
        <description><![CDATA[<p>Given the fair number of web applications we have developed with some aspect of social networking involved, naturally we were pretty excited about Google's <a href='http://opensocialapis.blogspot.com/2007/11/campfire-one-taking-social-applications.html'>OpenSocial announcement</a>.</p><p>Looking into the <a href='http://code.google.com/apis/opensocial/'>OpenSocial documentation</a> you can see that it exposes 3 different types of resources: people, that person's friends, and their activities. This structure maps nicely onto Rail's concept of nested resources, where we could have a top-level users controller with friends and activities nested underneath it.</p><p>The OpenSocial API looks quite well thought out, and built upon RESTful ideas. However it doesn't fit completely with the Rails ideals. Firstly the URL's for accessing the resources aren't the standard Rails routes and secondly it doesn't include a format extension, which means we can't use a responds_to block out of the box.</p><p>However both of these problems are easily solved by adding a few mappings to the routes file and adding our own custom mimetype. The example below shows the routing for mapping the users controller to the OpenSocial people resource.</p><pre><code class='ruby'>
# config/environment.rb
Mime::Type.register "application/x-opensocial", :opensocial

# config/routes.rb
map.opensocial_person '/feeds/people/:id', :controller => 'users', \
                            :action => 'show', :format => 'opensocial',\
                            :conditions => {:method => :get}

map.opensocial_person '/feeds/people/:id', :controller => 'users',\
                            :action => 'update', :format => 'opensocial', \
                            :conditions => {:method => :put}

map.opensocial_person '/feeds/people/:id', :controller => 'users', \
                             :action => 'destroy', :format => 'opensocial', \
                             :conditions => {:method => :delete}

# app/controllers/users_controller.rb

respond_to do |format|
  ...
  format.opensocial
end
</code></pre><p>Now you can render show.opensocial.erb or show.opensocial.builder to create atom feeds that the OpenSocial API requires.</p><p>To avoid repeating it throughout a number of applications I wrapped it up into a <a href='http://blog.nbwd.co.uk/assets/2007/11/7/opensocial_routing.tar.gz'>little plugin</a> that just sets the routes for those three resources, like so.</p><pre><code class='ruby'>
# config/routes.rb
map.resources :users do |user|
  user.resources :activities
  user.resources :relationships
end

map.opensocial_people :users do |user|
  user.opensocial_friends :relationships
  user.opensocial_activities :activities
end
</code></pre><a href='http://blog.nbwd.co.uk/assets/2007/11/7/opensocial_routing.tar.gz'>Download Plugin</a>]]></description>
        <pubDate>Wed, 07 Nov 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/11/7/opensocial-with-ruby-on-rails</guid>
      </item>
    
      <item>
        <title>Aspects of AOP</title>
        <link>http://blog.new-bamboo.co.uk/2007/10/9/aspects-of-aop</link>
        <author>matt
@new-bamboo.co.uk (matt
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/10/9/aspects-of-aop#disqus_thread</comments>
        <description><![CDATA[<p>Having worked on AspectJ Development Tools (<a href='http://www.eclipse.org/ajdt/'>AJDT</a>) a while back, I thought aspect-orientated programming (AOP) would be good topic for my Talk@2. I&#8217;m not going to go into too much depth but hopefully it&#8217;ll highlight some of the possible uses of AOP.</p>

<h2 id='a_bit_of_history'>A bit of history</h2>

<p>AOP started out with AspectJ from <a href='http://www.parc.xerox.com/'>Xerox PARC</a> about 10 years ago, and is now an open source project managed by the Eclipse Foundation. AspectJ is an extension to Java that adds aspect-orientated capabilities, while not the only implementation of AOP it&#8217;s practically the de facto in the Java camp.</p>

<p>But what about Ruby? There are a few AOP Frameworks for Ruby <a href='http://aspectr.sourceforge.net/'>AspectR</a> (hasn&#8217;t been updated in a while) and <a href='http://aquarium.rubyforge.org/index.html'>Aquarium</a>.</p>

<p>I use Aquarium later in the examples, you can get it with:</p>
<pre><code class='ruby'>sudo gem install aquarium</code></pre>
<p>Or the source:</p>
<pre><code class='ruby'>svn checkout svn://rubyforge.org/var/svn/aquarium/trunk aquarium_trunk</code></pre>
<h2 id='what_is_aop'>What is AOP?</h2>

<p>AOP is an effective technique for modularizing crosscutting concerns<sup id='fnref:1'><a href='#fn:1' rel='footnote'>1</a></sup>. Crosscutting concerns appear all over the place in applications such as, authentication, logging, caching etc. But why is this a problem? Current implementations of cross cutting concerns break modularization and can lead to spaghetti code, which is hard to maintain.</p>

<p>AOP to the rescue! With AOP concerns that crosscut many classes are modularized into an aspect. This allows you to write a logging aspect (for example), which can effect your entire application, a group of classes or just a single method.</p>

<h3 id='what_is_an_aspect'>What is an aspect?</h3>

<p>It describes how code for a concern should be integrated into the code for the whole application, without modifying the original code</p>

<p>In AspectJ the java bytecode is manipulated to include the code from the aspects before, after or around the specified method calls. But in Ruby it&#8217;s done using the meta-programming mechanisms available.</p>

<p>Aspects comprise of<sup id='fnref:2'><a href='#fn:2' rel='footnote'>2</a></sup>:</p>

<ul>
<li>Join Points: A point of execution in a program where &#8216;advice&#8217; might be invoked. </li>

<li>Pointcuts: A set of join points of interest, like a query over all join points in the system.</li>

<li>Advice: The behavior invoked at a join point.</li>
</ul>

<p>There are a few types of advice available. Before advice executes code before the join point, after advice does so after returning from the join point and around advise &#8216;wraps&#8217; a join point.</p>

<p>Here are some examples of Aquarium code, but for more detailed examples see the <a href='http://aquarium.rubyforge.org/examples.html'>example page</a></p>

<h3 id='join_point'>Join Point</h3>
<pre><code class='ruby'>JoinPoint.new :type => Post, :method => :new
    
JoinPoint.new :object => @post, :method => :new</code></pre>
<h3 id='pointcut'>Pointcut</h3>
<pre><code class='ruby'>Pointcut.new  :types => UsersController, 
  :methods => [:edit, :update, :destroy, :admin]</code></pre>
<h3 id='before_advise'>Before Advise</h3>
<pre><code class='ruby'>include Aquarium::Aspects::DSL::AspectDSL 

before :pointcut => your_point_cut do |jp, *args| 
  log “Entering: #{jp.inspect}” 
end </code></pre>
<h2 id='isnt_aop_just_metaprogramming'>Isn&#8217;t AOP just meta-programming?</h2>

<p>Meta-programming can support the essence of AOP, and can also support lots of other things that fall out of the scope of AOP. Ruby can achieve the functionality that AOP provides through meta-programming, why should I use a framework?</p>

<p>AOP frameworks provide another level of abstraction, a nice API (Domain Specific Language) to express crosscutting concerns intuitively.</p>

<h2 id='aop_vs_design_patterns'>AOP vs Design Patterns</h2>

<p>Design Patterns are templates for solving certain problems, many of which could be implemented better with AOP. As mentioned aspects modularize the concern for a problem. So you can reuse aspects in other applications unlike design patterns which have to be implemented each time.</p>

<p>Soon AOP will be the de-facto implementation for cross cutting concerns, just as Object-oriented programming is for inheritance, encapsulation etc.</p>

<p>It should be made clear that AOP isn’t a Redpill! You don’t gain extra functionality, but AOP make implementing the solutions to these problems easier, cleaner and is closer to God<sup id='fnref:3'><a href='#fn:3' rel='footnote'>3</a></sup>.</p>

<h2 id='aop_and_rails'>AOP and Rails?</h2>

<p>Rails has filters so why should I bother with AOP? Rails filters use method aliasing and Aquarium does basically the same thing, with a little more sophistication to make it more reliable to add multiple “filters” or to remove any one of them dynamically.</p>

<p>Also plugins that override <tt>method_missing</tt> can step on each other and behave in unexpected ways. With AOP we can overcome this problem.</p>

<p>There are roughly 180 cases of method aliasing in the Rails 1.2 stack, much of which would be cleaner and more robust if implemented as advice, instead.</p>

<h2 id='aop_isnt_the_future'>AOP isn&#8217;t the future</h2>

<p>It&#8217;s here NOW! The <a href='http://www.springframework.org/'>Spring Framework</a> is built on top of AspectJ and is a popular Java/JEE application framework.</p>

<p>There are some AOP features penned for Ruby 2.0, such as <tt>:pre</tt>, <tt>:post</tt>, and <tt>:wrap</tt> for methods.</p>

<p>Where I see a possibility AOP adoption starting out is with non obtrusive (orthogonal) aspects for logging, tracing, etc. After people get comfortable with AOP, then we&#8217;ll start to see the real power of AOP being utilized within business objects.</p>

<p>So there you have it a quick round up of AOP, when I get some time I might have a crack at using Rails and Aquarium to make a demo app or plugin.</p>

<h3 id='interesting_links'>Interesting Links</h3>

<ul>
<li><a href='http://www.google.com/search?q=AOP+site%3Awww.infoq.com'>AOP at infoQ</a></li>

<li><a href='http://video.google.com/videoplay?docid=8566923311315412414&amp;q=AOP&amp;total=275&amp;start=0&amp;num=10&amp;so=0&amp;type=search&amp;plindex=0'>AOP google talk</a></li>

<li><a href='http://cfis.savagexi.com/articles/2007/09/05/rails-unusual-architecture'>Rails&#8217; Unusual Architecture</a></li>

<li><a href='http://blog.aspectprogramming.com/articles/2007/10/02/presentation-on-aquarium-with-exercises'>Presentation on Aquarium with Exercises</a></li>
</ul>
<div class='footnotes'><hr /><ol><li id='fn:1'>
<p>Kiczales, G., Lamping, J., Mendhekar, A., Maeda, C., Lopes, OCP. C., Loingtier, J. and Irwin, J., Aspect-oriented programming. European Conference on Object-Oriented Programming (ECOOP), 1997, 220-242.</p>
<a href='#fnref:1' rev='footnote'>&#8617;</a></li><li id='fn:2'>
<p><a href='http://aquarium.rubyforge.org/documentation/API/aspects.html'>Aquarium</a></p>
<a href='#fnref:2' rev='footnote'>&#8617;</a></li><li id='fn:3'>
<p><a href='http://blog.aspectprogramming.com/articles/2007/09/17/ann-aquarium-v0-1-5-released'>Correspondence with &#8220;Dean Wampler&#8221;</a></p>
<a href='#fnref:3' rev='footnote'>&#8617;</a></li></ol></div>]]></description>
        <pubDate>Tue, 09 Oct 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/10/9/aspects-of-aop</guid>
      </item>
    
      <item>
        <title>FOWA post-mortem</title>
        <link>http://blog.new-bamboo.co.uk/2007/10/7/fowa-post-mortem</link>
        <author>max@new-bamboo.co.uk (max)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/10/7/fowa-post-mortem#disqus_thread</comments>
        <description><![CDATA[<p>So we are back from FOWA, after our first (and hopefully not last) experience of running a stand at an expo. It was a really fantastic experience and we all had an amazing time there. Thank you everyone who came along and spoke to us, it was great to meet so many people and chat with you all. We will be trying to process all the contacts we made and responding to enquiries in the next couple of weeks.</p>

<p>More thanks goes to our fantastic team who contributed so much to an engaging stall, though the lure of the Xbox was at some points too much for some. Our thanks also goes to Gavin Strange for the all the decorative paraphenalia he produced to make our lively, if rather bemusing, display.</p>

<p>For those of you who managed to grab one of Gavin&#8217;s amazing panda t-shirts, we hope you enjoy them, and wear them with pride. &#8220;Even Ryan got in on the panda love&#8221;:http://www.flickr.com/photos/carsonified/1482580870/.</p>

<p>However, I&#8217;m sure there now exist many poor souls who could not attend, and are now feeling rather devastated at the lost opportunity. Never fear, we are gathering names and sizes of people who would like a t-shirt, and will try to produce a limited run of them soon. We are probably going to be avoiding retina-destroying green this time.</p>

<p>If you are interested, please drop us a line at &#8220;gimmeafreakinpandashirt@new-bamboo.co.uk&#8221;:mailto:gimmeafreakinpandashirt@new-bamboo.co.uk, and we will get everything moving once we have enough demand.</p>

<p>We also have a collection of &#8220;new bamboo Flickr photos&#8221;:http://www.flickr.com/photos/new<em>bamboo</em>london/, and would love to see any of your own panda sightings.</p>]]></description>
        <pubDate>Sun, 07 Oct 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/10/7/fowa-post-mortem</guid>
      </item>
    
      <item>
        <title>Future of Web Apps</title>
        <link>http://blog.new-bamboo.co.uk/2007/10/2/future-of-web-apps</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/10/2/future-of-web-apps#disqus_thread</comments>
        <description><![CDATA[<p>It looks like &#8220;FOWA&#8221;:http://www.futureofwebapps.com/ is going to be a great event with an incredible line up of speakers. This time around there&#8217;s going to be an expo and because we&#8217;re one of the sponsors we&#8217;ll also have a nice little booth where you can see all our shiny faces and chat about Ruby on Rails, cutting edge web development and where the web&#8217;s going.</p>

<p>So if you&#8217;re interested in meeting the whole team, maybe picking up some New Bamboo &#8220;goodies&#8221;:http://www.flickr.com/photos/new<em>bamboo</em>london/1442939714/ come and say hello!</p>]]></description>
        <pubDate>Tue, 02 Oct 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/10/2/future-of-web-apps</guid>
      </item>
    
      <item>
        <title>Didn't you get the memo?</title>
        <link>http://blog.new-bamboo.co.uk/2007/9/5/didn-t-you-get-the-memo</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/9/5/didn-t-you-get-the-memo#disqus_thread</comments>
        <description><![CDATA[<em>This article was originally publish on my own "blog":http://www.jaikoo.com but some other people thought it was useful so I reproduced it here. Any bad language is representative of me and not New Bamboo who are all very polite chaps/chapettes</em>
<p>With Web 2.0 applications facing growing audiences with data hungry appetites &#8220;MOM&#8221;:http://en.wikipedia.org/wiki/Message<em>oriented</em>middleware has suddenly had a resurgence.</p>

<p>Messaging systems are a great way of scaling your site by placing operations/messages on a queue and processing them asynchronously. This allows you to get quite imaginative with your architecture and distribute worker processes in geographically disparate locations if you so wished and also distribute/delay the actual processing needs as and when you can.</p>

<p>This can be used with great effect with services such as Amazons own simple messaging offering known as SQS and EC2. As soon as load starts peaking on one EC2 node you can have a script that calls out to a mother-ship to instantiate another EC2 instance and as soon as it&#8217;s &#8216;born&#8217; starts processing messages off the SQS queue. If you wanted you could even have the original load stricken node contact the mother-ship via SQS (probably a more reliable solution). This is one of the architectures I&#8217;m currently advocating as EC2 is stateless and rather than worry about database replication across transient nodes, I try and share as much information as I can using messaging and web-services. I&#8217;m currently backing off exposing REST style interfaces for non public facing services purely for performance and scaling reasons. Don&#8217;t get me wrong, I love REST and for public a public facing API it makes life a lot easier. But as &#8220;Zed Shaw&#8221;:http://www.zedshaw.com/ says, HTTP isn&#8217;t exactly the most performant protocol.</p>

<p>I&#8217;ve seen people try and achieve similar architectures using a database and their own cobbled together mesh of scripts and while this may work great on small sites, you&#8217;re just pushing your scaling issues and problems onto your poor DB. And as anyone who&#8217;s had the misfortune to be stuck in a room with me would tell you, I hate relational databases on multiple fronts, they&#8217;re so 1980&#8217;s and I&#8217;ll tell you why in another blog post.</p>

<p>It&#8217;s seems these days that we&#8217;re currently quite spoiled with the cheap/open messaging solutions that are available. A few years ago most of what I had to play with were proprietary and expensive offerings from various large vendors. A few that I&#8217;ve played around with lately have been:</p>

<ul>
<li>&#8220;ActiveMQ&#8221;:http://activemq.apache.org/stomp.html - An open source solution utilising STOMP. Check out &#8220;ActiveMessaging&#8221;:http://code.google.com/p/activemessaging/wiki/ActiveMessaging for Ruby/Rails.</li>

<li>&#8220;Jabber&#8221;:http://www.jabber.org - While it&#8217;s not really intended for reliable delivery of messages, it does have a lot of support and with quite a few Ruby clients available.</li>

<li>&#8220;SQS&#8221;:http://aws.amazon.com/sqs - Amazons offering that is easy to plugin solution if you&#8217;re using EC2 as you don&#8217;t need to worrying about setting up the messaging infrastructure between distributed nodes that aren&#8217;t necessarily sharing a VPN. ActiveMessaging has support for SQS though I kinda feel it&#8217;s bit of a chain saw to a bunny rabbit as SQS really is a very simple service as it&#8217;s name suggests. I wrote my own lightweight SQS Rails plugin a while ago and will probably dust it off in the next week or so.</li>

<li>&#8220;RabbitMQ&#8221;:http://www.rabbitmq.com - Now this is the matrons knockers when it comes to kick ass, ultra fast and scalable messaging. It simply rocks, with performance off the hook. It&#8217;s written in Erlang and supports the &#8220;AMPQ&#8221;:https://jira.amqp.org/confluence/display/AMQP/Advanced+Message+Queuing+Protocol protocol. Unfortunately I haven&#8217;t seen much in the way of Ruby support. I&#8217;ve played with &#8220;QPid&#8217;s&#8221;:http://cwiki.apache.org/qpid/ (Apaches own open source AMPQ offering) &#8220;Ruby client library&#8221;:https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/ruby/ but haven&#8217;t actually tested this against RabbitMQ, so you&#8217;re mileage may vary.</li>
</ul>

<p>So there you have it, messaging. It rocks, it&#8217;s sexy and what&#8217;s more it&#8217;ll make you sexy&#8230; Honest guv&#8217;nor.</p>]]></description>
        <pubDate>Wed, 05 Sep 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/9/5/didn-t-you-get-the-memo</guid>
      </item>
    
      <item>
        <title>jQuery, an Introduction</title>
        <link>http://blog.new-bamboo.co.uk/2007/9/4/chaining-with-jquery</link>
        <author>andy
@new-bamboo.co.uk (andy
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/9/4/chaining-with-jquery#disqus_thread</comments>
        <description><![CDATA[<p>I&#8217;ve been a huge fan of jQuery for over a year now. As JavaScript frameworks go it&#8217;s light but still functional. As the &#8216;newbie&#8217; I wanted to demonstrate this to the rest of New Bamboo. I know this information has been said before but below is my quick introduction to jQuery, what it&#8217;s about and how to use it.</p>

<h2 id='find_things_do_stuff'>Find Things, Do Stuff</h2>

<p>This is the methodology behind jQuery and it&#8217;s very sensible. The whole API is geared towards selecting elements on the page and then performing actions on that collection of elements.</p>

<h2 id='finding_things'>Finding Things</h2>
<p><tt>$()</tt> is the root of everything, it allows you to search the DOM for elements and wrap them in jQuery functionality, searches can be passed in as CSS selectors, XPath selectors or native DOM nodes. The return value is always a jQuery collection, regardless of whether there is 1 match, many matches or no matches. This allows you to chain methods on without having to worry about the contents of the document.</p>
<h2 id='doing_stuff'>Doing Stuff</h2>
<p><tt>$()</tt> isn't the only method that returns a jQuery collection, all the functions in the library work this way (with a few exceptions where it makes sense), because of this you can easily chain methods onto each other. Method chaining makes a lot of sense in jQuery as it provides a way to build up complex functionality ontop of all the matched elements. Let's look at a simple example.</p>
<pre><code>$(&#39;a.button&#39;).html(&#39;hello&#39;).show()</code></pre>

<p>Here we are getting all links with a class of button, then setting there html to say &#8216;hello&#8217; and finally we &#8216;show&#8217; the element on the page (making the assumption it was hidden to begin with).</p>

<p>jQuery has a rich yet simple API for &#8216;doing stuff&#8217; to DOM elements, this includes:</p>

<ul>
<li>DOM Manipulation</li>

<li>Traversal</li>

<li>attribute and style getters and setters</li>

<li>Events</li>

<li>AJAX</li>

<li>FX</li>
</ul>

<h2 id='plugins'>Plugins</h2>

<p>One of jQuery&#8217;s greatest strengths is it&#8217;s size. By concentrating on selecting elements and manipulating them they have managed to keep the core API lean. But when additional functionality is needed writing plugins couldn&#8217;t be easier, this is reflected in the number and quality of plugins already available on the website.</p>

<h2 id='jquery__rails'>jQuery &amp; Rails</h2>

<p>I won&#8217;t lie to you, jQuery and rails isn&#8217;t always as natural a fit as Prototype however I think it is worth the little extra effort for the benefits of a lighter page size and truly unobtrusive JavaScript code.</p>

<p>If you&#8217;re a RJS fan then there is some <a href='http://mad.ly/2007/05/17/jquery-ajax-rails/'>helpful information available</a>, and there is also some <a href='http://yehudakatz.com/2007/05/17/jquery-on-rails-a-fresh-approach/'>promising work going</a> on in the jQuery community to bring Rails &amp; jQuery closer together via a rails plugin.</p>

<h2 id='links__downloads'>Links &amp; Downloads</h2>

<ul>
<li><a href='http://blog.nbwd.co.uk/assets/2007/9/4/talk_2.pdf'>Talk@2 Presentation (pdf)</a></li>

<li><a href='http://blog.nbwd.co.uk/assets/2007/9/4/jquery_demo.zip'>The simple example we built (zip)</a></li>

<li><a href='http://jquery.com'>jQuery Website</a></li>
</ul>

<h2 id='the_results_are_in'>The Results Are In!</h2>

<p>As a company we have embraced jQuery in several projects recently and the results so far have been very positive so watch out for more from us in this area.</p>]]></description>
        <pubDate>Tue, 04 Sep 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/9/4/chaining-with-jquery</guid>
      </item>
    
      <item>
        <title>Presenters &amp; Conductors on Rails</title>
        <link>http://blog.new-bamboo.co.uk/2007/8/31/presenters-conductors-on-rails</link>
        <author>olly
@new-bamboo.co.uk (olly
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/8/31/presenters-conductors-on-rails#disqus_thread</comments>
        <description><![CDATA[<p>In one of our current projects we have been experimenting with additional layers to the model-view-controller (MVC) pattern. Namely the presenter and conductor; the presenter sitting between the controller and view, and the conductor sitting between the model and controller. Whilst, granted, they are overkill for most projects when used correctly in the right situations they can make your code much easier to read and comprehend. </p><h3>Presenters</h3><p>We have had little use for presenters, so the example is a contrived one, but I liked to include it for completeness sake. (View <a href='http://blog.nbwd.co.uk/assets/2007/8/29/presentation.pdf'>the slides</a> for the full progression from bog-standard MVC to using the presenter)</p><pre><code class='ruby'>
# app/controllers/users_controller.rb
class UsersController
  def show
    @user = User.find(params[:id])
  end
end

# app/views/users/index.html.erb
&lt;div&gt;
  &lt;% if CONFIG.date_format == :us %&gt;
    &lt;%= @user.created_at.strftime(&#x27;%m/%d/%y&#x27;) %&gt;
  &lt;% elsif CONFIG.date_format == :rest_of_the_world %&gt;
    &lt;%= @user.created_at.strftime(&#x27;%d/%m/%y&#x27;) %&gt;
  &lt;% end %&gt;
&lt;/div&gt;

# app/presenters/user_presenter.rb
class UserPresenter
  def initialize(user)
    @user = user
  end
  attr_reader :user

  def signup_date
    if CONFIG.date_format == :us
      self.user.created_at.strftime(&#x27;%m/%d/%y&#x27;)
    elsif CONFIG.date_format == :rest_of_the_world
      self.user.created_at.strftime(&#x27;%d/%m/%y&#x27;)
    end
  end
end
</code></pre><h3>Conductors</h3><p>Conductors are extremely useful, having two major uses that we have come across so far. Firstly managing an object with associations submitted from one form (the example I use in the <a href='http://blog.nbwd.co.uk/assets/2007/8/29/presenters_and_conductors.zip'>demo application</a>). Or secondly to manage one object that has its information gathered from multiple forms. </p><p>The demo is a simple application to manage company information and payment details. Using a conductor to manage the company and its associated credit card and avatar we greatly simplify the model and controller. I have also abstracted some repetitive stuff into a super class, which provides some DSL-ish methods for describing how the conductor works (albeit verbose). The conductor in the demo application is shown below:</p><pre><code class='ruby'>
class CompanyConductor &lt; ActionConductor::Base
  conduct :company do |company|
    company.name
    company.phone
    company.website
  end
  
  conduct :credit_card do |credit_card|
    credit_card.owner_name :as =&gt; :card_holder_name
    credit_card.provider :as =&gt; :credit_card_provider
    credit_card.number :as =&gt; :credit_card_number
    credit_card.expiry_month
    credit_card.expiry_year
  end
  
  owner :company
  
  def credit_card
    @credit_card ||= (self.company.credit_card || self.company.credit_card = CreditCard.new)
  end
  
  def avatar
    @avatar ||= (self.company.avatar || Avatar.new)
  end
  
  def uploaded_data=(data)
    unless data.blank?
      self.avatar.uploaded_data = data
      @avatar_present = true
    end
  end
  
  def save
    if @avatar_present
      company.avatar = avatar if avatar.new_record?
      avatar.save
    end
    company.save &amp;&amp; credit_card.save
  end
  
  def errors
    method_map = @reversed_method_mappings
    
    errors = ActiveRecord::Errors.new(self)
    errors.add_conductor_errors_for(company, method_map)
    errors.add_conductor_errors_for(credit_card, method_map)
    errors
  end
end
</code></pre><p>So what advantages do we get from using a conductor? Firstly our code is much more concise. Compare the create action before and after adding a conductor.</p><pre><code class='ruby'>
# Create action without conductor
def create
  @company = Company.new(params[:company])
  @credit_card = CreditCard.new(params[:credit_card])
  @company.credit_card = @credit_card
  
  if (@company.valid? &amp; @credit_card.valid?) &amp;&amp; (@company.save &amp; @credit_card.save)
    unless params[:avatar][:uploaded_data].blank?
      @avatar = Avatar.new(params[:avatar])
      @company.avatar = @avatar
      @avatar.save
    end
    flash[:notice] = &#x27;Company was successfully created.&#x27;
    redirect_to companies_url
  else
    render :action =&gt; &#x27;new&#x27;
  end
end

# Create action with conductor
def create
  @company = Company.new
  @company_conductor = CompanyConductor.new(@company, params[:company_conductor])
    
  if @company_conductor.save
    flash[:notice] = &#x27;Company was successfully created.&#x27;
    redirect_to companies_url
  else
    render :action =&gt; &#x27;new&#x27;
  end
end
</code></pre><p>Secondly as much of that logic was duplicated between the create and update actions, we get a much DRY-er controller. Thirdly the views become more consistent, as we can now use f.text_field (and friends) instead of before where we had to use f.text_field for the company and then text_field for the associated objects.</p><p>The main thing to be said against conductors is that they add another layer of abstraction to deal with. Which increases the amount of time learning what the code does. However this can easily be solved by setting some conventions on how they are used. We haven't experimented nearly enough to come up with any good conventions that are suitable in the majority of cases. One of the possible additions we looked at is to infer the attributes to forward to the conductor from the model's attributes, to avoid the verbose conduct definitions.</p><p>The abstraction, that I whipped up for the demo, is lacking a bit of polish and is downright ugly in places. But feel free to download it, play with it, use it and improve it. </p><h3>Files &amp; Linkage</h3><p><a href='http://blog.nbwd.co.uk/assets/2007/8/29/presentation.pdf'>Presentation Slides (PDF)</a><br />
<a href='http://blog.nbwd.co.uk/assets/2007/8/29/presenters_and_conductors.zip'>Demo Application</a> - The demo application I made for the demonstration. Interesting stuff is in the app/ and lib/ directories.
</p><p>
<a href='http://blog.jayfields.com/'>Jay Fields'</a> blog post on the <a href='http://blog.jayfields.com/2007/03/rails-presenter-pattern.html'>presenter pattern</a> upon which this conductor is based.<br />
<a href='http://mcc.rubyforge.org/svn/trunk/'>Model-Conductor-Controller (MCC)</a> - another take on the conductor pattern.</p><p>It would be great to hear your thoughts on presenters and conductors. Comment away!</p>]]></description>
        <pubDate>Fri, 31 Aug 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/8/31/presenters-conductors-on-rails</guid>
      </item>
    
      <item>
        <title>ESI caching in Ruby on Rails</title>
        <link>http://blog.new-bamboo.co.uk/2007/8/23/esi-caching-in-ruby-on-rails</link>
        <author>max
@new-bamboo.co.uk (max
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/8/23/esi-caching-in-ruby-on-rails#disqus_thread</comments>
        <description><![CDATA[<p>Caching and performance is something that always lingers at the back of my mind, and the Holy Grail is yet to be found as far as I am concerned. While looking around on various blogs recently, I came across &#8220;some fantastic articles on caching&#8221;:http://revolutiononrails.blogspot.com/2007/08/advanced-rails-caching-on-edge.html that I found really exciting. In a nutshell the article introduces the concept of using ESI, or Edge side includes to create huge performance boosts in Rails applications. ESI is a standard that I had never heard of before, though it has been around since 2001. The &#8220;specification for ESI&#8221;:http://www.w3.org/TR/esi-lang was written by some interesting people who have great pedigree in the field of advanced scalabilty, Akamai for example.</p>

<p>ESI is very similar to SSI but has a more sophisticated method for including content that can use things like a try-catch type DSL to ask for content and fall back to alternatives if something goes wrong. To put this in context and to highlight why it is really cool, consider Rails&#8217; whole page caching. An app can write an entire action to a static file in the public directory. Your server can pass this file to its clients without touching Rails and its routing system. This has always seemed like a fantastic concept, but one that can hardly ever be used in the wild. Necessary elements in the page such as shopping cart contents, links to your account, user&#8217;s status etc., always spoil the fun when you are trying to avoid hitting the app. This forces you to use fragment caching and model caching when caching 99% of the page at the level of Mongrel would be far more ideal.</p>

<p>Enter ESI (SSI will also do, but ESI has some additional nice features). Whole actions can be cached as static content above the level of your app, but embedded in them is markup that the server executes to fetch more content where necessary. A high traffic shopping site could have something along these lines, in place of some content in the page that displays a cart:</p>
<pre><code>
...
  &lt;esi:include src=&quot;/cart&quot; max-age=&quot;0&quot;/&gt;
...
</code></pre>
<p>The users&#8217; session is passed through, and you can retrieve the items in their cart and render it as on a mini action in your Rails app. The server is responsible for inserting it into the markup and returning it to the client. The max-age of 0 means that it will ask for a fresh version from your app everytime it is run. However, setting it to a higher TTL value means that subsequent request will use a cached copy that the server stores and is responsible for. The really cool thing about this is that it puts responsibility of dealing out cached content onto your web server, rather than the actual Rails app. At the moment you need a custom version of mongrel that supports ESI to use it. Also, it is worth noting that the current version of mongrel-esi stores these cached fragments in memory, though the guys are working on a memcached version. Have a &#8220;look here&#8221;:http://revolutiononrails.blogspot.com/2007/08/fragmentfu-fun-with-fragments.html to get the install details and also a plugin that helps you integrate it into your Rails apps.</p>

<p>This is obviously cool stuff, but there are some other neat things you can add into the mix. The Rails default method of storing the cached pages is on the file system in your public directory, which gets a bit messy. However, in &#8220;this article&#8221;:http://blog.kovyrin.net/2007/08/05/using-nginx-ssi-and-memcache-to-make-your-web-applications-faster it describes putting them into memcache and then telling nginx to look in memcache before hitting your app to retrieve them. This is pretty fantastic as it makes managing the cache much easier.</p>

<p>Another option to consider for delivering the fragments would be a lovely lightweight merb application, since the src attribute of an esi:include can be another url, it doesn&#8217;t just have to be a path on the same server. I haven&#8217;t actually tried this, so it should be taken with a pinch of salt, but I think you should be able to do this:</p>
<pre><code>
  &lt;esi:include src=&quot;http://super_merb.my_app.com/cart&quot; max-age=&quot;0&quot;/&gt;
</code></pre>
<p>So things are looking pretty nifty in this area, and I am definitely going to be keeping an eye on it.</p>]]></description>
        <pubDate>Thu, 23 Aug 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/8/23/esi-caching-in-ruby-on-rails</guid>
      </item>
    
      <item>
        <title>Railsconf Europe</title>
        <link>http://blog.new-bamboo.co.uk/2007/8/22/railsconf-europe</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/8/22/railsconf-europe#disqus_thread</comments>
        <description><![CDATA[<p>I took some time out this week to finally tie up the loose ends such as hotels, flights etc and now can confirm that we&#8217;ll be deploying a five person strong New Bamboo entourage. Max, Michelle, Pablo, Gwyn and I will be arriving in Berlin on the 17th of September.</p>

<p>I haven&#8217;t been to Berlin in nearly 7 years when I worked at &#8220;Pixel Park&#8221;:http://www.pixelpark.com so I&#8217;ll be looking forward to seeing the sites and meeting fellow Railers! If you&#8217;re going to be at Railsconf Europe or just in the vicinity it would be great to meet up and hang out.</p>]]></description>
        <pubDate>Wed, 22 Aug 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/8/22/railsconf-europe</guid>
      </item>
    
      <item>
        <title>Good luck to all 268 Seedcamp applicants</title>
        <link>http://blog.new-bamboo.co.uk/2007/8/21/good-luck-to-all-268-seedcamp-</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/8/21/good-luck-to-all-268-seedcamp-#disqus_thread</comments>
        <description><![CDATA[<p>&#8220;Not long now!&#8221;:http://blog.seedcamp.com/2007/08/seedcamp-act-two.html The application deadline was last Sunday and by the end of this week the 20 finalists will be announced. This is certainly the start of something which will change the startup landscape throughout Europe indefinitely. The beauty of Seedcamp is it&#8217;s giving the passionate and committed engineers (but not forgetting entrepreneurs and designers) the chance to really get their idea produced and out there to the world. London is a wonderful place, and everyday I am amazed by the new people I meet here. If you&#8217;re already in London, don&#8217;t miss out on &#8220;OpenCoffee&#8221;:http://www.opencoffeeclub.org/, this week it will be a great time to meet other Seedcamp applicants.</p>

<p>We have had the pleasure of working with the Seedcamp team throughout from the site to development of the application, application. Knowing several teams in London taking part, there&#8217;s going to be some stiff competition, that&#8217;s for sure.</p>]]></description>
        <pubDate>Tue, 21 Aug 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/8/21/good-luck-to-all-268-seedcamp-</guid>
      </item>
    
      <item>
        <title>Talk @ 2: Using GIT for Rails Development</title>
        <link>http://blog.new-bamboo.co.uk/2007/8/16/using-git-for-rails-development</link>
        <author>pablo@new-bamboo.co.uk (pablo)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/8/16/using-git-for-rails-development#disqus_thread</comments>
        <description><![CDATA[<p>A few weeks ago we start experimenting with Git for Rails development.</p>

<p>I am not going to highlight the all of the advantages of using git here, but let me mention that Git is one of the few SCM out there that really understands merges.</p>

<p>As Linus, the creator of git, says &#8220;in this thread&#8221;:http://www.gelato.unsw.edu.au/archives/git/0504/2182.html bq. &#8220;The important part of a merge is not how it handles conflicts (which need to be verified by a human anyway if they are at all interesting), but that it should meld the history together right so that you have a new solid base for future merges. In other words, the important part is the <em>trivial</em> part: the naming of the parents, and keeping track of their relationship. Not the clashes. And it looks like 99% of SCM people seem to think that the solution to that is to be more clever about content merges. Which misses the point entirely.&#8221;</p>

<p>Git tracks content, not files. Git is really a filesystem, represented by tree structures. So you are really comparing trees between commits, and tracking the changes of the nodes in the tree.</p>

<p>There are enough tutorials out there for you to start grasping git. I can recommend these ones:</p>

<ul>
<li>&#8220;Beginners tutorial&#8221;:http://www.kernel.org/pub/software/scm/git/docs/tutorial.html</li>

<li>&#8220;Git user manual&#8221;:http://www.kernel.org/pub/software/scm/git/docs/user-manual.html</li>

<li>&#8220;Git for SVN users&#8221;:http://git.or.cz/course/svn.html</li>
</ul>

<p>You may want to consider Git if you have ever run into a merge marathon on a big project with SVN. If you have ever tried it, it is just painful and it does not work, not the SVN way.</p>

<p>h3. Enter Git</p>

<p>Git makes branches easy to create and merge. Git branches are only a 40-digit hex hash, with the origin of the branch. Branches are cheap, and easy. You start using git, and before you realise it, you are merging and branching all the time.</p>

<p>So a regular working pattern would be:</p>
<pre><code>
------------------------------------
git branch mywork
git checkout mywork
...hack some files (file1 file2 file3)...
git add file1 file2 file3
git commit
git checkout master
git merge mywork
------------------------------------
</code></pre>
<p>Yes that&#8217;s good, but what happens when all your development repositories are under SVN control? Well here comes a little script that will help us integrate with our existing repositories.</p>

<p>h3. Enter git-svn</p>

<p>git-svn is a little perl script (that comes bundled with the Git distribution) that lets you track SVN repositories. You can also commit back between Git and SVN. This is very good, you can start using git without converting your working repositories yet&#8230; well until you are ready to migrate them.</p>

<p>h3. GIT RAILS RECIPE</p>
<pre><code>
mkdir beast
cd beast
git-svn init http://svn.techno-weenie.net/projects/beast/trunk  (for example) 
git-svn fetch -r2940                                            (get a particular repository revision)
git-svn fetch                                                   ( get the whole repository history)

then REPEAT{ 

git checkout -b mywork        (start my own branch)
   ....START HACKING          (fix some bugs)
git commit -a                 (commit the changes to git)
git checkout master           (switch to master branch) 
git-svn rebase                (update master with the upstream svn repository)
git checkout -b mymerge       (create a branch mymerge and checkout from master)
git merge --squash mywork     (merge into current branch, mywork branch) 
git commit                    (commit the changes to git)
git-svn dcommit               (commit the changes to svn repository....if you have enough permissions)
git checkout master           (switch to master branch) 
git-svn rebase                (update master with the upstream svn repository again)
git branch -D mywork mymerge  (get rid of branch mywork and mymerge)

}
</code></pre>
<p>See <a href='http://cheat.errtheblog.com/s/gitsvn/'>http://cheat.errtheblog.com/s/gitsvn/</a> for a cheat sheet on this.</p>

<p>Here James Bowes tells us why rebase is important: <a href='http://jbowes.dangerouslyinc.com/2007/01/26/git-rebase-keeping-your-branches-current/'>http://jbowes.dangerouslyinc.com/2007/01/26/git-rebase-keeping-your-branches-current/
</a></p>

<p>There is one problem with git-svn. It can not handle svn:externals yet.</p>

<p>But there is a workaround to it. Once you fetch your RAILS svn project, you may want Rails edge in your vendor directory, or some handy plugins as well.</p>

<p>Get RAILS EDGE into a separate git clone as well as your handy plugins. Then do a symlink into the vendor directory.</p>
<pre><code>
mkdir rails_edge
cd rails_edge
git-svn init http://svn.rubyonrails.org/rails/trunk  (for example)
git-svn fetch -r7300  (get just the latest revision you know about) 

git-svn fetch   (perform a fetch once in a while to keep it up to date)

then cd myproj/vendor
ln -s ../../rails_edge rails
</code></pre>
<p>Perform the same task for every plugin you use. You may also track a stable version of rails like http://svn.rubyonrails.org/rails/branches/1-2-stable</p>

<p>There are pros and cons of this approach. You can decide if this is enough for your current needs or wait until git supports subprojects. Keeping Rails stable and edge in different repositories, and tracking their changes independently seems to be a very clean way to work.</p>

<p>I hope that gives you a warm welcome to the world of git, and that you start enjoying Rails development along with Git.</p>]]></description>
        <pubDate>Thu, 16 Aug 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/8/16/using-git-for-rails-development</guid>
      </item>
    
      <item>
        <title>Petes Swanky Framework</title>
        <link>http://blog.new-bamboo.co.uk/2007/8/14/petes-swanky-framework</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/8/14/petes-swanky-framework#disqus_thread</comments>
        <description><![CDATA[<img src='http://blog.new-bamboo.co.uk/assets/2007/8/14/1110989214_16a9afb39d_o.jpg' alt='PSF' />
<p>Source: <a href='http://www.flickr.com/photos/32392491@N00/1110989214/'>Mike Riley</a></p>]]></description>
        <pubDate>Tue, 14 Aug 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/8/14/petes-swanky-framework</guid>
      </item>
    
      <item>
        <title>ActionScript 3: or how I conquered dragons with my bare hands</title>
        <link>http://blog.new-bamboo.co.uk/2007/8/7/actionscript-3-or-how-i-conquered-</link>
        <author>max
@new-bamboo.co.uk (max
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/8/7/actionscript-3-or-how-i-conquered-#disqus_thread</comments>
        <description><![CDATA[<img class='picture' src='http://blog.nbwd.co.uk/assets/2007/8/7/zeus.gif' />
<p>From the start, I have to warn you that this melodramatic title may not have been terribly well considered. When Shelley asked me what my talk at 2 was going to be, I responded in rather a flustered manner without thinking through the ramifications. I think a sense of rather foolhardy bravado may have seeped into the name of the talk, and I am not ashamed to admit that.</p>

<p>Not wanting to disappoint, I wracked my brain for Actionscript 3 scenarios which could relate to the theme of Dragons. No luck. Cursing the English Literature degree that had seemingly created this intractable situation, I got to work on the talk.</p>

<p>The thing I wanted to convey most is that making things with AS3 is not really hard or indeed <strong>Evil</strong>. Many people have misconceptions of Flash, and most Rails developers I know don&#8217;t really like the idea of playing around with it at all. In my experience, Flash is a really great medium for doing loads of different things, though definitely not a replacement for HTML/CSS/JS in most cases. I really enjoy creating stuff in Flash, and intended to get some people to listen to me.</p>

<p>Also worth noting, this talk was not about Flex. I have experimented with Flex, and kind of like the idea behind it for RIAs. Sometimes though you just want to work with the actual language behind the interface, rather rely on a whole bunch of classes that get mysteriously instantiated from your MXML.</p>

<p>I wanted to educate people about the fundamentals of how these interfaces are build from the primary elements. We cover creating sprites on the stage, importing other classes, attaching behaviour and some of the basics of drawing. Sticking with the mythological theme (though not of a dragonesque nature), the examples are set in the depths of time in Ancient Greece. Gods chill out on Mount Olympus, and everything is hunky dory. With that in mind, here is the content of the talk. You can get the files here:</p>
<a href='http://blog.nbwd.co.uk/assets/2007/8/7/gods.zip'>http://blog.nbwd.co.uk/assets/2007/8/7/gods.zip</a>
<h1 id='zeus'>Zeus</h1>

<p>The first example we look at is as simple as you can get. We put this in the file &#8216;Main.as&#8217; in the example called <tt>01_zeus</tt>. When this is compiled, we include the Sprite class and make a class called Main that is inherited from it. The constructor function is named the same as the class, so when this is compiled and run, the Main function is called when an object of class Main is initialised and added to the stage.</p>

<p>In this Main function is where all the magic happens, we create a new Sprite (not a pun) called zeus, and start drawing his likeness. Now whatever people may claim, no-one has ever seen a greek god, and I don&#8217;t believe the likenesses of them on pots are right. In my opinion, Greek gods are all square in shape, and covered in colourful hair. This has nothing to do with it being easier to draw sprites like that, so don&#8217;t even suggest it. We specify that zeus will be black, and 50x50 pixels. We do this by calling methods on the Sprite&#8217;s built in graphics object. once we have draw him, we add him to the stage for all to admire. For fun, we nudge him into the middle a bit more to demonstrate how Sprites have x and y coords for positioning them.</p>
<pre><code class='javascript'>
  package {
	import flash.display.Sprite;
	
	public class Main extends Sprite {
		
		public function Main():void {
			var zeus:Sprite = new Sprite();
			zeus.graphics.beginFill(0x000000);
			zeus.graphics.drawRect(0, 0, 50, 50);
			this.addChild(zeus)
			zeus.x = 30
			zeus.y = 30
		}
	}
  } 
</code></pre>
<p>&#8220;Wow&#8221;, I hear thee cry, &#8220;surely the talk was over at this point and roses were being cast at your feet?&#8221;. This was not the case Gentle Reader, greater marvels were to appear shortly.</p>

<h1 id='athena'>Athena</h1>

<p>Now Zeus is a bit old and cannot move very well. To demonstrate a bit more action we pan to Athena. In this project (<tt>02_athena</tt>) we have a new directory called &#8220;gods&#8221;. This will contain all our other classes we are going to use. In our Main.as we import &#8220;gods.Athena;&#8221; which is the class in gods/Athena.as, and add it to the stage like we did with Zeus. Athena has all her internals nicely encapsulated inside her, unlike slovenly Zeus. She can do this because she is created from a class that extends Sprite and can add to it as we want. We have to import all the classses we are interested in inside the package that contains Athena. In this case we want the sprite class, and all the events &#8220;flash.events.*;&#8221; which we use a wildcard for.</p>

<p>When Athena is instantiated, we draw her as a square (totally correct), but her class has a public variable called hair color: <pre><code class='javascript'>
public var hairColor:Number = 0xe4a406;
</code></pre> this is used to make a nice golden square instead of the black we used on Zeus. She also has event handlers added to her. When someone clicks on her, the method startDrag() is called on her which is inherited from Sprite. She can now move around on the stage with the user&#8217;s mouse (though the ironic notion of humans being in control of the motor functions of the Gods is a whole debate I am not entering into atm). Interestingly, we attach the mouseup event to the stage object of the Sprite rather than the sprit itself. This prevents the situation where you drag something and inadvertantly move out of the window. The stage can tell if you release outside the window, but if it is on the object, it won&#8217;t get the hint.</p>

<h1 id='olympus'>Olympus</h1>

<p>Again, a miraculous thing, but the fun doesn&#8217;t stop there. We now decide to make a home for all these gods, and feel that Olympus is a fiting name. In <tt>03_olympus</tt>, we make an olympus object and add it to the stage. Like we did with the gods, we represent Olympus as a square, it is big and grey. Olympus has an event handler on it which takes mouse clicks and creates new instances of RandomGod on the x and y of the event. Clicking on the stage creates a multitude of gods. Unfortunately there is a bit of a bug here which was noticed afterwards and that I haven&#8217;t fixed, if you click on a random god, the x and y are taken from it rather than Olympus. Not the end of the world, but there ya go.</p>

<h1 id='naughty_gods'>Naughty Gods</h1>

<p>In the final example a bit more goes on. We create olympus as before, and also create zeus with his own special class. The random gods are populated automatically this time, and Olympus takes care of creating them when it is instantiated. Unfortunately these gods have been drinking some rather bad wine. This has the unintended consequence of turning their hair green, and making them rather unruly. Intolerable.</p>

<p>Zeus, not gifted with ample amounts of patience for his immortal responsibilities responds to this chaos by delivering a thunderbolt into the rabble, killing them dead.</p>

<p>Let&#8217;s analyse what is involved in this catastrophe.</p>

<p>When Zeus was created, he was not created equal. The Zeus class has the following:</p>
<pre><code class='javascript'>
		public function Zeus(posX:Number, posY:Number):void {
			...
			this.addEventListener(MouseEvent.MOUSE_DOWN, mouseIsDown);
		}

		public function mouseIsDown(event:MouseEvent):void {
			strikeDownNaughtyGods();
		}
		
		public function strikeDownNaughtyGods():void{
			var lightning:LightningBolt = new gods.LightningBolt(this, olympus.naughtyGods);
			olympus.addChild(lightning)
		}
</code></pre>
<p>When clicked, Zeus calls the function strikeDownNaughtyGods(). This makes a new ferocious lightningbolt taking Zeus (this), as the thrower, and olympus.naughtyGods as the targets. The variable olympus is just a reference to olympus we set on zeus at the beginning. When our lightnning bolt is made, the fun starts. The graphics object is called, and we say we are going to start drawing with the thickness of boltWidth and a nice blueish colour. the drawing pointer is moved to the center of the srcGod, and we send the bolt off.</p>
<pre><code class='javascript'>
 package gods{
  import flash.display.Sprite;
  import flash.events.\*;
  import flash.utils.Timer;
  import flash.events.TimerEvent;
  import flash.display.Sprite;
  
  public class LightningBolt extends Sprite {
    
    ...
    
    public function sendBolt():void{
      killCount = 0;
      timer = new Timer(500);
      timer.addEventListener("timer", timerHandler);
      timer.start();
    }
    
     public function timerHandler(event:TimerEvent):void {
        if (killCount == targetGods.length){
          timer.stop();
          this.graphics.clear();
        }else{
          this.graphics.lineStyle(boltWidth*=0.8, 0xb2ebf4);
          this.graphics.lineTo(targetGods[killCount].heartX(), targetGods[killCount].heartY());
          targetGods[killCount].kill();
          killCount++;
        }
     }
  }
 }
</code></pre>
<p>A Timer object is created, and every 500 miliseconds it will call timerHandler. When we haven&#8217;t killed all the gods yet, the bolt width diminishes to reflect the principle of Conservation of Energy, and then we draw a line to the next god in the array. This new god has the method kill() called on him, which in itself is scary. This method makes him change colour, shake from side to side and finally expire, turning red in the process. When the naughty gods are dealt with, the timer stops, and Zeus can read a book in peace.</p>

<p>So there you have it, some basic principles explained. I am by no means an expert, I just play around every now and again for fun. Hopefully a few brave souls will have a go and come up with some other neat stuff. I have found that graphing in particular is an area where Flash shines, especially where it is being fed xml from a beautiful Rails app. Maybe I will blog about my recreation of a zoomable graphing for showing tons of data at some point if anyone is interested (a bit like Google Finance, but made by me).</p>]]></description>
        <pubDate>Tue, 07 Aug 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/8/7/actionscript-3-or-how-i-conquered-</guid>
      </item>
    
      <item>
        <title>Talk at 2</title>
        <link>http://blog.new-bamboo.co.uk/2007/8/7/talk-at-2</link>
        <author>max@new-bamboo.co.uk (max)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/8/7/talk-at-2#disqus_thread</comments>
        <description><![CDATA[<p>Things are alway rather busy here, everyone working away on our various projects. It can sometimes be hard to spend a bit of time getting back to our roots and hacking away on stuff which is not related to our work. It is even harder to share this knowledge with everyone when it has no bearing on the task at hand.</p>

<p>With this in mind, we have been trying out a new scheme in New Bamboo Towers to encourage sharing of all those crazy gems of knowledge we discover in our own time. Thus the &#8220;Talk at 2&#8221; came into being (Damien has suggested &#8220;talk@2&#8221; but this seems a little 2002 to me). On Friday afternoon, one member of the team presents something that they find particularly interesting, and that other people might not be familiar with. It can be related to anything really, but usually has an element of programming in it.</p>

<p>So far Jonathan has done a talk on rSpec, and on Friday I presented an unbelievably useful and emotional piece about ActionScript 3 that moved many of the guys to tears. We will try to get these written up after the event so everyone can share the fun.</p>

<p>At this point I have to give a disclaimer that this might not happen every week, sometimes there just isn&#8217;t enough time, but we will certainly try to be diligent with it for as long as we can.</p>

<p>I also feel it needs a theme tune and maybe a logo, but don&#8217;t feel capable of creating either. In my head, the theme tune should be a mixture of the Countdown theme tune, and the Darth Vader music from Star Wars. If anyone can come up with something that encapsulates this, I&#8217;d be very interested in hearing from you.</p>]]></description>
        <pubDate>Tue, 07 Aug 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/8/7/talk-at-2</guid>
      </item>
    
      <item>
        <title>BDD and state</title>
        <link>http://blog.new-bamboo.co.uk/2007/7/6/bdd-and-state</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/7/6/bdd-and-state#disqus_thread</comments>
        <description><![CDATA[<p>So client worked has kept me too busy to write up the second part of my &#8220;mocking&#8221;:http://blog.new-bamboo.co.uk/2007/2/25/defining-interfaces-through-mocking tutorial. Part two is coming along nicely&#8230; Honest guv&#8217;nor and describes some advanced mocking strategies but it&#8217;s taking a bit longer to write than I anticipated. In the meantime I&#8217;d like to explain a little more about behavioral driven design. As there is one particular thing I find I&#8217;m constantly asked with regards to BDD and mocking and it&#8217;s driving me nuts explaining it all the time, so listen up BDD nay sayers.</p>

<p>With BDD you&#8217;re interested in the behavior of the object you&#8217;re testing.. Simple duh! OK so I&#8217;ve just given you the Zen master says little but explains a lot explanation, not very helpful I now. Anyway, objects in a system never work in solitary at least in any sort of semi-complex system Of course you could cheat and do what one supposed &#8220;Sun&#8221;:http://www.sun.com certified engineer I interviewed once did and write a whole forum in a monolith of a class, but that&#8217;s another story and it&#8217;s wrong for me to mock the man like this (did you see the pun! Mock! Ha! Ah, stop throwing the tomatoes!).</p>

<p>Now that I&#8217;ve managed to totally side track this conversation I&#8217;ll get us back onto topic. The other entities that an objects interacts with are called collaborators and with BDD we&#8217;re interested in how our objects interact with each other via their interfaces and verifying these interactions. The way we do this in BDD is by the use of mock objects. With a mock object you can not only stub out methods but also attach assertions to verify behavior of the object with it&#8217;s collaborator. And this is what we&#8217;re interested in, <em>behaviour</em> not state.</p>

<p>We&#8217;re not interested in verifying the return value i.e the collaborators state. This is actually something that shocks quite a few Rails developers who are entrenched in their world of fixtures and testing concrete instances. Sure, I&#8217;m not saying that I never instantiate a concrete collaborator and use it in memory but I&#8217;m very rarely interested in state especially in my unit tests and the Rails &#8216;functional&#8217; tests.</p>

<p>The point at which I get interested in state usually occurs around the point of my integration tests and end to end tests. Being interested in behavior rather than state can be quite a big mind shift for a lot of developers but if you give it a try I promise that you&#8217;ll find your path to testing will be a more enjoyable one.</p>]]></description>
        <pubDate>Fri, 06 Jul 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/7/6/bdd-and-state</guid>
      </item>
    
      <item>
        <title>More job opportunities</title>
        <link>http://blog.new-bamboo.co.uk/2007/7/5/more-job-opportunities</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/7/5/more-job-opportunities#disqus_thread</comments>
        <description><![CDATA[<p>We need great senior software engineers!. For more details take a look at the &#8220;SVN Job board&#8221;:http://jobs.37signals.com/jobs/1799</p>]]></description>
        <pubDate>Thu, 05 Jul 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/7/5/more-job-opportunities</guid>
      </item>
    
      <item>
        <title>Happy Birthday Damien!!</title>
        <link>http://blog.new-bamboo.co.uk/2007/6/27/what-are-you-doing-with-your-vps</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/6/27/what-are-you-doing-with-your-vps#disqus_thread</comments>
        <description><![CDATA[<p>W00t! IM IN URZ COOKERZ BAKIN UR CAKEZ!</p>]]></description>
        <pubDate>Wed, 27 Jun 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/6/27/what-are-you-doing-with-your-vps</guid>
      </item>
    
      <item>
        <title>Team Bamboo</title>
        <link>http://blog.new-bamboo.co.uk/2007/6/22/team-bamboo</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/6/22/team-bamboo#disqus_thread</comments>
        <description><![CDATA[<img src='http://farm2.static.flickr.com/1346/592832826_f3f6dcbd83_d.jpg' />]]></description>
        <pubDate>Fri, 22 Jun 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/6/22/team-bamboo</guid>
      </item>
    
      <item>
        <title>Quick Textmate tip</title>
        <link>http://blog.new-bamboo.co.uk/2007/5/25/quick-textmate-tip</link>
        <author>max@new-bamboo.co.uk (max)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/5/25/quick-textmate-tip#disqus_thread</comments>
        <description><![CDATA[<p>For all those people making Rails apps in Textmate, here&#8217;s a little thing that I&#8217;ve suggested to other people in the office recently. It affects people like me, who generally cd into my rails app and run mate . to open up the entire thing in everyone&#8217;s favourite editor. The problem in this situation, which most of us have encountered at one point or other, occurs when doing a find in project. There is a certain sinking feeling you get as you press the find button, and at that exact moment, realise that the log and tmp directories (which have been accumulating in size over the last month) are gonna be searched as well. The ball starts spinning and there&#8217;s nothing you can do&#8230;</p>

<p>One solution to this is to remove the directories manually, and optionally save the project for the future. But that is boring, and can lead to mistakes. Enter neato trick in the form of folder references. If you go to Preferences &gt; Advanced &gt; Folder References, you can see the regular expression in the second box for folders that TextMate will ignore.</p>

<p>If you change it to this, your log directory will be ignored automatically:</p>
<pre>
<code>
!.*/(\.[^/]*|log|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$
</code>
</pre>
<p>If you are feeling Spartan, you could also add sessions, cache, rails and anything else you don&#8217;t search often which that slows search down.</p>

<p>I hope this is helpful to someone, it seemed rather obvious but overlooked feature to me.</p>]]></description>
        <pubDate>Fri, 25 May 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/5/25/quick-textmate-tip</guid>
      </item>
    
      <item>
        <title>Job opportunities</title>
        <link>http://blog.new-bamboo.co.uk/2007/5/18/job-opportunities</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/5/18/job-opportunities#disqus_thread</comments>
        <description><![CDATA[<p>Well it&#8217;s that time of the year again and we&#8217;ve got a few more vacancies for Ruby on Rails developers. If you&#8217;d like to work on some of the hottest start ups and with a young/vibrant team then maybe you&#8217;d like to come work for us!</p>

<p>We&#8217;re hiring across the board from juniors just out of univeristy to seniors with years in the web industry.</p>

<p>For all applicants we&#8217;d expect the following:</p>

<ul>
<li>Experience developing web applications</li>

<li>Scripting/programmer experience of some kind</li>

<li>You like using Macs</li>

<li>You&#8217;re comfortable in a Unix environment</li>

<li>Good OO knowledge</li>

<li>At least 6 months experience with Ruby and/or Ruby on Rails</li>

<li>An eye for detail</li>
</ul>

<p>For senior level developers we expect:</p>

<ul>
<li>TDD experience</li>

<li>Ruby on Rails experience</li>

<li>Scripting experience in another language apart from Ruby</li>

<li>CSS and XHTML/HTML knowledge</li>

<li>Good knowledge of JavaScript</li>

<li>Understanding of common design patterns</li>
</ul>

<p>For junior developers you&#8217;ll be given mentoring/guidance and if you&#8217;ve contributed to any open source projects then we&#8217;d consider this as extra brownie points.</p>

<p>Salaries are dependant on experience and start from &#163;23K+ for juniors and even more for seniors.</p>

<p>Please send your CV and salary expectations to <a href='%69%77%61%6E%74%61%6A%6F%62%40%6E%65%77%2D%62%61%6D%62%6F%6F%2E%63%6F%2E%75%6B'>&#105;&#119;&#97;&#110;&#116;&#97;&#106;&#111;&#98;&#64;&#110;&#101;&#119;&#45;&#98;&#97;&#109;&#98;&#111;&#111;&#46;&#99;&#111;&#46;&#117;&#107;</a></p>]]></description>
        <pubDate>Fri, 18 May 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/5/18/job-opportunities</guid>
      </item>
    
      <item>
        <title>Io on OS X</title>
        <link>http://blog.new-bamboo.co.uk/2007/5/15/io-on-os-x</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/5/15/io-on-os-x#disqus_thread</comments>
        <description><![CDATA[<p>For all you language whores out there wanting to get &#8220;Io&#8221;:http://www.iolanguage.com/about/ running on OS X.</p>

<ul>
<li>Install &#8220;MacPorts&#8221;:http://www.macports.org/</li>

<li />

<li />

<li>Start up the Io VM (Assuming you have /opt/local/bin in your path) <pre><code class='ruby'>osxvm</code></pre></li>
</ul>

<p>Enjoy</p>]]></description>
        <pubDate>Tue, 15 May 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/5/15/io-on-os-x</guid>
      </item>
    
      <item>
        <title>Move over Erlang here comes x86 Assembler!</title>
        <link>http://blog.new-bamboo.co.uk/2007/5/14/move-over-erlang-here-comes-x86-</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/5/14/move-over-erlang-here-comes-x86-#disqus_thread</comments>
        <description><![CDATA[<p>Yes, you&#8217;ve probably been reading the blogs telling you about the scalability of Erlang. However lets not forget boys and girls old faithful x86 Assembler the ultimate speed freaks language of the web;).</p>
<em>Note
I'm just kidding btw I actually quite like Erlang...
</em>]]></description>
        <pubDate>Mon, 14 May 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/5/14/move-over-erlang-here-comes-x86-</guid>
      </item>
    
      <item>
        <title>Speaking engagement tonight</title>
        <link>http://blog.new-bamboo.co.uk/2007/4/26/speaking-engagement-tonight</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/4/26/speaking-engagement-tonight#disqus_thread</comments>
        <description><![CDATA[<p>I&#8217;ve been so busy I forgot to mention that I&#8217;m doing a talk tonight for the BCS/Open source specialist group at Oxford University.</p>

<p>Subjects of tonights talk will be:</p>

<pre><code>*  A brief overview of Ruby on Rails
* REST in Rails
* Javascript-less Javascript with RJS
* Beautiful testing with Rails (This will actually be about RSpec)</code></pre>

<p>If you&#8217;re a member of the BCS then please drop by. Also if I have time I&#8217;ll try and make it down to the Oxford geek night tonight.</p>]]></description>
        <pubDate>Thu, 26 Apr 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/4/26/speaking-engagement-tonight</guid>
      </item>
    
      <item>
        <title>We are 1 !!!</title>
        <link>http://blog.new-bamboo.co.uk/2007/4/4/we-are-1</link>
        <author>max@new-bamboo.co.uk (max)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/4/4/we-are-1#disqus_thread</comments>
        <description><![CDATA[<p>On Monday night we celebrated our first year of New-Bambooness (though it is actually slightly longer).</p>

<p>A big thanks to everyone who joined us on the night and those who have supported us along the way. We had a really awesome evening and hope you enjoyed it as much as we did!</p>

<p>We are going to try to get some photos up on Flickr in the next few days, but many of the ones we have collected seem to have been taken by persons in an advanced state of inebriation&#8230; mysterious.</p>

<p>Now we just need to think of a new excuse to celebrate so we can keep the parties going&#8230;</p>]]></description>
        <pubDate>Wed, 04 Apr 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/4/4/we-are-1</guid>
      </item>
    
      <item>
        <title>Prototype to the rescue...</title>
        <link>http://blog.new-bamboo.co.uk/2007/3/27/prototype-helping-spruce-up-your-forms</link>
        <author>jonathan
@new-bamboo.co.uk (jonathan
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/3/27/prototype-helping-spruce-up-your-forms#disqus_thread</comments>
        <description><![CDATA[<p>So anybody who has ever spoken to one of us at New Bamboo knows how much we try to do the &#8220;right thing&#8221;&#169; and Rails makes it just so easy to do the right thing that you&#8217;d either have to be very very lazy or ignorant to the power Rails (and Prototype) provides you.</p>

<p>We&#8217;ve all heard about unobtrusive Javascript and hopefully you&#8217;ve had a look at &#8220;Dan&#8217;s&#8221;:http://www.danwebb.net and &#8220;Lukes&#8221;:http://www.lukeredpath.co.uk &#8220;UJS4Rails&#8221;:http://www.ujs4rails.com plugin. Which allows you to still use the Rails helpers in an unobtrusive way but more importantly declare behaviors in an easy and declarative manner. It&#8217;s also a great springboard into the world of unobstrusive Javascript for those people who&#8217;s Javascript-fu isn&#8217;t yet strong enough to roll your own.</p>

<p>With the release of &#8220;Prototype 1.5.1&#8221;:http://prototypejs.org/assets/2007/3/12/prototype.js rolling your own just got a bit easier. Let&#8217;s take your standard rails form (ignore the loader gif, I&#8217;m getting a bit ahead of myself).</p>
<pre><code class='ruby'>
&lt;% form_for(:section,@section, :url =&gt; sections_path, :html =&gt; 
  {:id =&gt; &quot;new_section_form&quot;}) do |f|%&gt;
	&lt;%= f.text_field :name %&gt;
	&lt;%= submit_tag &#x27;Add Section&#x27;%&gt;
        &lt;%= image_tag(&#x27;spinner.gif&#x27;, :id =&gt; &#x27;spinner_create&#x27;, 
                                              :style =&gt; &#x27;display:none;&#x27;) %&gt;
&lt;% end %&gt;
</code></pre>
<p>It&#8217;s a pretty standard form that creates a section given a name, nothing exciting but it works and does this in a non Ajax way. Now this is the bit of Javascript that I would probably use before in order to Ajaxify this form:</p>
<pre><code class='javascript'>
$(&#x27;new_section_form).onsubmit = function(){	
	  if(!$(this.name).present()){alert(&#x27;Please enter a name&#x27;); 
                                                return false;}

		new Ajax.Request(&#x27;&lt;%= sections_path) %&gt;&#x27;, {
			asynchronous: true, 
			evalScripts: true,
			method: &#x27;post&#x27;,
			parameters: this.serialize(), 
			onLoading: function(request){
			    Element.toggle(&#x27;spinner_create&#x27;);
			  },
			onFailure: function(request){
			    alert(&#x27;Sorry, there were problems processing your request.\n 
                                   Please try again.&#x27;);
				},
			onComplete: function(request){
				Element.toggle(&#x27;spinner_create&#x27;);
			  }
		});
		return false;
	};
</code></pre>
<p>Nothing exciting here really, the code would normally be called in a dom ready block in case you were wondering. Apart from looking a bit verbose you&#8217;ve probably noticed that I&#8217;m not being very DRY in that I&#8217;ve duplicated information that could and should be retrieved from the actual form. Things such as the path to post the details to and the method in which to do this (in this case &#8216;post&#8217;).</p>

<p>Yeah I know I could just use a bit more javascript-fu to get those details (which I normally would do especially if I wanted to separate this code into a another JS file), but again this is going to get even more verbose. No wonder people like to use the Rails Javascript abstractions.</p>

<p>Let&#8217;s tidy this up a bit. In Prototype 1.5.1 the guys have given you the <em>request()</em> method which does pretty much the same as the Ajax.Request method. In fact it even accepts the same optional parameters. The main difference though between request() and Ajax.Request() is that the former gets all those little details it needs for the form itself so the above now can be shortened to this:</p>
<pre><code class='javascript'>
$('new_section_form').onsubmit = function(){
		if(!this.section_name.present()){
			alert('Section name cannot be blank');
			return false;
		}
		this.request({
                   onLoading: function(){
                                     Element.toggle('spinner_create')
                  ;}, 
                   onComplete: function(){
                                    Element.toggle('spinner_create');
                  }
                 });
		return false;
	}
</code></pre>
<p>I could of probably extracted the spinner out to a separate variable and called toggle on it to make it even DRY&#8217;er, but at least you get the idea. There are lots of other little improvements and goodies to be found in the new release of Prototype and I seriously encourage you to play around and have some fun.</p>

<p>Tagged with &#8220;Prototype&#8221;:http://technorati.com/tag/Prototype, &#8220;Javascript&#8221;:http://technorati.com/tag/Javascript, &#8220;Unobtrusive Javascript&#8221;:http://technorati.com/tag/Unobtrusive%20Javascript</p>]]></description>
        <pubDate>Tue, 27 Mar 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/3/27/prototype-helping-spruce-up-your-forms</guid>
      </item>
    
      <item>
        <title>We're expanding again</title>
        <link>http://blog.new-bamboo.co.uk/2007/3/12/we-re-expanding-again</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/3/12/we-re-expanding-again#disqus_thread</comments>
        <description><![CDATA[<p>So I&#8217;ve got plans. Alot of plans. Crazy, mad plans. One of them is to open up a New Bamboo office in every major Thai fishing village&#8230; Eerrm hang on a sec, that sounded better in my head while I was drinking a beer. OK forget that idea.</p>

<p>But seriously things are pretty busy and we&#8217;re on the look out for someone with crazy Ruby skills. If you&#8217;re interested get in touch. Contact details are to be found on our main company website.</p>]]></description>
        <pubDate>Mon, 12 Mar 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/3/12/we-re-expanding-again</guid>
      </item>
    
      <item>
        <title>Rubies this week</title>
        <link>http://blog.new-bamboo.co.uk/2007/3/11/rubies-this-week</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/3/11/rubies-this-week#disqus_thread</comments>
        <description><![CDATA[<p>A few ruby links of note this week.</p>

<ul>
<li>
<p>&#8220;S3DFS for EC2&#8221;:http://www.openfount.com/blog/s3dfs-for-ec2 - Now we can really scale with EC2, no more worries about where the database is.</p>
</li>

<li>
<p>&#8220;8 OpenID Resources for Rails Developers&#8221;:http://www.rubyinside.com/7-openid-resources-for-rails-developers-418.html - Everyone&#8217;s at it these days.</p>
</li>

<li>
<p>&#8220;Sequel&#8221;:http://code.google.com/p/ruby-sequel/ is an ORM framework for Ruby. Sequel provides thread safety, connection pooling, and a DSL for constructing queries and table schemas - Have to check this out!</p>
</li>
</ul>]]></description>
        <pubDate>Sun, 11 Mar 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/3/11/rubies-this-week</guid>
      </item>
    
      <item>
        <title>Squawk - Twitter Growl notifications</title>
        <link>http://blog.new-bamboo.co.uk/2007/3/2/squawk-twitter-growl-notifications</link>
        <author>damien@new-bamboo.co.uk (damien)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/3/2/squawk-twitter-growl-notifications#disqus_thread</comments>
        <description><![CDATA[<p>Most of us here are &#8220;Twitter&#8221;:http://twitter.com addicts (&#8220;Damen&#8221;:http://twitter.com/dctanner, &#8220;Jonathan&#8221;:http://twitter.com/noodlesinmysandals and &#8220;Max&#8221;:http://twitter.com/maxthelion). However I keep getting frustrated and distracted by either Twitterific having a connection problem or the Twitter AIM bot going offline. Surprised that no one had done this already, I put &#8220;two&#8221;:http://twitter.rubyforge.org/ and &#8220;two&#8221;:http://segment7.net/projects/ruby/growl/ together, and with the magic of ruby we have Twitter updates as &#8220;Growl&#8221;:http://growl.info/ notifications (we&#8217;ll call them Squawks!) - easy.</p>

<p>!http://blog.nbwd.co.uk/assets/2007/3/2/squawk.png!</p>

<p>&#8220;Download the Squawk ruby script&#8221;:http://pastie.caboo.se/44304</p>]]></description>
        <pubDate>Fri, 02 Mar 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/3/2/squawk-twitter-growl-notifications</guid>
      </item>
    
      <item>
        <title>Defining interfaces through mocking</title>
        <link>http://blog.new-bamboo.co.uk/2007/2/25/defining-interfaces-through-mocking</link>
        <author>jonathan
@new-bamboo.co.uk (jonathan
)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/2/25/defining-interfaces-through-mocking#disqus_thread</comments>
        <description><![CDATA[<p>This post is a re-work of my Barcamp presentation I did the other day on good OO design though mocking.</p>

<p>Now when I first started doing TDD over six years ago the major thing that struck me was how well designed the interactions between associated objects were compared to applications I&#8217;d written in the past without TDD. This was all because by writing test first I was forcing myself to think about how my objects were going to interact with my tests and thus with other objects within the system. Another thing I discovered was also how using mocks even more so allowed me to concentrate even more on the interfaces of the object while developing my models. OK so thats a bit of a lie, back then when I was doing TDD I was doing J2EE development with EJB 2.0 and as we all know this led to anaemic models because all our business logic had to be pulled in via service objects. So no, back then I wasn&#8217;t thinking too much about the interfaces of my &#8220;models&#8221;, more on my service objects;).</p>

<p>I&#8217;m going to run through a fairly trivial example just to give you a little taste of what I&#8217;m talking about. I&#8217;m going to be using &#8220;RSpec&#8221;:http://rspec.rubyforge.org with its built in mocking support in my examples but I could of easily used Test::Unit with &#8220;Flexmock&#8221;:http://onestepback.org/software/flexmock/ or &#8220;Mocha/Stubba&#8221;:http://mocha.rubyforge.org/.</p>

<p><em>Step one</em> - Make a cup of tea</p>

<p><em>Step two</em> - Install the RSpec gem</p>
<pre><code class='ruby'>
$ sudo gem install rspec
</code></pre>
<p><em>Step three</em> - Create a Rails application, in this case we&#8217;re going to work on a small part of an auction application.</p>
<pre><code class='ruby'>
$ rails rspec_auction
$ cd rspec_auction
</code></pre>
<p>Setup both your development and test database by editing the database.yml. We won&#8217;t actually be hitting the database in the tests but unfortunately you&#8217;ll need to set it up.</p>

<p><em>Step four</em> - Install the RSpec on Rails plugin and generate the helpers and directories that the plugin needs.</p>
<pre><code class='ruby'>
$ ruby script/plugin install \ 
svn://rubyforge.org/var/svn/rspec \
/tags/REL_0_7_5_1/rspec_on_rails/vendor/plugins/rspec_on_rails

$ script/generate rspec
</code></pre>
<p><em>Step five</em> - Make another cup of tea.</p>

<p><em>Step six</em> - Lets create an Auction model. Now the RSpec plugin for Rails provides you with several generators which will be familiar to you if you&#8217;ve used the Rails generators before.</p>
<pre><code class='ruby'>
$ script/generate rspec_model auction
</code></pre>
<p><em>Step seven</em> - Hopefully you&#8217;ve already got the project open in a text editor or your favourite IDE. We need to make a small alteration the the file <em>spec_helper.rb</em> otherwise we&#8217;re going to have problems with Activerecords type checking. Yes, you heard me correctly, I said type checking;). Anyways at the bottom of the <tt>spec_helper.rb</tt> file you&#8217;ll need to add the following lines.</p>
<pre><code class='ruby'>
module ActiveRecord
  module Associations
    class AssociationProxy
      def raise_on_type_mismatch(record)
        # do nothing
      end
    end
  end
end
</code></pre>
<p>Oh and you&#8217;ll also need a basic migration file for your auction model so in the file <em>001_create_auctions.rb</em> that&#8217;s already been generated for you add the following:</p>
<pre><code class='ruby'>
class CreateAuctions &lt; ActiveRecord::Migration
  def self.up
    create_table :auctions do |t|
      t.column :title, :string
      t.column :description, :text
      t.column :updated_at, :datetime
      t.column :created_at, :datetime
      t.column :expires_at, :datetime
      t.column :starting_bid, :float
      t.column :active, :boolean
    end
  end

  def self.down
    drop_table :auctions
  end
end
</code></pre>
<p><em>Step eight</em> - OK, now we&#8217;re all set to do some coding. Open up the <em>auction_spec.rb</em> file and the first thing you&#8217;ll see is something called a context with a description and specifications. When testing our model we may have multiple contexts all depicting the model in different states such as a new auction, an auction about to expire or an auction that has been canceled for example.</p>

<p>In this case the default context generated for us is &#8220;Given a generated <tt>auction_spec.rb</tt> with fixtures loaded&#8221;&#8230; Not exactly what we&#8217;re looking for. So delete this entire context including the specification and replace it with this:</p>
<pre><code class='ruby'>
require File.dirname(__FILE__) + '/../spec_helper'

module AuctionSpecHelper
  #Valid attributes for an auction
  def self.valid_auction_attributes
    {
     :title => "Big bamboo",
     :description => "One caring owner, still in good condition",
     :expires_at => 7.days.from_now,
     :starting_bid => 0.20,
     :active => true
    }
  end
  
  def self.invalid_auction_attributes
    {
      :title => nil,
      :description => nil,
      :expires_at => 7.days.ago,
      :starting_bid => "moo",
      :active => true
    }
  end
  
end

context "A general auction" do
  
  setup do
    @auction = Auction.new
  end
  
  specify "should have errors given invalid values" do
    @auction.attributes = AuctionSpecHelper.invalid_auction_attributes
    @auction.valid?.should_equal false
  end
  
  specify "should have no errors given valid values" do
    @auction.attributes = AuctionSpecHelper.valid_auction_attributes
    @auction.valid?.should_equal true
  end
  
end
</code></pre>
<p>The module helper at the top of the source should be self explanatory. I&#8217;m using it to store hashes of invalid and valid attributes for an auction.</p>

<p>Run the model spec&#8217;s by typing the following at the command line: <pre><code class='ruby'>
rake spec:models
</code></pre></p>

<p>You&#8217;ve probably noticed that I&#8217;ve shot ahead and added not one but two specifications. There wouldn&#8217;t be much point in the &#8220;should have no errors&#8221; specification as I haven&#8217;t actually written any constraints in the auction model yet. Really my tests or specifications in this case should always fail first and then it&#8217;s up to me to do what ever is required to make them spec pass&#8230;. Even if involves secret underground Artic bases&#8230; Eerrmm OK, maybe not. Anyway, as you can tell I&#8217;ve added the first specification in at the top to intentionally fail. Let&#8217;s make this pumpkin pass. Make your auction.rb look like this:</p>
<pre><code class='ruby'>
class Auction &lt; ActiveRecord::Base
  
  validates_presence_of :title
  validates_presence_of :description
  validates_numericality_of :starting_bid
  
end
</code></pre>
<p>If you run the specifications now you&#8217;ll see that they all now pass.</p>

<p><em>Step nine</em> - I&#8217;m going to digress for a minute and talk about stubs and mocks. If you don&#8217;t already know what a stub or a mock is then &#8220;here&#8221;:http://www.martinfowler.com/bliki/TestDouble.html is a good description of the various terms. Now you&#8217;ve probably been told to use stubs and/or mocks when working with systems external to your application, 3rd party libraries or dependencies that aren&#8217;t solely related to what you&#8217;re currently testing. In this case the database is a dependency so lets stub out any database calls. The only problem is that we&#8217;re currently not hitting the database, so let write a specification that would normally hit the database, but errrm doesn&#8217;t. Sorry if I sound like a fortune cookie. Add this context and specifcation to the <tt>auction_spec.rb</tt> file.</p>
<pre><code class='ruby'>
context "An auction that we want to remove" do
  
  setup do
    @auction = Auction.new
    @auction.attributes = AuctionSpecHelper.valid_auction_attributes
    @auction.stub!(:save!).and_return(:true)
  end
    
  specify "should be able to deactivate auction" do
    @auction.deactivate!
    @auction.active?.should_equal false
  end
  
end
</code></pre>
<p>The first thing you&#8217;ll see is that I&#8217;ve actually stubbed out the default Activerecord save! method of the auction model so that it doesn&#8217;t hit the database. You currently don&#8217;t see me calling save! but thats a coming implementation detail which will become obvious in a few minutes. Of course if you try running the above you&#8217;ll get a few errors, the first being that there isn&#8217;t a deactivate! method on auction yet. Let&#8217;s fix that now, inside auction.rb add:</p>
<pre><code class='ruby'>
def deactivate!
    self.active = false
    #Aha! The fabled save! method!!!
    self.save!
 end
</code></pre>
<p>OK, the spec should now pass, but you know what? Call me picky but I&#8217;d like to hide the active attribute of the model, I don&#8217;t want any Tom, Dick or Tony Blair playing with the auction innards. Let&#8217;s make the following modifications:</p>

<p>In auction.rb add:</p>
<pre><code class='ruby'>
def activated?
    self.active?
end
</code></pre>
<p>And then change the previous spec to: <pre><code class='ruby'>
specify "should be able to deactivate auction" do
    @auction.deactivate!
    @auction.activated?.should_equal false
 end
</code></pre></p>

<p>The specifications should now still pass.</p>

<p><em>Step ten</em> - Let&#8217;s now interact with another model, in this case it&#8217;s going to be a user model as our auction belongs<em>to one owner who created the auction. Things are going to start getting interesting. Add the following context and specification to your auction</em>spec.rb file:</p>
<pre><code class='ruby'>
context "An auction with an owner" do
  
  setup do
    @auction = Auction.new
    @auction.attributes = AuctionSpecHelper.valid_auction_attributes
    @owner = mock('user')
    @owner.stub!(:new_record?).and_return(false)
    #make sure you put in the Activerecord type checking hack in 
    #the spec_helper.rb file or this bit will go BOOM BOOM
    @auction.owner = @owner
  end
  
  specify "should have an associated owner" do
    @auction.owner.should_not_equal nil
  end
  
end
</code></pre>
<p>OK, so this isn&#8217;t a very helpful test/spec in itself. However did you notice that we didn&#8217;t drop to the command line, run the RSpec model generator, create a User model migration, decide what fields to put into our migration, run the migration and then add various methods to our User class that our Auction model might need to interact with. Instead we can concentrate purely on our Auction model&#8217;s business logic and it&#8217;s interfaces/interactions with other models. You&#8217;ll find that by using stubs and mocks it allows you to experiment a little more easily without detracting from your train of thought.</p>

<p>I think here&#8217;s a good place to stop for now. Next time in part two I&#8217;ll introduce you lovely people to more advanced examples of mocking, but first some sleep. Good night chaps (and chapettes).</p>

<p>Tagged with &#8220;Mocks&#8221;:http://technorati.com/tag/mocks, &#8220;Ruby&#8221;:http://technorati.com/tag/ruby, &#8220;Mocking&#8221;:http://technorati.com/tag/mocking, &#8220;RSpec&#8221;:http://technorati.com/tag/rspec</p>]]></description>
        <pubDate>Sun, 25 Feb 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/2/25/defining-interfaces-through-mocking</guid>
      </item>
    
      <item>
        <title>Anti-theft and anti traffic warden for your car</title>
        <link>http://blog.new-bamboo.co.uk/2007/2/19/anti-theft-and-anti-traffic-warden-for-</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/2/19/anti-theft-and-anti-traffic-warden-for-#disqus_thread</comments>
        <description><![CDATA[<p>Overheard octopod on #Caboose, &#8220;Eye of Sauron&#8221;:http://www.makezine.com/blog/archive/2007/02/maker<em>version</em>o.html anti-theft device for you car;).</p>

<p>Tagged with &#8220;Eye of sauron&#8221;:http://technorati.com/tag/eye%20of%20sauron</p>]]></description>
        <pubDate>Mon, 19 Feb 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/2/19/anti-theft-and-anti-traffic-warden-for-</guid>
      </item>
    
      <item>
        <title>Barcamp London 2</title>
        <link>http://blog.new-bamboo.co.uk/2007/2/18/barcamp-london-2</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/2/18/barcamp-london-2#disqus_thread</comments>
        <description><![CDATA[<p>Phew! Both Damien and I are soooo tired. Barcamp London ended just a few hours ago and as usual it was a fun and crazy weekend. There were many interesting talks, lots of great people and much fun to be had. I can&#8217;t wait till the next Barcamp!</p>

<p>Unfortunately I only had the camera on my Sony mobile phone to talk pictures with, which is why the quality of the photos isn&#8217;t terribly sharp, but all the same they&#8217;re up on our Flickerstream to be seen.</p>

<p>A big thank you to Ian Forrester, all the Barcamp organisors and the sponsors for making this possible!</p>

<p>Tagged with &#8220;Barcamplondon2&#8221;:http://technorati.com/tag/barcamplondon2</p>]]></description>
        <pubDate>Sun, 18 Feb 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/2/18/barcamp-london-2</guid>
      </item>
    
      <item>
        <title>Netbeans == Smarter Ruby editing</title>
        <link>http://blog.new-bamboo.co.uk/2007/2/12/netbeans-smarter-ruby-editing</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/2/12/netbeans-smarter-ruby-editing#disqus_thread</comments>
        <description><![CDATA[<p>You&#8217;ve all heard that Sun hired some of the <a href='http://jruby.codehaus.org/'>JRuby</a> guys and that they&#8217;ve been beavering away on improving Ruby running within the JVM. One of the by products of this has been the inclusion of Ruby code editing into <a href='http://www.netbeans.org'>Netbeans</a>, an IDE usually associated with Java development and until a year ago was the butt of every other Java IDE&#8217;s jokes.</p>

<p>Now I&#8217;m a happy <a href='http://macromates.com/'>Textmate</a> user and have been using it for quite a while now, but I have to admit there are various things that have become annoying such as slow search, no inbuilt refactoring support, intellisense and just &#8220;understanding&#8221; my code within the context of the project. Now I don&#8217;t <em>need</em> these things but it would sure make working with large Ruby on Rails projects a heck of a lot easier.</p>
<img src='http://blog.nbwd.co.uk/assets/2007/2/12/Picture_8.png' />
<p>If you want to check out some of the smarter intellisense features for Ruby that are to be found in Netbeans then you&#8217;re going to currently have to do a little bit of work. There&#8217;s currently a <a href='http://wiki.netbeans.org/wiki/view/RubyBuildInstructions'>wiki page</a> up detailing everything you need to do in order to build the latest Ruby modules for the IDE. A link to precompiled binaries that can be added to an existing install of Netbeans can also be found on the page. A word of caution though that it&#8217;s all very work in progress and bad things may happen so I wouldn&#8217;t go dumping your copy of Textmate just yet.</p>

<p>Tagged with &#8220;Netbeans&#8221;:http://technorati.com/tag/netbeans, &#8220;Ruby editing&#8221;:http://technorati.com/tag/ruby%20editing</p>]]></description>
        <pubDate>Mon, 12 Feb 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/2/12/netbeans-smarter-ruby-editing</guid>
      </item>
    
      <item>
        <title>Ruby on Rails Exchange</title>
        <link>http://blog.new-bamboo.co.uk/2007/2/12/ruby-on-rails-exchange</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/2/12/ruby-on-rails-exchange#disqus_thread</comments>
        <description><![CDATA[<p>Well, right at the last minute I managed to get a seat at the Ruby on Rails at <a href='http://www.skillsmatter.com/'>Skills Matter</a>. Overall I found it quite enjoyable and glad I managed to take a day out of my mad schedule to attend.</p>

<p>Talks of note for me were <a href='http://chadfowler.com/'>Chad Fowlers</a> talk on Quick and clean Rails, <a href='http://hobotek.net/blog/'>Tom Locke's</a> Hobo demonstration and <a href='http://www.reevoo.com/blogs/bengriffiths/'>Ben Griffith's</a> talk on testing.</p>

<p>Of particular note was Chad Fowler describing how Ruby is designed/best used by smart programmers who know what they&#8217;re doing. He continued to explain that if put into the wrong hands Ruby gives a bad programmer enough rope to hang himself with. And this is something I have to totally agree with, as over the past two and a half years I&#8217;ve either audited or rewritten some very crazily written Ruby on Rails projects. These were coded by either people trying to write large commercial applications after reading the <a href='http://www.pragmaticprogrammer.com/titles/rails/'>Agile Web development with Rails</a> book, or written by people who didn&#8217;t have a good grounding in Ruby and/or web application architecture. There&#8217;s nothing wrong with this if it&#8217;s a simple site with a small number of users, but not when it&#8217;s a high usage site that people expect to be available around the clock.</p>

<p>After everyones talks were over Skills Matter were nice enough to take everyone down to <a href='http://www.urbanpath.com/london/dj-bars/fluid.htm'>Fluid</a> for some drinks, with the first one hundred being on the house. Overall a very nice end to an interesting day.</p>]]></description>
        <pubDate>Mon, 12 Feb 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/2/12/ruby-on-rails-exchange</guid>
      </item>
    
      <item>
        <title>Merger</title>
        <link>http://blog.new-bamboo.co.uk/2007/2/6/merger</link>
        <author>jonathan@new-bamboo.co.uk (jonathan)</author>
        <comments>http://blog.new-bamboo.co.uk/2007/2/6/merger#disqus_thread</comments>
        <description><![CDATA[<p>You may be wondering what I, Jonathan, am doing writing an entry in the Bamboo blog? We&#8217;ll if you haven&#8217;t guessed it already both New Bamboo and Agile Evolved decided late last year to form a new company. OK, that&#8217;s a lie, to tell the truth, I lost the company to Damien and Max over a World of Warcraft match.</p>

<p>But seriously, the reason that Agile Evolved joined forces with New Bamboo was to combine all of our talents to form the UK&#8217;s premier Ruby on Rails consultancy. With us all coming together you&#8217;ll have:</p>

<ul>
<li>Max and Damien&#8217;s impeccable experience with working with cutting edge start ups</li>

<li>My experience of applying web 2.0 technologies/principles in the enterprise</li>

<li>Dr Fishers knowledge of AI, search algorithms et al</li>

<li>And finally Marek&#8217;s deep understanding of all things Ruby</li>
</ul>

<p>The first thing we did when the merger occurred was to make a cup of Early Grey. The second thing we did was to finally kit the office out with some furniture. Previously I&#8217;d gone for the minimalist approach of a few desks and some Aerons, yep I never said I was the artistic one in the company. Pictures of our new refurbished office can be found in our &#8220;Flickr account&#8221;:http://www.flickr.com/photos/new<em>bamboo</em>london.</p>

<p>If you&#8217;re ever around the King&#8217;s Cross area feel more than free to pop in for a chat. However don&#8217;t expect to escape without a game of Wii tennis!</p>

<p>Tagged with &#8220;New Bamboo&#8221;:http://technorati.com/tag/new%20bamboo, &#8220;Agile Evolved&#8221;:http://technorati.com/tag/Agile%20Evolved, &#8220;Company Merger&#8221;:http://technorati.com/tag/company%20merger</p>]]></description>
        <pubDate>Tue, 06 Feb 2007 00:00:00 +0000</pubDate>
        <guid>http://blog.new-bamboo.co.uk/2007/2/6/merger</guid>
      </item>
    
  </channel>
</rss>
