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

<channel>
	<title>Mike&#039;s Blabberings</title>
	<atom:link href="http://www.mike-griffith.com/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.mike-griffith.com/blog</link>
	<description>on software, testing, and the web.</description>
	<lastBuildDate>Tue, 29 Jun 2010 14:37:52 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>A Culinary Analogy</title>
		<link>http://www.mike-griffith.com/blog/2010/06/a-culinary-analogy/</link>
		<comments>http://www.mike-griffith.com/blog/2010/06/a-culinary-analogy/#comments</comments>
		<pubDate>Tue, 29 Jun 2010 13:17:44 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[methodology]]></category>
		<category><![CDATA[planning]]></category>
		<category><![CDATA[project management]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=541</guid>
		<description><![CDATA[
We used to build software like a crappy banana split stand.  Grab the banana off the shelf (which probably is about to expire), throw on ice cream, cover it in chocolate, add some cherries, whip cream, nuts, stir it up, put a spoon in it, hand it to the customer, take their money, and [...]]]></description>
			<content:encoded><![CDATA[<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/06/bananasplit-242x300.jpg" alt="" title="bananasplit" width="242" height="300" class="aligncenter size-medium wp-image-543" /></p>
<p>We used to build software like a crappy banana split stand.  Grab the banana off the shelf (which probably is about to expire), throw on ice cream, cover it in chocolate, add some cherries, whip cream, nuts, stir it up, put a spoon in it, hand it to the customer, take their money, and let them walk away.</p>
<p>STOP THAT RIGHT NOW!</p>
<p>Stop the gargantuan effort up front, stop compromising the quality of pieces that don&#8217;t get seen, stop making your customer wait til the end of the project.</p>
<p>Find a beautiful banana and show it to your customer.  Let them take a bite.  That might be all they need.  Or they might want to take a small piece of it and try it in banana bread.  They might not like that.  But that&#8217;s OK, because we have lots of time and lots of banana left.  Take the next piece and make a banana smoothie for them.  Maybe add a strawberry for the next sip.</p>
<p><a href="http://twitter.com/aaron_oliver" target="_blank">Aaron Oliver</a> might suggest that a good time to have these tastings is <a href="http://www.codesoftly.com/2010/06/dont-forget-the-meeting-after-the-standup-meeting.html" target="_blank">right after your daily stand-up</a>.</p>
<p>Let your customer be involved at each step.  Let them help you make the right changes early and often.  Be agile.  Don&#8217;t be the hobo behind the bad banana split counter.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/06/a-culinary-analogy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Python decorators, SRP, and testability</title>
		<link>http://www.mike-griffith.com/blog/2010/06/python-decorators-srp-and-testability/</link>
		<comments>http://www.mike-griffith.com/blog/2010/06/python-decorators-srp-and-testability/#comments</comments>
		<pubDate>Fri, 04 Jun 2010 06:30:02 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[patterns]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[unit testing]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=504</guid>
		<description><![CDATA[On the SRP:
For those unfamiliar with the Single Responsibility Principle (SRP), it states that there should never be more than one reason for a class to change.
That is to say: do one thing, do it well.
Decorators (not to be confused with the decorator pattern) can add behaviors or side-effects to a method, and this can [...]]]></description>
			<content:encoded><![CDATA[<p><strong>On the SRP:</strong><br />
For those unfamiliar with the <a href="http://www.butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod" target="_blank">Single Responsibility Principle</a> (SRP), it states that <em>there should never be more than one reason for a class to change</em>.</p>
<p>That is to say: do one thing, do it well.</p>
<p><a href="http://www.python.org/dev/peps/pep-0318/" target="_blank">Decorators</a> (not to be confused with the <a href="http://en.wikipedia.org/wiki/Decorator_pattern" target="_blank">decorator pattern</a>) can add behaviors or side-effects to a method, and this can be dangerous.  It seems harmless, because by adding a decorator, you&#8217;re likely taking boilerplate code and shuffling it elsewhere.  However, they often encourage badness because of how easy it is to add these behaviors.</p>
<p><strong>On testing decorated methods:</strong></p>
<p>Adding an @decorator to a python object unarguably makes the undecorated code difficult to test in isolation.  Decorators are applied at compile-time, and cannot be mocked or made to not-execute without some pain.</p>
<p>There are certainly a few common tricks that can help test a decorated method with minimal side-effects, but they require changes to the decorator itself.  There&#8217;s just plain and simple no way to un-decorate a method for isolated testing.</p>
<p><strong>Let&#8217;s look at a few common examples:</strong></p>
<p><strong><em>@expose</em></strong>: register a URL for a view function in a web framework</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> BlogPostController<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    @expose<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;/blog/{post_id}&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> index<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request, post_id=<span style="color: #008000;">None</span><span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; show a blog post &quot;&quot;&quot;</span>
        post = adapter.<span style="color: black;">get_post</span><span style="color: black;">&#40;</span>post_id<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> render<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;show_entry.html&quot;</span>, <span style="color: #008000;">dict</span><span style="color: black;">&#40;</span>post=post<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Without the `@expose`, your `show_entry` knows how to get a given post and render it in the proper template.  With the decorator, it also now knows which URL corresponds to that.  You now have multiple reasons for this block of code to change, including pointing to a different URL or using a different template.  It&#8217;s preferred to have a separate module for managing which urls point to which views.</p>
<p>Harm factor: low.  Ick factor: medium &#8211; high.</p>
<p><strong><em>@cache</em></strong>: try to get the result from memcache, otherwise, execute the function and stick it in cache for next time</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> PostAdapter<span style="color: black;">&#40;</span>DataAdapter<span style="color: black;">&#41;</span>:
    @cache
    <span style="color: #ff7700;font-weight:bold;">def</span> get_post<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, post_id<span style="color: black;">&#41;</span>
        <span style="color: #483d8b;">&quot;&quot;&quot; grab a blog post from the database &quot;&quot;&quot;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #008000;">self</span>.<span style="color: black;">query</span><span style="color: black;">&#40;</span>Post<span style="color: black;">&#41;</span>.<span style="color: #008000;">filter</span><span style="color: black;">&#40;</span><span style="color: #008000;">id</span>=post_id<span style="color: black;">&#41;</span>.<span style="color: black;">one</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;"># SQLAlchemy folks need to talk to Mr Demeter...</span></pre></div></div>

<p>OK this seems cool right?  You only hit the database when you have to, otherwise we get it even quicker by looking it up in the cache.</p>
<p>What happens if you want to disable caching?  A separate cache abstraction layer would reduce volatility in your data adapter.</p>
<p>And what does the unit test look like?</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> TestGettingAPost<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> setup<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #008000;">self</span>.<span style="color: black;">query</span> = Mock<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span> <span style="color: #808080; font-style: italic;">#don't hit the production database!</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">post_adapter</span> = PostAdapter<span style="color: black;">&#40;</span>query=<span style="color: #008000;">self</span>.<span style="color: black;">query</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> test_getting_a_post<span style="color: black;">&#40;</span><span style="color: #008000;">self</span><span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">assert</span> <span style="color: #008000;">self</span>.<span style="color: black;">post_adapter</span>.<span style="color: black;">get_post</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">123</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Damn, there&#8217;s no way to mock out the @cache decorator so it doesn&#8217;t run.  Try to, I dare you.  You&#8217;re likely going to actually get post 123 from your production memcache.  Crappy.  The only thing you can do is make the @cache grab the cache implementation from the PostAdapter instance (and mock that out in you test), or find some other sneaky way of disabling caching for test runs.  But the @cache decorator isn&#8217;t all self-contained and fun anymore.</p>
<p>Harm factor: medium &#8211; high.</p>
<p><strong><em>@validate</em></strong>: Make sure the request matches the specified schema, otherwise hand-off to error handler</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #ff7700;font-weight:bold;">class</span> EditPostController<span style="color: black;">&#40;</span><span style="color: #008000;">object</span><span style="color: black;">&#41;</span>:
    <span style="color: #ff7700;font-weight:bold;">def</span> _save_error<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request, errors<span style="color: black;">&#41;</span>:
        <span style="color: #483d8b;">&quot;&quot;&quot; @validate decorator kicked flow here, redisplay edit page with errors &quot;&quot;&quot;</span>
        ...
    @validate<span style="color: black;">&#40;</span>schema, error_handler=_save_error<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">def</span> save<span style="color: black;">&#40;</span><span style="color: #008000;">self</span>, request<span style="color: black;">&#41;</span>
        post = <span style="color: #008000;">self</span>.<span style="color: black;">schema</span>.<span style="color: black;">to_python</span><span style="color: black;">&#40;</span>request.<span style="color: black;">params</span><span style="color: black;">&#41;</span>
        <span style="color: #008000;">self</span>.<span style="color: black;">post_adapter</span>.<span style="color: black;">save_post</span><span style="color: black;">&#40;</span>post<span style="color: black;">&#41;</span>
        <span style="color: #ff7700;font-weight:bold;">return</span> redirect<span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;/blog/{0}&quot;</span>.<span style="color: black;">format</span><span style="color: black;">&#40;</span>post_id<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span></pre></div></div>

<p>Without this @validate, there would be a lot of boilerplate code inside the `save` method.  With it, any unit test for the save method will be likely linked to your schema.  You&#8217;d have to make an actual valid request in order to test this method.  That&#8217;s outside of any tests for your schema directly. That means double-coverage but 2 tests to update when requirements shift.</p>
<p>Harm factor: low-medium</p>
<p><strong>Conclusions (or tl;dr):</strong></p>
<p>As easy as it is to become infatuated with Python decorators, they definitely encourage you to violate the SRP.  This can create a myriad of problems:</p>
<ul>
<li>Difficultly in isolating system under test</li>
<li>Added complexity to enable testing</li>
<li>Redundant redundant unit tests</li>
<li>Making a code module more volatile than it ought to</li>
</ul>
<p>They still have some valid use cases and can lead to cleaner code, however,  as Master Yoda once said, &#8220;when you look at the dark side, careful you must be&#8230;&#8221;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/06/python-decorators-srp-and-testability/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Rally pairing</title>
		<link>http://www.mike-griffith.com/blog/2010/05/rally-pairing/</link>
		<comments>http://www.mike-griffith.com/blog/2010/05/rally-pairing/#comments</comments>
		<pubDate>Mon, 10 May 2010 19:50:00 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[methodology]]></category>
		<category><![CDATA[productivity]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=485</guid>
		<description><![CDATA[Iwein Fuld posted a great article about different styles of pair programming.  It&#8217;s a great post, and I encourage you to read it if you&#8217;ve tried pairing but haven&#8217;t bought in yet. His rally car analogy is spot-on.
My favorite driver is an outspoken dutch guy. He&#8217;s quick on the wheel and if he doesn&#8217;t [...]]]></description>
			<content:encoded><![CDATA[<p>Iwein Fuld posted a great article about different <a href="http://blog.xebia.com/2010/05/09/practical-styles-of-pair-programming/" target="_blank">styles of pair programming</a>.  It&#8217;s a great post, and I encourage you to read it if you&#8217;ve tried pairing but haven&#8217;t bought in yet. His rally car analogy is spot-on.</p>
<blockquote><p>My favorite driver is an outspoken dutch guy. He&#8217;s quick on the wheel and if he doesn&#8217;t get the idea I&#8217;m trying to convey to him he&#8217;ll just type something to try if that works. When he does get what I&#8217;m mumbling it&#8217;s on the screen faster than when I would have typed it myself, so it doesn&#8217;t give me time to get frustrated over things not being like the would be when I had the keyboard. And that gives me time to look for exits and pittfalls.
</p></blockquote>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/05/rally-300x187.jpg" alt="" title="rally" width="300" height="187" class="aligncenter size-medium wp-image-487" /></p>
<p>He also dismisses anyone that tries to say pairing isn&#8217;t as effective as coding solo.</p>
<blockquote><p>No you&#8217;re not faster on your own, you&#8217;re just creating more crap for your colleagues to puzzle over and eventually delete. The code you write alone sucks. That guy that is getting on your nerves is trying to tell you (clumsily) that your code sucks, try to listen to him and you&#8217;ll turn into a better programmer. Or maybe you can teach him something and he&#8217;ll stop getting on your nerves. &#8230; If you&#8217;re slowing the other guy down, that&#8217;s a good thing. That will prevent him from writing code that you cannot maintain. If you don&#8217;t feel worthy of your colleagues code, get over it, or get off the team.</p></blockquote>
<p>My biggest stumbling block to date has been the ratio of time driving to navigating when you&#8217;re pairing a senior and junior dev.  Trying to balance productivity and mentoring isn&#8217;t always easy.  I find allowing the Sr. dev to drive 2/3 of the time and Jr. dev to drive 1/3 strikes close to the optimal balance.  Any more driving for the Sr and you&#8217;ll lose the Jr entirely as he rides along for the tour.  Any less and you lose valuable time in guiding-by-doing.</p>
<p>Whether you&#8217;re the driver or navigator, take your role seriously.  Share in the responsibility and help foster a mutual respect for the other.  </p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/05/rally-pairing/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Installing Aventail VPN (and Sun Java6) on Ubuntu 10.04</title>
		<link>http://www.mike-griffith.com/blog/2010/05/installing-aventail-vpn-and-sun-java6-on-ubuntu-10-04/</link>
		<comments>http://www.mike-griffith.com/blog/2010/05/installing-aventail-vpn-and-sun-java6-on-ubuntu-10-04/#comments</comments>
		<pubDate>Sat, 01 May 2010 19:08:14 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[off topic]]></category>
		<category><![CDATA[linux]]></category>
		<category><![CDATA[ubuntu]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=479</guid>
		<description><![CDATA[The Aventail VPN client requires Sun Java to correctly function on Ubuntu.  For whatever reason, the OpenJDK is not sufficient, and the client exhibits numerous bugs.  Unfortunately, for Ubuntu 10.04 LTS, Canonical decided to move Sun Java to the partner repository.  It&#8217;s no-longer apt-get-able by default.
No worries, we can easily add the [...]]]></description>
			<content:encoded><![CDATA[<p>The Aventail VPN client requires Sun Java to correctly function on Ubuntu.  For whatever reason, the OpenJDK is not sufficient, and the client exhibits numerous bugs.  Unfortunately, for Ubuntu 10.04 LTS, Canonical <a href="http://www.ubuntu.com/getubuntu/releasenotes/1004#Sun%20Java%20moved%20to%20the%20Partner%20repository">decided to move Sun Java</a> to the partner repository.  It&#8217;s no-longer <code>apt-get</code>-able by default.</p>
<p>No worries, we can easily add the parter repository, then install Sun Java.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> add-apt-repository <span style="color: #ff0000;">&quot;deb http://archive.canonical.com/ lucid partner&quot;</span>
<span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get</span> update
<span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> sun-java6-jre</pre></div></div>

<p>Once completed, the Aventail VPN installation can proceed.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">tar</span> xf AventailConnect-Linux.tar
<span style="color: #c20cb9; font-weight: bold;">sudo</span> .<span style="color: #000000; font-weight: bold;">/</span>install.sh</pre></div></div>

<p>If you did attempt to install Aventail using the OpenJDK, you&#8217;ll need to uninstall it and re-install after installing Sun Java.  See <code>/usr/local/Aventail/uninstall.sh</code>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/05/installing-aventail-vpn-and-sun-java6-on-ubuntu-10-04/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Agile Auto Restoration?</title>
		<link>http://www.mike-griffith.com/blog/2010/05/agile-auto-restoration/</link>
		<comments>http://www.mike-griffith.com/blog/2010/05/agile-auto-restoration/#comments</comments>
		<pubDate>Sat, 01 May 2010 05:59:12 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[planning]]></category>
		<category><![CDATA[project management]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=430</guid>
		<description><![CDATA[I was over at my buddy Chad&#8217;s garage again tonight tinkering with his 1958 Chevy pickup.  Chad bought the truck when he turned 16 and planned to restore it and drive it all through college.  Ten years later, the truck still doesn&#8217;t run. To an untrained eye, there&#8217;s little different from when he [...]]]></description>
			<content:encoded><![CDATA[<p>I was over at my buddy Chad&#8217;s garage again tonight tinkering with his 1958 Chevy pickup.  Chad bought the truck when he turned 16 and planned to restore it and drive it all through college.  <strong>Ten years later, the truck still doesn&#8217;t run.</strong> To an untrained eye, there&#8217;s little different from when he bought it.  What&#8217;s wrong with this picture?</p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/05/pickup-300x224.jpg" alt="as it stands today" title="as it stands today" width="300" height="224" class="alignright size-medium wp-image-444" /></p>
<p>Chad had high hopes early.  He bought the truck on a whim and, without too much inspection, figured it&#8217;d be a few month job to get it on the road again.  Needless to say, months passed, and it was still in his dad&#8217;s garage.  Life (and college) got in the way, and the truck sat abandoned for several years, with only an occasional wrench gracing it&#8217;s steel.</p>
<p>He&#8217;d buy new parts from time to time, and store them away to be put on at a later date.  He&#8217;d spend the occasional weekend tearing something off, grinding on it, patching, and welding.  Tearing off one part would show deeper damage.  This was Chad&#8217;s baby, and he couldn&#8217;t simply mask that damage, so he&#8217;d plan to fix the next issue before continuing, or order another new body panel.  Little visible progress was made, but lots of hours and dollars were invested.</p>
<p>Finally, 3 weeks ago, after lots of pressure from his friends and family over the last few years, Chad took the truck out of storage at his parents 2 hours away and brought it to his own garage.  He&#8217;s got a reinvigorated passion to get this thing running, and I&#8217;m excited to help him.</p>
<p><strong>Some of these challenges sound familiar to software veterans, but are any analogies actually relevant?</strong></p>
<p>Consider for a moment that you&#8217;re assigned to a project that requires you to migrate an existing web application from Java+Oracle to PHP+MySQL.  Doing rewrites of applications can often be driven by a desire to consolidate platforms and reduce costs, and there is often a fixed timeline.  The project is locked and loaded, and you haven&#8217;t had time to do technical due diligence.</p>
<p>You dive right in without much planning, and before you know it, you&#8217;ve passed your deadline.  You got hung up digging into the guts of the old software.  What little new code you were able to write relies on features that your new database doesn&#8217;t have, but you didn&#8217;t realize because you were testing against the old one.</p>
<p><strong>What has agile software development taught us that can right this project-gone-bad?</strong></p>
<p>It would be a stretch to suggest the core tenets of <a href="http://agilemanifesto.org/" target="_blank">the agile manifesto</a> are entirely applicable.  There really aren&#8217;t any processes/tools getting in the way, he&#8217;s not held up by a desire to document everything about the truck, contract negotiation doesn&#8217;t matter, and he really doesn&#8217;t have much of a plan that would inhibit responding to change.</p>
<p>However, the <a href="http://www.agilemanifesto.org/principles.html" target="_blank">principles behind an agile team</a> can be very useful to this situation.</p>
<ul>
<li>Rapid, continuous delivery of a useful <del>software</del> <strong>truck</strong>.  Don&#8217;t worry if, for example, the interior dome lights aren&#8217;t working right away.  But let&#8217;s get it running, and make sure it&#8217;s poised to keep getting better.</li>
<li>A working <del>software</del> <strong>truck</strong> is the principal measure of progress.  Not how many new body parts got ordered off ebay, or how cool the sketch of flames on the doors might look.</li>
<li>The <del>project</del> <strong>truck</strong> is built by motivated individuals, who should be trusted.  The friends that help need to be committed to the goal.</li>
<li>Continuous attention to technical excellence and good design.  Don&#8217;t rush it.  And avoid duct tape.</li>
<li>Simplicity.  One of the beautiful things about old cars is how few moving parts there really are under the hood.  Don&#8217;t ruin it by adding flamethrowers to the exhaust or an automatic transmission.</li>
<li>Regular adaptation to changing circumstances.  Expect that we&#8217;ll uncover a few unanticipated problems, but be ready to figure them out and keep charging forward.</li>
</ul>
<p>Chad is a perfectionist, and he&#8217;s not in a rush to get this thing done.   However, we have made some progress already in the last few weeks.  Here&#8217;s where we are now:  most barriers to working on the truck have been removed, we&#8217;ve started a &#8220;spike&#8221; of work to get the motor close to starting, I&#8217;m going to help put together a prioritized list of features (similar to user stories), and we&#8217;ll identify incremental milestones along the path to winning car shows.</p>
<p>With any luck, we&#8217;ll get the motor fired up after our first sprint.  And maybe, just maybe, I&#8217;ll learn something along the way that can help us develop better software!</p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/05/1958_chevy_pickup-300x199.jpg" alt="" title="will it every look this sexy?" width="300" height="199" class="alignnone size-medium wp-image-440" /></p>
<p><em>Photo courtesy of <a href="http://www.flickr.com/photos/chicanerii/195977388/">chicanerii on flickr</a></em></p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/05/agile-auto-restoration/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Animal pictures for baby room</title>
		<link>http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/</link>
		<comments>http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/#comments</comments>
		<pubDate>Wed, 21 Apr 2010 01:42:18 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[off topic]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=388</guid>
		<description><![CDATA[The wife and I are trying to pick out some pictures for the baby&#8217;s room.  We can&#8217;t decide whether to go realistic or whimsical.
What works best in a nursery?
]]></description>
			<content:encoded><![CDATA[<p>The wife and I are trying to pick out some pictures for the baby&#8217;s room.  We can&#8217;t decide whether to go realistic or whimsical.</p>

<a href='http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/elephant/' title='elephant'><img width="150" height="150" src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/04/elephant-150x150.jpg" class="attachment-thumbnail" alt="" title="elephant" /></a>
<a href='http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/turtle/' title='turtle'><img width="150" height="150" src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/04/turtle-150x150.jpg" class="attachment-thumbnail" alt="" title="turtle" /></a>
<a href='http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/flamingo/' title='flamingo'><img width="150" height="150" src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/04/flamingo-150x150.jpg" class="attachment-thumbnail" alt="" title="flamingo" /></a>
<a href='http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/tiger/' title='tiger'><img width="150" height="150" src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/04/tiger-150x150.jpg" class="attachment-thumbnail" alt="" title="tiger" /></a>
<a href='http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/dolphin/' title='dolphin'><img width="150" height="150" src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/04/dolphin-150x150.jpg" class="attachment-thumbnail" alt="" title="dolphin" /></a>
<a href='http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/puppy/' title='puppy'><img width="150" height="150" src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/04/puppy-150x150.jpg" class="attachment-thumbnail" alt="" title="puppy" /></a>

<p>What works best in a nursery?</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/04/animal-pictures-for-baby-room/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Show me the $80 million dollar Blue (#0044cc)</title>
		<link>http://www.mike-griffith.com/blog/2010/03/show-me-the-80-million-dollar-blue-0044cc/</link>
		<comments>http://www.mike-griffith.com/blog/2010/03/show-me-the-80-million-dollar-blue-0044cc/#comments</comments>
		<pubDate>Thu, 18 Mar 2010 17:01:40 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[testing]]></category>
		<category><![CDATA[design]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=371</guid>
		<description><![CDATA[So what&#8217;s this fancy shade of blue that made Bing so much money look like?

&#160;

Eh.  Doesn&#8217;t really do it for me as a blob of color.
How about on a link?  Well, that is actually quite pleasant against a white background.
Regardless, the takeaway from the whole story isn&#8217;t that you should switch all your [...]]]></description>
			<content:encoded><![CDATA[<p>So what&#8217;s this <a href="http://www.lukew.com/ff/entry.asp?1025" target="_blank">fancy shade of blue that made Bing so much money</a> look like?</p>
<div style="border: 1px solid #888888;background-color: #0044cc;width: 150px;height: 50px;">
&nbsp;
</div>
<p>Eh.  Doesn&#8217;t really do it for me as a blob of color.</p>
<p>How about <a style="color: #0044cc;" href="javascript:void(0);">on a link</a>?  Well, that is actually quite pleasant against a white background.</p>
<p>Regardless, the takeaway from the whole story isn&#8217;t that you should switch all your colors to #0044cc, but rather that you need to rigorously test your audience and be able to make changes based on the observed behavior.  Having the discipline to carefully react to the measurements can and will drive success.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/03/show-me-the-80-million-dollar-blue-0044cc/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Baby boy gear round-up</title>
		<link>http://www.mike-griffith.com/blog/2010/01/baby-boy-gear-round-up/</link>
		<comments>http://www.mike-griffith.com/blog/2010/01/baby-boy-gear-round-up/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 22:36:58 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[off topic]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=352</guid>
		<description><![CDATA[With a baby boy on the way (due in early June 2010), I decided to start looking for fun stuff for my lil dude.  I thought that maybe I&#8217;d have to wait a few years to unleash him on the world, but lo and behold there&#8217;s some seriously badass gear for infants and toddlers! [...]]]></description>
			<content:encoded><![CDATA[<p>With a baby boy on the way (due in early June 2010), I decided to start looking for fun stuff for my lil dude.  I thought that maybe I&#8217;d have to wait a few years to unleash him on the world, but lo and behold there&#8217;s some seriously badass gear for infants and toddlers!  If I wasn&#8217;t excited enough already, now I&#8217;m entirely stoked to take on parenting and turn this guy into the coolest kid on the block!</p>
<p>I&#8217;m still looking for lots more goodies.  I have yet to find the perfect off-road running stroller, and I&#8217;m a bit disappointed by the selection of baby snowboards, but I&#8217;ve got a few more months to sort it all out.</p>
<p>Here&#8217;s some of the clothes and gear I&#8217;ve found so far&#8230;</p>
<p><strong>Nerdy T-shirts:</strong></p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/01/exothermic.gif" alt="" title="exothermic" width="275" height="227" /><br />
Exothermic reaction t-shirt &#8211; clevercuties.com
</p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/01/BIBONACCI-186x300.jpg" alt="" title="BIBONACCI" width="186" height="300" /><br />
Bibonacci t-shirt &#8211; clevercuties.com.  The nerd in me cannot resist something this awesome.
</p>
<p><strong>Big boys clothes in little man sizes:</strong></p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/01/adidastracksuit-195x300.jpg" alt="" title="adidastracksuit" width="195" height="300" /><br />
Baby track suit &#8211; Nordstrom&#8217;s.  Oh heck yes, he gonna be a soccer playa!
</p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/01/chucks.jpg" alt="" title="chucks" width="240" height="240" /><br />
Baby Chuck&#8217;s &#8211; zappos.com
</p>
<p><strong>Outdoor gear:</strong></p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/01/helmet.jpg" alt="" title="helmet" width="220" height="220" /><br />
Full-face helmet &#8211; nybikergear.com.  This is a prerequisite for the snowboard, dirt bike, skateboard, and baby sky-diving outfit that he&#8217;ll be donning after a few months.</p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/01/childcarrier1-300x300.jpg" alt="" title="childcarrier" width="300" height="300" /><br />
Kid in a bag &#8211; rei.com.  Before he&#8217;s able to walk, he&#8217;ll be able to tag along on hikes.  Throw him in a bag on my back and off we go.  They even sell sun visors and other accessories.</p>
<p><img src="http://www.mike-griffith.com/blog/wp-content/uploads/2010/01/snowsuit-300x300.jpg" alt="" title="snowsuit" width="300" height="300"  /><br />
Snowsuit &#8211; backcountry.com.  He&#8217;s gonna be the hippest kid on the slopes for sure.  Gotta look good, especially considering he&#8217;ll almost certainly be the youngest person to ever bust a double cork.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/01/baby-boy-gear-round-up/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Browser history sniffing with Dojo</title>
		<link>http://www.mike-griffith.com/blog/2010/01/browser-history-sniffing-with-dojo/</link>
		<comments>http://www.mike-griffith.com/blog/2010/01/browser-history-sniffing-with-dojo/#comments</comments>
		<pubDate>Thu, 14 Jan 2010 06:40:52 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[dojo]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[tips & tricks]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=346</guid>
		<description><![CDATA[Niall Kennedy posted a now-famous article about using some browser trickery to determine what websites a user on your site has visited.  I&#8217;ve taken that concept and created a module that can be used with the Dojo Toolkit javascript framework.
It provides two methods that you can use in your code, isVisited and isAnyVisited.
One important [...]]]></description>
			<content:encoded><![CDATA[<p>Niall Kennedy posted a <a href="http://www.niallkennedy.com/blog/2008/02/browser-history-sniff.html" target="_blank">now-famous article</a> about using some browser trickery to determine what websites a user on your site has visited.  I&#8217;ve taken that concept and created a module that can be used with the <a href="http://www.dojotoolkit.org/" target="_blank">Dojo Toolkit</a> javascript framework.</p>
<p>It provides two methods that you can use in your code, <code>isVisited</code> and <code>isAnyVisited</code>.</p>
<p>One important note about your URL specification is that you must specify an *exact* URL that the user has been to.  So, for example, if someone has visited several twitter profiles, but never actually went to the homepage or signin screen, then it would be difficult/impossible to tell whether or not they were a twitter user without a massive brute-force query.</p>
<p>You can download <a href="/js/dojo1.4/mdg/sniff.js" target="_blank">sniff.js</a>, or <a href="/code/browsersniff.html" target="_blank">view a demo</a>.</p>
<p><strong>Sample usage:</strong></p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">dojo.<span style="color: #660066;">require</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;mdg.sniff&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #003366; font-weight: bold;">var</span> usedYahoo <span style="color: #339933;">=</span> mdg.<span style="color: #660066;">sniff</span>.<span style="color: #660066;">isVisited</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;http://www.yahoo.com&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #003366; font-weight: bold;">var</span> usedGoogleMaps <span style="color: #339933;">=</span> mdg.<span style="color: #660066;">sniff</span>.<span style="color: #660066;">isAnyVisited</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span>
      <span style="color: #3366CC;">&quot;http://maps.google.com&quot;</span><span style="color: #339933;">,</span>
      <span style="color: #3366CC;">&quot;http://maps.google.com/maps&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #003366; font-weight: bold;">var</span> usedFacebook <span style="color: #339933;">=</span> mdg.<span style="color: #660066;">sniff</span>.<span style="color: #660066;">isAnyVisited</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#91;</span>
      <span style="color: #3366CC;">&quot;http://www.facebook.com&quot;</span><span style="color: #339933;">,</span>
      <span style="color: #3366CC;">&quot;http://www.facebook.com/index.php&quot;</span><span style="color: #339933;">,</span>
      <span style="color: #3366CC;">&quot;https://login.facebook.com/login.php&quot;</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

<p><strong>Source code:</strong></p>

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">dojo.<span style="color: #660066;">provide</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;mdg.sniff&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #006600; font-style: italic;">//</span>
<span style="color: #006600; font-style: italic;">// Browser history sniffing, based on infamous blog post:</span>
<span style="color: #006600; font-style: italic;">// &lt;http://www.niallkennedy.com/blog/2008/02/browser-history-sniff.html&gt;</span>
<span style="color: #006600; font-style: italic;">//</span>
<span style="color: #006600; font-style: italic;">// Sample usage:</span>
<span style="color: #006600; font-style: italic;">//</span>
<span style="color: #006600; font-style: italic;">//    dojo.require(&quot;mdg.sniff&quot;);</span>
<span style="color: #006600; font-style: italic;">//    var usedYahoo = mdg.sniff.isVisited(&quot;http://www.yahoo.com&quot;);</span>
<span style="color: #006600; font-style: italic;">//    var usedGoogleMaps = mdg.sniff.isAnyVisited([</span>
<span style="color: #006600; font-style: italic;">//          &quot;http://maps.google.com&quot;,</span>
<span style="color: #006600; font-style: italic;">//          &quot;http://maps.google.com/maps&quot;]);</span>
<span style="color: #006600; font-style: italic;">//    var usedFacebook = mdg.sniff.isAnyVisited([</span>
<span style="color: #006600; font-style: italic;">//          &quot;http://www.facebook.com&quot;,</span>
<span style="color: #006600; font-style: italic;">//          &quot;http://www.facebook.com/index.php&quot;,</span>
<span style="color: #006600; font-style: italic;">//          &quot;https://login.facebook.com/login.php&quot;]);</span>
<span style="color: #006600; font-style: italic;">//</span>
<span style="color: #006600; font-style: italic;">// Works with Dojo 1.3 and 1.4 (*may* work with 1.2 as well)</span>
<span style="color: #006600; font-style: italic;">//</span>
&nbsp;
dojo.<span style="color: #660066;">require</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;dojox.html.styles&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#40;</span><span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #003366; font-weight: bold;">var</span> _this <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">sniffCache</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
    dojox.<span style="color: #660066;">html</span>.<span style="color: #660066;">insertCssRule</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;.dojohistorysniff a&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;color:#000000;&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    dojox.<span style="color: #660066;">html</span>.<span style="color: #660066;">insertCssRule</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;.dojohistorysniff a:visited&quot;</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;color:#ff0000 !important;&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">isAnyVisited</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #006600; font-style: italic;">/*Array*/</span>urls<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066; font-weight: bold;">for</span> <span style="color: #009900;">&#40;</span>i<span style="color: #339933;">=</span><span style="color: #CC0000;">0</span><span style="color: #339933;">;</span> i<span style="color: #339933;">&lt;</span>urls.<span style="color: #660066;">length</span><span style="color: #339933;">;</span> i<span style="color: #339933;">++</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>_this.<span style="color: #660066;">isVisited</span><span style="color: #009900;">&#40;</span>urls<span style="color: #009900;">&#91;</span>i<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">;</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">isVisited</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #006600; font-style: italic;">/*String*/</span>url<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span><span style="color: #000066; font-weight: bold;">typeof</span><span style="color: #009900;">&#40;</span>_this.<span style="color: #660066;">sniffCache</span><span style="color: #009900;">&#91;</span>url<span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> <span style="color: #3366CC;">'undefined'</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000066; font-weight: bold;">return</span> _this.<span style="color: #660066;">sniffCache</span><span style="color: #009900;">&#91;</span>url<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #003366; font-weight: bold;">var</span> link <span style="color: #339933;">=</span> _this.<span style="color: #660066;">addLink</span><span style="color: #009900;">&#40;</span>url<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #003366; font-weight: bold;">var</span> color <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">new</span> dojo.<span style="color: #660066;">Color</span><span style="color: #009900;">&#40;</span>dojo.<span style="color: #660066;">style</span><span style="color: #009900;">&#40;</span>link<span style="color: #339933;">,</span> <span style="color: #3366CC;">&quot;color&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>color.<span style="color: #660066;">r</span> <span style="color: #339933;">==</span> <span style="color: #CC0000;">255</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            _this.<span style="color: #660066;">sniffCache</span><span style="color: #009900;">&#91;</span>url<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">;</span>
            <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">true</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        _this.<span style="color: #660066;">sniffCache</span><span style="color: #009900;">&#91;</span>url<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #003366; font-weight: bold;">false</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">insertSniffDiv</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066; font-weight: bold;">return</span> dojo.<span style="color: #660066;">create</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;div&quot;</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span>className<span style="color: #339933;">:</span> <span style="color: #3366CC;">&quot;dojohistorysniff&quot;</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> dojo.<span style="color: #660066;">body</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">getSniffDiv</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #003366; font-weight: bold;">var</span> divs <span style="color: #339933;">=</span> dojo.<span style="color: #660066;">query</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;div.dojohistorysniff&quot;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">if</span> <span style="color: #009900;">&#40;</span>divs.<span style="color: #660066;">length</span> <span style="color: #339933;">&gt;</span> <span style="color: #CC0000;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000066; font-weight: bold;">return</span> divs<span style="color: #009900;">&#91;</span><span style="color: #CC0000;">0</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
        <span style="color: #000066; font-weight: bold;">return</span> _this.<span style="color: #660066;">insertSniffDiv</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">addLink</span> <span style="color: #339933;">=</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #006600; font-style: italic;">/*String*/</span>url<span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #003366; font-weight: bold;">var</span> div <span style="color: #339933;">=</span> _this.<span style="color: #660066;">getSniffDiv</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
        <span style="color: #000066; font-weight: bold;">return</span> dojo.<span style="color: #660066;">create</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">&quot;a&quot;</span><span style="color: #339933;">,</span> <span style="color: #009900;">&#123;</span>href<span style="color: #339933;">:</span> url<span style="color: #009900;">&#125;</span><span style="color: #339933;">,</span> div<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    <span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">/**
     * mdg.sniff.isVisited
     * Check whether or not a URL has been visited
     * @param url String
     * @return boolean
     */</span>
    mdg.<span style="color: #660066;">sniff</span>.<span style="color: #660066;">isVisited</span> <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">isVisited</span><span style="color: #339933;">;</span>
&nbsp;
    <span style="color: #006600; font-style: italic;">/**
     * mdg.sniff.isAnyVisited
     * Check whether or not *any* of the URLs specified have been visited
     * @param urls Array of Strings
     * @return boolean
     */</span>
    mdg.<span style="color: #660066;">sniff</span>.<span style="color: #660066;">isAnyVisited</span> <span style="color: #339933;">=</span> <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">isAnyVisited</span><span style="color: #339933;">;</span>
&nbsp;
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/01/browser-history-sniffing-with-dojo/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Batch convert images to sepia tone with python</title>
		<link>http://www.mike-griffith.com/blog/2010/01/batch-convert-images-to-sepia-tone-with-python/</link>
		<comments>http://www.mike-griffith.com/blog/2010/01/batch-convert-images-to-sepia-tone-with-python/#comments</comments>
		<pubDate>Thu, 14 Jan 2010 03:49:20 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[photography]]></category>
		<category><![CDATA[software development]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[tips & tricks]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=329</guid>
		<description><![CDATA[The Python Imaging Library (PIL) offers easy photo manipulation from python scripts.  There&#8217;s some handy sample code on effbot.org that demonstrates how to alter an image&#8217;s palette to generate a sepia tone effect.  It first desaturates the image, then applies a new palette based on a linear ramp.
I&#8217;ve cleanup up that sample code [...]]]></description>
			<content:encoded><![CDATA[<p>The Python Imaging Library (PIL) offers easy photo manipulation from python scripts.  There&#8217;s some handy <a href="http://effbot.org/zone/pil-sepia.htm" target="_blank">sample code on effbot.org</a> that demonstrates how to alter an image&#8217;s palette to generate a sepia tone effect.  It first desaturates the image, then applies a new palette based on a linear ramp.</p>
<p>I&#8217;ve cleanup up that sample code and tucked it into a script.  You can pass a list of files to the script, and it will apply a sepia effect to each, making sure to backup the originals.</p>

<div class="wp_syntax"><div class="code"><pre class="python" style="font-family:monospace;"><span style="color: #808080; font-style: italic;">#!/usr/bin/python</span>
<span style="color: #483d8b;">&quot;&quot;&quot;
Apply sepia filter in batch to images
&nbsp;
Usage:
    python batch_sepia.py [--no-backup] file1 [file2] ...
&quot;&quot;&quot;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">import</span> Image <span style="color: #ff7700;font-weight:bold;">as</span> PIL_Image
<span style="color: #ff7700;font-weight:bold;">import</span> <span style="color: #dc143c;">shutil</span>, <span style="color: #dc143c;">os</span>
<span style="color: #ff7700;font-weight:bold;">from</span> <span style="color: #dc143c;">optparse</span> <span style="color: #ff7700;font-weight:bold;">import</span> OptionParser
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> open_image<span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; grab a PIL image from the given location
    &quot;&quot;&quot;</span>
    image = PIL_Image.<span style="color: #008000;">open</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>
    image.<span style="color: black;">load</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> image
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> save_image<span style="color: black;">&#40;</span>image, filename, quality=<span style="color: #ff4500;">95</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; save the PIL image to disk
    &quot;&quot;&quot;</span>
    image.<span style="color: black;">save</span><span style="color: black;">&#40;</span>filename, <span style="color: #483d8b;">&quot;JPEG&quot;</span>, quality=quality<span style="color: black;">&#41;</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> make_linear_ramp<span style="color: black;">&#40;</span>white<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; generate a palette in a format acceptable for `putpalette`, which
        expects [r,g,b,r,g,b,...]
    &quot;&quot;&quot;</span>
    ramp = <span style="color: black;">&#91;</span><span style="color: black;">&#93;</span>
    r, g, b = white
    <span style="color: #ff7700;font-weight:bold;">for</span> i <span style="color: #ff7700;font-weight:bold;">in</span> <span style="color: #008000;">range</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span>:
        ramp.<span style="color: black;">extend</span><span style="color: black;">&#40;</span><span style="color: black;">&#40;</span>r<span style="color: #66cc66;">*</span>i/<span style="color: #ff4500;">255</span>, g<span style="color: #66cc66;">*</span>i/<span style="color: #ff4500;">255</span>, b<span style="color: #66cc66;">*</span>i/<span style="color: #ff4500;">255</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">return</span> ramp
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> apply_sepia_filter<span style="color: black;">&#40;</span>image<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; Apply a sepia-tone filter to the given PIL Image
        Based on code at: http://effbot.org/zone/pil-sepia.htm
    &quot;&quot;&quot;</span>
    <span style="color: #808080; font-style: italic;"># make sepia ramp (tweak color as necessary)</span>
    sepia = make_linear_ramp<span style="color: black;">&#40;</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">255</span>, <span style="color: #ff4500;">240</span>, <span style="color: #ff4500;">192</span><span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># convert to grayscale</span>
    orig_mode = image.<span style="color: black;">mode</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> orig_mode <span style="color: #66cc66;">!</span>= <span style="color: #483d8b;">&quot;L&quot;</span>:
        image = image.<span style="color: black;">convert</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;L&quot;</span><span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># optional: apply contrast enhancement here, e.g.</span>
    <span style="color: #808080; font-style: italic;">#image = ImageOps.autocontrast(image)</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># apply sepia palette</span>
    image.<span style="color: black;">putpalette</span><span style="color: black;">&#40;</span>sepia<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #808080; font-style: italic;"># convert back to its original mode</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> orig_mode <span style="color: #66cc66;">!</span>= <span style="color: #483d8b;">&quot;L&quot;</span>:
        image = image.<span style="color: black;">convert</span><span style="color: black;">&#40;</span>orig_mode<span style="color: black;">&#41;</span>
&nbsp;
    <span style="color: #ff7700;font-weight:bold;">return</span> image
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> convert_image<span style="color: black;">&#40;</span>filename, make_backup<span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; convert an image at the given path to sepia tone.
        @param filename
        @param make_backup - if True, will copy original file to file.bak
    &quot;&quot;&quot;</span>
    <span style="color: #ff7700;font-weight:bold;">if</span> <span style="color: #ff7700;font-weight:bold;">not</span> <span style="color: #dc143c;">os</span>.<span style="color: black;">path</span>.<span style="color: black;">exists</span><span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span>:
        <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Skipping %s'</span> <span style="color: #66cc66;">%</span> filename
        <span style="color: #ff7700;font-weight:bold;">return</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Processing %s...'</span> <span style="color: #66cc66;">%</span> filename
    <span style="color: #ff7700;font-weight:bold;">if</span> make_backup:
        <span style="color: #dc143c;">shutil</span>.<span style="color: black;">copyfile</span><span style="color: black;">&#40;</span>filename, <span style="color: #483d8b;">'%s.bak'</span> <span style="color: #66cc66;">%</span> filename<span style="color: black;">&#41;</span>
    save_image<span style="color: black;">&#40;</span>apply_sepia_filter<span style="color: black;">&#40;</span>open_image<span style="color: black;">&#40;</span>filename<span style="color: black;">&#41;</span><span style="color: black;">&#41;</span>, filename<span style="color: black;">&#41;</span>
    <span style="color: #ff7700;font-weight:bold;">print</span> <span style="color: #483d8b;">'Done.'</span>
&nbsp;
<span style="color: #ff7700;font-weight:bold;">def</span> convert_images<span style="color: black;">&#40;</span>files, make_backup=<span style="color: #008000;">True</span><span style="color: black;">&#41;</span>:
    <span style="color: #483d8b;">&quot;&quot;&quot; convert the list of filenames to sepia tone.
        @param filename
        @param make_backup - if True, will copy original file to file.bak
    &quot;&quot;&quot;</span>
    <span style="color: #008000;">map</span><span style="color: black;">&#40;</span><span style="color: #ff7700;font-weight:bold;">lambda</span> f: convert_image<span style="color: black;">&#40;</span>f, make_backup<span style="color: black;">&#41;</span>, files<span style="color: black;">&#41;</span>
&nbsp;
&nbsp;
<span style="color: #ff7700;font-weight:bold;">if</span> __name__ == <span style="color: #483d8b;">'__main__'</span>:
    <span style="color: #dc143c;">parser</span> = OptionParser<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    <span style="color: #dc143c;">parser</span>.<span style="color: black;">add_option</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;-x&quot;</span>, <span style="color: #483d8b;">&quot;--no-backup&quot;</span>, dest=<span style="color: #483d8b;">&quot;no_backup&quot;</span>, default=<span style="color: #008000;">False</span>,
            action=<span style="color: #483d8b;">&quot;store_true&quot;</span><span style="color: black;">&#41;</span>
    <span style="color: black;">&#40;</span>options, files<span style="color: black;">&#41;</span> = <span style="color: #dc143c;">parser</span>.<span style="color: black;">parse_args</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>
    convert_images<span style="color: black;">&#40;</span>files, make_backup=<span style="color: #ff7700;font-weight:bold;">not</span> options.<span style="color: black;">no_backup</span><span style="color: black;">&#41;</span></pre></div></div>

<p>You can execute this from the command line as:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ python batch_sepia.py image1.jpg image2.jpg image3.jpg</pre></div></div>

<p>If you want to recursively apply the filter to a bunch of images, you might consider mixing this with some find/xargs-fu:</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;">$ <span style="color: #c20cb9; font-weight: bold;">find</span> <span style="color: #007800;">$HOME</span><span style="color: #000000; font-weight: bold;">/</span>pictures <span style="color: #660033;">-name</span> <span style="color: #ff0000;">&quot;*.jpg&quot;</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">xargs</span> python batch_sepia.py</pre></div></div>

<p>Once you&#8217;ve got your photos in order, head over to <a href="http://www.photoworks.com" target="_blank">photoworks.com</a> to get them printed!&lt;/shameless-plug&gt;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/01/batch-convert-images-to-sepia-tone-with-python/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
