<?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>The next 10,000 hours &#187; Uncategorized</title>
	<atom:link href="http://www.trouble.net.au/blog/korny/category/uncategorized/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.trouble.net.au/blog/korny</link>
	<description>Korny&#039;s tech blog</description>
	<lastBuildDate>Fri, 09 Apr 2010 23:02:17 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Face the Face</title>
		<link>http://www.trouble.net.au/blog/korny/2010/04/10/face-the-face/</link>
		<comments>http://www.trouble.net.au/blog/korny/2010/04/10/face-the-face/#comments</comments>
		<pubDate>Fri, 09 Apr 2010 23:02:17 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.trouble.net.au/blog/korny/?p=172</guid>
		<description><![CDATA[I&#8217;ve been playing with new features in html5 recently &#8211; there are lots of things getting a bit of media coverage, but the wider support of embedded fonts really caught my eye.  By including an @font-face entry in your css file, and the appropriate font files on a server somewhere, you can get really [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been playing with new features in html5 recently &#8211; there are lots of things getting a bit of media coverage, but the wider support of embedded fonts really caught my eye.  By including an @font-face entry in your css file, and the appropriate font files on a server somewhere, you can get really nice typography, without needing to use flash-based hacks like <a href="http://en.wikipedia.org/wiki/Scalable_Inman_Flash_Replacement">sifr</a>,  or newer javascript hacks like <a href="http://cufon.shoqolate.com/">cufon</a></p>
<p><a href="http://cufon.shoqolate.com/"></a><a href="http://www.trouble.net.au/blog/korny/files/2010/04/beautiful_es.png"><img class="aligncenter size-full wp-image-178" title="beautiful_es" src="http://www.trouble.net.au/blog/korny/files/2010/04/beautiful_es.png" alt="" width="291" height="80" /></a> <span id="more-172"></span></p>
<p>(Note, on the usual question of browser support &#8211; check out the <a href="http://webfonts.info/wiki/index.php?title=@font-face_browser_support">webfonts wiki</a> page, or the nifty &#8220;<a href="http://a.deveria.com/caniuse/">When can I use</a>&#8221; page.  The short answer is, @font-face is supported in *all* desktop browsers, as long as you can get a font in the right format.)</p>
<p>Anyway, the point of this post is not to describe how @font-face works &#8211; you can find that information <a href="http://webfonts.info/wiki/index.php?title=What_are_webfonts%3F">all</a> <a href="http://paulirish.com/2009/bulletproof-font-face-implementation-syntax/">over</a> <a href="http://nicewebtype.com/notes/2009/10/30/how-to-use-css-font-face/">the</a> web, and there are <a href="http://www.fontsquirrel.com/">many</a> <a href="http://www.theleagueofmoveabletype.com/">good</a> <a href="http://webfonts.info/wiki/index.php?title=Fonts_available_for_@font-face_embedding">free</a> (and <a href="http://typekit.com/">non-free</a>) fonts out there.</p>
<p><a href="http://www.trouble.net.au/blog/korny/files/2010/04/sniglet.png"><img class="aligncenter size-full wp-image-179" title="sniglet" src="http://www.trouble.net.au/blog/korny/files/2010/04/sniglet.png" alt="" width="557" height="110" /></a></p>
<p>But I did want to point folks to Tim Brown&#8217;s <a href="http://webfontspecimen.com/">Web Font Specimen</a>, a nifty way to look at web fonts.  I found this when trying to wade through a pile of free fonts, looking for nice ones.  The only problem was, it needed editing by hand to change the font specimen &#8211; too much work.<br />
&#8220;That&#8217;d be easy to automate with Sinatra&#8221; I thought, so I went looking on github to see if anyone had done it, and found the <a href="http://github.com/robertjwhitney/web-font-specimen">web font specimen</a> Sinatra mini-app, by Robert J Whitney.  Which was neat, but still involved editing a sass file to specify the font you want to view.<br />
This wasn&#8217;t quite what I wanted &#8211; (a) I&#8217;m not a big fan of Sass, I prefer something closer to CSS like Less, and (b) it only showed a single font, and needed configuration to change fonts, while I wanted to browse several.</p>
<p>So I forked it &#8211; see <a href="http://github.com/kornysietsma/web-font-specimen">my shiny new Sinatra app on github</a>.<br />
It actually ended up being more of a reboot than a fork &#8211; I undid a pile of other people&#8217;s hard work, and reverted the sass and haml files back to their original css and html, then added some logic to scan font directories dynamically &#8211; all the details are in the code and the <a href="http://github.com/kornysietsma/web-font-specimen#readme">readme</a>.<br />
But this is how it looks, once you install some fonts and load up the main index page:</p>
<p><a href="http://www.trouble.net.au/blog/korny/files/2010/04/index.png"><img class="alignnone size-medium wp-image-176" title="index page" src="http://www.trouble.net.au/blog/korny/files/2010/04/index-300x208.png" alt="some pretty font samples" width="300" height="208" /></a></p>
<p>Click on a font, and it takes you to a the Specimen page &#8211; this is basically Tim Brown&#8217;s original page, unmodified (except for a &#8216;back&#8217; link):</p>
<p><a href="http://www.trouble.net.au/blog/korny/files/2010/04/goudy.png"><img class="alignnone size-medium wp-image-177" title="OFL-Sorts-Mill-Goudy Font Sample" src="http://www.trouble.net.au/blog/korny/files/2010/04/goudy-300x205.png" alt="pretty font sample page" width="300" height="205" /></a></p>
<p>&#8230; And that&#8217;s about it.  I&#8217;m probably not going much further with this &#8211; it&#8217;s pretty handy as-is, for browsing your local font files and seeing what looks nice on different browsers.  If anyone wants any changes, feel free to hassle me &#8211; or fork the project yourself!</p>
<p><a href="http://www.trouble.net.au/blog/korny/files/2010/04/aquiline_two.png"><img class="aligncenter size-full wp-image-180" title="aquiline_two" src="http://www.trouble.net.au/blog/korny/files/2010/04/aquiline_two.png" alt="" width="527" height="293" /></a></p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2010/04/10/face-the-face/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android shortcut for sending mail</title>
		<link>http://www.trouble.net.au/blog/korny/2010/03/26/android-shortcut-for-sending-mail/</link>
		<comments>http://www.trouble.net.au/blog/korny/2010/03/26/android-shortcut-for-sending-mail/#comments</comments>
		<pubDate>Fri, 26 Mar 2010 00:24:12 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.trouble.net.au/blog/korny/?p=168</guid>
		<description><![CDATA[I&#8217;m blogging about this because the second time I needed to make a mail shortcut, I couldn&#8217;t remember how &#8211; so if I blog it, I can always come here when I need to do it again!
I&#8217;m using a couple of different applications that allow you to update them by e-mail &#8211; mostly, task management [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m blogging about this because the second time I needed to make a mail shortcut, I couldn&#8217;t remember how &#8211; so if I blog it, I can always come here when I need to do it again!</p>
<p>I&#8217;m using a couple of different applications that allow you to update them by e-mail &#8211; mostly, task management apps.  I&#8217;m teetering between using <a href="http://www.rememberthemilk.com">Remember the Milk</a> and <a href="http://runwayapp.com/">Runway</a> to manage my to-do tasks.  Both of them have nice web interfaces, but adding a new idea/task via a web interface is a hassle &#8211; you have to be online, and you need to use a Browser UI &#8211; and even though both have nice mobile UIs, they are overkill for just adding a new &#8220;to be categorised&#8221; item.</p>
<p>Thankfully, both have magic e-mail addresses that can receive tasks as mail.  And on an Android phone, it&#8217;s very simple indeed to fire off a new mail &#8211; you can compose an e-mail and hit &#8220;send&#8221; or &#8220;save as draft&#8221;, and when the phone next syncs mail, it&#8217;ll be sent or put into your &#8216;drafts&#8217; folder on gmail.  Sure, you can do the same with a standalone app such as <a href="http://weloveastrid.com/">Astrid</a>, or the RTM-specific Android app (which I&#8217;ve never tried), but again, for basic &#8220;I need to remember this&#8221; tasks, a full app is overkill; and while Astrid will auto-sync, I trust the Gmail auto-sync rather more!</p>
<p>Anyway, to the shortcut business.  Rather than loading up the whole gmail client every time, you can simply make a desktop shortcut which loads the &#8220;Compose Mail&#8221; interface.  (Note, I have a HTC Hero, which has HTC&#8217;s custom Sense UI, so some of this may not apply to non-Sense phones).  The steps are as follows:</p>
<ol>
<li>Add the mail address to your gmail contacts, however you like (I use a desktop web browser generally, why bother doing this on the phone? Just add the contact in gmail, and wait for it to sync to the phone&#8217;s contacts list)</li>
<li>On the phone, press the &#8220;+&#8221; button on the bottom-right of the screen, or however you add icons in your Android OS</li>
<li>Select &#8220;Shortcut&#8221; (not &#8220;widget&#8221;, strangely)</li>
<li>Select &#8220;Person&#8221;</li>
<li>Choose the newly added contact</li>
<li>Select &#8220;send mail&#8221; &#8211; and a &#8220;send mail to XXX&#8221; icon will be put on your desktop</li>
</ol>
<p>If you find you have a lot of shortcuts, it&#8217;s well worth putting them in folders &#8211; I have a folder called &#8220;Planning&#8221; that has my &#8220;send to RTM&#8221; shortcut, plus bookmarks for the web interface, links to google calendar, etc.</p>
<p>Note this is also how you make a shortcut to send a SMS or to call a person.  Very handy for quick contacts for loved ones!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2010/03/26/android-shortcut-for-sending-mail/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Conclusions on CoffeeScript</title>
		<link>http://www.trouble.net.au/blog/korny/2010/03/02/conclusions-with-coffeescript/</link>
		<comments>http://www.trouble.net.au/blog/korny/2010/03/02/conclusions-with-coffeescript/#comments</comments>
		<pubDate>Mon, 01 Mar 2010 23:57:41 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.trouble.net.au/blog/korny/?p=165</guid>
		<description><![CDATA[Well, having played with CoffeeScript for a few days, I think it&#8217;s a wonderful thing &#8211; but for the moment, at least, I&#8217;m going back to JavaScript, with some regret.

It&#8217;s a fabulous, clean, simple language &#8211; I love the terseness of the code:

class TagPanel
  constructor: (width, height, paper) -&#38;gt;
    @width: width
 [...]]]></description>
			<content:encoded><![CDATA[<p>Well, having played with <a href="http://jashkenas.github.com/coffee-script/">CoffeeScript</a> for a few days, I think it&#8217;s a wonderful thing &#8211; but for the moment, at least, I&#8217;m going back to JavaScript, with some regret.<br />
<span id="more-165"></span><br />
It&#8217;s a fabulous, clean, simple language &#8211; I love the terseness of the code:</p>
<pre class="brush: php">
class TagPanel
  constructor: (width, height, paper) -&amp;gt;
    @width: width
    @height: height
    @paper: paper
    @boxes: []

  add_box: (box) -&amp;gt;
    box.display(@paper)
    @boxes.push box

  animate: (count) -&amp;gt;
    for i in [1...count]
      for box in @boxes
        box.step()

this.TagPanel: TagPanel # make global
</pre>
<p>compared with the JavaScript output:</p>
<pre class="brush: javascript">
(function(){
  var TagPanel;
  TagPanel = function TagPanel(width, height, paper) {
    this.width = width;
    this.height = height;
    this.paper = paper;
    this.boxes = [];
    return this;
  };
  TagPanel.prototype.add_box = function add_box(box) {
    box.display(this.paper);
    return this.boxes.push(box);
  };
  TagPanel.prototype.animate = function animate(count) {
    var _a, _b, _c, _d, _e, _f, _g, _h, _i, box, i;
    _a = []; _d = 1; _e = count;
    for (_c = 0, i = _d; (_d &lt;= _e ? i  _e); (_d &lt;= _e ? i += 1 : i -= 1), _c++) {
      _a.push((function() {
        _f = []; _g = this.boxes;
        for (_h = 0, _i = _g.length; _h &lt;; _i; _h++) {
          box = _g[_h];
          _f.push(box.step());
        }
        return _f;
      }).call(this));
    }
    return _a;
  };
  this.TagPanel = TagPanel;
  // make global
})();
</pre>
<p>However, I think it fails (for me) for a few reasons; some of these may be resolved over time, some may not.<br />
I think the main problem is that it&#039;s too different to Javascript.  This is the same reason I&#039;m not a fan of <a href="http://haml-lang.com/">HAML</a> or <a href="http://sass-lang.com/">SASS</a>, but quite like <a href="http://lesscss.org/">LessCSS</a> &#8211; if you are too different to the core language, a number of things no longer work:</p>
<ul>
<li>Syntax highlighting and IDE support are minimal</li>
<li>You aren&#8217;t learning enough about the core language</li>
<li>You can&#8217;t easily use existing code snippets or examples, with mentally de-compiling them back to CoffeeScript</li>
</ul>
<p><a href="http://lesscss.org/">LessCSS</a>, or in-language templating tools like <a href="http://github.com/defunkt/mustache">Mustache</a> or even good ol&#8217; <a href="http://www.ruby-doc.org/stdlib/libdoc/erb/rdoc/">ERB</a>, let you mix the old and the new; sometimes you IDE still gets screwed up by the differences from the core language, but you can generally work it out.</p>
<p>I have a few other minor issues with CofeeScript &#8211; I&#8217;m not such a big fan of Python-style indentation-as-code; I used to be more of a fan, and I could learn to love it again, but without IDE support it&#8217;s a bit of a pain.  Also there are a few other things I&#8217;d quibble about; but they are just that, quibbles, most of it is just fine as a language.</p>
<p>But a key thing for me is that I&#8217;m still learning to do Javascript properly &#8211; after years of using it badly as a make-my-browser-clicks-work tool, I went to a talk by <a href="http://javascript.crockford.com/">Doug Crockford</a>, and realised I need to learn this language properly.  And that means becoming familiar with the language itself, learning the right way and the wrong way to do things; using CoffeeScript encourages a few particular idioms, which work well, but I&#8217;m not learning beyond those idioms.</p>
<p>So I&#8217;ll take my code spiked in CoffeeScript and try to do it properly in JavaScript, probably working on TDDing it with <a href="http://visionmedia.github.com/jspec/">JSpec</a> or other such tools.  Maybe when I&#8217;m completely confident in JavaScript I&#8217;ll come back to the sweet smell of the Coffee&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2010/03/02/conclusions-with-coffeescript/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Playing with CoffeeScript</title>
		<link>http://www.trouble.net.au/blog/korny/2010/02/25/playing-with-coffeescript/</link>
		<comments>http://www.trouble.net.au/blog/korny/2010/02/25/playing-with-coffeescript/#comments</comments>
		<pubDate>Thu, 25 Feb 2010 03:03:35 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[javascript coffeescript]]></category>

		<guid isPermaLink="false">http://www.trouble.net.au/blog/korny/?p=148</guid>
		<description><![CDATA[I&#8217;ve been fiddling with Coffeescript, a nifty language for writing Javascript &#8211; well, really it&#8217;s more like a pre-compiler than a language; it compiles directly to readable javascript (including comments!) and generally one Coffeescript statement produces one javascript statement.
So why would you use it?  Because it bypasses some of the horribleness of Javascript syntax, [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been fiddling with <a href="http://jashkenas.github.com/coffee-script">Coffeescript</a>, a nifty language for writing Javascript &#8211; well, really it&#8217;s more like a pre-compiler than a language; it compiles directly to readable javascript (including comments!) and generally one Coffeescript statement produces one javascript statement.</p>
<p>So why would you use it?  Because it bypasses some of the horribleness of Javascript syntax, and implements a few handy patterns, like anonymous function creation, in much clearer code.<br />
<span id="more-148"></span><br />
For example, the following Coffeescript:</p>
<pre class="brush: text">
square: (x) -&gt;
alert &#039;squaring: &#039; + x
x * x
</pre>
<p>converts to the following javascript:</p>
<pre class="brush: javascript">
square: function square(x) {
  alert(&#039;squaring:&#039; + x);
  return x * x;
}
</pre>
<p>There are many more examples at the <a href="http://jashkenas.github.com/coffee-script">Coffeescript</a> home page.</p>
<p>I&#8217;m not 100% sold on this, as I&#8217;m still learning (proper) Javascript, and this amplifies some of my ignorance.  For example, I really struggle with &#8216;this&#8217; and functions/objects &#8211; and while Coffeescript automatically enforces some good Javascript behaviour, when I do something wrong it&#8217;s a bigger than usual struggle to work out why.<br />
A longer example: I&#8217;ve been playing with <a href="http://raphaeljs.com/">Raphael</a> (and JQuery) and decided to do some fiddling in Coffeescript:</p>
<pre class="brush: text">
app: {
  init: -&gt;
    app.paper: Raphael(&#039;playspace&#039;,320,200)
    app.draw_circle()
    app.draw_square()

  # simple circle with event-based on-click event
  draw_circle: -&gt;
    circle: app.paper.circle(50,40,30)
    circle.attr(&quot;fill&quot;,&quot;#f00&quot;)
    circle.attr(&quot;stroke&quot;,&quot;#fff&quot;)
    $(circle.node).click((e) -&gt; app.kick(e))

  # square with object-based on-click event
  draw_square: -&gt;
    square: app.paper.rect(150,40,50,30)
    square.attr(&quot;fill&quot;,&quot;#0f0&quot;)
    square.attr(&quot;stroke&quot;,&quot;#fff&quot;)
    $(square.node).click((e) -&gt; app.punt(square,e))

  kick: (e) -&gt;
    app.paper.text(e.pageX + 10, e.pageY + 10, &quot;Raphaël\nkicks\nbutt!&quot;)

  punt: (obj,e) -&gt;
    obj.translate(10,10)
}

# make it globally visible - not really needed, handy for debugging
if window?
  window.APP: app

$(app.init)
</pre>
<p>The javascript version is:</p>
<pre class="brush: javascript">
(function(){
  var app;
  app = {
    init: function init() {
      app.paper = Raphael(&#039;playspace&#039;, 320, 200);
      app.draw_circle();
      return app.draw_square();
    },
    // simple circle with event-based on-click event
    draw_circle: function draw_circle() {
      var circle;
      circle = app.paper.circle(50, 40, 30);
      circle.attr(&quot;fill&quot;, &quot;#f00&quot;);
      circle.attr(&quot;stroke&quot;, &quot;#fff&quot;);
      return $(circle.node).click(function(e) {
        return app.kick(e);
      });
    },
    // square with object-based on-click event
    draw_square: function draw_square() {
      var square;
      square = app.paper.rect(150, 40, 50, 30);
      square.attr(&quot;fill&quot;, &quot;#0f0&quot;);
      square.attr(&quot;stroke&quot;, &quot;#fff&quot;);
      return $(square.node).click(function(e) {
        return app.punt(square, e);
      });
    },
    kick: function kick(e) {
      return app.paper.text(e.pageX + 10, e.pageY + 10, &quot;Raphaël\nkicks\nbutt!&quot;);
    },
    punt: function punt(obj, e) {
      return obj.translate(10, 10);
    }
  };
  // make it globally visible - not really needed, handy for debugging
  (typeof window !== &quot;undefined&quot; &amp;&amp; window !== null) ? (window.APP = app) : null;
  $(app.init);
})();
</pre>
<p>Now, this works, it&#8217;s a lot more readable than the Javascript version, and it&#8217;s nifty how Coffeescript wrapped everything in an anonymous function so as to not pollute the global namespace (except where I did so deliberately).</p>
<p>But this was about my 8th try.  I had endless problems getting the &#8216;click&#8217; handlers to hook into the &#8216;kick&#8217; and &#8216;punt&#8217; methods.  I tried (initially) using &#8216;this.kick&#8217; but it failed as when the click handler is called, this is bound elsewhere.  I tried storing &#8216;this&#8217; in a variable, but had pain with the fact that somewhere in the mess of anonymous function wrappers, &#8216;this&#8217; never seemed to mean the right thing.</p>
<p>In the end, I hit on storing the whole thing as an object &#8216;app&#8217; &#8211; I was already going to make it part of &#8216;window.APP&#8217; but didn&#8217;t want to prefix all calls with &#8216;APP&#8217;&#8230; but in the end, that&#8217;s kind-of what I did, only the global APP variable isn&#8217;t actually needed, just the local &#8216;app&#8217; variable.</p>
<p>Anyway, I&#8217;m sure there is a better way to do this &#8211; but I haven&#8217;t found it, and this one works.  Now I have to decide if I want to keep playing with Coffeescript, having worked all this out, or go back to ugly old Javascript, where at least there are hundreds of net resources to help me when I get stuck!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2010/02/25/playing-with-coffeescript/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Simple ruby wrapper for Delcom build lights on Linux</title>
		<link>http://www.trouble.net.au/blog/korny/2010/02/12/simple-ruby-wrapper-for-delcom-build-lights-on-linux/</link>
		<comments>http://www.trouble.net.au/blog/korny/2010/02/12/simple-ruby-wrapper-for-delcom-build-lights-on-linux/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 05:53:24 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://www.trouble.net.au/blog/korny/?p=138</guid>
		<description><![CDATA[This isn&#8217;t particularly new &#8211; I wrote this code a while ago, but it never got &#8220;finished&#8221; so I never wrote it up&#8230;
Many agile dev teams use Delcom USB build lights:
http://delcomproducts.com/productdetails.asp?productnum=804005
Coding for these is apparently quite complex&#8230; unless you are on a recent-ish Linux machine, in which case it&#8217;s amazingly trivial.

When you plug the light [...]]]></description>
			<content:encoded><![CDATA[<p>This isn&#8217;t particularly new &#8211; I wrote this code a while ago, but it never got &#8220;finished&#8221; so I never wrote it up&#8230;</p>
<p>Many agile dev teams use Delcom USB build lights:<br />
<a href="http://delcomproducts.com/productdetails.asp?productnum=804005">http://delcomproducts.com/productdetails.asp?productnum=804005</a></p>
<p>Coding for these is apparently quite complex&#8230; unless you are on a recent-ish Linux machine, in which case it&#8217;s amazingly trivial.<br />
<span id="more-138"></span><br />
When you plug the light in, if you have the right modules loaded, you&#8217;ll get a set of magic files in a directory somewhere under:<br />
<code>/sys/bus/usb/drivers/usbled</code> &#8211; on my PC with my light they show up under <code>/sys/bus/usb/drivers/usbled/7-2:1.0</code>, and you only need to care about 3 little virtual files:</p>
<pre class="brush: bash">
-rw-rw-rw- 1 root root 4096 2010-02-12 16:26 blue
-rw-rw-rw- 1 root root 4096 2010-02-12 16:26 green
-rw-rw-rw- 1 root root 4096 2010-02-12 16:26 red
</pre>
<p>Those 3 files control the 3 components of the light.  You can set or get the colour trivially from the shell, once you know the name:</p>
<pre class="brush: bash">
$ cat /sys/bus/usb/drivers/usbled/7-2:1.0/blue
0
$ echo &quot;1&quot; &gt; /sys/bus/usb/drivers/usbled/7-2:1.0/blue
# Blam!  The light goes blue!
$ cat /sys/bus/usb/drivers/usbled/7-2:1.0/blue
1
$ echo &quot;1&quot; &gt; /sys/bus/usb/drivers/usbled/7-2:1.0/red
# and now the light goes purple - red + blue!
</pre>
<p>That&#8217;s really all you need to know &#8211; you can read, and write, the red/green/blue elements of the light.  A little ruby wrapper I wrote makes for nicer colour names, and automatically works out the path to the light (ok, it just finds the first directory that looks right in the usbled path&#8230;):</p>
<pre class="brush: ruby">
#!/usr/bin/env ruby
class UsbLight
  COLOURNAMES = {
      :black =&gt; [0,0,0],
      :red =&gt; [1,0,0],
      :green =&gt; [0,1,0],
      :blue =&gt; [0,0,1],
      :cyan =&gt; [0,1,1],
      :magenta =&gt; [1,0,1],
      :yellow =&gt; [1,1,0],
      :white =&gt; [1,1,1]
    }

  def initialize
    @usbledpath = &#039;/sys/bus/usb/drivers/usbled&#039;
    @usbleddev = Dir.new(@usbledpath).find { |f| f =~ /^\d-\d.*/ }
    raise &quot;Can&#039;t find device in #{@usbledpath}&quot; unless @usbleddev
    @usbled = File.join(@usbledpath,@usbleddev)
    @lights = {}
    %w(red green blue).each do |light|
      @lights[light] = File.join(@usbled,light)
      raise &quot;can&#039;t read #{light} light&quot; unless File.readable?(@lights[light])
      raise &quot;can&#039;t write #{light} light&quot; unless File.writable?(@lights[light])
    end
  end
  def colour
    lights = [read_light(&quot;red&quot;),read_light(&quot;green&quot;),read_light(&quot;blue&quot;)]
    colour_pattern = COLOURNAMES.detect { |colour, pattern| pattern == lights }
    raise &quot;bad colour combo: #{lights.inspect}&quot; if colour_pattern.nil?
    colour_pattern[0]
  end
  def colour=(name)
    lights = COLOURNAMES[name.to_sym]
    raise &quot;unknown lamp colour: #{name}&quot; unless lights
    write_light(&quot;red&quot;,lights[0])
    write_light(&quot;green&quot;,lights[1])
    write_light(&quot;blue&quot;,lights[2])
  end
  def off
    self.colour = :black
  end

  private
  def read_light(name)
    File.new(@lights[name]).read.to_i
  end
  def write_light(name,value)
    open(@lights[name],&quot;w&quot;) do |f|
      f.puts(value.to_s)
    end
  end
end
</pre>
<p>I have a Hudson watcher that uses this to set the light colour &#8211; but that&#8217;s a subject for another post!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2010/02/12/simple-ruby-wrapper-for-delcom-build-lights-on-linux/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Behaviour Driven Development with Cucumber and Selenium</title>
		<link>http://www.trouble.net.au/blog/korny/2009/11/03/behaviour-driven-development-with-cucumber-and-selenium/</link>
		<comments>http://www.trouble.net.au/blog/korny/2009/11/03/behaviour-driven-development-with-cucumber-and-selenium/#comments</comments>
		<pubDate>Tue, 03 Nov 2009 10:22:08 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[bdd]]></category>
		<category><![CDATA[cucumber]]></category>
		<category><![CDATA[selenium]]></category>
		<category><![CDATA[tdd]]></category>
		<category><![CDATA[testing]]></category>

		<guid isPermaLink="false">http://www.trouble.net.au/blog/korny/?p=102</guid>
		<description><![CDATA[[Please note - if you are familiar with BDD, Cucumber, or Selenium, parts of this may be a tad basic - but I thought it was worth writing a from-scratch guide for those to whom this is unfamiliar territory...]
What is Behaviour Driven Development?
BDD is a new-ish term used as a contrast to Test Driven Development [...]]]></description>
			<content:encoded><![CDATA[<p>[Please note - if you are familiar with BDD, Cucumber, or Selenium, parts of this may be a tad basic - but I thought it was worth writing a from-scratch guide for those to whom this is unfamiliar territory...]</p>
<h3>What is Behaviour Driven Development?</h3>
<p>BDD is a new-ish term used as a contrast to Test Driven Development &#8211; it was coined by Dan North in 2006, as described in his article at <a href="http://dannorth.net/introducing-bdd" target="_blank">http://dannorth.net/introducing-bdd</a> &#8211; there&#8217;s also a good introduction in <a href="http://en.wikipedia.org/wiki/Behavior_Driven_Development" target="_blank">wikipedia</a>.</p>
<p>In a nutshell, BDD is all about describing the behaviour of an application, in a plain-text language that can be understood by end users, testers, and developers &#8211; and then hopefully automating acceptance testing, so you can prove that at any time, the application meets the BDD description.<br />
<span id="more-102"></span></p>
<h3>How about Cucumber? and Selenium?</h3>
<p><a href="http://cukes.info/" target="_blank">Cucumber</a>, which grew out of the Ruby <a href="http://rspec.info/">Rspec</a> framework, is a tool to enable the writing of acceptance criteria in a controlled plain-text format, and then running those criteria via some ruby back-end code.</p>
<p><a href="http://seleniumhq.org/" target="_blank">Selenium</a> is a set of tools for automating tests in web browsers.  There are actually two main flavours of Selenium, Selenium IDE that runs in a browser-based gui, and Selenium RC, a client/server version with a java server and clients in a number of languages. We&#8217;ll use Selenium RC, with the Ruby front end (as it makes integrating with Cucumber very easy).  You can also use alternative tools such as <a href="http://watir.com/" target="_blank">Watir</a> or <a href="http://celerity.rubyforge.org/" target="_blank">Celerity</a> to do similar things in different ways; each has it&#8217;s merits and limitations.</p>
<h3>Ok, give me an example</h3>
<p>I&#8217;ve used the Twitter home page as an example, as it has a bunch of asynchronous ajax behaviour, which is very hard to test without automating a web browser.</p>
<p>The scenarios for a related set of Twitter features are defined in a feature file, such as <code><strong>trending_topics.feature</strong></code> :</p>
<pre>Feature: Twitter trending topics
In order to tap into the zeitgeist
as a web surfer
I should be able to see what's being discussed on twitter

Scenario: view popular topics
When I visit the Twitter home page
Then I should be able to see popular topics right now

Scenario: search for most popular topics
Given I am on the Twitter home page
And I see the most popular topic
When I search for the most popular topic
Then I should see results containing the most popular topic
And I should see a message indicating more results exist within 40 seconds</pre>
<p>The first few lines are a preamble &#8211; they define the background of the feature.</p>
<p>The actual scenarios are runnable definitions of how the feature actually behaves.</p>
<h3>Making the features runnable</h3>
<p>There are several layers of code to make these features actually runnable.</p>
<p>First, we have step definitions &#8211; these are code that matches each &#8220;Given&#8221;, &#8220;When&#8221; or &#8220;Then&#8221; step with a regular expression or a plain text string, that identifies the matching ruby logic, and possibly extracts user parameters.  For example, the step &#8220;And I should see a message indicating more results exist within 40 seconds&#8221; is matched by ruby code like:</p>
<pre class="brush: ruby">
Then /^I should see a message indicating more results exist within (\d+) seconds$/ do |timeout|
</pre>
<p>- the regular expression matches the step, and pulls out the timeout parameter into a variable.</p>
<p>The full code of the steps file is as follows:</p>
<p><code><strong>twitter_steps.rb</strong></code></p>
<pre class="brush: ruby">
Given &quot;I am on the Twitter home page&quot; do
  @home_page.visit
end

Given &quot;I see the most popular topic&quot; do
  @most_popular = @home_page.most_popular_topic
  puts &quot;Most popular topic: &#039;#{@most_popular}&#039;&quot;
end

When &quot;I visit the Twitter home page&quot; do
  Given &quot;I am on the Twitter home page&quot;
end

When &quot;I search for the most popular topic&quot; do
  @home_page.search(@most_popular)
end

Then &quot;I should be able to see popular topics right now&quot; do
  @home_page.should have_popular_topics
end

Then &quot;I should see results containing the most popular topic&quot; do
  @results_page.tweets.each do |result|
     result[:tweet].downcase.should include @most_popular.downcase
  end
end

Then /^I should see a message indicating more results exist within (\d+) seconds$/ do |timeout|
  @results_page.wait_for_more_results(timeout.to_i)
end
</pre>
<p>As you can see, the step implementations are very simple &#8211; all the heavy lifting is done by page model objects &#8211; @home_page and @results page wrap all the logic related to two different web pages.  The twitter_steps.rb file just handles matching the steps, and tracking some state, such as the @most_popular variable.</p>
<p><code><strong>twitter_home_page.rb</strong></code> defines the home page model:</p>
<pre class="brush: ruby">
class TwitterHomePage
  PAGE_URL = &quot;http://www.twitter.com&quot;
  def initialize(world)
    @world = world
    @browser = $selenium_helper.browser
  end

  def visit
    @browser.open PAGE_URL
    @browser.wait_for_page_to_load
    @browser.title.should == &quot;Twitter&quot;
  end

  def has_popular_topics?
    @browser.element? POP_TOPICS_LOCATOR
  end

  def most_popular_topic
    @browser.text FIRST_POP_TOPIC_LOCATOR
  end

  def search(topic)
    @browser.type SEARCH_BOX_LOCATOR, topic
    @browser.click SEARCH_BUTTON_LOCATOR
    @browser.wait_for_element RESULTS_HEADING_LOCATOR
  end

  private

  POP_TOPICS_LOCATOR = %Q{//div[@id = &quot;trends&quot;]//div[@class = &quot;current&quot;]}
  FIRST_POP_TOPIC_LOCATOR = &quot;#{POP_TOPICS_LOCATOR}/ul/li[1]/a&quot;
  SEARCH_BOX_LOCATOR = %Q{//input[@id=&quot;home_search_q&quot;]}
  SEARCH_BUTTON_LOCATOR = %Q{//a[@id=&quot;home_search_submit&quot;]}
  RESULTS_HEADING_LOCATOR = %Q{//div[@id=&quot;content&quot;]/h2[@id=&quot;timeline_heading&quot;]}
end
</pre>
<p><code><strong>twitter_results_page.rb</strong></code> is for handling search results:</p>
<pre class="brush: ruby">
class TwitterResultsPage
  def initialize(world)
    @world = world
    @browser = $selenium_helper.browser
  end

  def tweets
    # ajax results, give them a chance to load
    @browser.wait_for_element(TIMELINE_LOCATOR)
    result_count = @browser.get_xpath_count(TIMELINE_LOCATOR).to_i

    (1..result_count).collect do |count|
      {
        :author =&gt; @browser.get_text(tweet_author_locator(count)),
        :tweet =&gt; @browser.get_text(tweet_text_locator(count))
      }
    end
  end

  def wait_for_more_results(timeout_secs)
    @browser.wait_for_element(MORE_RESULTS_LOCATOR,{:timeout_in_secs =&gt; timeout_secs})
  end

  private

  def tweet_author_locator(count)
    %Q{#{tweet_locator(count)}//a[contains(@class, &quot;screen-name&quot;)]}
  end

  def tweet_text_locator(count)
    %Q{#{tweet_locator(count)}//span[contains(@class, &quot;msgtxt&quot;)]}
  end

  def tweet_locator(count)
    %Q{#{TIMELINE_LOCATOR}[#{count}]}
  end

  TIMELINE_LOCATOR = %Q{//ol[@id=&quot;timeline&quot;]/li}
  MORE_RESULTS_LOCATOR = %Q{//div[@id=&quot;new_results_notification&quot;]/a[@id=&quot;results_update&quot; and not(contains(@style, &quot;display: none&quot;))]}
end
</pre>
<p>The page models use a lot of xpath locators to find items in web pages; they work well on most modern browsers but might be inconsistent on IE6 &#8211; if you want to test on IE6 you might have to look into other ways to locate elements, such as finding them by ID.</p>
<p>There are some utility classes that set up the environment, user configuration, and the Selenium interface:</p>
<p><code><strong>env.rb</strong></code> is the main entry point for Cucumber &#8211; it is loaded first, before any other ruby files, and it sets up globals and startup/shutdown code:</p>
<pre class="brush: ruby">
BASEDIR = File.join(File.dirname(__FILE__),&quot;..&quot;) unless defined? BASEDIR
PRJDIR = File.join(File.dirname(__FILE__),&quot;..&quot;,&quot;..&quot;,&quot;..&quot;) unless defined? PRJDIR
require &#039;spec/expectations&#039; # rspec extras
require File.join(BASEDIR,&#039;support/selenium_helper.rb&#039;)
require File.join(BASEDIR,&#039;support/user_config.rb&#039;)

# globals - keep these to a minimum!
$user_config = UserConfig.new
$selenium_helper = SeleniumHelper.new($user_config)  # better a global than a singleton - still need something global as it&#039;s used in monkey-patching bits below

at_exit do
  $stderr.puts &quot;global exit block - closing browser&quot;
  $selenium_helper.shutdown
end

module MyWorld
  # add methods here you want accessible from all cucumber steps
end

World(MyWorld)

Before do
  @home_page = TwitterHomePage.new(self)
  @results_page = TwitterResultsPage.new(self)
end
</pre>
<p>The &#8216;MyWorld&#8217; module is there as a starting point for your own extensions &#8211; generally I have often-needed functions in this module; see <a href="http://wiki.github.com/aslakhellesoy/cucumber/a-whole-new-world">the cucumber documentation</a> for more.</p>
<p>The &#8216;Before&#8217; block is called before every scenario &#8211; here it just sets up the page objects, but other per-scenario stuff can also be added here.</p>
<p>Other than that, the main things included are a user config class, that loads user configuration from a file named for the user&#8217;s host name (so on &#8220;my_pc&#8221; it will load a config file called &#8220;config/my_pc.config&#8221;); see <a href="http://gist.github.com/224106">http://gist.github.com/224106</a> for code and a typical example.</p>
<p>&#8230; and the real work of loading Selenium is in the <strong><code>selenium_helper.rb</code></strong> file:</p>
<pre class="brush: ruby">
require &#039;selenium/client&#039;
require &#039;selenium/rspec/spec_helper&#039;
require File.join(BASEDIR,&#039;support/user_config.rb&#039;)

if defined? JRUBY_VERSION
  # jruby has it&#039;s own process-handling code, as &#039;fork&#039; is unreliable in java
  require &#039;java&#039;
end

class SeleniumHelper
  attr_accessor :max_timeout, :browser
  def initialize(user_config)
    @user_config = user_config
    @max_timeout = 45  # maximum allowed timeout
    @selenium_port = user_config[&#039;selenium.port&#039;]
    @selenium_browser = @user_config[&#039;selenium.browser.name&#039;]
    @selenium_process = nil
    @browser = nil
    start_selenium
    start_browser
  end

  def shutdown
    stop_selenium
  end

  private

  def start_selenium
    selenium_jar_path = File.expand_path(File.join(PRJDIR,&quot;lib&quot;,&quot;selenium&quot;,&quot;selenium-server.jar&quot;))
    raise &quot;Can&#039;t find #{selenium_jar_path}&quot; unless File.exists?(selenium_jar_path)
    cmd = &quot;java -jar #{selenium_jar_path} -timeout #{@max_timeout} -port #{@selenium_port}&quot;

    if defined? JRUBY_VERSION # java magic to run a process
      @selenium_process = java.lang.ProcessBuilder.new(cmd.split(&quot; &quot;)).redirectErrorStream(true).start
      # spawn a thread to redirect background process to log file
      $stderr.puts &quot;selenium process started in background&quot;
      output_stream_to_log(@selenium_process.getInputStream, &quot;selenium.log&quot;)
    else # not java - use fork
      @selenium_process = Process.fork do
          # Note: you need to redirect stdout this way
          # - if you try using &quot;cmd &gt; selenium.log&quot; ruby spawns a subprocess, which you can&#039;t kill
         $stdout.reopen(File.new(&quot;selenium.log&quot;, &quot;w&quot;))
         exec cmd
      end
      $stderr.puts &quot;selenium process started with pid #{@selenium_process}&quot;
    end
    sleep 2  # give it a chance to start
    $stderr.puts &quot;Selenium output sent to selenium.log&quot;
  end

  def start_browser
    begin
      base_url = &quot;http://localhost&quot;
      @browser = Selenium::Client::Driver.new(&quot;localhost&quot;, @selenium_port, @selenium_browser, base_url, @max_timeout)
      @browser.start_new_browser_session
    rescue Exception
      stop_selenium
      raise
    end
  end

  def stop_selenium
    $stderr.puts &quot;killing background selenium task&quot;
    # could open http://localhost:selenium_port/selenium-server/driver/?cmd=shutDown - but this is more reliable!
    if defined? JRUBY_VERSION
      @selenium_process.destroy
      $stderr.puts &quot;and waiting...&quot;
      @selenium_process.waitFor
    else
      Process.kill(&quot;HUP&quot;, @selenium_process)
      $stderr.puts &quot;and waiting...&quot;
      Process.wait
    end
    $stderr.puts &quot;dead.&quot;
  end

  def output_stream_to_log(inputStream, logfilename)
    Thread.new do
      File.open(logfilename,&quot;w&quot;) do |f|
        output = java.io.BufferedReader.new(java.io.InputStreamReader.new(inputStream))
        while (line = output.readLine) != nil
          f.puts line
        end
        output.close
      end
    end

  end
end
</pre>
<p>Note you <b>can</b> just load selenium.jar from a command line, and save yourself much of the effort here &#8211; but this will load and unload it for you, which can keep things simpler.  There&#8217;s some complexity involved in getting it to work both in JRuby (which has to use Java&#8217;s ProcessBuilder class) and in vanilla Ruby.</p>
<h3>Building and running this example</h3>
<p>To get this example up and running, you need:</p>
<ul>
<li><a href="http://www.ruby-lang.org">Ruby</a>, or <a href="http://jruby.org">JRuby</a> &#8211; native Ruby is faster, but if you are in the Java world, JRuby can be handy as it&#8217;s 100% java</li>
<li><a href="http://www.rubygems.org">Ruby-gems</a>, the ruby packaging system (included in JRuby)</li>
<li> The ruby gems:
<ul>
<li>rspec (tested with version 1.2.6) &#8211; not essential, but adds many nice matchers and features to cucumber</li>
<li>cucumber (tested with version 0.3.9)</li>
<li>Selenium (tested with version 1.1.14)</li>
<li>selenium-client (tested with version 1.2.15)</li>
</ul>
</li>
<li>Java 1.5 or later (needed for the selenium server)</li>
<li> The selenium-rc server (version 1.0.1 or later)
<ul>
<li>you really only need the selenium-server jar file, everything else is included in the ruby gems above</li>
<li>download selenium-rc from <a href="http://seleniumhq.org/download/">http://seleniumhq.org/download/</a> , unzip selenium-remote-control-???.zip, and extract the file &#8217;selenium-server.jar&#8217;</li>
</ul>
</li>
</ul>
<p>The file structure is as follows:</p>
<ul>
<li> Root folder &#8211; optionally contains a cucumber.yml file (see cucumber docs for more)
<ul>
<li> test/features &#8211; base directory for all feature files
<ul>
<li>/trending_topics.feature</li>
<li> /step_definitions
<ul>
<li>twitter_steps.rb</li>
</ul>
</li>
<li> /support
<ul>
<li>env.rb</li>
<li>selenium_helper.rb</li>
<li>user_config.rb</li>
<li> /pages
<ul>
<li>twitter_home_page.rb</li>
<li>twitter_results_page.rb</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>lib/selenium/selenium-server.jar &#8211; the selenium server itself</li>
</ul>
</li>
</ul>
<p>The complete set of sample files (except the selenium jar file!) is available at <a href='http://sietsma.com/korny/cuke_sample.zip'>http://sietsma.com/korny/cuke_sample.zip</a>.</p>
<h3>Running it!</h3>
<p>To run all the scenarios in the feature file, assuming all the software is installed and in the path as required, run:</p>
<pre>$ cucumber test/features/trending_topics.feature</pre>
<p>The output should be similar to:</p>
<pre>
selenium process started with pid 25298
Selenium output sent to selenium.log
Feature: Twitter trending topics
  In order to tap into the zeitgeist
  as a web surfer
  I should be able to see what's being discussed on twitter

  Scenario: view popular topics                           # test/features/trending_topics.feature:6
    <i>When I visit the Twitter home page</i>                    # test/features/step_definitions/twitter_steps.rb:10
    <i>Then I should be able to see popular topics right now</i> # test/features/step_definitions/twitter_steps.rb:18

  Scenario: search for most popular topics                                     # test/features/trending_topics.feature:10
    <i>Given I am on the Twitter home page</i>                                        # test/features/step_definitions/twitter_steps.rb:1
Most popular topic: '#unseenprequels'
    <i>And I see the most popular topic</i>                                           # test/features/step_definitions/twitter_steps.rb:5
    <i>When I search for the most popular topic</i>                                   # test/features/step_definitions/twitter_steps.rb:14
    <i>Then I should see results containing the most popular topic</i>                # test/features/step_definitions/twitter_steps.rb:22
    <i>And I should see a message indicating more results exist within 40 seconds</i> # test/features/step_definitions/twitter_steps.rb:26

2 scenarios (2 passed)
7 steps (7 passed)
0m33.328s
global exit block - closing browser
killing background selenium task
and waiting...
dead.
</pre>
<p>Note sections in <i>italics</i> are actually green on a terminal, to indicate success.  You can also format output as html, for prettier display, or for embedding in a Continuous Integration report.<br />
&#8212;</p>
<p>There is far more that could be said on this topic &#8211; this is just the tip of the iceberg.  Hopefully this is a useful starting point however!</p>
<p>As mentioned earlier, you can download all the code in this article from <a href='http://sietsma.com/korny/cuke_sample.zip'>http://sietsma.com/korny/cuke_sample.zip</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2009/11/03/behaviour-driven-development-with-cucumber-and-selenium/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Refactoring my blogs</title>
		<link>http://www.trouble.net.au/blog/korny/2009/10/04/refactoring-my-blogs/</link>
		<comments>http://www.trouble.net.au/blog/korny/2009/10/04/refactoring-my-blogs/#comments</comments>
		<pubDate>Sun, 04 Oct 2009 10:18:33 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[lean]]></category>
		<category><![CDATA[meta]]></category>
		<category><![CDATA[pragmatic]]></category>
		<category><![CDATA[rant]]></category>

		<guid isPermaLink="false">http://www.trouble.net.au/blog/korny/2009/10/04/refactoring-my-blogs/</guid>
		<description><![CDATA[Well, I&#8217;ve been reading again (always a bad move) &#8211; first Outliers, by Malcolm Gladwell, which talks about the 10,000 hour rule.  He theorises that to truly master something, you need to spend 10,000 hours practising it.
I&#8217;m an old-school geek &#8211; I started coding in year 7 on my sister&#8217;s university account, in Basic; and [...]]]></description>
			<content:encoded><![CDATA[<p>Well, I&#8217;ve been reading again (always a bad move) &#8211; first Outliers, by Malcolm Gladwell, which talks about the 10,000 hour rule.  He theorises that to truly master something, you need to spend 10,000 hours practising it.</p>
<p>I&#8217;m an old-school geek &#8211; I started coding in year 7 on my sister&#8217;s university account, in Basic; and I spent many, many years coding bad procedural programs in Basic, Assembler, Pascal, and C.  I racked up at least 10,000 hours of procedural coding &#8211; sometimes with cool results, but generally it was pretty ugly.</p>
<p>Somewhere along the way, I got paid to code, and eventually I discovered C++, and a while later, Java and object-oriented ideas, and a more &quot;professional&quot; approach to coding; which, sadly, often meant forays into realms of ugly complexity commonly associated with &quot;professional&quot; coding &#8211; it can be summed up with a pile of acronyms, most of which still make me break out in a cold sweat &#8211; UML, STL, J2EE, Corba, RUP, COM, MDA &#8230; I shudder to go on.  Another 10,000+ years of &#8216;practice&#8217; &#8211; and a lot of useful lessons learned, though mostly about what kind of things to avoid, and what kind of promises to be sceptical about.</p>
<p>Thankfully, things got somewhat better after that &#8211; moving to Agile development made the world a lot more sane; test-driven and behaviour-driven development have drastically improved code quality and transparency; and I discovered shiny new languages like Ruby, which re-introduced me to Functional Programming, which led to Clojure and Scala and a host of new ideas and technologies.  And all of these connected me to new communities of people with new ideas&#8230; This starts to look like a great 10,000 hours!</p>
<p>That brings me to the other book I read recently &#8211; from the always excellent Pragmatic Press, &quot;The Passionate Programmer&quot;, all about building and enhancing your IT career.  It helped me to realise I should pull my socks up, update my websites and my blogs, and actually share what interests me with the world, rather than geeking along in a vacuum.</p>
<p>So hence this long ramble and this re-branded blog.  I have a pile of things to write about &#8211; a short list includes:</p>
<ul>
<li>My (so far pretty limited) experiments with Google Wave</li>
<li>My team&#8217;s great use of Cucumber for Behaviour Driven Development, including some cool wiki integration</li>
<li>Some half-formed thoughts I&#8217;ve had on unifying several things I&#8217;ve heard and read on Agile and Lean Values, Principles, and Practices &#8211; not sure I&#8217;m anywhere near a grand unified theory of everything, but if my thoughts turn into something coherent, I&#8217;ll post them</li>
<li>My next big project &#8211; tentatively titled &quot;Kanban Sync&quot;, it will use an Augmented Reality interface on a smartphone to synchronize a physical card wall (a.k.a. <a href="http://www.infoq.com/articles/agile-kanban-boards">Kanban board</a>) with back-end tracking tools such as <a href="http://studios.thoughtworks.com/mingle-agile-project-management">Mingle</a> or <a href="http://agilebench.com/">Agile Bench</a> or <a href="http://www.atlassian.com/software/jira/">Jira</a>.  This is still in very early planning stages &#8211; but if it works, it&#8217;ll be very cool indeed.</li>
</ul>
<p>Plus, of course, whatever <strong>actually</strong> comes up&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2009/10/04/refactoring-my-blogs/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Curved corner images in rmagick</title>
		<link>http://www.trouble.net.au/blog/korny/2008/07/20/curved-corner-images-in-rmagick/</link>
		<comments>http://www.trouble.net.au/blog/korny/2008/07/20/curved-corner-images-in-rmagick/#comments</comments>
		<pubDate>Sun, 20 Jul 2008 02:05:08 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.sietsma.com/2008/07/20/curved-corner-images-in-rmagick/</guid>
		<description><![CDATA[RMagick is a great ruby library for image manipulation &#8211; it&#8217;s basically a wrapper around the venerable ImageMagick libraries.  There are some complexities around installing it on some platforms, and you have to be careful with garbage collection, but it&#8217;s still very easy to use and very powerful.
The curved corner images I used in previous [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://rmagick.rubyforge.org/" target="_blank">RMagick</a> is a great ruby library for image manipulation &#8211; it&#8217;s basically a wrapper around the venerable ImageMagick libraries.  There are some complexities around installing it on some platforms, and you have to be careful with garbage collection, but it&#8217;s still very easy to use and very powerful.</p>
<p>The curved corner images I used in previous blog posts could be built in photoshop or gimp or other gui applications, but I wanted something scriptable, so I&#8217;m trying to build all images in RMagick &#8211; and it&#8217;s really not that difficult.</p>
<p>RMagick has a whole set of vector graphics functions, but I&#8217;m mainly using one, the <a href="http://studio.imagemagick.org/RMagick/doc/draw.html#roundrectangle" target="_blank">roundrectangle</a> function &#8211; it&#8217;s pretty simple:<br />
<code><br />
image = Magick::Image.new(width+bordsize, height+bordsize) { self.background_color = 'transparent'}<br />
gc = Magick::Draw.new<br />
gc.stroke(bordercolour)<br />
gc.stroke_linecap('round')<br />
gc.stroke_width(bordsize)<br />
gc.fill(innercolour)<br />
gc.roundrectangle(bordsize/2,bordsize/2, width,height, radius, radius)<br />
gc.draw(image)<br />
</code><br />
This basically draws a round rectangle, with a solid border, at offset (bordsize/2,bordsize/2) &#8211; you need the offset as otherwise the border edge will overlap the edge of the image.<br />
Adding a shadow is pretty simple, rmagick has the <a href="http://studio.imagemagick.org/RMagick/doc/image3.html#shadow" target="_blank">shadow</a> method:<br />
<code><br />
shadow = image.shadow()<br />
image = shadow.composite(image,Magick::NorthWestGravity, Magick::OverCompositeOp)<br />
</code><br />
- this basically works, but sadly you get issues when you save the image.  The shadow() method creates a shadow of a given image, with a specified shadow radius, which results in a larger image than the original &#8211; so the code above gives you an image which has shadow pixels at negative x and y coordinates!  Some file formats cope with this just fine &#8211; png files seem to do ok &#8211; but I wanted gif images for ie6 fallback, and gif doesn&#8217;t cope at all.</p>
<p>Anyway, to cut a long post short, the final code (see below) has a fair bit of logic for allocating extra space in the image for the shadow.  An example of the basic image-with-border-and-shadow image:</p>
<p><img src="http://www.sietsma.com/korny/corner_test/simple_shadow.png" alt="simple shadow sample" height="123" width="123" /></p>
<p>One last tweak I did &#8211; I wanted to have a shadow on the borders &#8211; the dark border is nice for some effects, but I wanted a slightly more 3d look:<br />
<img src="http://www.sietsma.com/korny/corner_test/dark_border.png" alt="sample with dark border" height="123" width="123" /><br />
And a more subtle effect with the raised border the same colour as the diagram:<br />
<img src="http://www.sietsma.com/korny/corner_test/shad_border.png" alt="sample with light border" height="123" width="123" /></p>
<p>I also added some logic to set transparency of the image, for pretty backgrounds like the example from my last blog post (sample page <a href="http://www.sietsma.com/korny/corner_test/test2.html">here</a>) &#8211; and to automatically save a gif version for ie6 display.</p>
<p>The full code can be seen at <a href="http://www.sietsma.com/korny/corner_test/corners_code.html" target="_blank">http://www.sietsma.com/korny/corner_test/corners_code.html </a></p>
<p>Obviously there&#8217;s lots of other things you can do &#8211; but this is a useful start!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2008/07/20/curved-corner-images-in-rmagick/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Sigh &#8211; ie6 defeats my cunningness</title>
		<link>http://www.trouble.net.au/blog/korny/2008/07/06/sigh-ie6-defeats-my-cunningness/</link>
		<comments>http://www.trouble.net.au/blog/korny/2008/07/06/sigh-ie6-defeats-my-cunningness/#comments</comments>
		<pubDate>Sat, 05 Jul 2008 23:47:28 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.sietsma.com/2008/07/06/sigh-ie6-defeats-my-cunningness/</guid>
		<description><![CDATA[Well, my previous post and linked page seems to work nicely.  (I did fix a minor glitch in ie7)
However, since then I&#8217;ve done some more reading &#8211; and my conclusion is, don&#8217;t do what I did &#8211; ie6 png transparency is just too risky.  Have a read of the discussion at http://blogs.cozi.com/tech/2008/03/transparent-png.html?cid=106552420 &#8211; [...]]]></description>
			<content:encoded><![CDATA[<p>Well, my previous post and linked page seems to work nicely.  (I did fix a minor glitch in ie7)</p>
<p>However, since then I&#8217;ve done some more reading &#8211; and my conclusion is, don&#8217;t do what I did &#8211; ie6 png transparency is just too risky.  Have a read of the discussion at <a href="http://blogs.cozi.com/tech/2008/03/transparent-png.html?cid=106552420" target="_blank">http://blogs.cozi.com/tech/2008/03/transparent-png.html?cid=106552420</a> &#8211; they found that when used heavily, the AlphaImageLoader filter causes ie6 to deadlock.  Ow.  If it&#8217;s critical, you can work around this, but it sounds really really painful.</p>
<p>So I&#8217;ve reverted to using something that is basically Scott Schiller&#8217;s technique &#8211; see  <a href="http://www.sietsma.com/korny/corner_test/test2.html" target="_blank">http://www.sietsma.com/korny/corner_test/test2.html</a> &#8211; but on ie6, you get no translucency.</p>
<p>The good news is, I can now use a single image, instead of slicing the image up.  And I&#8217;ve fiddled my rmagick rounded-corner generation code, it&#8217;s now getting pretty nice &#8211; I&#8217;ll post it when it&#8217;s done.  Not as nice as what you can do if you&#8217;re a photoshop/gimp guru, but it&#8217;s nice to have something generated by code, so I can change colours/sizes at will.</p>
<p>The next step is probably to make this stuff javascriptable, so I can use a single div for a minimal styling version, and then prettify it in javascript&#8230; possibly.  I&#8217;m still a bit unsure if using javascript makes the code more &#8217;semantic&#8217; or not.</p>
<p>[ed: sigh - I thought I'd posted this weeks ago.  Somehow, it got saved as a draft...]</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2008/07/06/sigh-ie6-defeats-my-cunningness/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Curved Corners with transparent png background on IE6</title>
		<link>http://www.trouble.net.au/blog/korny/2008/06/11/curved-corners-with-transparent-png-background-on-ie6/</link>
		<comments>http://www.trouble.net.au/blog/korny/2008/06/11/curved-corners-with-transparent-png-background-on-ie6/#comments</comments>
		<pubDate>Wed, 11 Jun 2008 11:10:13 +0000</pubDate>
		<dc:creator>korny</dc:creator>
				<category><![CDATA[Uncategorized]]></category>

		<guid isPermaLink="false">http://blog.sietsma.com/2008/06/11/curved-corners-with-transparent-png-background-on-ie6/</guid>
		<description><![CDATA[I&#8217;ve been playing in the wonderful world of css &#8211; partly for work, and partly for fun.
I wanted to have fluid sized, png-based, rounded-corner dialogs for a web site.  This is something that has been solved many times, but not (as far as I can see) for internet explorer 6 &#8211; which still has [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been playing in the wonderful world of css &#8211; partly for work, and partly for fun.</p>
<p>I wanted to have fluid sized, png-based, rounded-corner dialogs for a web site.  This is something that has been solved many times, but not (as far as I can see) for internet explorer 6 &#8211; which still has a big share of the browser market.</p>
<p>The best solution I&#8217;ve found for other browsers is the one by Scott Schiller at Schillmania &#8211; <a href="http://www.schillmania.com/content/projects/even-more-rounded-corners/">http://www.schillmania.com/content/projects/even-more-rounded-corners/</a> &#8211; which uses a single image, with cunning sprite tricks to get it into position.  But on ie6, it has to revert to gif images.</p>
<p>The problem is, you <strong>can</strong> use pngs on ie6 &#8211; most simply, by using the iepngfix.htc behaviour trick (see <a href="http://www.twinhelix.com/css/iepngfix/">http://www.twinhelix.com/css/iepngfix/</a>) &#8211; but it has one big problem &#8211; you can&#8217;t align the png images to the right or bottom, so most of the usual tricks used to display corners don&#8217;t work.</p>
<p>So, I&#8217;ve been hacking around with ways to do corners where all the images are top-left aligned.  I have a solution, I&#8217;m not sure it&#8217;s perfect, but it works &#8211; see the demo page at <a href="http://www.sietsma.com/korny/corner_test/test.html">http://www.sietsma.com/korny/corner_test/test.html</a>.  This works in ie6, ie7, Firefox 2 and 3, and Safari.</p>
<p>Some caveats:</p>
<p>- it uses the iepngfix.htc behaviour &#8211; I experimented with using the Microsoft AlphaImageLoader stuff directly, but iepngfix does some tricks that I can&#8217;t reproduce easily, so for now I&#8217;m using the code directly.</p>
<p>- it&#8217;s a long way from semantic markup; there are a number of divs in there just to make it look right.  I can&#8217;t see a way around this &#8211; I could make some javascript to turn a semantically-nice html page into the mess shown, which might be a good idea &#8211; but the end result would be the same (and doing it in javascript can make for ugly corner loading, and hassles re-running the javascript if you populate stuff via ajax).  Anyone with hints on how to make this more web-standards-friendly, please tell!  I&#8217;m still pretty new to the world of front-end stuff.</p>
<p>- there&#8217;s a bit of fiddling to make links work properly &#8211; the ie png stuff causes screwups if you use relative or absolute positioning on a div with a background image, and then add links to it.  So I had to mess around a bit to work around this &#8211; and I probably need to mess around more to make it cleaner.</p>
<p>- there are some ie-specific bits in there, added via conditional comments; at least one of them was pure trial-and-error to get things lining up right!</p>
<p>- there is probably more work needed to make padding and things consistent across browsers.</p>
<p>I&#8217;ll try to blog more later about the details, and about how I generated the images&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.trouble.net.au/blog/korny/2008/06/11/curved-corners-with-transparent-png-background-on-ie6/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
