<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-3046883935487020939</id><updated>2011-06-11T13:02:47.161+02:00</updated><category term='ruby'/><category term='nant'/><category term='clickonce'/><category term='mentoring'/><category term='regular expressions'/><category term='polyphasic'/><category term='msbuild'/><category term='open-source'/><category term='rant'/><category term='coding convention'/><title type='text'>Pragmatism Not Idealism</title><subtitle type='html'>Comments on agile software development and whatever else strikes my fancy.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>11</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-2268478620844918775</id><published>2008-03-02T22:53:00.004+02:00</published><updated>2008-03-02T23:07:34.275+02:00</updated><title type='text'>Cry, The Beloved Country</title><content type='html'>With apologies to MJK. If you don't know where this is comes from walk - no - run! to your nearest CD store and buy &lt;i&gt;&lt;a href="http://en.wikipedia.org/wiki/%C3%86nima" title="Ænima"&gt;Ænima&lt;/a&gt; &lt;/i&gt;by Tool.

&lt;blockquote&gt;Some say the end is near&lt;br&gt;
Some say we'll see armageddon soon&lt;br&gt;
I certainly hope we will&lt;br&gt;
I sure could use a vacation from this&lt;br&gt;
&lt;br&gt;
Bullshit three ring circus side show of&lt;br&gt;
Freaks here in this hopeless fucking hole we call SA&lt;br&gt;
The only way to fix it is to flush it all away&lt;br&gt;
Any fucking time, any fucking day&lt;br&gt;
Learn to swim, I'll see you down in Drakensberg Bay&lt;br&gt;
&lt;br&gt;
Fret for your parents and&lt;br&gt;
Fret for your children and&lt;br&gt;
Fret for the crime rate and&lt;br&gt;
Fret for the hijack and&lt;br&gt;
Fret for the rapist and&lt;br&gt;
Fret for the power and&lt;br&gt;
Fret for your petrol and&lt;br&gt;
Fret for your life it's a&lt;br&gt;
&lt;br&gt;
Bullshit three ring circus side show of&lt;br&gt;
Freaks here in this hopeless fucking hole we call SA&lt;br&gt;
The only way to fix it is to flush it all away&lt;br&gt;
Any fucking time, any fucking day&lt;br&gt;
Learn to swim, I'll see you down in Drakensberg Bay&lt;br&gt;
&lt;br&gt;
Some say a comet will fall from the sky&lt;br&gt;
Followed by meteor showers and tidal waves&lt;br&gt;
Followed by faultlines that cannot sit still&lt;br&gt;
Followed by millions of dumbfounded dipshits&lt;br&gt;
&lt;br&gt;
Some say the end is near&lt;br&gt;
Some say we'll see armageddon soon&lt;br&gt;
I certainly hope we will cuz&lt;br&gt;
I sure could use a vacation from this&lt;br&gt;
&lt;br&gt;
Stupid shit, silly shit, stupid shit...&lt;br&gt;
&lt;br&gt;
One great big festering rainbow distraction&lt;br&gt;
I've a suggestion to keep you all occupied&lt;br&gt;
&lt;br&gt;
Learn to swim.&lt;br&gt;
Learn to swim.&lt;br&gt;
Learn to swim.&lt;br&gt;
&lt;br&gt;
Mom is going to fix it all soon&lt;br&gt;
Mom is coming round to put it back&lt;br&gt;
the way it oughtta be...&lt;br&gt;
&lt;br&gt;
Learn to swim.&lt;br&gt;
&lt;br&gt;
Fuck Jacob Zuma and&lt;br&gt;
Fuck all his clones&lt;br&gt;
Fuck all these gun-toting&lt;br&gt;
Hip gangster ministers&lt;br&gt;
&lt;br&gt;
Learn to swim.&lt;br&gt;
&lt;br&gt;
Fuck your load-shedding and&lt;br&gt;
Fuck BEE&lt;br&gt;
Fuck all you junkies and&lt;br&gt;
Fuck HIV&lt;br&gt;
&lt;br&gt;
Learn to swim.&lt;br&gt;
&lt;br&gt;
Fuck smiley glad-hands&lt;br&gt;
With hidden agendas&lt;br&gt;
Fuck this dysfunctional&lt;br&gt;
Insecure government&lt;br&gt;
&lt;br&gt;
Learn to swim.&lt;br&gt;
&lt;br&gt;
Cuz I'm praying for rain&lt;br&gt;
And I'm praying for tidal waves&lt;br&gt;
I wanna see the ground give way&lt;br&gt;
I wanna watch it all go down&lt;br&gt;
Mom please flush it all away&lt;br&gt;
I wanna see it go right in and down&lt;br&gt;
I wanna watch it go right in&lt;br&gt;
Watch you flush it all away&lt;br&gt;
&lt;br&gt;
Time to bring it down again&lt;br&gt;
Don't just call me pessimist&lt;br&gt;
Try and read between the lines&lt;br&gt;
&lt;br&gt;
I can't imagine why you wouldn't&lt;br&gt;
Welcome any change, my friend&lt;br&gt;
&lt;br&gt;
I wanna see it come down&lt;br&gt;
Suck it down&lt;br&gt;
Flush it down&lt;br&gt;
&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-2268478620844918775?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/2268478620844918775/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=2268478620844918775' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/2268478620844918775'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/2268478620844918775'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2008/03/cry-beloved-country.html' title='Cry, The Beloved Country'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-7775851033032399555</id><published>2007-12-22T07:47:00.001+02:00</published><updated>2007-12-22T07:47:19.210+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='polyphasic'/><title type='text'>Can I quit sleeping like I quit coffee and alcohol?</title><content type='html'>&lt;p&gt;In an ideal world there would be no sleep. Everybody would be awake all of the time - we would be like gods - surely if god knows everything you do then he hasn't got time for sleep, unless his name is also Bruce and he gets a daily summary in his inbox.&lt;/p&gt; &lt;p&gt;All of you people (all maybe 1 or 2 of you) are probably sitting there thinking to yourselves "Hey, I like my sleep. I wouldn't trade it in for anything". But you're missing the point. In the utopia I'm describing, you wouldn't have any of those feelings of emptiness at the loss of something vital - something that you've always had, because it would never have been there to begin with.&lt;/p&gt; &lt;p&gt;I'll give you an example. In 20 years or so your kids won't be able to imagine a time when it wasn't possible to communicate with a friend in under 30 seconds, no matter where in the world that friend is. Your kids will grow up in a world where cellphones and the internet are ubiquitous, and actually probably obsolete.&lt;/p&gt; &lt;p&gt;My mom likes to tell stories about how in those days they used to listen to test match cricket (no Twenty20 back then, or even ODI's) on the radio, while building jigsaw puzzles. There was no television back then. She still remembers the test picture that was broadcast in the months leading up to actual &lt;strong&gt;programs&lt;/strong&gt; being launched. They used to watch the test picture! My gran remembers this too.&lt;/p&gt; &lt;p&gt;For you and I television is a basic human right. It's something that's been there ever since we first started yelling our opinions into this world, and it's not going away any time soon. For the older generations this isn't true, and this difference in opinions is blatantly obvious to me. My gran, for example, cannot bear the thought of the television not being on, regardless of the time of day or the quality of the programming.&lt;/p&gt; &lt;p&gt;I suspect this stems from remembering a worse time - a time when you didn't have a choice as to whether or not you wanted to watch TV. Either there was no TV, or it was prohibitively expensive, and in the latter case you probably buckled down, worked hard and saved up enough money to buy a set and the ridiculous "license" that goes with it, so you could enjoy your basic human rights.&lt;/p&gt; &lt;p&gt;As a quick aside, I wonder if the Americans were to re-examine their constitution and tack on a few more amendments if they majority of them might not choose to scratch out "the right to bear arms" and replace it with "the right to watch TV"? Looking at their average physique I think they might, despite all those NRA crackpots. I wonder what would happen if you asked them to vote on it - if they could only have one of those phrases in their constitution - which one would come out. I guess one of the phrases would end up with more votes and they'd end up putting the other phrase into the amendment.&lt;/p&gt; &lt;p&gt;Now, far be it for me to stand here and point fingers without examining my own tendencies. I'm no different in mechanics when it comes to this sort of behaviour - it's just the details that vary. For me, I personally get a mental equivalent of "the shakes" if I'm away from an internet connection for more than a day or two. It's the same thing. I can remember a time when I didn't have an internet connection. I can also remember a time when I did have one, but it was prohibitively expensive. Finally it's gotten to the point where it's still prohibitively expensive, at least &lt;a href="http://www.telkom.co.za"&gt;where I live&lt;/a&gt;, but I fork out for it anyway, because now I can't imagine living without it.&lt;/p&gt; &lt;p&gt;But anyway, I digress slightly. Back to that ideal world. As always, there's no such thing as Utopia, so I guess we'll just have to find a way to hack what we have got a little bit. In the real world, most people sleep between 6 and 9 hours a day - or rather a night. They do this in one long phase and are therefore said to have a monophasic sleeping pattern. These people are, by and large, content with this status quo - they never question whether there's any other way to structure their sleep.&lt;/p&gt; &lt;p&gt;However, thanks to the &lt;a href="http://www.stevepavlina.com/blog/2005/10/polyphasic-sleep/"&gt;glorious age of the internet&lt;/a&gt;, a few square-peg-in-a-round-hole kind of people have experimented with other sleeping schedules, including &lt;a href="http://en.wikipedia.org/wiki/Polyphasic_sleep"&gt;polyphasic sleep&lt;/a&gt;. These people have found that it's possible to function normally (or even better than "normally") on as little as 2 or 3 hours of sleep a day, spread out over 20-25 minute naps every four to six hours, depending on the details of the actual schedule.&lt;/p&gt; &lt;p&gt;To cut a long story short, I first discovered this idea over 2 years ago, and decided I'd like to give it a try. Unfortunately, at the time I was less than a year into my first job out of university, and this is the sort of thing that takes a month or so to adjust to, so I haven't had an opportunity to experiment until now. I've saved up basically all my leave over the last 3 years and now, over the December holiday period, I'm going to try and go polyphasic.&lt;/p&gt; &lt;p&gt;I think I'll try and post occasional updates here to journal my progress, but I don't really want to do the whole cliched blog-your-polyphasic-experiment thing. With that out of the way, let me launch straight into a status report (there's nothing like a little hypocrisy on zero sleep) : &lt;/p&gt; &lt;ul&gt; &lt;li&gt;I'm now starting what I consider to be day 2 - it's almost 8 AM and I haven't been to bed since I woke up almost 24 hours ago. I did have a 20 minute nap around 5:20 this morning, but I don't think it was very restful. Surprisingly, I didn't struggle to wake up after it, but that may partly be down to a cool mp3 I found which I think will help me drop off more easily, and will then also wake me up after the right amount of time.&lt;/li&gt; &lt;li&gt;I did sort of ease into the experiment by staying up late (3 AM) the previous 2 days before my all-nighter, and waking up relatively early (9 AM, which is my usual waking time) - so I have been running short on sleep for a couple of days already.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;For now, I have nothing more to report, so I'm going to wrap this entry up, because it's time to try and take a nap again. Coincidentally, I think I'm going to try naps at 4, 8 and 12 AM and PM - so feel free to not phone or sms me at these times - at least until I get an alarm mp3 player that isn't my cellphone :). Enjoy hibernating.&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-7775851033032399555?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/7775851033032399555/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=7775851033032399555' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/7775851033032399555'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/7775851033032399555'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/12/can-i-quit-sleeping-like-i-quit-coffee.html' title='Can I quit sleeping like I quit coffee and alcohol?'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-5588764754416256834</id><published>2007-11-30T23:50:00.001+02:00</published><updated>2007-11-30T23:50:44.772+02:00</updated><title type='text'>Experimenting with IronRuby</title><content type='html'>&lt;p&gt;In an ideal world, the last 2 months would not have blown me away in a hurricane of work and study, and I would have had some time to play with IronRuby. Unfortunately as you by now know, I don't live in an ideal world. :(  &lt;p&gt;Anyway, enough feeling sorry for myself and on with the experimentation. I &lt;a href="http://pragmatism-not-idealism.blogspot.com/2007/09/contributing-to-ironruby.html"&gt;resumed&lt;/a&gt; tonight (after updating my working copy from the server) in my efforts to get some simple http programming going in IronRuby. I've spent the entire week in Juval Lowy's WCF Masterclass and although http is just the tip of the iceberg in terms of WCF, watching an expert in action has inspired me somewhat. A large part of this inspiration stems from the absolutely phenomenal amount of extremely high-quality material (including free source code!!) that Juval makes available not only to attendees, but to the public in general at &lt;a href="www.idesign.net"&gt;IDesign&lt;/a&gt;.  &lt;p&gt;So, first things first, I took a quick look in the file rss.rb in my oddly named "projects\rss" folder to try and remember what I'd been working on the last time. Inside I found the following:  &lt;p&gt;&lt;pre&gt;&lt;br&gt;require 'net/http' 
&lt;p&gt;h = Net::HTTP.new('www.pragmaticprogrammer.com', 80)&lt;br&gt;resp, data = h.get('/index.html', nil)&lt;br&gt;if resp.message == "OK"&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; data.scan(/&amp;lt;img src="(.*?)"/) { |x| puts x }&lt;br&gt;end&lt;br&gt;&lt;/p&gt;&lt;/pre&gt;
&lt;p&gt;Oh yes, that's right! I'd pulled this piece of code almost directly (maybe directly?) out of Ruby's Pickaxe book - the .chm version which installs with MRI. This was supposed to be my glorious start on the road to understand the inner workings of RSS - hence the (for now) irrelevant folder and file names. 
&lt;p&gt;I remember I was trying to get it to run under IronRuby and I also remember that the IronRuby parser had some trouble with parsing the net/http file. At the time, I spent about 3 hours tracing through the debugger just trying to figure out which line in net/http the problem was on and eventually I narrowed it down to a couple of methods with the (for me) unusual syntax of: 
&lt;p&gt;&lt;pre&gt;&lt;br&gt;...&lt;br&gt;variable = dictionary_lookup["key"] or return nil&lt;br&gt;...&lt;/pre&gt;
&lt;p&gt;It turned out that at the time, the parser couldn't handle this (the &lt;em&gt;or return nil&lt;/em&gt; part), so I dutifully wrote a test case which reproduced the problem without requiring all 2300 lines of net/http.rb: 
&lt;p&gt;&lt;pre&gt;&lt;br&gt;def fun(input)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; a = input == 'test' or return nil&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; return a&lt;br&gt;end&lt;br&gt;puts fun('not_test')&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, at this point I had no idea where to even begin to look to sort this bug out, so I simply submitted the test case to the mailing list, got snowed under at work and forgot about the bug. 
&lt;p&gt;In between, a few months have passed and I remember seeing an email on the list a few weeks ago saying that this problem had been resolved, so tonight I decided to try my luck again. After spending half an hour or so trying to puzzle my way through the solution configuration (&lt;em&gt;rake compile config=release&lt;/em&gt; is no good if you want to step through the debugger) I eventually managed to get the interactive console running in the debugger. A quick &lt;em&gt;require 'net/http'&lt;/em&gt; later and I was once again up to my elbows in exceptions: 
&lt;p&gt;&lt;pre&gt;&lt;br&gt;System.NotImplementedException: Statement transformation not implemented: WhileLoopStatement&lt;br&gt;&amp;nbsp; at Ruby.Compiler.Ast.Statement.Transform(AstGenerator gen) in D:\Projects\IronRuby\trunk\src\ironruby\Compiler\Ast\Statements\Statement.cs:line 32&lt;br&gt;&amp;nbsp; ...&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;Unfortunately, I'd rolled back all the code that I'd put in to print the lines of ruby code as they were being parsed to track down the parser errors (so that I didn't get conflicts while updating from the repository), so again it took a little fiddling with the debugger to puzzle out exactly where the problem was. Apparently my previous bug has been sorted out, but that's just allowed the compiler to progress a little further through the file before stumbling on something else. It looks like the code for while loop statements (not expressions) hasn't been written yet, and that's causing the compiler to throw a NotImplementedException while trying to understand the request method on class HTTP which has the following code: 
&lt;p&gt;&lt;pre&gt;&lt;br&gt;def request(...)&lt;br&gt;...&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; begin&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; res = HTTPResponse.read_new(@socket)&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; end while res.kind_of?(HTTPContinue)&lt;br&gt;...&lt;br&gt;end&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;It looks like the code is throwing an exception in the base class (there's no overriding implementation of Transform in WhileLoopStatement yet), so my first shot at getting past this problem was just to override the method and return null. That seemed to help a little, because I no longer saw the same exception, but it was just the illusion of progress because a short while later I got an assertion about an object that cannot be null. 
&lt;p&gt;A little bit of searching through the solution led me to the WhileLoop&lt;strong&gt;Expression&lt;/strong&gt; class in Ruby/Compiler/Ast/Expressions. This class already has an implementation in it, so I figured I'd try and just copy/paste (shudder) it into the WhileLoop&lt;strong&gt;Statement&lt;/strong&gt; class and see what happened. After resolving a few compiler issues (I had to instantiate a List&amp;lt;Statement&amp;gt; object and put the single Statement object that I had into the list before passing this list through to gen.TransformStatements) I was able to compile and start the interactive console up again. Another quick &lt;i&gt;require 'net/http'&lt;/i&gt; and I seem to be past the issue, but now I get: 
&lt;p&gt;&lt;pre&gt;&lt;br&gt;System.NotImplementedException: TODO&lt;br&gt;&amp;nbsp; at Ruby.Compiler.Ast.SuperCall.TransformRead(AstGenerator gen) in D:\Projects\IronRuby\trunk\src\ironruby\Compiler\Ast\Expressions\SuperCall.cs:line 42&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;Hmm, it seems something in the file is trying to call the superclass's implementation of whatever it's overriding. Two minutes looking into SuperCall.cs and I realize that I'm not going to get so lucky with this one. I don't know enough about the internal workings of the compiler (or ruby itself for that matter), so I try a different tack. I open http.rb and I search through it for the word "super". It turns out that it's only called from 2 places in the file, and it doesn't look like the compiler will break if I just comment those two lines out. (Note: At this point I've made a &lt;strong&gt;copy&lt;/strong&gt; of net/http.rb in case I ever actually want to use it for anything - the copy sits in a net folder inside of where Visual Studio builds my interactive console to). 
&lt;p&gt;Back to rbx and I type my single line of ruby once again and... Success! Well, sort of. It looks like the compiler is done with net/http - it's managed to get all the way through a 2300 line ruby library - not bad for pre-alpha software. Unfortunately, net/http doesn't live in a bubble - it requires net/protocol, which I haven't yet copied to my working directory. This causes rbx to throw an exception: 
&lt;p&gt;&lt;pre&gt;&lt;br&gt;Ruby.Runtime.LoadError: Could not load file or assembly 'net\/protocol' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047) ---&amp;gt; System.IO.FileLoadException: Could not load file or assembly 'net\/protocol' or one of its dependencies. The given assembly name or codebase was invalid. (Exception from HRESULT: 0x80131047)&lt;br&gt;File name: 'net\/protocol'&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;Apart from the HRESULT (COM -&amp;gt; *shudder*) this seems like a fairly easy problem to solve. I copy protocol.rb from the same place I got http.rb and try again, and now I'm met with the exception: 
&lt;p&gt;&lt;pre&gt;&lt;br&gt;Ruby.Runtime.LoadError: Could not load file or assembly 'socket' or one of its dependencies. The system cannot find the file specified. ---&amp;gt; System.IO.FileNotFoundException: Could not load file or assembly 'socket' or one of its dependencies. The system cannot find the file specified.&lt;br&gt;File name: 'socket'&lt;br&gt;&lt;/pre&gt;
&lt;p&gt;"This is going to be easy" I think to myself as I alt-tab back to the ruby libraries folder. "What's taking Microsoft so long?" I think to myself as I start to search for socket.rb. Unfortunately this thought is cut short when I realize that there doesn't seem to be a socket.rb file anywhere in my ruby libraries folder. Fearing the worst, and already regretting my hasty MS-bashing, I widen my search to just look for files with &lt;i&gt;socket&lt;/i&gt; in them. This turns up socket.c =&amp;gt; uh-oh. It looks like the socket library hasn't been implemented in ruby itself but rather in C (for reasons of speed, cross-platform...ity and the convenience of working closer to the hardware I presume). "So this is what they've been talking about on the list - re-implementing the native C libraries" I mutter. 
&lt;p&gt;After giving it some thought while writing this article, it looks like my RSS reader is going to have to wait for a while - at least until somebody adds enough socket support to IronRuby to allow me to do some http communication. Unfortunately, the only code I've been able to add so far is the copy/pasting I did in WhileLoopStatement, and from what I understand, Microsoft is not taking community contributions to the compiler - only to the libraries. 
&lt;p&gt;I guess I'll fire off an email to the list anyway with the code in it, in case it is actually correct and they do actually accept it, and even if they don't perhaps I can get some feedback as to what I should have done differently so that I'll know better for next time. In the mean time, I guess I can get started implementing some of the socket functionality - it doesn't look like anybody else has offered to tackle this yet. At least there, I've got more chance of having my work accepted, and it also seems like a smaller chunk to bite off than an entire http library. 
&lt;p&gt;Wish me luck!&lt;/p&gt;  &lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-5588764754416256834?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/5588764754416256834/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=5588764754416256834' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/5588764754416256834'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/5588764754416256834'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/11/experimenting-with-ironruby.html' title='Experimenting with IronRuby'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-6598606907047844249</id><published>2007-11-03T12:29:00.001+02:00</published><updated>2007-11-03T12:29:59.686+02:00</updated><title type='text'>Does this make me a racist or am I just reporting the facts?</title><content type='html'>&lt;p&gt;&lt;/p&gt; &lt;p&gt;In an ideal world, I'd have the time (and drive) to do the following experiment. I'd like to sit near the intersection where I used to get onto the highway on my way home from work with a pair of binoculars and a notebook and tally up the number of idiot drivers passing through the intersection in one afternoon's rush-hour traffic according to the race of the driver.  &lt;p&gt;I'm convinced, based on the empirical evidence that I have collected through observation from driving this route for over two years, that well over 90% of the people who cut into the front of the queue turning right across traffic to get onto the onramp are black people. At this point in the story, any South African will immediately assume that I mean minibus taxi drivers, who in Johannesburg are overwhelmingly black and uniformly reckless - but they're just the tip of the iceberg here. Yes, every single one of them - without exception - follows this practice, but there are many other types of vehicle drivers (especially drivers of large trucks) that also believe that they are special enough to warrant jumping to the front of the queue. Does making this observation make me a racist?  &lt;p&gt;Another example: Earlier this week I was driving home late from work (around 19:00), when I hit a sudden buildup of stationary cars on the highway - the results of an accident up ahead. I was in the left-most lane crawling down the freeway when I started to notice a rush of cars next to me, in the emergency lane. As far as I'm aware, it's illegal to drive in the emergency lane unless you're in an emergency vehicle (and even if it's not illegal it is extremely rude)! Again I wondered how there could be so many people who had more important things to do than me, so I started to watch them, and the demographics were the same - only by this time of the evening most of the taxis and trucks are off the road, so the (almost exclusively) black drivers who were breaking the law were driving passenger vehicles of all make and manufacture - obviously working class people on the way home from work like myself, but who clearly need to get home faster to hasten a cure for AIDS or cancer or something.  &lt;p&gt;I don't mean to imply the inverse of my observation is necessarily true - that almost all drivers who are misbehaving are black does not imply that I think that all black drivers are misbehaving - there are plenty of black drivers who are courteous and well-mannered on the roads of Johannesburg. Similarly, it must not be assumed that with &lt;a href="http://en.wikipedia.org/wiki/Johannesburg#Demographics"&gt;73% of Johannesburg's population being black and only 16% being white&lt;/a&gt;&amp;nbsp;that those percentages correlate to the make-up of the average Johannesburg driver.  &lt;p&gt;I don't have any hard figures to backup claims of the racial distribution of drivers in &lt;a href="http://en.wikipedia.org/wiki/Gauteng"&gt;Gauteng&lt;/a&gt;, but I would estimate the figure to be somewhere in the range of only 20% - 40% of drivers being black, and I'd attribute this largely to the thriving minibus taxi and bus industries,&amp;nbsp;which are largely used by black people and which together form the bulk of public transport in the city. Also, given that we're only 13 odd years into the new democracy, I would assume that not every black person has the means yet to own his own car (and the money to power it with ridiculously expensive fuel). The South African government is hoping that &lt;a href="http://en.wikipedia.org/wiki/Black_economic_empowerment"&gt;BEE&lt;/a&gt; will act to correct this imbalance sooner rather than later.  &lt;p&gt;I have a couple of theories as to why I'm seeing this behaviour, but I have no doubt that they're based largely on misconceptions, so take them with a grain of salt. Disclaimer: I'm a computer scientist, not a social scientist - so judge for yourself, and if you have any better ideas please enlighten me.  &lt;p&gt;Firstly, let me say that I think it's a cultural thing. I don't know the first thing about any of the cultures of any of the black peoples of Southern Africa, but what I do know is that in a lot of instances their thinking is radically different to mine. Perhaps they go home at night and don't lie awake in bed thinking about all those people who are still stuck on the highway.  &lt;p&gt;Secondly, I think that traffic law enforcement (or rather the lack of it) in the townships where I guess a lot of these people must live (or must have lived before 1994) may be setting a bad example. From the few black people that I interact with, I've heard that the traffic violations in suburban Johannesburg that I complain about are nothing compared to the lawlessness that can be seen in the townships. Perhaps some of these people choose not to differentiate between law-abiding areas and the wild west, and I think this is the fault of the Johannesburg Metro Police Department (JMPD).  &lt;p&gt;I think that all it would take to fix this problem would be more security cameras at intersections&amp;nbsp;(to catch red-light jumpers), more roadblocks to fine those people who don't have any license plates, and a JMPD unit dispatched to any highway accident scene to fine people caught driving in the emergency lane. Certainly this is economically feasible, as the JMPD apparently managed to generate &lt;a href="http://www.joburg.org.za/help/traffic_fines.stm"&gt;a massive R644 million in a one year period&lt;/a&gt; from collecting traffic fines. In fact, there is already public outcry that the traffic department is just trapping and fining to make cash - but I say bring it on! If you don't want to fork over your cash, don't break the traffic&amp;nbsp;laws. If you don't like them, open your mouth and say so! You could even vote&amp;nbsp;if you think that will help!  &lt;p&gt;So what should the JMPD do with all of that money? My suggestions: Plough it into the &lt;a href="http://www.raf.co.za/"&gt;Road Accident Fund&lt;/a&gt;, use it to buy more equipment and train more traffic police, use it to reduce insurance premiums and use it for road and highway maintenance, although I must say&amp;nbsp;the last is&amp;nbsp;one thing that the South African government definitely seems to be getting 100% right already!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-6598606907047844249?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/6598606907047844249/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=6598606907047844249' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/6598606907047844249'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/6598606907047844249'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/11/does-this-make-me-racist-or-am-i-just.html' title='Does this make me a racist or am I just reporting the facts?'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-6389189649681827379</id><published>2007-10-09T18:11:00.001+02:00</published><updated>2007-10-09T18:11:38.897+02:00</updated><title type='text'>Sweeping comments under the rug</title><content type='html'>&lt;p&gt;In an ideal world, you would never have to deal with source code where there are more lines of code commented out than there&amp;nbsp;are actually being compiled (I'm not talking about informational comments here, I'm talking about commented out lines of code). The harsh reality however, is that there is at least one such source tree in exactly this condition, and it's the one that I've been trying (&lt;strong&gt;not&lt;/strong&gt; single-handedly) to replace for the last three years.&lt;/p&gt; &lt;p&gt;As far as I can see, a code base can only get into a state like this if the following are true:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;em&gt;&lt;strong&gt;Poor or no&amp;nbsp;source control&lt;/strong&gt;&lt;/em&gt;. We need to change or remove this code, but we're not sure if it's going to work, so rather than delete it and have to re-code it again later, we're going to just comment it out until we're sure the new solution works. (Yeah, right). The right answer is to just delete the code you're not executing, because there's always undo, and for longer-term undo's the code&amp;nbsp;is still in your repository and you can always go back and get it out from there.&lt;/li&gt; &lt;li&gt;&lt;strong&gt;&lt;em&gt;A culture of ugly source code&lt;/em&gt;&lt;/strong&gt;. &lt;a href="http://www.pragmaticprogrammer.com/ppbook/extracts/no_broken_windows.html"&gt;Broken windows&lt;/a&gt;, &lt;a href="http://en.wikipedia.org/wiki/Mise_en_place"&gt;mise en place&lt;/a&gt;&amp;nbsp;or entropy, call it what you will, but bad code begets much worse code. Keeping code clean is a constant uphill battle against multiple coding conventions, dead or commented-out code, poor design choices, magic numbers and any number of other code smells, but more than anything I think it keeps technical debt low and productivity high.&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;So now we've examined the problem, let's consider two potential&amp;nbsp;solutions.&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Go into your editor's options page and alter the colour scheme so that comments show up as a very light gray over a white background, making them &lt;font color="#c0c0c0"&gt;almost invisible&lt;/font&gt;, unless the text is highlighted. I've got a coworker who does this, and it seems like completely the wrong "solution" to the problem. It doesn't benefit anyone else, and it means in some cases that he's only got&amp;nbsp;7 or&amp;nbsp;8 lines of usable space onscreen at a time. Any&amp;nbsp;useful comments he may actually want to put into the code will either require him to strain his eyes, rely on guesswork or very accurate typing, or will require him to select the text after typing it to see that what he typed is what he got. &lt;strong&gt;Don't do this&lt;/strong&gt;. It's clever, but not smart.&lt;/li&gt; &lt;li&gt;&lt;a href="http://www.hrwiki.org/index.php/unused_emails"&gt;&lt;font face="Courier"&gt;DELETED!!&lt;/font&gt;&lt;/a&gt;. &lt;strong&gt;Do this&lt;/strong&gt;. Delete all the unused source code you can (especially if it was active at some point in the past&amp;nbsp;in the repository), and also delete as many "helpful" comments as is pragmatically possible. If something is unclear, refactor it to make it clearer, don't try to make excuses for your first attempt at banging the code out. Nobody's perfect, but what differentiates readable code from unreadable code is that the programmer who wrote the readable code first wrote unreadable code, realized how confusing it was, and then refactored it. The other programmer didn't realize (in which case he is either naïve or &lt;a href="http://www.acmqueue.com/modules.php?name=Content&amp;amp;pa=showpage&amp;amp;pid=271"&gt;trying to write fortran programs in the language&lt;/a&gt;) or he did realize but was too lazy or unprofessional to tidy up after himself.&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;As a final rant, I'd also like to request the collective effort of every .NET programmer on the planet to ensure the swift and timely death of triple-slash documentation comments. You wouldn't believe how many times I've sat in abject frustration while going through someone else's source code where there are more triple-slash comments than lines of code. They also either go out of date very quickly, or reduce the amount of useful refactoring done, because the two are effectively mutually exclusive.&lt;/p&gt; &lt;p&gt;What makes this even more horrific is that the triple-slash comment is rarely anything other than the return type and the name of the property or method. Having triple-slash comments (and bothering to maintain them when you're on a refactoring binge is one thing), but having generic tool-generated ones that just duplicate what the compiler understands is something even more insane.&lt;/p&gt; &lt;p&gt;Yes, I get that you can collapse them, but then you're left with all these ugly /*...*/ blocks all over the place. And not all of us use Visual Studio for everything - there are &lt;a href="http://www.vim.org/"&gt;alternative text editors&lt;/a&gt; out there.&lt;/p&gt; &lt;p&gt;Once again this is an area where pragmatic naming conventions can really make this problem go away. I can sort of understand the need to document the usage of method parameters when your framework almost requires every variable to start with &lt;em&gt;lpsz&lt;/em&gt;, as in lpszMyLongPointerToANullTerminatedString, but in .NET with it's reflective capabilities, powerful IDE&amp;nbsp;and generally good naming conventions,&amp;nbsp;triple-slash comments just add to the cruft that I have to wade through to fix your bug.&lt;/p&gt; &lt;p&gt;A better middle-ground approach for those who produce API's which must be consumed by others with no access to the source appears to be &lt;a href="http://blogs.msdn.com/sandcastle/"&gt;Sandcastle&lt;/a&gt;. According to the &lt;a href="http://www.dotnetrocks.com/default.aspx?showNum=260"&gt;DNR podcast&lt;/a&gt; I listened to today, Microsoft programmers also baulked at the idea of documentation writers all up in their code, and so Sandcastle supports the notion of keeping the documentation tied to, but in a separate file to&amp;nbsp;the code. Apparently you can link an external documentation file to the code in Sandcastle, and when you compile the HTML help, it will issue all the relevant warnings as though the comments were in the source file. Good luck keeping the two in synch and up to date, but if you have to produce MSDN-style documentation, I think Sandcastle's probably going to be your best bet.&lt;/p&gt; &lt;p&gt;In conclusion, I'd just like to reiterate that I encourage all programmers to think very carefully about the negative effects of adding any kind of comment whatsoever to your code. Write those comments which are pragmatic and essential, but leave everything else up to good programming style.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-6389189649681827379?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/6389189649681827379/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=6389189649681827379' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/6389189649681827379'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/6389189649681827379'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/10/sweeping-comments-under-rug.html' title='Sweeping comments under the rug'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-81523807950403770</id><published>2007-09-13T23:57:00.001+02:00</published><updated>2007-09-13T23:57:23.405+02:00</updated><title type='text'>Contributing to IronRuby</title><content type='html'>&lt;p&gt;In an ideal world, &lt;a href="http://www.iunknown.com/"&gt;John Lam&lt;/a&gt; would email me personally begging me to write him an efficient YAML parser using the DLR, because Microsoft just doesn't have the expertise to pull this off internally. In reality, he's obviously more than competent, and based on the &lt;a href="http://rubyforge.org/mail/?group_id=4359"&gt;IronRuby mailing list activity&lt;/a&gt; in just the first couple of days it looks like he's going to have no shortage of people willing to step in and help out getting IronRuby off the ground. He's&amp;nbsp;posted a &lt;a href="http://www.ironruby.net/"&gt;Getting Started guide&lt;/a&gt; for contributions to the IronRuby project, in which he says his most pressing need is for C# implementations of&amp;nbsp;a YAML parser and zlib.&lt;/p&gt; &lt;p&gt;I've been toying with the idea of contributing to an open source project in my spare time (for me, spare time only exists in my delusional, ideal world by the way - not the real world), and I've been trying to decide which project is worthy of &lt;a href="http://www.stevepavlina.com/blog/2005/10/polyphasic-sleep/"&gt;all the free time&lt;/a&gt; I'm going to have after I go &lt;a href="http://en.wikipedia.org/wiki/Polyphasic_sleep"&gt;polyphasic&lt;/a&gt; at the end of the year. Here's a brief list of the best candidates, as well as the reasons why I don't think&amp;nbsp;the relationship would&amp;nbsp;work out:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;a href="http://www.nhibernate.org/"&gt;NHibernate&lt;/a&gt; - Appears to be stable now and I'm not sure if I want to do lots of maintenance coding. Also, with LINQ coming, I'm not sure&amp;nbsp;where NHibernate's future lies. Lastly, although I do use it on occasion, I'm not nearly proficient enough to know how/where to contribute.  &lt;li&gt;&lt;a href="http://www.boost.org/"&gt;Boost&lt;/a&gt; - Although I am part C++ programmer, I'm mostly (98% at this point) focused on Windows development, and as such I'm worried I'd be writing non-portable code, which they'd naturally reject. Plus, the complexities of hardcore template meta-programming, and the issues involved in compatibility between different compilers scares the hell out of me. Just looking at the boost source code (we use a couple of the libraries) is enough to discourage one from contributing.  &lt;li&gt;&lt;a href="http://subversion.tigris.org/"&gt;Subversion&lt;/a&gt; - For me, source control is a tool, not a domain. All my professional development is focused on financial software, and while it's true that I couldn't get by day-to-day without subversion, the mechanics of source control just aren't that interesting to me. Plus again there's platform issues, and while C is notably easier than boost-level C++, it's still not that pleasant to work in for a .NET junkie.  &lt;li&gt;&lt;a href="http://www.mono-project.com/"&gt;Mono&lt;/a&gt; - Not enough Linux experience. I would say this is sort of my holy grail, &lt;a href="http://en.wikipedia.org/wiki/My_Porcelain_God"&gt;my Everest&lt;/a&gt; if you will. Perhaps one day I will be fit to join the ranks of contributors to the Mono project. All Hail &lt;a href="http://tirania.org/blog/"&gt;Miguel&lt;/a&gt;!&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;Since starting to work in the real world and thereby having all day internet access, I've been exposed to the wonders of &lt;a href="http://www.bloglines.com/public/TerenceLewis"&gt;RSS&lt;/a&gt; and more recently to &lt;a href="http://programming.reddit.com/"&gt;programming.reddit.com&lt;/a&gt;. One of the first things you learn when&amp;nbsp;reading the likes of &lt;a href="http://martinfowler.com/bliki/"&gt;Martin Fowler&lt;/a&gt; and &lt;a href="http://blog.objectmentor.com/"&gt;Uncle Bob&lt;/a&gt; is that the language that all the cool kids are using these days is &lt;a href="http://www.ruby-lang.org/"&gt;Ruby&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;Similarly, when listening to .NET&amp;nbsp;&lt;a href="http://www.dotnetrocks.com/"&gt;podcasts&lt;/a&gt; and &lt;a href="http://www.dnrtv.com/"&gt;screencasts&lt;/a&gt;, one of the first things you learn is that Microsoft is aware of the rise of Ruby, and has launched a &lt;a href="http://weblogs.asp.net/scottgu/"&gt;Dr. Evil&lt;/a&gt; (or Pinky and the Brain maybe) -style plan to take over the world by having .NET &lt;a href="http://silverlight.net/"&gt;run in all modern browsers&lt;/a&gt; on &lt;a href="http://www.mono-project.com/Moonlight"&gt;all modern desktop platforms&lt;/a&gt;, with John Lam as the &lt;a href="http://en.wikipedia.org/wiki/Number_2_%28Austin_Powers%29"&gt;Number 2 Man&lt;/a&gt;. ("This is my Number 2 man. His name... is Number 2.")&lt;/p&gt; &lt;p&gt;Coupled to these two nuggets is Microsoft's recent stance reversal in the areas of &lt;a href="http://www.codeplex.com/"&gt;open/closed source code&lt;/a&gt; and &lt;a href="http://www.microsoft.com/resources/sharedsource/licensingbasics/permissivelicense.mspx"&gt;source code licensing&lt;/a&gt;, and together this trinity of events has presented me with what I believe to be the ideal target for my over-eager programming zeal. So, I'm going to explore the possibility of contributing to the IronRuby project.&lt;/p&gt; &lt;p&gt;This is of course easier said than done - John's &lt;a href="http://www.ironruby.net/"&gt;contribution guide and request list&lt;/a&gt; is rather threadbare at this point (to be fair, the project isn't even two weeks old yet from a public point of view).&amp;nbsp;With only two items on the wishlist (the YAML and zlib bits), and seemingly a plethora of people with more time and ruby knowledge than myself eager to contribute, it looks like I'll have to wait around in the cold until things mature a little more. At least, this is what I thought after my first read through of the Getting Started Guide.&lt;/p&gt; &lt;p&gt;However, when I read it through a second time, an innocuous little sentence jumped out at me and gave me a really good idea: &lt;em&gt;"We're also looking for bug reports and bug fixes."&lt;/em&gt; I realized that I don't have to be an idiom wielding&amp;nbsp;ruby wizard to contribute, and I also don't have to have tons of free time on my hands. All I need to do is write ruby programs and make sure that they work in IronRuby. Surely at this point in the implementation it wouldn't be hard for me to write something (albeit perhaps not in idiomatic ruby), that runs under &lt;a href="http://martinfowler.com/bliki/RubyMicrosoft.html"&gt;MRI&lt;/a&gt; but fails to run under IronRuby?&lt;/p&gt; &lt;p&gt;I've already got a few pet-project ideas (a simple RSS reader, a Sudoku game and a html-screen-scraper for my country's satellite TV guide (&lt;a href="http://dotnet.org.za/colin/archive/2007/08/20/he-that-fights-and-runs-away.aspx"&gt;for which the service provider&amp;nbsp;doesn't provide an XMLTV service for fear of losing sales of their proprietary PVR system&lt;/a&gt;)) which I think I could pull off without too much effort in ruby, so for now my strategy is going to consist of making a start on one of these pet projects in MRI and make it work in IronRuby and on Moonlight as well.&lt;/p&gt; &lt;p&gt;I do also have a fallback strategy for&amp;nbsp;helping with those&amp;nbsp;parts of the code which I may not be&amp;nbsp;allowed to contribute to at the present.&amp;nbsp;Martin Fowler appears to have started a &lt;a href="http://martinfowler.com/bliki/RubyMicrosoft.html"&gt;rumour&lt;/a&gt; a while back that John and his team may not be allowed (from a corporate legal policy point of view) to look directly into the source code of MRI (for fear of being blinded I guess) when implementing IronRuby. This will&amp;nbsp;naturally make it difficult for them to make a compliant runtime. &lt;/p&gt; &lt;p&gt;One possible remedy I see to this situation is for those of us who have no such restrictions to write a suite of unit tests for the core developers - a set of guide rails (no pun intended) which they can use to flesh out their implementation. That is - if I can write a unit test which exercises some portion of MRI's standard libraries (which I can do easily as I have access to the source) and which passes under MRI but fails under IronRuby, then I can pass that test along to Microsoft and have them write the code to make the tests pass.&lt;/p&gt; &lt;p&gt;By doing this, they aren't exposed to legal issues of looking at the source directly (there's a geeky solar eclipse metaphor in here somewhere I'm sure), and the community develops a set of unit tests for core library functionality which will be useful not only for IronRuby, but for MRI as well (for backwards-compatibility reasons) as well as &lt;a href="http://jruby.codehaus.org/"&gt;JRuby&lt;/a&gt; and also to anyone else who wants to write or fork a ruby implementation.&lt;/p&gt; &lt;p&gt;In all, I think the future looks bright for Microsoft and Open Source and myself. Now if I could only get around to sleeping less than 3 hours a day...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-81523807950403770?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/81523807950403770/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=81523807950403770' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/81523807950403770'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/81523807950403770'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/09/contributing-to-ironruby.html' title='Contributing to IronRuby'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-2625642295669896186</id><published>2007-03-28T10:02:00.000+02:00</published><updated>2007-03-28T10:07:29.360+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mentoring'/><title type='text'>Mentoring</title><content type='html'>&lt;p&gt;In an ideal world, mentoring would be easy. You would already know ahead of time where your pupil's skills were most lacking, and you'd immediately know the best way of building those skills - making the tiny tears in his mental muscles everyday that cause them to grow and strengthen. Just to remind you, this is all happening in that ideal world I live in - the one in my head.&lt;/p&gt;&lt;p&gt;Back out here in the real world however, it turns out that mentoring isn't quite as easy as all that. There are the inevitable shortcomings of the pupil that just don't seem to take to the exercises, but thankfully those incidents have been few and far between (mostly to do with low-level stuff that they just don't teach kids these days). There's also the issue of the shortcomings of the mentor, but let's just gloss over that for a minute shall we? Then there's the fact that he appears to pick up a lot of the things that I believe are important in software development (basically anything in Uncle Bob's PPP book) without any trouble, which is leaving me struggling to find enough work for him to do.&lt;/p&gt;&lt;p&gt;Before I proceed, a quick disclaimer: At the present moment, I've been a mentor for around one month, and I've only had to mentor one individual and he's come to work for us straight out of university - i.e. he's got no previous professional software development experience as far as I know. This is important for a couple of reasons:
&lt;ol&gt;&lt;li&gt;I'm acting as the customer. The product we're building is as close to shrink-wrapped software that we as a company get, and at present we don't have it running at any clients, so I'm acting as the customer, and more importantly as the business expert until we get a stable, feature-complete version to take out to clients for further input. This means that I get to shoot down any suggestion I don't like by providing a valid (but probably incomprehensible to him) business reason as to why we can't do it. (I try not to abuse this power too much MUAHAHA).&lt;/li&gt;&lt;li&gt;I've recently been promoted to the position of architect for our entire product line going forward, a bold and risky move on the part of my employer if I do say so myself :). There are all sorts of issues here - enough for at least a separate article, but as far as this topic goes, it means that the program he's working on is my baby (my first baby), and its success will be the feather in my cap that proves that my company's confidence in me is not misplaced. In other words, I desperately need this system to score big time.
&lt;/li&gt;&lt;/ol&gt;
Given my role as architect / coach, it appears that more of this mentoring work lies in my near future, but I suspect the circumstances for the next pupil will be remarkably different. Firstly, the person I'll be mentoring next has actually been in professional software development a lot longer than I have - my two-and-a-bit year's worth hardly seems enough to actually be an architect or write about mentoring, whereas I suspect he's been with the company on and off (mostly on) for close to 10 years now. That being said, 10 years ago there was no automated testing or continuous integration at my company - there wasn't even object orientation, and most developers worked in a proprietary Pascal-like language over a BTrieve database which is still driving a whole bunch of our product lines today. Don't even think about there being any web-based development - we're still trying to catch up in this area in 2007.&lt;/p&gt;&lt;p&gt;Another obstacle is going to be the fact that this time round I'm coming in with almost no business expertise - this system is only very loosely tied to the project I've been on for the last two years. My new pupil on the other hand wrote almost the entire old system that we've been asked to replace. This is scary for me, because I like to have as many facts as possible, and I worry that I'm going to make some terrible decision down the road because I didn't know enough about the business. I'm trying to counteract this risk with agile development, where almost by definition no decision is made before as much is known about it as possible, but that means investing a lot of time and effort into training TDD into the pupil. (Not that I'm bemoaning that time or effort - it's fun seeing the spark in someone's mind when they realize there's a better way, but it is time consuming, and there may be some resistance to the approach that I'll need to try and work around / dissolve.)&lt;/p&gt;&lt;p&gt;As far as my own short-comings with regards to being a mentor (and you thought I'd just skip the self-flagellation), it turns out that one of my deficiencies is an inability to communicate verbally when it comes to source code. That is, I find myself trying to explain how we're going to do something, but after a two minute whiteboard explanation when I'm still met with a blank stare, I just resort to typing the code that's burning a hole in my brain into the editor and hitting compile myself, with the understudy watching on (probably in disbelief). This usually takes less than the two minutes that I wasted on an explanation, which leads me to wonder why I'm even wasting time mentoring other people when I could be cranking out code :).&lt;/p&gt;&lt;p&gt;It's a problem in the other direction as well, in that I have a hard time visualising other people's code ideas in my head. I can't count the number of times where someone has explained a problem with their code to me, and I just can't line the pieces up in my head - I always resort to saying "I can't really make a comment without actually seeing the code". This leads me to wonder if I'm unique or at least in the minority in this regard, or if this is a fairly common incident.&lt;/p&gt;&lt;p&gt;On the whole, as far as mentoring goes (remember I'm doing this in between writing code for the projects I'm personally still responsible for, and architecting our new systems going forward), I guess things could be going much worse. It's useful to have a lackey to do the odd job that you just don't get around to (like changing our server apps from console applications to windows services), and it's encouraging to think that maybe in a couple of years more people I work with will know about the Single Responsibility Principle (or even polymorphism would be nice), than those that don't. Also I'm enjoying the teaching aspects of the job, but that could just be because he's a pretty sharp learner. We'll just have to see how things go with pupil number two, which should start this week still if I can find the time for it.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-2625642295669896186?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/2625642295669896186/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=2625642295669896186' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/2625642295669896186'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/2625642295669896186'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/03/mentoring.html' title='Mentoring'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-2812087139039575588</id><published>2007-03-05T19:12:00.000+02:00</published><updated>2007-03-06T12:14:43.953+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='open-source'/><title type='text'>Go blow a fish</title><content type='html'>&lt;p&gt;In an ideal world, everybody would be an expert on everything (not just my girlfriend), and we wouldn't need to stand on the shoulders of others to see over the next hill. However, back here in the real world, this isn't the case - some people just know more about some stuff than other people.&lt;/p&gt;&lt;p&gt;Now, usually I'd be the last person to admit that I don't know something about something, but when it comes to the security of applications which are responsible for the transferral of huge sums of money every day, I'm more than willing to step back and say to someone else "It's OK. You can field this one."&lt;/p&gt;&lt;p&gt;Take my boss for instance. Sometime late last year, around September I should imagine, he implemented the security layer for our financially-oriented system. The basic plan was as follows:
&lt;ol&gt;&lt;li&gt;We know which encryption scheme we want to use - blowfish. We know this because our old system, which we're sort of replacing, uses blowfish and we haven't had any complaints with it.&lt;/li&gt;&lt;li&gt;Let's find some source code on the net that does "blowfish encryption" bend it to our will, wrap it up in a dll, and call it from our code.&lt;/li&gt;&lt;li&gt;&amp;lt;Insert typical pot-at-the-end-of-the-rainbow step 3 here&amp;gt;. Something like "Drink coffee" or the underpants gnomes "Profit" would be appropriate.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;So everything proceeded according to plan, and soon enough we had a workable encryption solution, endorsed by our client's Microsoft consultant, and my words of caution were dismissed as negativity. These words of caution went something along the lines of "we don't know anything about security or encryption, so we have no way of verifying whether this algorithm we've spot welded in will be reproducible by anyone else".&lt;/p&gt;&lt;p&gt;A little bit about our architecture: Our system is client-server, but the API to the server is both proprietary and open - proprietary in the sense of it's not HTTP, or BitTorrent, or SMTP or any other common protocol - and open in the sense that our API is documented in a publicly available document and we've just written a reference implementation for our client to pass on to their clients.&lt;/p&gt;&lt;p&gt;So, amidst the congratulations at securing a multi-million dollar a day application in under a week, my protests and cautions were drowned out with comments like "Blowfish is a standard algorithm". Turns out though, now that we're almost into external testing (where these fabled "other developers" will have a chance to execute and test the code that they've written to our API on our test servers), and all of a sudden, none of them are getting the expected outputs when they run their inputs through &lt;span style="font-weight: bold;"&gt;their &lt;/span&gt;blowfish encryption algorithms.&lt;/p&gt;&lt;p&gt;So we were at a fork in the road today: We could either
&lt;ul&gt;&lt;li&gt;drop the blowfish (inadvisable at this late stage in the game, but still a possibility) in favour of something built into .NET
&lt;/li&gt;&lt;li&gt;give everyone else our blowfish implementation (in dll or C++ source form), and hope that none of them want to write a client in java (this was standard practice on older systems)
&lt;/li&gt;&lt;li&gt;change our code to the &lt;span style="font-style: italic;"&gt;new standard blowfish implementation&lt;/span&gt;, which we've managed to find on another website.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Hmm, that last one didn't come out nearly sarcastic-sounding enough, considering that's the approach we've gone with, and the 20 extra heartbeats I'm generating every minute since the decision was made.&lt;/p&gt;&lt;p&gt;Something just seems very wrong to me here. My boss is insistent that encryption is just something you do - you get some source code from somewhere (hopefully you're allowed to copy and paste it, but we won't go into that here), you build it into a dll, write a test program that makes sure "Hello world" comes out as "@#$@9asdm,we*" on the other end, and finally make sure that "@#$@9asdm,we*" can be decrypted back to "Hello world" again and then you're good to go.&lt;/p&gt;&lt;p&gt;But we have no way of knowing. We have no way of knowing how big our key is supposed to be, we have no way of knowing if anyone else is actually going to be able to encrypt anything in a form we can understand - we don't know anything about security because we write financial software.&lt;/p&gt;&lt;p&gt;At the moment our key is 8 bytes long - so I presume this is "64-bit encryption", but I don't really have any way of knowing - other than spending some time researching this stuff - time I really don't have right now. We are planning on increasing this once everyone's comfortably able to encrypt and decrypt the data though.&lt;/p&gt;&lt;p&gt;So for now, we've spent another day patching our blowfish algorithm to work the same way our API user's does. I guess this means we need to email the two other developers who've told us that they can't get blowfish working - the ones that we already gave copies of our (broken) code and we'll need to send them new code.&lt;/p&gt;&lt;p&gt;It just seems so unprofessional to me. We should have just conceded that we knew nothing about encryption in the first place, and at least tried to use something that didn't come off of Joe Bloggs's website, but rather from some reputable source like boost, or the .NET BCL. It's a common theme I see all the time here at my work - leak-plugging: "Ooh, the dam wall is leaking here, let me just stick my finger in it until the tide goes out".&lt;/p&gt;&lt;p&gt;&lt;blockquote&gt;This type of thinking can do us in.&lt;/blockquote&gt;&lt;/p&gt;&lt;p&gt;I'm all for open source, and we use it quite a lot, but this is security, and your security is paramount, especially in finance.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-2812087139039575588?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/2812087139039575588/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=2812087139039575588' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/2812087139039575588'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/2812087139039575588'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/03/go-blow-fish.html' title='Go blow a fish'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-5515491882096349887</id><published>2007-01-15T18:01:00.000+02:00</published><updated>2007-01-15T18:27:08.060+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ruby'/><category scheme='http://www.blogger.com/atom/ns#' term='nant'/><category scheme='http://www.blogger.com/atom/ns#' term='coding convention'/><category scheme='http://www.blogger.com/atom/ns#' term='regular expressions'/><title type='text'>Convention is better than cure</title><content type='html'>&lt;p&gt;In a pragmatic world, programmers working in a team adopt a common coding convention because it reduces the amount of mental acrobatics that members of the team have to perform when maintaining each other's code. In an ideal world however, it doesn't matter what coding style you adopt, because it doesn't make a tangible difference to maintaining the code base. Everyone just mindlessly uses whatever convention was drilled into him by well-meaning but short-sighted professors, with no regard for the nuances of good style.&lt;/p&gt;&lt;p&gt;It appears therefore that I'm living in an ideal world - which ironically, is far from ideal. I was the only one working on the project for the first year of its development, and so naturally all the code exhibited a single style - bar minor differences where my style changed over the course of that year (I was still developing my C++ knowledge to a large extent), and even those minor differences I was dilligent about reconciling back to &lt;b&gt;The One Style&lt;/b&gt;.&lt;/p&gt;&lt;p&gt;In that first year, I managed to write a large portion of the system - which I'd inherited in the crudest of forms (a couple of hundred lines of shaky &lt;span style="font-style: italic;"&gt;(C++)--&lt;/span&gt; code) from a colleague who has since gone on to hopefully better things. However, about 18 months ago, my department finished it's other major project and the entire crew was put full force onto my project, and I had to contend with three other people &lt;i&gt;maintaining&lt;/i&gt; my beautiful code.&lt;/p&gt;&lt;p&gt;What made matters worse was that my boss was busy implementing a separate piece of the system (the money-maker component, you could say) from a Delphi spike implementation (almost line for line) - basically independently of the application it was going to have to fit into. I didn't think much of it at the time, but looking back, I'd take continuous integration over the nightmare that resulted from that integration any day - the interface between the two components still feels like a house of cards. His coding conventions were obviously different to mine, but because we weren't even working in the same repository, let alone the same solution, neither of us realised the headache it was going to cause.&lt;/p&gt;&lt;p&gt;So now we're at the point where the system is nearly two and a half years old, and convention is basically non-existent. The particular style element that's got a bee in my bonnet of late is the declaration of pointer variables, and specifically, whether the * should accompany the type, or the variable name. I'll give you an example:&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;ol&gt;&lt;li&gt;int&lt;b style="color: rgb(255, 0, 0);"&gt;*&lt;/b&gt; myInt = &amp;someInt;&lt;/li&gt;&lt;li&gt;int &lt;b style="color: rgb(255, 0, 0);"&gt;*&lt;/b&gt;myInt = &amp;someOtherInt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;As silly as it sounds, this one character difference has caused a bunch of problems in our team and for our project. Obviously this convention clash hasn't caused bugs - semantically the two statements are equivalent, but we have had problems related to day-to-day feature addition and maintenance programming. For example, when I need to fix a bug in a class, and I want to browse subversion's history for the class's header file, I see that the file has about 20 different changesets and it takes about 10 minutes just to isolate which of those changesets don't alternatively have me changing the convention within the file to style 1 and one of my colleagues changing it to style 2.&lt;/p&gt;&lt;p&gt;This is the kind of crap that can derail your train of thought and kill your productivity for the rest of the day. What makes matters worse is that the "tidying up" is usually done by the programmer in exactly this situation - when they come across the file looking for a bug. This means, when the bug is fixed and the code is checked in, the "tidying up" code is hidden amongst all the bug-fixes, or more commonly and much more dangerously, the bug fix (which is important) is hidden amongst all the "tidying up" (which isn't) - that is, there's a bad signal to noise ratio.&lt;/p&gt;&lt;p&gt;I could go into a 3 page discussion on why style 1 is better than style 2 (which it clearly is), but let's just say for now that if I were asked to join a team working on a code-base with 250K lines of code, and every line used style 2, I'd hopefully be pragmatic (or extreme) about it and just adopt style 2 while working on that project - a common convention makes it easier for everybody.&lt;/p&gt;&lt;p&gt;Or alternatively, being the stick in the mud that I am, I'd write a little script to report all the places where the one instance was used, and I'd either manually go and change all of them (I'm also a sucker for punishment), or write another script to do that. In fact, that's just what I did do to our code today. I threw together a little regular expression (first using Windows' findstr command, but quickly switching to a ruby script) which would tell me all the places in the code where style 2 was used, so I could change them all to style 1.&lt;/p&gt;&lt;p&gt;What I should have done first I guess was write the script and run it for both combinations - so that I could figure out which style was used more. That's what I should have done. What I actually did was assume that my preferred style was more widespread (given that myself and two of the other three maintainers use it exclusively, and the fact that I'd written so much of the code in the first place), and so I jumped straight in and started changing the bad style to the good.&lt;/p&gt;&lt;p&gt;To cut a long story short, about six hours (this is all in my spare time, how sad is that?) and a number of exceptional cases&lt;sup&gt;(1)&lt;/sup&gt; later, my ruby script was reporting that there were no more occurrences of the offending pattern. Feeling very satisfied with myself, I integrated the ruby script into our nant build script and set the build to fail if a violation of the convention occurs again. Luckily, I'd had the foresight to make these changes in a separate working copy of the code, and so I still had the original code to refer to. I made some modifications to the script and ran it to count how many instances there were of style 1 and style 2 on the original code, and the results very gratifying. Approximately 1800 instances of style 1, and 300 instances of style 2 - which just proved my suspicions nicely.&lt;/p&gt;&lt;p&gt;So all that's left to do is check the code into the repository tomorrow when I get back to work, and also the rather distasteful task of informing my team that there is now a strict convention regarding the placement of the * when declaring pointer types, and any instances of going against the convention will cause the build to fail (I believe there's a joke aboyt aryan variables and my coding-naziness hiding in there).&lt;/p&gt;&lt;p&gt;I've toyed with the idea of rather hooking the script into subversion's pre-commit hooks, so that no bad code even gets into the repository, and the checks don't add onto our already straining build time, but unfortunately I haven't had the time to play with such an idea. I can see great benefits though if a coding convention is enforced at this level across all our projects in all the languages we use (mostly C# lately), so perhaps I'll spend some more spare time over the coming weeks identifying little violations like this to build up a comprehensive library of checks and integrating them one by one. I've already installed a pre-commit hook to check that comments always accompany checkins, but even that just gets subverted by people typing in crappy, useless comments.&lt;/p&gt;&lt;p&gt;Thankfully though, it doesn't appear that the script is going to have any major effect on our build-time, which Riaan and I reduced by 15 minutes to 30 minutes total last week (I know, I know, 10 minute build :( ). The script runs on my laptop in about 1.6 seconds, so I think we're OK. For now, I'll just have to wait and see whether this has a positive or negative influence - and the next item on my hitlist is curly braces - both ones missing from single-line conditionals, and ones hiding on the right hand side of the expression, instead of on their own line below. Hopefully that won't meet with too much resistance either.&lt;/p&gt;&lt;p&gt;&lt;sup&gt;1. The exception cases included instances where a variable or iterator was being dereferenced, or where multiplication was taking place, or where a function call in a dll was declared.&lt;/sup&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-5515491882096349887?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/5515491882096349887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=5515491882096349887' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/5515491882096349887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/5515491882096349887'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/01/convention-is-better-than-cure.html' title='Convention is better than cure'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-3592314144856829677</id><published>2007-01-03T17:11:00.000+02:00</published><updated>2007-01-16T12:53:48.888+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rant'/><category scheme='http://www.blogger.com/atom/ns#' term='clickonce'/><category scheme='http://www.blogger.com/atom/ns#' term='msbuild'/><title type='text'>The key to fixing problems quickly</title><content type='html'>&lt;p&gt;In an ideal world, problems are solved instantaneously, or don't even arise in the first place. Here in the real world though, the key to fixing problems quickly is having enough information. I'm particularly frustrated with developers who, when an error occurs and they need to inform the user, come up with a really useless error message, something about as useful as

&lt;/p&gt;&lt;blockquote&gt;"error MSB3152: The install location for prerequisites has not been set to 'component vendor's web site' and the file 'DotNetFX\instmsia.exe' in item '.NET Framework 2.0' can not be located on disk. See Help for more information."&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;span style="font-style: italic;"&gt;See help for more information.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;To cap it all off, the online help is about as useful as matches to an astronaut. It actually says: "&lt;span style="font-style: italic;"&gt;To correct this error - Determine whether the file exists on disk&lt;/span&gt;". The file does indeed exist on disk. It exists on disks all over the world. It even exists on both disks in the machine in question. It just doesn't appear to exist in the place where the program is looking for it. &lt;/p&gt;
&lt;p&gt;I wouldn't need to see help for more information if the stupid program would just tell me where it was looking for the files. This is happening on my build server - the machine I'm setting up to monitor our subversion repository and republish this particular program every time someone checks something into the source tree. Up until now, this has been a manual process triggered by my colleague Karin using an existing Nant script which I threw together a couple of months ago. What this basically means is that all the hard work has been done already, I just need to hook up the monitor part (CruiseControl.NET) to the source control part (Subversion) and the build automation part (NAnt) and the ClickOnce technolgy (MSBuild). But somewhere along the line, the build machine is configured differently to Karin's machine, and MSBuild can't find the prerequisite files.&lt;/p&gt;
&lt;p&gt;No problem, easy to fix, right? I'll just copy the files off Karin's machine onto the build server, presumably to the same path (they're just install files for the .Net Framework 2.0 after all), and away we go. Not so. Apparently there's some other place on this particular machine that those files need to be for MSBuild to pick them up - which in theory I'm fine with. There could be any one of a number of reasons for this - the source files and the MSBuild files are on different drive letters, Windows Server 2003 vs. Windows XP, etc, and I don't really care where the files are stored on the build server, as long as I get my automated build published every time someone changes something.&lt;/p&gt;
&lt;p&gt;What does bug me is that when MSBuild fails inside of my NAnt task, it fails with that wonderful error message that I copy/pasted above. This is literally a 5 minute fix if they just put all the locations where they search for the files into the error message. Then, all I have to do is go and paste the files into one of those locations and I'm good to go. Or, at least then I can start to hunt around and see where those locations are stored and try and figure out why they're different on the build server and Karin's machine. Instead, I've spent the last two hours moving files all over my hard drives, trying to get MSBuild and ClickOnce to pick them up, to no avail - and I'm still no closer to a solution than I was when I started.&lt;/p&gt;
&lt;p&gt;So my advice to programmers: Don't be scared to put as much information as you have into error messages - especially stack traces if you have them - but any other useful information as well. At least if you provide a decent message, then a competent user (which I know probably only accounts for about 3% of all your users) can at least do basic troubleshooting himself, without having to either:

&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Trouble your tech support with a problem that they will probably be no better equipped to solve than he is (because tech support also might not know where this particular configuration is looking for files).&lt;/li&gt;&lt;li&gt;Spend hour after frustrating hour scouring the web for solutions - this is NOT a great way to build customer relations and loyalty.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;As an aside (and proof that I'm not overly anti-Microsoft), the system we're currently replacing (that we wrote) has an error message which pops up when something goes wrong on login and it looks like this:

&lt;/p&gt;&lt;blockquote&gt;"INCORRECT PASSWORD NO RIGHTS OR DATE"&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;
&lt;p&gt;This is an exact quote, even down to SHOUTING AT OUR USERS in ALL-CAPS. What this excellent piece of information basically means is either you typed the password in incorrectly, you don't have rights to send in a login message, or the date you passed through in your login message doesn't correspond with the server's date. Why all three error conditions are reported together I'll never know, but clearly providing too much information is almost as bad as providing too little (at least the problem can be accurately troubleshooted).&lt;/p&gt;
&lt;p&gt;Every time this error comes up in our test environment, we need to open the management system and check the password, check the rights and then log into the server (which is Novell, not Windows) check that the server's date is the same as the client's date. Why not, as the developer who wrote this code, just take 5 minutes out of your busy schedule to return three different error codes (don't even get me started on error codes vs. exceptions {I'm pro-exceptions}, but this is an old C system), for the three different errors? That simple, once off 5 minute task would have saved 5 minutes of troubleshooting for probably 200 logins over the course of the last 2 years, which equates to around 800 minutes or almost 14 hours of time saved.&lt;/p&gt;
&lt;p&gt;I attribute such techniques partly to laziness, but also partly to something I come across all the time - thinking strictly in terms of very short-term goals. "I have to get this login routine finished today" as opposed to "This needs to be as easy to use and troubleshoot as possible, because every single person who logs into the system in its entire lifetime is going to have to pass these checks, and if their login fails, it's going to frustrate every single one of them."&lt;/p&gt;
&lt;p&gt;However, a discussion of short-term goals - of thinking too narrowly - is a discussion for another day.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-3592314144856829677?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/3592314144856829677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=3592314144856829677' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/3592314144856829677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/3592314144856829677'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2007/01/key-to-fixing-problems-quickly.html' title='The key to fixing problems quickly'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-3046883935487020939.post-7963221104829334308</id><published>2006-12-29T04:06:00.000+02:00</published><updated>2007-06-11T07:07:27.133+02:00</updated><title type='text'>Boosting your test framework</title><content type='html'>&lt;p&gt;I was reading through a discussion the other day on one of the agile development mailing lists and in response to a post about C++ unit test frameworks for Visual Studio, a poster mentioned that it’s possible to use NUnit to run unit tests on &lt;span style="font-weight: bold"&gt;unmanaged&lt;/span&gt; C++ code. &lt;/p&gt; &lt;p&gt;This is cool because NUnit has some nifty features that the other C++ unit test framework that I’m familiar with (boost::test) appears to lack – namely Setup and Teardown methods. For the record, I introduced the boost test library at my company about two years ago – around the same time as introducing smart pointers, program options, lexical casts, and a few of the other boost library goodies. It's been great not having to reinvent tons of (buggy) code for going from ints to strings and back again, etc. not to mention the benefits of smart pointers, which we still don't use as much as we should.&lt;/p&gt; &lt;p&gt;Already having built and integrated boost was pretty much the driving force at the time for me in terms of selecting a unit test framework for our unmanaged C++ application. At the time, I don’t think I even considered shopping around to see if there was a better option – I was new to TDD and XP at the time, and applying the same techniques that I’d had success with in C# seemed more important than making sure I had exactly the right tool for the job. Besides which, I don’t remember struggling overly long in getting the environment set up and getting the code to build using boost::test. &lt;/p&gt; &lt;p&gt;However, I quickly discovered that unit testing in C++ has its own set of problems that need to be considered. These include the following: &lt;/p&gt; &lt;ol&gt; &lt;li&gt;Asserts are usually implemented via macros, which are then expanded by the pre-processor and will typically cause the method call being asserted to be executed more than once. There are ways around this (I remember reading of at least one C++ unit test framework which subverted this problem), but at the time when I wrote a few assert wrappers on top of the boost::test ones, I didn’t realise that my "actuals" were going to be invoked twice, and I spent a fair bit of time in the debugger trying to figure out what was going on. See Listing 1 below which shows some code straight out of our solution.  &lt;li&gt;There’s no really simple way to do setup and teardown methods (that I’ve been able to find), and organising tests into hierarchies of fixtures was either not possible or too much effort at the time (I can’t remember which now). &lt;/li&gt;&lt;/ol&gt;My wrapper over boost's assert framework &lt;pre&gt;&lt;code&gt;&lt;textarea rows="20" cols="80" wrap="off"&gt;// This macro allows us to print the expected and actual values of a test
// more succinctly than writing this code on every line where an assert
// is performed
#define TEST_MESSAGE(expected, actual) "\nExpected\t" &amp;lt;&amp;lt; expected &amp;lt;&amp;lt; "\nGot\t\t" &amp;lt;&amp;lt; actual
#define TEST_MESSAGE_NOT_EQUAL(expected, actual) "\nExpected\t" &amp;lt;&amp;lt; expected &amp;lt;&amp;lt; " != " &amp;lt;&amp;lt; actual
#define TEST_MESSAGE_NOT_NULL "\nAn object was unexpectedly NULL"
#define TEST_MESSAGE_NULL "\nAn object was unexpectedly not null. Was it properly initialized?"
#define TEST_MESSAGE_TRUE expected &amp;lt;&amp;lt; " was not true"
#define TEST_MESSAGE_FALSE expected &amp;lt;&amp;lt; " was not false"

// These macros will ease the pain of asserting in boost
// !!!!!!!!!!!!!!!!!!!!! NOTE !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// DO NOT DIRECTLY ASSERT THE RETURN VALUES OF FUNCTION CALLS
// WITHIN THESE MACROS! THE MACRO WILL CAUSE THE FUNCTION TO BE
// EXECUTED TWICE! SO, SAVE THE RESULT OF THE FUNCTION CALL INTO A
// TEMPORARY, AND THEN ASSERT THE TEMPORARY AGAINST YOUR EXPECTED
// RESULT
///////////////////////////////////////////////////////////
#define STT_CHECK_EQUAL(expected, actual) BOOST_CHECK_MESSAGE(expected == actual, TEST_MESSAGE(expected, actual))
#define STT_CHECK_NOT_EQUAL(expected, actual) BOOST_CHECK_MESSAGE(expected != actual, TEST_MESSAGE_NOT_EQUAL(expected, actual))
#define STT_CHECK_NOT_NULL(actual) BOOST_CHECK_MESSAGE(actual != NULL, TEST_MESSAGE_NOT_NULL)
#define STT_CHECK_NULL(actual) BOOST_CHECK_MESSAGE(actual == NULL, TEST_MESSAGE_NULL)
#define STT_CHECK_TRUE(actual) STT_CHECK_EQUAL(true, (actual))
#define STT_CHECK_FALSE(actual) STT_CHECK_EQUAL(false, (actual))
#define STT_FAIL_EXPECTED_EXCEPTION(exception) BOOST_FAIL("\nExpected exception " &amp;lt;&amp;lt; exception &amp;lt;&amp;lt; " to be thrown, but it was not")

#pragma warning(disable : 4127 4267 4511 4512)
#include &amp;lt;boost/lexical_cast.hpp&amp;gt;
#include &amp;lt;boost/test/unit_test.hpp&amp;gt;
#include &amp;lt;boost/test/auto_unit_test.hpp&amp;gt;
#pragma warning(default : 4127 4267 4511 4512)
&lt;/textarea&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As far as point number 2 goes, I tried a couple of options before settling for a solution that still seems very unsatisfactory to me. The first thing I tried was macro trickery - a newbie mistake. I had some tests which required a fairly elaborate setup (a smell in itself, I know) and I tried something like the following:&lt;/p&gt;
&lt;table border="1" unselectable="on"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="font-weight: bold"&gt;Listing 2&lt;/span&gt; - &lt;span style="font-style: italic"&gt;When will we learn to stop leaning on the preprocessor?&lt;/span&gt; &lt;span style="font-size: 85%"&gt;(Note: Production code retranslated into "pseudocode" to protect IP)&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;pre&gt;&lt;code&gt;
#ifndef SETUP_FOR_FIXTURE1
#define SETUP_FOR_FIXTURE1()                                                       \
                                                                                   \
// Declare an instance of the class under test                                     \
                                                                                   \
/* Comment line */                                                                 \
// Setup a temporary variable                                                      \
// Compute the sizeof a struct from our system                                     \
// Perform a calculation using a const and this struct size                        \
// 5 More lines of code!!! including setting up a boost::scoped_array&amp;lt;char&amp;gt;        \
// STT_CHECK_EQUAL(expected, actual);                                              \
#endif
&lt;/pre&gt;&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;There are still 5 tests in our system which call SETUP_FOR_FIXTURE1() at the top of the tests. In practice, this approach didn't hurt me too badly, mostly because it was only used in two "fixtures", and both pieces of code were fairly small - this one of 5 tests and another "fixture" with 3 tests (although there was one longish one with around 70 lines of code) and about 15 lines of setup code. However, just because I didn't get burned, it doesn't mean that I don't realize I was playing with fire. I quickly realised that debugging these things was difficult (the compiler would spew a message about an object that didn't even appear to be defined anywhere in the test), and the readability was pretty low (because the setups were defined in different header files in a different Visual Studio sub-folder). &lt;/p&gt;
&lt;p&gt;So after writing in this style for a while, and struggling to reconcile the ease of setups and teardowns in NUnit with the monster I'd created in C++, I switched tack and tried a more object oriented approach - create one new class to act as a setup class (to setup our class under test), and another new class to act as a validator and overload methods on this class to assert the various properties of the class. However, instead of solving the setup/teardown problem, I'd just minimised its impact (I still had to declare the class under test (CUT), the setup class and the validator class in each test, and still had to make sure I passed the CUT around in the right order). I do have some excuses for doing this however: &lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;At the time, I was introducing my boss - the guy who'd basically written all the classes we were retrofitting tests for - to unit testing - so I was concentrating more on imparting the philosophy behind testing than making sure we had great code. 
&lt;li&gt;In the heat of the moment, and due to the specific nature of the classes we were testing - I mostly forgot about the setup problem. The classes are a heirarchy about four or five layers deep (including a redundant, completely abstract base class) where most classes in the heirarchy are very similar and each only does a few things slightly different from its neighbours and ancestors. Also, there is no explicit (premeditated) use of the decorator pattern, but some of the classes aggregate others at different levels of the heirarchy and delegate first to this one, and then the result goes to that one, etc. - so I think I could refactor to decorators at some point in the future fairly easily. Because each of these classes was basically similar in most aspects, I was striving more not to repeat myself in terms of the asserts and validation at the bottom of the tests than in terms of setting the classes up. Overloaded validator methods would delegate back to the base case and then also validate whatever additional functionality the particular class in question was providing. 
&lt;li&gt;The code itself was a real mess. For starters, it was hand-translated basically line for line from a Delphi prototype. Concepts which are taken for granted in Delphi which weren't readily available in C++ were written from scratch - including a horrendous container class to replace Delphi's StringList (If that even is a Delphi class), and we never once used the container to store simple strings. MFC code was scattered all over the place and had to be replaced with standard library equivalents or removed altogether. Use of the standard library was non-existent. In short, we were working without a safety net and making sure we didn't break anything and that was more important than not repeating ourselves. 
&lt;li&gt;Refactoring in C++ is harder than in other languages. Although to be fair, most of my refactoring is still manual even in C#, doing it in C++ where #includes are all over the place and compile times take forever means longer feedback loops in the short term while you wait for the compiler to hook together 200K lines of C++ code. 
&lt;li&gt;I simply didn't have the C++ experience that I do now (although I probably never would've admitted that at the time :))&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;I guess what I should have done was to use less of an "object-oriented" approach, and more of a "Resource Acquisition is Initialisation" (RAII) approach. An ideal solution would be to have a fixture class, and have its constructor setup the CUT, have the destructor do any cleanup (a-la a teardown method) and then have methods to test each piece of functionality - like a [Test] method in NUnit. In fact, this might have worked really well if we'd integrated templates into the picture. One test fixture class, templated based on the particular class we were testing, and coupled with specialization, might have solved a whole host of problems. In this case, the test "function" would instantiate the fixture, and then just call the correct test method on it - delegating immediately to the fixture class. This might have looked something like the following:&lt;/p&gt;
&lt;p&gt;
&lt;table border="1" unselectable="on"&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span style="font-weight: bold"&gt;Listing 3&lt;/span&gt; - &lt;span style="font-style: italic"&gt;A much more C++-like solution to the setup/teardown problem&lt;/span&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="font-style: italic"&gt;
&lt;td&gt;Shapes.h&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;pre&gt;&lt;code&gt;
#ifndef SHAPES_H
#define SHAPES_H

struct Shape
{
  virtual int sides()=0;
  virtual int width()=0;
  virtual int height()=0;
  virtual int area()=0;
};

struct Triangle : public Shape
{
  virtual int sides() { return 3; }
  virtual int width() { return 50; }
  virtual int height() { return 100; }
  virtual int area() { return width() * height() / 2; }
};

struct Rectangle : public Shape
{
  virtual int sides() { return 4; }
  virtual int width() { return 100; }
  virtual int height() { return 50; }
  virtual int area() { return width() * height(); }
};

struct Square : public Rectangle
{
  virtual int width() { return height(); }
};

#endif
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="font-style: italic"&gt;
&lt;td&gt;ShapeTestFixture.h&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;pre&gt;&lt;code&gt;
#ifndef SHAPE_TEST_FIXTURE_H
#define SHAPE_TEST_FIXTURE_H

#include &amp;lt;cassert&amp;gt;
#include "Shapes.h"

template &amp;lt;class T&amp;gt;
class ShapeTestFixture
{
private:
  T* shape;
public:
  ShapeTestFixture() { shape = new T(); }
  ~ShapeTestFixture() { delete shape; }
  void TestShapeSides() { std::cout &amp;lt;&amp;lt; "!!!! Untested shape sides !!!! \n"; }
  void TestShapeArea() { std::cout &amp;lt;&amp;lt; "!!!! Untested shape area !!!! \n"; }
};

void ShapeTestFixture&amp;lt;Rectangle&amp;gt;::TestShapeSides()
{
  std::cout &amp;lt;&amp;lt; "Checking rectangle\n";
  assert(shape-&amp;gt;sides() == 4);
  assert(shape-&amp;gt;width() == 100);
  assert(shape-&amp;gt;height() == 50);
}

void ShapeTestFixture&amp;lt;Rectangle&amp;gt;::TestShapeArea()
{
  assert(shape-&amp;gt;area() == 5000);
}

void ShapeTestFixture&amp;lt;Square&amp;gt;::TestShapeSides()
{
  std::cout &amp;lt; "Checking square\n";
  assert(shape-&amp;gt;sides() == 4);
  assert(shape-&amp;gt;width() == 50);
  assert(shape-&amp;gt;height() == 50);
}

void ShapeTestFixture&amp;lt;Square&amp;gt;::TestShapeArea()
{
  assert(shape-&amp;gt;area() == 2500);
}

void ShapeTestFixture&amp;lt;Triangle&amp;gt;::TestShapeSides()
{
  std::cout &amp;lt;&amp;lt; "Checking triangle\n";
  assert(shape-&amp;gt;sides() == 3);
  assert(shape-&amp;gt;width() == 50);
  assert(shape-&amp;gt;height() == 100);
}

void ShapeTestFixture&amp;lt;Triangle&amp;gt;::TestShapeArea()
{
  assert(shape-&amp;gt;area() == 2500);
}
#endif
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="font-style: italic"&gt;
&lt;td&gt;RAIIUnitTest.cpp&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;pre&gt;&lt;code&gt;
#include "stdafx.h"
#include "ShapeTestFixture.h"
#include "Shapes.h"

int main(int argc, char* argv[])
{
  ShapeTestFixture&amp;lt;Rectangle&amp;gt; rectangleFixture;
  rectangleFixture.TestShapeSides();
  rectangleFixture.TestShapeArea();

  ShapeTestFixture&amp;lt;Square&amp;gt; squareFixture;
  squareFixture.TestShapeSides();
  squareFixture.TestShapeArea();

  ShapeTestFixture&amp;lt;Triangle&amp;gt; triangleFixture;
  triangleFixture.TestShapeSides();
  triangleFixture.TestShapeArea();

  std::cout &amp;lt;&amp;lt; "Finished all tests\n";
}
&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/p&gt;
&lt;p&gt;Pretty simple right? There is still some duplication here - we need to manually instantiate the fixture we're interested in and then also manually call the correct test method, but the scope of duplication has been reduced greatly, and not by sacrificing readability.&lt;/p&gt;
&lt;p&gt;There are however still a few improvements that we can make to this code - namely to drop boost::test completely in favour of NUnit. I've gotten that to work and I'm impressed by how quickly I was able to adapt my way of working to the way I do things in C#. I'll talk about that next time.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/3046883935487020939-7963221104829334308?l=pragmatism-not-idealism.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pragmatism-not-idealism.blogspot.com/feeds/7963221104829334308/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=3046883935487020939&amp;postID=7963221104829334308' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/7963221104829334308'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/3046883935487020939/posts/default/7963221104829334308'/><link rel='alternate' type='text/html' href='http://pragmatism-not-idealism.blogspot.com/2006/12/boosting-your-test-framework.html' title='Boosting your test framework'/><author><name>Terence Lewis</name><uri>http://www.blogger.com/profile/06258016094645679321</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
