<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kerry Buckley</title>
	<atom:link href="http://www.kerrybuckley.org/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kerrybuckley.org</link>
	<description>What's the simplest thing that could possibly go wrong?</description>
	<lastBuildDate>Tue, 26 Mar 2013 20:00:15 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.5.1</generator>
		<item>
		<title>FESuffolk Jasmine lightning talk</title>
		<link>http://www.kerrybuckley.org/2013/03/26/fesuffolk-jasmine-lightning-talk/</link>
		<comments>http://www.kerrybuckley.org/2013/03/26/fesuffolk-jasmine-lightning-talk/#comments</comments>
		<pubDate>Tue, 26 Mar 2013 20:00:15 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[javascript]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[fesuffolk]]></category>
		<category><![CDATA[jasmine]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=501</guid>
		<description><![CDATA[At last night&#8217;s FESuffolk meeting I gave a lightning talk on getting started with unit-testing Javascript/coffeescript using jasmine-headless-webkit. I made a screen recording, so if you missed it you can still experience my frantic babbling (I had Keynote set to auto-advance, using the Ignite 20 × 15 second slide format, and as usual I tried [...]]]></description>
				<content:encoded><![CDATA[<p>At last night&#8217;s FESuffolk meeting I gave a lightning talk on getting started with unit-testing Javascript/coffeescript using jasmine-headless-webkit. I made a screen recording, so if you missed it you can still experience my frantic babbling (I had Keynote set to auto-advance, using the Ignite 20 × 15 second slide format, and as usual I tried to cram too much into each slide).</p>
<p>If you don&#8217;t want to <a href="http://www.youtube.com/watch?v=ao86wy14u1Q">watch the recording</a>, you can <a href="http://www.slideshare.net/kjbuckley/jasmine-17722492">look at the slides</a> and guess what I might have been saying, read the <a href="https://github.com/kerryb/jasmine-demo">example code</a> (the <a href="https://github.com/kerryb/jasmine-demo/commits/master">commits</a> try to build up in small steps), or just play with the <a href="http://jasmine-demo.herokuapp.com/">utterly pointless app</a> that the example implements.</p>
<p><iframe width="420" height="315" src="http://www.youtube.com/embed/ao86wy14u1Q" frameborder="0" allowfullscreen></iframe></p>
<p><iframe src="http://www.slideshare.net/slideshow/embed_code/17722492" width="427" height="356" frameborder="0" marginwidth="0" marginheight="0" scrolling="no" style="border:1px solid #CCC;border-width:1px 1px 0;margin-bottom:5px" allowfullscreen webkitallowfullscreen mozallowfullscreen> </iframe></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2013/03/26/fesuffolk-jasmine-lightning-talk/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Interesting little rSpec gotcha</title>
		<link>http://www.kerrybuckley.org/2013/03/20/interesting-little-rspec-gotcha/</link>
		<comments>http://www.kerrybuckley.org/2013/03/20/interesting-little-rspec-gotcha/#comments</comments>
		<pubDate>Wed, 20 Mar 2013 15:04:01 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[rspec]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=496</guid>
		<description><![CDATA[This one had Adam and me stumped for a while. Trying to check that a method is only called on objects that respond to it: describe "Foo#call_all_the_things" do let(:foo_1) { stub :foo_1, bar: "hello" } let(:foo_2) { stub :foo_2 } subject { Foo.new foo_1, foo_2 } it "only calls bar on objects that respond to [...]]]></description>
				<content:encoded><![CDATA[<p>This one had Adam and me stumped for a while. Trying to check that a method is only called on objects that respond to it:</p>
<blockquote>
<pre>
describe "Foo#call_all_the_things" do
  let(:foo_1) { stub :foo_1, bar: "hello" }
  let(:foo_2) { stub :foo_2 }
  subject { Foo.new foo_1, foo_2 }

  it "only calls bar on objects that respond to it" do
    foo_1.should_receive :bar
    foo_2.should_not_receive :bar
    subject.call_all_the_things(:bar)
  end
end

class Foo
  def initialize *things
    @things = things
  end

  def call_all_the_things method
    @things.each do |thing|
      thing.send method if thing.respond_to? method
    end
  end
end


  1) Foo#call_all_the_things only calls bar on objects that respond to it
     Failure/Error: thing.send method if thing.respond_to? method
       (Stub :foo_2).bar(no args)
           expected: 0 times
           received: 1 time
</pre>
</blockquote>
<p>Hmm. Why is it calling <code>bar</code> on the thing that doesn&#8217;t respond to it? Perhaps rSpec doubles don&#8217;t handle <code>respond_to?</code> properly?</p>
<blockquote>
<pre>
[1] pry(main)> require "rspec/mocks/standalone"
=> true
[2] pry(main)> foo = stub foo: 123
=> #<RSpec::Mocks::Mock:0x3fd07d246f34 @name=nil>
[3] pry(main)> foo.respond_to? :foo
=> true
[4] pry(main)> foo.respond_to? :bar
=> false
</pre>
</blockquote>
<p>Nope.</p>
<p>FX: lightbulb above head</p>
<p>Of course! To do the <code>should_not_receive</code> check, it needs to stub the method, which means it responds to it!</p>
<p>Two possible solutions: either let the fact that the missing method isn&#8217;t called be tested implicitly, or specify that when objects that don&#8217;t respond to the method exist, no <code>NoMethodError</code> is raised.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2013/03/20/interesting-little-rspec-gotcha/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Adastral Park code retreat</title>
		<link>http://www.kerrybuckley.org/2011/11/01/adastral-park-code-retreat/</link>
		<comments>http://www.kerrybuckley.org/2011/11/01/adastral-park-code-retreat/#comments</comments>
		<pubDate>Tue, 01 Nov 2011 11:42:27 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[BT]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[adastral park]]></category>
		<category><![CDATA[bt]]></category>
		<category><![CDATA[code retreat]]></category>
		<category><![CDATA[conway's game of life]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=426</guid>
		<description><![CDATA[Ever since attending code retreat at Bletchley Park (the UK leg of Corey Haines&#8217;s code retreat tour) last year, I&#8217;d been vaguely intending to try and run an internal one at work. I finally got round to it this month, when a few people expressed an interest in a code retreat as a one-off alternative [...]]]></description>
				<content:encoded><![CDATA[<p><a href="http://www.flickr.com/photos/kerrybuckley/6258543252/" title="Code retreat poster by Kerry Buckley, on Flickr"><img src="http://farm7.static.flickr.com/6240/6258543252_6219ecbb29.jpg" width="198" height="530" alt="Code retreat poster" class="alignleft"></a></p>
<p>Ever since attending <a href="http://coderetreat.ning.com/events/code-retreat-bletchley-park">code retreat at Bletchley Park</a> (the UK leg of <a href="http://twitter.com/#!/coreyhaines">Corey Haines</a>&rsquo;s code retreat tour) last year, I&#8217;d been vaguely intending to try and run an internal one at work. I finally got round to it this month, when a few people expressed an interest in a code retreat as a one-off alternative to the usual developer conferences we run a couple of times a year. I thought I&#8217;d share my experiences facilitating a code retreat for the first time, in case it&#8217;s helpful to anyone thinking of doing the same.</p>
<h3>Beforehand</h3>
<p>The &#8220;official&#8221; <a href="http://coderetreat.com/">code retreat site</a> has some good information on things to consider when <a href="http://coderetreat.com/hosting.html">hosting</a> and <a href="http://coderetreat.com/facilitation.html">facilitating</a>. I suspect organising an internal company event is easier than a public one, because hopefully you&#8217;ll have a venue available and you won&#8217;t need to worry about sponsorship. The only one of Corey&#8217;s rules I ignored was the one about giving people decent food at lunchtime, but we did at least manage to beg enough budget (thanks Mel!) to be able to provide cheap Tesco sandwiches at lunchtime and a few random snacks and coffee during the day. I don&#8217;t feel too bad because rather than asking people to &#8220;come out at eight in the morning to spend the day coding&#8221;, we were giving them an excuse to spend a work day away from their projects, getting paid to practice.</p>
<p>We also planned to start at nine o&#8217;clock rather than the traditional eight. I think for public events the early start, usually on a Saturday, is a good way of selecting only people who really want to take part, but running it at work I wanted to swing the balance a bit towards encouraging people to come (I work on a large site with probably a couple of hundred developers scattered amongst the 3000-odd BT people, and part of the motivation for running this kind of event is to try and put developers from different teams in touch with each other).</p>
<p>With a bit of publicity via mailing lists and posters on site, 27 people had signed up. That seemed like about the right number, although I had no idea how many of those would turn up, or whether anyone would turn up without registering. I turned up at the office in plenty of time, and was lucky enough to get some last-minute tips from the master:</p>
<p><img src="http://www.kerrybuckley.org/wp-content/uploads/2011/10/corey_tips.png" alt="tips" title="Tips" width="549" height="263" class="aligncenter size-full wp-image-435" /></p>
<h3>Getting started</h3>
<p>As it turned out we had a few glitches to deal with before we could get going &ndash; firstly the venue was locked and we only managed to get in because <a href="http://twitter.com/#!/roovo">Rupert</a> knew how to find the secret back entrance, then <a href="http://twitter.com/#!/adamwhittingham">Adam</a> and <a href="http://twitter.com/#!/yedennek">Anna</a> got kicked out of Tesco for a fire alarm while buying the food &ndash; so we ended up starting at closer to 9.30.</p>
<p><a href="http://www.flickr.com/photos/kerrybuckley/6290950476/" title="Coffee machine hack by Kerry Buckley, on Flickr"><img src="http://farm7.static.flickr.com/6116/6290950476_22a171bc20_t.jpg" width="91" height="100" alt="Coffee machine hack" class="alignright"></a></p>
<p>Potentially the biggest disaster though was that the hopper was missing from the coffee machine (most likely locked away to stop people like us using it with our own beans instead of paying for catering). Fortunately it takes more than a little detail like that to defeat a room full of engineers, and we soon had a workaround in place using a plastic cup with the bottom cut out.</p>
<p>Once the caffeine supply was assured and we were finally ready to start, I gave a quick presentation covering the purpose of the day, the four rules of simple design and Conway&#8217;s game of life:</p>
<div style="width:425px; margin: 0 auto" id="__ss_9945517"> <strong style="display:block;margin:12px 0 4px"><a href="http://www.slideshare.net/kjbuckley/adastral-park-code-retreat-introduction" title="Adastral Park code retreat introduction" target="_blank">Adastral Park code retreat introduction</a></strong> <iframe src="http://www.slideshare.net/slideshow/embed_code/9945517" width="425" height="355" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
<div style="padding:5px 0 12px"> View more <a href="http://www.slideshare.net/" target="_blank">presentations</a> from <a href="http://www.slideshare.net/kjbuckley" target="_blank">Kerry Buckley</a> </div>
</p></div>
<h3>First session</h3>
<p>With everyone hopefully having a reasonable idea of what was going on, people paired up and started the first session. There were about 25&ndash;30 people (I forgot to count), split something like 70:20:10 between Java, Ruby and Groovy. In the first session, as expected, everyone was mostly just getting a feel for the problem, and realising that it&#8217;s not quite as simple as it appears from the list of rules.</p>
<p><a href="http://www.flickr.com/photos/kerrybuckley/6290429483/" title="Getting started by Kerry Buckley, on Flickr"><img src="http://farm7.static.flickr.com/6036/6290429483_fb2f82c192_z.jpg" width="640" height="316" alt="Getting started" class="aligncenter"></a></p>
<p><a href="http://www.flickr.com/photos/kerrybuckley/6290429891/" title="First session by Kerry Buckley, on Flickr"><img src="http://farm7.static.flickr.com/6218/6290429891_9912a2e934_z.jpg" width="640" height="317" alt="First session" class="aligncenter"></a></p>
<p>After 45 minutes, I put on my best exam invigilator voice and got everyone to stop typing, delete their code and gather round. Pairs were split roughly evenly between those that started with a grid and those that started with a cell, and we had a short discussion about how a grid approach using a two-dimensional array fitted in with the &#8220;infinite grid&#8221; constraint. Some of those who had started from a cell were thinking about approaches for allowing cells to know about their neighbours, but no-one had got that far yet.</p>
<h3>Mixing it up</h3>
<p>For the second session, I suggested that those that had started with a grid tried starting from a cell instead, and vice versa. There were also quite a few people who wanted a chance to try Ruby, so they tried to grab the rubyists before anyone else did. Little did they know that they would find themselves exposed to Vim (and in some cases Mac OS X) at the same time, but that&#8217;s just a bonus.</p>
<p>In general people who tried both seemed to prefer the cell approach, partly because it made it easier to test-drive the rules. With a grid, people found they were having to implement some kind of <code>to_string</code> or <code>render</code> method to allow the transformed grid to be compared with the expected result. The biggest puzzle with the cell approach was how to handle the relationship between multiple cells.</p>
<p>I introduced a list of possible constraints people might want to consider to make things more interesting:</p>
<ul>
<li>No if statements (or other conditionals).</li>
<li>No loops.</li>
<li>No methods longer than five lines (or four, or three&hellip;)</li>
<li>No more than three methods per class.</li>
<li>No &#8216;naked&#8217; language primitives.</li>
<li>No built-in data structures.</li>
</ul>
<p>I also attempted to explain <a href="http://gojko.net/2009/02/27/thought-provoking-tdd-exercise-at-the-software-craftsmanship-conference/">TDD as if you meant it</a>, in case anyone fancied giving it a go. Unfortunately my description was rather garbled, not helped by the fact that I&#8217;m not sure I fully understand it myself (I&#8217;m still annoyed that I picked the wrong track at <a href="/2009/03/02/software-craftsmanship-2009/">SC2009</a> and missed Keith&#8217;s original presentation of the exercise, and it turns out Adam made the same mistake at SPA2010).</p>
<p><a href="http://www.flickr.com/photos/kerrybuckley/6290432001/" title="Pairing by Kerry Buckley, on Flickr"><img src="http://farm7.static.flickr.com/6106/6290432001_aef3d32e26_z.jpg" width="320" height="240" alt="Pairing" class="alignleft"></a></p>
<p><a href="http://www.flickr.com/photos/kerrybuckley/6290952678/" title="Flying fingers by Kerry Buckley, on Flickr"><img src="http://farm7.static.flickr.com/6036/6290952678_48402a5675_z.jpg" width="320" height="240" alt="Flying fingers" class="alignright"></a></p>
<h3 class="clear" style="padding-top: 1em;">Session three</h3>
<p>By the third session several pairs were starting to experiment with storing cell objects either in a set (with each cell knowing its position) or a hash, keyed by position. Some people used a comma-separated &#8220;x,y&#8221; string as the key, but with a bit of prodding most ended up replacing it with a point class of some kind.</p>
<p>One pair decided to start with a single cell, then initialise other cells with their neighbours. This initially seemed promising, but soon led to complications with circular references, as well as the need to keep dead cells around to maintain a connection between cells with space between them.</p>
<p>Another pair started developing the rules with a cell class, and initially had an <code>alive?</code> method which performed the life calculation. After some discussion they split the class into two &ndash; one for a living cell and another for a dead one. This removed a level of nesting from the calculations (which ended up in a different method), but interestingly left <code>alive?</code> in both classes, hardcoded to true and false respectively. We decided that this would have looked less odd in Java, where the method would have been declared abstractly in a superclass or interface.</p>
<p>One pair had decided to try implementing a solution with no if statements, but resorted to cheating by using a case instead!</p>
<p>In general, a lot of people &ndash; particularly those new to TDD &ndash; were starting to get frustrated, feeling like they were just getting to the point where they were &#8220;about to start the interesting bit&#8221; as the 45 minutes were up. I told them that we&#8217;d just have two sessions after lunch rather than three, with the second session lengthened to give people a chance of implementing a working game.</p>
<p><a href="http://www.flickr.com/photos/kerrybuckley/6290951344/" title="The countdown begins by Kerry Buckley, on Flickr"><img src="http://farm7.static.flickr.com/6117/6290951344_7bd41d6f6c_z.jpg" width="640" height="378" alt="The countdown begins" class="aligncenter"></a></p>
<h3>Fourth &ndash; and as it turned out final &ndash; session</h3>
<p>After lunch we started the fourth session, but it soon became clear that several pairs had already decided to abandon TDD and clean code in favour of trying to solve the problem. I completely understand how they felt &ndash; I wasted most of the sessions at the code retreat I attended simply pushing for the finish line instead of letting it go and concentrating on perfecting the internal quality of the code &ndash; but it seemed like we&#8217;d lost sight of the key goal of the day a little. I suspect I&#8217;m mostly to blame for that, and I&#8217;m sure a more experienced facilitator could have done a better job of gently guiding people in the right direction. Anyway, it seemed sensible to bow to the inevitable, let everyone carry on for another 45 minutes and wrap up a bit earlier than planned.</p>
<p><a href="http://www.flickr.com/photos/kerrybuckley/6290953086/" title="Basking in the glow by Kerry Buckley, on Flickr"><img src="http://farm7.static.flickr.com/6058/6290953086_66f867669c_z.jpg" width="640" height="306" alt="Basking in the glow" class="aligncenter"></a></p>
<p>With the extra time, a few pairs did manage to get a working implementation, but the prize for the most impressive (cancelled out by a booby prize for abandoning TDD and neglecting the quality of their code!) goes to <a href="http://twitter.com/#!/jon_d_leach">Jon</a> and Jia Yan, who produced an SWT app that allowed graphical editing of the grid, variable speed and saving and loading files. They used the &#8220;set of live cells&#8221; approach, with an effectively infinite grid. Resizing the window on screen allowed you to show or hide cells, but the ones off the edge of the visible area continued to multiply.</p>
<p><object type="application/x-shockwave-flash" width="400" height="225" class="aligncenter" data="http://www.flickr.com/apps/video/stewart.swf?v=109786" classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"><param name="flashvars" value="intl_lang=en-us&#038;photo_secret=72b87bb2a3&#038;photo_id=6294084155"></param><param name="movie" value="http://www.flickr.com/apps/video/stewart.swf?v=109786"></param><param name="bgcolor" value="#000000"></param><param name="allowFullScreen" value="true"></param><embed type="application/x-shockwave-flash" src="http://www.flickr.com/apps/video/stewart.swf?v=109786" bgcolor="#000000" allowfullscreen="true" flashvars="intl_lang=en-us&#038;photo_secret=72b87bb2a3&#038;photo_id=6294084155" height="225" width="400"></embed></object></p>
<p>Matthew and Rupert decided to look at <a href="http://clj-me.cgrand.net/2011/08/19/conways-game-of-life/">someone else&#8217;s solution in Clojure</a>, to understand how a functional approach using list comprehensions can lead to a very concise solution. They also played around with a Ruby version using the same techniques. Looking at the Clojure code led to a slightly rearranged version of the rules for life at a particular location after a clock tick:</p>
<ol>
<li>Cells which had three neighbours are alive</li>
<li>Cells which had two neighbours are unchanged</li>
<li>All other cells are dead</li>
</ol>
<p>Paul, stuck on his own after his pair got dragged off onto a conference call (the perils of being a senior manager!), had a play with TDD as if you meant it. I think by the end of the session he was starting to get somewhere. Further than me, at any rate.</p>
<h3>Wrapping it all up</h3>
<p>After letting Jon show off a glider gun running in their SWT universe, we gathered round for the closing circle, asking everyone what they&#8217;d learned, what had surprised them, and what they&#8217;d do differently back at work on Monday as a result. Probably the most common comments revolved around the fact that neither of the two most obvious solutions turned out to be the best, suggesting that it&#8217;s worth spiking a few alternative approaches before heading blindly off down what may be a blind alley. Quite a few people were new to pairing, TDD or both, and generally seemed to agree that both provided benefits, although a couple of people felt that TDD was making things take much longer. Reassuringly, of the pairs that had abandoned TDD to concentrate on trying to get a working solution, most had found themselves descending into confusion without the tests to guide them.</p>
<h3>Final thoughts</h3>
<p>Overall, the event seemed to be a success. For quite a few people this was their first experience of TDD, and the opportunity to get first-hand experience by pairing with someone more experienced seemed to be useful. I was particularly pleased to see someone who&#8217;s recently come back into software development after a long time away doing a great job of holding his pair back from rushing into implementation without a failing test.</p>
<p>On a less positive note, I didn&#8217;t feel we really concentrated enough on &#8220;perfect&#8221; code and the four rules of simple design. I know from experience that it&#8217;s hard to let go of the natural desire to try and finish solving the problem, and I felt that a lot of people were getting frustrated enough without me peering over their shoulders picking holes in their code (however nicely I tried to do it). Perhaps this is a case of horses for courses, with the concentration on design perfection more suitable for a different mix of participants, or perhaps it just reflects my lack of experience facilitating this kind of event.</p>
<p>Also, I don&#8217;t think we really had enough sessions for the code retreat magic to start kicking in properly. We&#8217;d planned to have the recommended five or six, but a combination of the late start and a general loss of direction after lunch reduced that to four (with the fourth being double-length).</p>
<p>The important thing, of course, is that people learned something by participating, and I hope that was the case. Maybe we&#8217;ll do another one one day, and use the experience of this one to make it even better.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2011/11/01/adastral-park-code-retreat/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Build and push to upstream git repository in the background</title>
		<link>http://www.kerrybuckley.org/2010/12/20/build-and-push-to-upstream-git-repository-in-the-background/</link>
		<comments>http://www.kerrybuckley.org/2010/12/20/build-and-push-to-upstream-git-repository-in-the-background/#comments</comments>
		<pubDate>Mon, 20 Dec 2010 17:14:00 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[git]]></category>
		<category><![CDATA[Software]]></category>
		<category><![CDATA[ci]]></category>
		<category><![CDATA[development]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=408</guid>
		<description><![CDATA[[Updated 11 Feb 2011 &#8211; I'm not sure the original version actually worked properly.] Quite often I&#8217;ll make small changes to a project&#8217;s code which I&#8217;m 99% sure won&#8217;t break the build. Of course thanks to Murphy&#8217;s Law that probability falls to about 10% if I decide to risk it push the changes upstream without [...]]]></description>
				<content:encoded><![CDATA[<p>[Updated 11 Feb 2011 &ndash; I'm not sure the original version actually worked properly.]</p>
<p>Quite often I&#8217;ll make small changes to a project&#8217;s code which I&#8217;m 99% sure won&#8217;t break the build. Of course thanks to Murphy&#8217;s Law that probability falls to about 10% if I decide to risk it push the changes upstream without running the full build locally.</p>
<p>Now even if the build only takes a few minutes to run, that&#8217;s a few minutes which I can&#8217;t spend doing anything else with the code, because if I save a file I can&#8217;t be sure whether the original or changed version will be used in the tests. What I&#8217;d like to be able to do is to run the build in the background, with changes automatically pushed upstream if everything&#8217;s green, or an alert shown if either the build fails or the push is rejected (because someone else has pushed a change while the build was running).</p>
<p>Fortunately with a distributed version control system like git, this is fairly easy.</p>
<p>Firstly, clone the local working repository and set up a remote pointing to  the clone:</p>
<p>[code]<br />
git clone . ../myproject-staging<br />
git remote add staging ../myproject-staging<br />
[/code]</p>
<p>Now set up the cloned copy:</p>
<p>[code]<br />
cd ../myproject-staging</p>
<p># Allow pushing into the checked-out branch<br />
git config receive.denyCurrentBranch ignore</p>
<p># Create a remote for the upstream server you want to push to (change the URI!)<br />
git remote add upstream git@your.upstream.server/your/repository/path.git<br />
[/code]</p>
<p>Do anything that needs doing to allow the cloned project to build (if you have a test database, create a separate instance to avoid tests interacting with your main workspace), and verify that the build runs.</p>
<p>Now add the following to <code>.git/hooks/post-receive</code>:</p>
<p>[code]<br />
#!/bin/bash</p>
<p>cd ..<br />
export GIT_DIR=.git<br />
git reset --hard<br />
nohup .git/hooks/post-reset 1> build_output 2>&#038;1 &#038;<br />
[/code]</p>
<p>This will reset your checked-out working copy to the pushed master, then run <code>.git/hooks/post-reset</code> in the background. Create that file with the following content:</p>
<p>[code]<br />
#!/bin/bash</p>
<p>function error {<br />
  growlnotify -s -t `basename $PWD` -m "$1"<br />
  exit 1<br />
}</p>
<p># Replace this with your build command<br />
bundle install --local &#038;&#038; rake || error "Rake failed"</p>
<p>git push upstream master || error "Git push failed"<br />
growlnotify -s -t `basename $PWD` -m "Built and pushed successfully"<br />
[/code]</p>
<p>Make sure both these hooks are executable:</p>
<p>[code]<br />
chmod u+x .git/hooks/post*-receive<br />
[/code]</p>
<p>Now, back in your normal working repository you should just be able to type <code>git push staging</code> (add <code>--force</code> if you&#8217;ve rebased your normal master and get an error about the push not being a fast-forward), and the project will build in the staging repo. If everything works, it&#8217;ll push upstream and you&#8217;ll get a Growl notification (assuming you have <a href="http://growl.info/">Growl</a> and growlnotify, or the equivalents for your OS, installed). If it fails, you&#8217;ll also get a Growl notification. I&#8217;ve made the alerts sticky, so I don&#8217;t miss them &ndash; if you don&#8217;t want that, just remove the <code>-s</code> flag. The build errors will be in the <code>build_output</code> file.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2010/12/20/build-and-push-to-upstream-git-repository-in-the-background/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The Wandering Book</title>
		<link>http://www.kerrybuckley.org/2010/07/02/the-wandering-book/</link>
		<comments>http://www.kerrybuckley.org/2010/07/02/the-wandering-book/#comments</comments>
		<pubDate>Fri, 02 Jul 2010 08:56:11 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=394</guid>
		<description><![CDATA[After procrastinating and holding on to the wandering book for much too long, I finally made my entry. You can read it on the site, and here&#8217;s the text if you can&#8217;t decipher my handwriting: I work in a large &#8220;enterprisey&#8221; company where craftsmanship is, unsurprisingly, not the dominant model of software development. However, a [...]]]></description>
				<content:encoded><![CDATA[<p>After procrastinating and holding on to the <a href="http://www.nexwerk.com/the_wandering_book">wandering book</a> for much too long, I finally made my entry. You can read it <a href="http://www.nexwerk.com/the_wandering_book/view?page=11">on the site</a>, and here&#8217;s the text if you can&#8217;t decipher my handwriting:</p>
<blockquote><p>I work in a large &#8220;enterprisey&#8221; company where craftsmanship is, unsurprisingly,<br />
not the dominant model of software development. However, a craftsmanship ethos<br />
still exists in some of the remaining pockets of in-house development, and if<br />
anything appears to be strengthening.</p>
<p>For the first few years of my software career, I treated it very much as a day<br />
job. I turned up; I wrote some code (or, for a horrendous proportion of the<br />
time, some documents); I went home. I learned just enough to enable me to<br />
complete whatever I was working on, but no more. I used the tools I was given,<br />
or those I was familiar with, regardless of their suitability for the task at<br />
hand.</p>
<p>Then I started working with someone who was trying cool new stuff. Dependency<br />
injection. Automated testing. Continuous integration. I realised how<br />
out-of-touch I was in my ideas of how software should be developed, and started<br />
trying to put that right. I read about XP and Agile, and started following<br />
blogs and reading articles. I went to XPDay, and turned up at the odd XTC pub<br />
night when I happened be in London on a Tuesday.</p>
<p>More importantly, I started caring about my code. Instead of thousand-line<br />
procedures, I started separating concerns and following OO principles. I wrote<br />
unit tests, and after a while I got the hang of TDD. Month-long manual testing<br />
and bug fixing phases gave way to an automated nightly build and a quick<br />
confidence check, and eventually to something approaching continuous<br />
deployment.</p>
<p>Of course, the more widely I look at what others are doing, the more aware I am<br />
that even when I think I might be doing something reasonably well, there are<br />
always people out there doing it much, much better. I may never approach their<br />
level, but I can learn from their work and thus improve mine.  In turn I hope I<br />
can pass something on to others.</p>
<p>So, to finally get to the point, I agree with earlier contributors that<br />
craftsmanship is about caring, learning, practicing and sharing. Underpinning<br />
all this though is community. Not &#8216;the&#8217; software craftsmanship community, but<br />
the community of other developers you work with; the communities around the<br />
languages and tools you use; the agile, lean and XP communities; the community<br />
of random people you met once at a conference – or maybe have never met at all<br />
– and started following on Twitter.</p>
<p>Without these communities, we become isolated, and may start believing that the<br />
way we currently work is &#8216;best practice&#8217;. We need our fellow professionals to<br />
compare ourselves against and to learn from, otherwise we can lapse into<br />
complacency and let our skills (and our profession) stagnate.</p></blockquote>
<p>Technorati Tags: <a href="http://technorati.com/tag/software" rel="tag">software</a>, <a href="http://technorati.com/tag/craftsmanship" rel="tag">craftsmanship</a>, <a href="http://technorati.com/tag/wanderingbook" rel="tag">wanderingbook</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2010/07/02/the-wandering-book/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Memoised remote attribute readers for ActiveRecord</title>
		<link>http://www.kerrybuckley.org/2010/04/27/memoised-remote-attribute-readers-for-activerecord/</link>
		<comments>http://www.kerrybuckley.org/2010/04/27/memoised-remote-attribute-readers-for-activerecord/#comments</comments>
		<pubDate>Tue, 27 Apr 2010 11:29:29 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=377</guid>
		<description><![CDATA[I was recently working with an ActiveRecord class that exposed some attributes retrieved from a remote API, rather than from the database. The rules for handling the remote attributes were as follows: If the record is unsaved, return the local value of the attribute, even if it&#8217;s nil. If the record is saved and we [...]]]></description>
				<content:encoded><![CDATA[<p>I was recently working with an ActiveRecord class that exposed some attributes retrieved from a remote API, rather than from the database. The rules for handling the remote attributes were as follows:</p>
<ul>
<li>If the record is unsaved, return the local value of the attribute, even if it&#8217;s nil.</li>
<li>If the record is saved and we don&#8217;t have a local value, call the remote API and remember and return the value.</li>
<li>If the record is saved and we already have a local value, return that.</li>
</ul>
<p>Here&#8217;s the original code (names changed to protect the innocent):</p>
<p>[ruby]<br />
class MyModel < ActiveRecord::Base<br />
  attr_writer :foo, :bar</p>
<p>  def foo<br />
    (new_record? || @foo) ? @foo : remote_object.foo<br />
  end</p>
<p>  def bar<br />
    (new_record? || @bar) ? @bar : remote_object.bar<br />
  end</p>
<p>  def remote_object<br />
    @remote_object ||= RemoteService.remote_object<br />
  end<br />
end<br />
[/ruby]</p>
<p>The <code>remote_object</code> method makes a call to the remote service, and memoises the returned object (which contains all the attributes we are interested in).</p>
<p>I didn't really like the duplication in all these accessor methods &ndash; we had more than the two I've shown here &ndash; so decided to factor it out into a common <code>remote_attr_reader</code> class method. Originally I had the method take a block which returned the remote value, but that made the tests more complicated, so I ended up using convention over configuration and having the accessor for <em>foo</em> call a <em>remote_foo</em> method.</p>
<p>Here's the new code in the model:</p>
<p>[ruby]<br />
class MyModel < ActiveRecord::Base<br />
  remote_attr_reader :foo, :bar</p>
<p>  def remote_foo<br />
    remote_object.foo<br />
  end</p>
<p>  def remote_bar<br />
    remote_object.bar<br />
  end</p>
<p>  def remote_object<br />
    @remote_object ||= RemoteService.remote_object<br />
  end<br />
end<br />
[/ruby]</p>
<p>Here's the <code>RemoteAttrReader</code> module that makes it possible:</p>
<p>[ruby]<br />
module RemoteAttrReader<br />
  def remote_attr_reader *names<br />
    names.each do |name|<br />
      attr_writer name<br />
      define_method name do<br />
        if new_record? || instance_variable_get("@#{name}")<br />
          instance_eval "@#{name}"<br />
        else<br />
          instance_eval "remote_#{name}"<br />
        end<br />
      end<br />
    end<br />
  end<br />
end<br />
[/ruby]</p>
<p>To make the module available to all models, I added an initialiser containing this line:</p>
<p>[ruby]<br />
ActiveRecord::Base.send :extend, RemoteAttrReader<br />
[/ruby]</p>
<p>Here's the spec for the module:</p>
<p>[ruby]<br />
require File.dirname(__FILE__) + '/../spec_helper'</p>
<p>class RemoteAttrReaderTestClass<br />
  extend RemoteAttrReader<br />
  remote_attr_reader :foo</p>
<p>  def remote_foo<br />
    "remote value"<br />
  end<br />
end</p>
<p>describe RemoteAttrReader do<br />
  let(:model) { RemoteAttrReaderTestClass.new }</p>
<p>  describe "for an unsaved object" do<br />
    before do<br />
      model.stub(:new_record?).and_return true<br />
    end</p>
<p>    describe "When the attribute is not set" do<br />
      it "returns nil" do<br />
        model.foo.should be_nil<br />
      end<br />
    end</p>
<p>    describe "When the attribute is set" do<br />
      before do<br />
        model.foo = "foo"<br />
      end</p>
<p>      it "returns the attribute" do<br />
        model.foo.should == "foo"<br />
      end<br />
    end<br />
  end</p>
<p>  describe "for a saved object" do<br />
    before do<br />
      model.stub(:new_record?).and_return false<br />
    end</p>
<p>    describe "When the attribute is set" do<br />
      before do<br />
        model.foo = "foo"<br />
      end</p>
<p>      it "returns the attribute" do<br />
        model.foo.should == "foo"<br />
      end<br />
    end</p>
<p>    describe "When the attribute is not set" do<br />
      it "returns the result of calling remote_<attribute>" do<br />
        model.foo.should == "remote value"<br />
      end<br />
    end<br />
  end<br />
end<br />
[/ruby]</p>
<p>To simplify testing of the model, I created a matcher, which I put into a file in <code>spec/support</code>:</p>
<p>[ruby]<br />
class ExposeRemoteAttribute<br />
  def initialize attribute<br />
    @attribute = attribute<br />
  end</p>
<p>  def matches? model<br />
    @model = model<br />
    return false unless model.send(@attribute).nil?<br />
    model.send "#{@attribute}=", "foo"<br />
    return false unless model.send(@attribute) == "foo"<br />
    model.stub(:new_record?).and_return false<br />
    return false unless model.send(@attribute) == "foo"<br />
    model.send "#{@attribute}=", nil<br />
    model.stub("remote_#{@attribute}").and_return "bar"<br />
    model.send(@attribute) == "bar"<br />
  end</p>
<p>  def failure_message_for_should<br />
    "expected #{@model.class} to expose remote attribute #{@attribute}"<br />
  end</p>
<p>  def failure_message_for_should_not<br />
    "expected #{@model.class} not to expose remote attribute #{@attribute}"<br />
  end</p>
<p>  def description<br />
    "expose remote attribute #{@attribute}"<br />
  end<br />
end</p>
<p>def expose_remote_attribute expected<br />
  ExposeRemoteAttribute.new expected<br />
end<br />
[/ruby]</p>
<p>Testing the model now becomes a simple case of testing the <em>remote_</em> methods in isolation, and using the matcher to test the behaviour of the <em>remote_attr_reader</em> call(s).</p>
<p>[ruby]<br />
require File.dirname(__FILE__) + '/../spec_helper'</p>
<p>describe MyModel do<br />
  it { should expose_remote_attribute(:name) }<br />
  it { should expose_remote_attribute(:origin_server) }<br />
  it { should expose_remote_attribute(:delivery_domain) }</p>
<p>  describe "reading remote foo" do<br />
    # test as a normal method<br />
  end<br />
end<br />
[/ruby]</p>
<p>Technorati Tags: <a href="http://technorati.com/tag/ruby" rel="tag">ruby</a>, <a href="http://technorati.com/tag/rails" rel="tag">rails</a>, <a href="http://technorati.com/tag/activerecord" rel="tag">activerecord</a>, <a href="http://technorati.com/tag/metaprogramming" rel="tag">metaprogramming</a>, <a href="http://technorati.com/tag/rspec" rel="tag">rspec</a>, <a href="http://technorati.com/tag/matcher" rel="tag">matcher</a>, <a href="http://technorati.com/tag/refactoring" rel="tag">refactoring</a>, <a href="http://technorati.com/tag/dry" rel="tag">dry</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2010/04/27/memoised-remote-attribute-readers-for-activerecord/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Managing gems in a Rails project</title>
		<link>http://www.kerrybuckley.org/2009/11/02/managing-gems-in-a-rails-project/</link>
		<comments>http://www.kerrybuckley.org/2009/11/02/managing-gems-in-a-rails-project/#comments</comments>
		<pubDate>Mon, 02 Nov 2009 17:24:10 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=321</guid>
		<description><![CDATA[Over the years I&#8217;ve tried a number of approaches for managing gem dependencies in a Rails project. Here&#8217;s a quick round-up of what I&#8217;ve tried, and the pros and cons of each. Just use what&#8217;s on the system This is probably most people&#8217;s default approach when first starting with Rails. Just sudo gem install whatever [...]]]></description>
				<content:encoded><![CDATA[<p>Over the years I&#8217;ve tried a number of approaches for managing gem dependencies in a Rails project. Here&#8217;s a quick round-up of what I&#8217;ve tried, and the pros and cons of each.</p>
<h4>Just use what&#8217;s on the system</h4>
<p>This is probably most people&#8217;s default approach when first starting with Rails. Just <code>sudo gem install</code> whatever you need, require the appropriate gems (either in <code>environment.rb</code> or in the class that uses them), and you&#8217;re away.</p>
<p>This mostly works OK for small projects where you&#8217;re the only developer, but you still need to make sure the right gems are installed on the machine you&#8217;re deploying the application to.</p>
<p>Worse, though, is what happens when you come back to the project after a while, various gems have been updated, and things mysteriously don&#8217;t work any more. Not only do you have to mess around getting the code to work with the latest gem versions, but you probably don&#8217;t even know exactly which versions it used to work with.</p>
<h4>Freeze (unpack) gems</h4>
<p>I think I first came across this technique in Err the Blog&#8217;s <a href="http://errtheblog.com/posts/50-vendor-everything">Vendor Everything</a> post. The idea is to install copies of all your gems into the project&#8217;s vendor/gems directory, meaning that wherever the code is running, you can guarantee that it has the correct versions of all its dependencies.</p>
<p>This <a href="http://ryandaigle.com/articles/2008/4/1/what-s-new-in-edge-rails-gem-dependencies">got much easier in Rails 2.1</a>, which allowed you to specify all your gems using <code>config.gem</code> lines in <code>environment.rb</code> (you can also put gems only needed in specific environments in the appropriate file, eg you might only want to list things like rspec and cucumber in <code>config/enviroments/test.rb</code>). You can then run <code>sudo rake gems:install</code> to install any gems that aren&#8217;t on your system, and <code>rake gems:unpack</code> to freeze them into <code>vendor/rails</code>, and be sure that wherever you check out or deploy the code, you&#8217;ll be running the same versions of the gems. There&#8217;s even a gems:build task to deal with gems that have native code (but more on that later).</p>
<p>Subsequent versions of Rails have improved on the original rake tasks &ndash; dependencies are now handled much better, for example &ndash; but there are still a few problems. The main one is the handling of gems that are required by rake tasks in your project, rather than just from your application code.</p>
<p>When you call a rake task in your Rails project, this is more-or-less what happens (I may have got some of the details slightly wrong):</p>
<ol>
<li>The top-level <code>Rakefile</code> is loaded.</li>
<li>This in turn requires <code>config/boot.rb</code>, but <em>not</em> <code>config/environment.rb</code>.</li>
<li>It then requires some standard rake stuff, and finally <code>tasks/rails</code> (which is part of Rails &ndash; specifically railties). This finds and requires all the <code>.rake</code> files in your plugins and your project&#8217;s <code>lib/rake</code> directory.</li>
</ol>
<p>The problems start when you have a task depends on the rails <code>environment</code> task, and also requires a gem which is listed in <code>environment.rb</code>. Because the gem-loading magic only happens when the environment is loaded, the rake task will be blissfully unaware of your frozen gems, and will load them from the system instead.</p>
<p>If the system gem is newer than the frozen one, you get errors like this:</p>
<pre>
can't activate foo (= 1.2.3, runtime) for [], already activated foo-1.2.4 for []
</pre>
<p>If you work on two projects that use different versions of a gem like this, you end up having to uninstall and reinstall them as you switch from one to the other, which gets tedious fairly quickly.</p>
<h4>Specify gems, but don&#8217;t freeze</h4>
<p>You can get round the wrong-version problem to some extent by specifying version numbers in <code>environment.yml</code> as &#8216;>=x.z.y&#8217; (or by not specifying them at all). If you&#8217;re doing that, though, there&#8217;s not really much benefit in unpacking the gems, and you may as well just use <code>rake gems:install</code> to make sure they&#8217;re on the system. Of course the downside of this approach is that you can&#8217;t be sure that everyone&#8217;s running the exact same versions of the gems. Worse still, you can&#8217;t be sure that what&#8217;s on your production box matches your development and test environments.</p>
<h4>GemInstaller</h4>
<p><a href="http://geminstaller.rubyforge.org/">GemInstaller</a> solves most of the problems with the built-in Rails gem management by running as a <a href="http://ryandaigle.com/articles/2007/11/18/what-s-new-in-edge-rails-pre-environment-load-hook">preinitializer</a>, meaning it gets loaded before the other <code>boot.rb</code> gubbins.</p>
<p>GemInstaller uses the gems installed on the system rather than freezing them into the project, but because it gets to run first it ensures that the correct versions are used, even if there are newer versions installed. By default it checks your project&#8217;s gem list and installs anything that&#8217;s missing every time it runs (which is whenever you start a server, run the console, execute a rake task etc). You create a YAML file listing the gems you need (dependencies are handled automatically), and other options such as an HTTP proxy if necessary.</p>
<p>Of course on Unix-like systems, which is most of them (although I hear there are still people developing Rails projects on Windows), gems are generally installed as root. GemInstaller can get round this in two ways &ndash; either by setting the <code>--sudo</code> option and setting a rule in <code>/etc/sudoers</code> to allow the appropriate user(s) to run the gem commands as root without having to provide a password, or by using the built-in gem behaviour that falls back to installing in <code>~/.gem</code>.</p>
<p>Personally I like to keep all my gems in one place, accessible to any user, so I went for the sudo approach. The only problem with this is that it uses sudo for all gem commands, rather than just install or update, which means it runs a <code>sudo gem list</code> every time your app starts up. Depending on the way you have Apache and Passenger set up this may mean granting sudo access to what should be a low-privileged user.</p>
<p>I ended up disabling the automatic updating of gems, and just warning when they&#8217;re missing instead. In fact later versions of GemInstaller don&#8217;t try to handle the update automatically anyway.</p>
<p>I created a separate script to do the update, which can be run manually, on a post-merge git hook, or as part of the Capistrano deployment task.</p>
<p>Because GemInstaller needs to go out to the network to fetch any new or updated gems, things get a bit more painful (as always) if you are unfortunate enough to be stuck behind a corporate HTTP proxy. Actually it&#8217;s easy enough to configure if you&#8217;re <em>always</em> behind a proxy, but it gets slightly trickier if your web access is sometimes proxied and sometimes direct. Nothing that can&#8217;t be solved of course.</p>
<p>Unfortunately you can still end up with version conflicts if a gem is required by one you have specified, then you explicitly require an older version, but these can usually be resolved by shuffling the order of the gems in <code>geminstaller.yml</code>.</p>
<h4>Bundler</h4>
<p><a href="http://github.com/wycats/bundler">Bundler</a> is the newest kid on the gem management block, and looks to have solved pretty much all the problems faced by the other approaches. It&#8217;s based on the gem management approach from Merb, and can be used in any Ruby project (not just Rails).</p>
<p>Bundler works by unpacking gems into the project (I recommend using a directory other than the default <code>vendor/gems</code> to avoid confusing Rails &ndash; this can be configured by setting <code>bundle_path</code> and <code>bin_path</code> in the Gemfile), but the intention is that you only commit the <code>.gem</code> files in the cache directory to source control. Gems are then installed locally within the project, including any platform-specific native code as well as the commands in <code>bin</code>.</p>
<p>Because Bundler resolves all dependencies up-front, you only need to specify the gems you&#8217;re using explicitly, and let it handle the rest, which hopefully means an end to version conflicts at last.</p>
<p>Here&#8217;s an example Gemfile:</p>
<p>[ruby]<br />
source &#8216;http://gemcutter.org&#8217;<br />
source &#8216;http://gems.github.com&#8217;<br />
bundle_path &#8216;vendor/bundled_gems&#8217;<br />
bin_path &#8216;vendor/bundled_gems/bin&#8217;</p>
<p>gem &#8216;rails&#8217;, &#8217;2.3.4&#8242;<br />
gem &#8216;bundler&#8217;, &#8217;0.6.0&#8242;</p>
<p>gem &#8216;capistrano&#8217;, &#8217;2.5.8&#8242;<br />
gem &#8216;capistrano-ext&#8217;, &#8217;1.2.1&#8242;<br />
gem &#8216;cucumber&#8217;, &#8217;0.4.3&#8242;, :except => :production<br />
# [more gems here]</p>
<p>disable_system_gems<br />
[/ruby]</p>
<p>Note the two additional sources (rubyforge.org is configured by default), the path overrides, and the last line, which removes the system gems from the paths, avoiding any potential confusion.</p>
<p>I&#8217;ve put this in <code>config/preinitializer.rb</code> to update from the cached gems on startup (this doesn&#8217;t hit the network):</p>
<p>[ruby]<br />
$stderr.puts &#8216;Updating bundled gems&#8230;&#8217;<br />
system &#8216;gem bundle &#8211;cached&#8217;<br />
require &#8220;#{RAILS_ROOT}/vendor/bundled_gems/environment&#8221;<br />
[/ruby]</p>
<p>To avoid any startup delays after an upgrade, I also call <code>system 'gem bundle --cached'</code> from the <code>after_update_code</code> hook in the capfile.</p>
<p>Finally, to make sure only the .gem files are checked in, add these lines to <code>.gitignore</code> (you&#8217;ll still need to explicitly <code>git add</code> the <code>bundled_gems/cache</code> directory):</p>
<pre>
vendor/bundled_gems
!vendor/bundled_gems/cache
</pre>
<p>[Update 3 November] Yehuda Katz just <a href="http://yehudakatz.com/2009/11/03/using-the-new-gem-bundler-today/">posted an article all about Bundler</a>, including features coming in the imminent 0.7 release.</p>
<p>Technorati Tags: <a href="http://technorati.com/tag/ruby" rel="tag">ruby</a>, <a href="http://technorati.com/tag/rails" rel="tag">rails</a>, <a href="http://technorati.com/tag/gems" rel="tag">gems</a>, <a href="http://technorati.com/tag/rubygems" rel="tag">rubygems</a>, <a href="http://technorati.com/tag/geminstaller" rel="tag">geminstaller</a>, <a href="http://technorati.com/tag/bundler" rel="tag">bundler</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2009/11/02/managing-gems-in-a-rails-project/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Maintaining a Read-Only svn Mirror of a git Repository</title>
		<link>http://www.kerrybuckley.org/2009/10/06/maintaining-a-read-only-svn-mirror-of-a-git-repository/</link>
		<comments>http://www.kerrybuckley.org/2009/10/06/maintaining-a-read-only-svn-mirror-of-a-git-repository/#comments</comments>
		<pubDate>Tue, 06 Oct 2009 10:17:39 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[git]]></category>
		<category><![CDATA[Software]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=243</guid>
		<description><![CDATA[Our team been using git at work for the past couple of years, but there&#8217;s now a corporate push to keep everything in a centrally-managed subversion repository. We lost the battle to get corporate approval for git (apparently we&#8217;re happy to employ people to write our code that we wouldn&#8217;t trust to be able to [...]]]></description>
				<content:encoded><![CDATA[<p>Our team been using <a href="http://git.or.cz/">git</a> at work for the past couple of years, but there&#8217;s now a corporate push to keep everything in a centrally-managed subversion repository. We lost the battle to get corporate approval for git (apparently we&#8217;re happy to employ people to write our code that we wouldn&#8217;t trust to be able to use a DVCS), but at least we have agreement that we can continue using git as long as we mirror the code into subversion.</p>
<p>This isn&#8217;t entirely trivial to do using git-svn (which is really intended for use with subversion acting as the master), but I found a few sets of instructions on the web. The simplest one was this <a href="http://blog.nanorails.com/articles/2008/1/31/git-to-svn-read-only">Nano Rails blog post</a>, which is what the steps below are based on.</p>
<p>The end result should be a subversion <em>trunk</em> which is a mirror of the git repository&#8217;s <em>master</em> branch. This is only intended to be a one-way mirror &ndash; I wouldn&#8217;t recommend also trying to commit into subversion and merge those commits back upstream into git.</p>
<p>You need to have git-svn installed (this comes with the default installation, or you can use the <code>+svn</code> option when installing via MacPorts).</p>
<h4>Create subversion repository</h4>
<p>Create the subversion repository in the usual way, using <code>svnadmin</code>.</p>
<p>Once you&#8217;ve got an empty repository to point to (we&#8217;ll imagine it&#8217;s at <code>http://svn.example.com/foo</code>), you also need to commit an initial version (I also created a <code>trunk</code> directory in this step, in case we later decide to mirror branches too):</p>
<pre>
svn co http://svn.example.com/foo
cd myproj
svn mkdir trunk
svn commit -m'Created trunk directory'
</pre>
<p>Once this is done, you can throw away the directory you checked out of subversion.</p>
<h4>Set up the subversion remote</h4>
<p>This step, and subsequent ones, need to be performed on whichever git repository you want to mirror from.</p>
<p>In our case, we have a central repository running on a local installation of <a href="http://gitorious.org/">Gitorious</a>. This is a bare repository, which makes things a little tricker, as git-svn requires a working copy. To get round this, we create a clone, which we&#8217;ll use as an intermediate step in the mirroring process. If you&#8217;re not mirroring a bare repository, you can omit this step.</p>
<p>The repositories we want to mirror are in <code>~git/repositories</code>, and we&#8217;ve created a directory <code>~git/repositories/svn-mirror </code>where we&#8217;ll put the clones. For this example, we&#8217;ll use a repository called <code>foo/mainline.git</code>.</p>
<p>Create the clone:</p>
<pre>
git clone ~git/repositories/foo/mainline.git ~git/repositories/svn-mirror/foo
cd ~git/repositories/svn-mirror/foo
</pre>
<p>Now add the following to <code>.git/config</code> (with the correct svn URI, of course):</p>
<pre>
[svn-remote "svn"]
	url = http://svn.example.com/foo/trunk
	fetch = :refs/remotes/git-svn
</pre>
<p>Now do an initial fetch of the empty subversion remote, and check it out as a new git branch (called <em>svn</em>):</p>
<pre>
git svn fetch svn
git checkout -b svn git-svn
</pre>
<p>You can now merge in all your commits from master, and push them to subversion. You&#8217;ll probably want to go and make a coffee or something while the <em>dcommit</em> runs &ndash; if you haven&#8217;t used subversion for a while you&#8217;ve probably forgotten just how much slower it is than git.</p>
<pre>
git merge master
git svn dcommit
</pre>
<p>To allow pushing to svn from master, rebase master to the svn branch (which can then be deleted):</p>
<pre>
git checkout master
git rebase svn
git branch -d svn
</pre>
<p>At this point you should be able to manually update subversion at any time by running <code>git svn dcommit</code> from the master branch.</p>
<h4>Automating subversion updates</h4>
<p>In theory it should be possible to set up post-receive hooks to push from the gitorious repository to the clone and from there to subversion, but I decided to separate the two by just periodically polling for changes, as we don&#8217;t really care about subversion being right up-to-the-minute. To poll hourly, add something like this to the git user&#8217;s crontab:</p>
<pre>
0 * * * * (cd /usr/local/git/repositories/svn-mirror/foo;/usr/local/bin/git pull origin master;/usr/local/bin/git svn dcommit) >>/usr/local/git/gitorious/log/svn-mirror.log 2>&#038;1
</pre>
<p>Technorati Tags: <a href="http://technorati.com/tag/svn" rel="tag">svn</a>, <a href="http://technorati.com/tag/subversion" rel="tag"> subversion</a>, <a href="http://technorati.com/tag/git" rel="tag"> git</a>, <a href="http://technorati.com/tag/mirror" rel="tag"> mirror</a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2009/10/06/maintaining-a-read-only-svn-mirror-of-a-git-repository/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Upgrading to Snow Leopard</title>
		<link>http://www.kerrybuckley.org/2009/09/09/upgrading-to-snow-leopard/</link>
		<comments>http://www.kerrybuckley.org/2009/09/09/upgrading-to-snow-leopard/#comments</comments>
		<pubDate>Wed, 09 Sep 2009 14:33:36 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[Apple]]></category>
		<category><![CDATA[10.6]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[mac]]></category>
		<category><![CDATA[macports]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[passenger]]></category>
		<category><![CDATA[Rails]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[snowleopard]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/?p=341</guid>
		<description><![CDATA[A quick list of things I had to sort out after upgrading to Mac OS X 10.6 Snow Leopard: Developer Tools Dont&#8217; forget to run the XCode installer on the Snow Leopard DVD, otherwise you&#8217;ll have trouble getting stuff to compile, even if you don&#8217;t use XCode. You&#8217;ll also have to download and install the [...]]]></description>
				<content:encoded><![CDATA[<p>A quick list of things I had to sort out after upgrading to Mac OS X 10.6 Snow Leopard:</p>
<h4>Developer Tools</h4>
<p>Dont&#8217; forget to run the XCode installer on the Snow Leopard DVD, otherwise you&#8217;ll have trouble getting stuff to compile, even if you don&#8217;t use XCode. You&#8217;ll also have to <a href="http://developer.apple.com/iphone/index.action#downloads">download</a> and install the iPhone SDK separately if you need it (and possibly even if you don&#8217;t &ndash; I installed it anyway, just in case).</p>
<h4>Ruby and RubyGems</h4>
<p>I had both of these installed from source, and although most things seemed to work OK, I couldn&#8217;t get Passenger to work at all until I reinstalled them. Instructions for installing are <a href="http://hivelogic.com/articles/compiling-ruby-rubygems-and-rails-on-snow-leopard/">available on HiveLogic</a> &ndash; this will overwrite any existing versions, assuming they&#8217;re in <code>/usr/local</code> (the system version of Ruby isn&#8217;t touched).</p>
<p>Before installing rubygems I removed all my installed gems (<code>gem list|awk '{print $1}'|xargs sudo gem unin -a</code> &ndash; there&#8217;s probably an easier way), then I reinstalled the ones I needed afterwards.</p>
<h4>MySQL</h4>
<p>Although I mostly use Postgres, I reinstalled MySQL following the<a href="http://norbauer.com/notebooks/code/notes/snow-leopard-upgrading-for-rails-developers"> instructions on the Norbauer blog</a>.</p>
<h4>MacPorts</h4>
<p>Apparently you can rebuild your ports by just running <code>sudo port upgrade --force installed</code>, but by the time I came across that I&#8217;d already trashed and reinstalled as recommended  on the link above.</p>
<p>For some reason the MacPorts installer hung while running the postinstall scripts, but after force-quitting the installer then running <code>sudo port sync</code> everything seemed fine.</p>
<p>I added <code>+svn</code> to the arguments for installing git-core (as if it didn&#8217;t have enough dependencies to build already!), and also installed <code>postgresql84-server</code> and <code>imagemagick</code>.</p>
<h4> Apache and Passenger</h4>
<p>I tried a whole bunch of stuff to get Passenger running, but it turned out in the end that rebuilding Ruby was the answer (see above). Once I&#8217;d done that, it was a simple case of installing the passenger gem and running <code>sudo passenger-install-apache2-module</code> to install the module.</p>
<h4>Vim</h4>
<p>The standard version of <a href="http://code.google.com/p/macvim/">MacVim</a> mostly works under 10.6, but there&#8217;s a <a href="http://b4winckler.wordpress.com/2009/09/02/macvim-on-snow-leopard/">custom-built binary</a> that seems much more stable and a bit snappier.</p>
<h4>Reader Notifier</h4>
<p>The release version of <a href="http://troelsbay.eu/software/reader">Reader Notifier</a> doesn&#8217;t work on 10.6, but for now there&#8217;s <a href="http://bubba.org/tmp/Reader%20Notifier.zip">a patched version</a>.</p>
<h4>Safari Plugins</h4>
<p><a href="http://rentzsch.github.com/clicktoflash">ClickToFlash</a> needs to be upgraded to 1.5fc2.</p>
<p><a href="http://delicioussafari.com/">DeliciousSafari</a> hasn&#8217;t been updated for 64-bit Safari yet, but as a workaround you can force Safari to run in 32-bit mode. Do a &#8216;get info&#8217; on the Safari app (in the Applications folder), and tick &#8216;Open in 32-bit mode&#8217;.</p>
<h4>iStat Menus</h4>
<p>Turns out I was using an old version (1.3) of <a href="http://www.islayer.com/apps/istatmenus/">iStat Menus</a>, which doesn&#8217;t work in 10.6 (I noticed the missing menu when I went to check how high all the <code>port install</code> shenanigans were pushing the CPU temperatures). Upgrading to 2.0 sorted it out.</p>
<h4>FlickrExport</h4>
<p>Again, I was using an old version of this <a href="http://connectedflow.com/flickrexport/iphoto/index.php">iPhoto exporter</a>, but £6.90 and an upgrade to 3.0.2 later and everything was working again.</p>
<h4>Remapping caps lock to escape</h4>
<p>Not strictly 10.6-specific, but this was something I&#8217;d been meaning to get round to since switching back to vim. I was already to start installing input manager hacks until I stumbled across a blog post somewhere mentioning that it is already configurable (and has been for a while). Just open the keyboard preferences, hit &#8216;Modifier Keys&hellip;&#8217; and change the action.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2009/09/09/upgrading-to-snow-leopard/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Commenting broken</title>
		<link>http://www.kerrybuckley.org/2009/08/15/commenting-broken/</link>
		<comments>http://www.kerrybuckley.org/2009/08/15/commenting-broken/#comments</comments>
		<pubDate>Sat, 15 Aug 2009 07:48:51 +0000</pubDate>
		<dc:creator>Kerry</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[comments blog fail]]></category>

		<guid isPermaLink="false">http://www.kerrybuckley.org/2009/08/15/commenting-broken/</guid>
		<description><![CDATA[Commenting seems to be broken on this blog since I upgraded WordPress. Not sure why yet, but I&#8217;m working on it. [Update] It&#8217;s working again now.]]></description>
				<content:encoded><![CDATA[<p>Commenting seems to be broken on this blog since I upgraded WordPress. Not sure why yet, but I&#8217;m working on it.</p>
<p>[Update] It&#8217;s working again now.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kerrybuckley.org/2009/08/15/commenting-broken/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

<!-- Dynamic Page Served (once) in 0.441 seconds -->
<!-- Cached page served by WP-Cache -->
