<?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 &#187; software development</title>
	<atom:link href="http://www.mike-griffith.com/blog/category/software-development/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.mike-griffith.com/blog</link>
	<description>on software, testing, and the web.</description>
	<lastBuildDate>Mon, 14 Feb 2011 10:51:40 +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>Go fast!</title>
		<link>http://www.mike-griffith.com/blog/2010/08/go-fast/</link>
		<comments>http://www.mike-griffith.com/blog/2010/08/go-fast/#comments</comments>
		<pubDate>Tue, 10 Aug 2010 21:22:49 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[productivity]]></category>
		<category><![CDATA[quotes]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=552</guid>
		<description><![CDATA[ 

I was reading Kill Zone by Gunnery Sgt. Jack Coughlin, and was struck by the personal mantra of one of the Marine snipers:
Slow is smooth, smooth is fast.
Even though lives are not usually on the line, it&#8217;s important in the software industry to understand that sometimes we shouldn&#8217;t rush around like chickens with our [...]]]></description>
			<content:encoded><![CDATA[<div style="float:right; margin: 5px;"><a title="&quot;Going nowhere fast&quot;, or my $800 picture by NathanFromDeVryEET, on Flickr" target="_blank" href="http://www.flickr.com/photos/thatguyfromcchs08/2300190277/" title="&quot;Going nowhere fast&quot;, or my $800 picture by NathanFromDeVryEET, on Flickr"><img src="http://farm3.static.flickr.com/2036/2300190277_360853ae0d.jpg" alt="&quot;Going nowhere fast&quot;, or my $800 picture" width="250" height="184" /> </a>
</div>
<p>I was reading <em><a href="http://www.amazon.com/Kill-Zone-Sgt-Jack-Coughlin/dp/0312360185" target="_blank">Kill Zone</a></em> by Gunnery Sgt. Jack Coughlin, and was struck by the personal mantra of one of the Marine snipers:</p>
<blockquote><p>Slow is smooth, smooth is fast.</p></blockquote>
<p>Even though lives are not usually on the line, it&#8217;s important in the software industry to understand that sometimes we shouldn&#8217;t rush around like chickens with our heads cut off.  During your project&#8217;s crunch time, this is most important.</p>
<p>Slow down, take a breather, and think before you slam in a bunch of crap code.  All the technical debt you create has to be accounted for by someone.  Be the rabbit, not the hare.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/08/go-fast/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<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>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>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>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>3</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>
		<item>
		<title>Recent svn commit statistics</title>
		<link>http://www.mike-griffith.com/blog/2010/01/recent-svn-commit-statistics/</link>
		<comments>http://www.mike-griffith.com/blog/2010/01/recent-svn-commit-statistics/#comments</comments>
		<pubDate>Wed, 13 Jan 2010 21:23:36 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[scripts]]></category>
		<category><![CDATA[tips & tricks]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=320</guid>
		<description><![CDATA[Here&#8217;s a little script-fu that can help determine how many lines of code were added vs. deleted for a single Subversion commit.

#!/bin/bash
if &#91; -z &#34;$1&#34; &#93;; then
    echo &#34;usage: $0 revision&#34;
    exit
fi
&#160;
REV=$1
&#160;
# Execute a svn diff on the revision, and search the output for deleted lines
# (begin with a [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a little script-fu that can help determine how many lines of code were added vs. deleted for a single Subversion commit.</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/bin/bash</span>
<span style="color: #000000; font-weight: bold;">if</span> <span style="color: #7a0874; font-weight: bold;">&#91;</span> <span style="color: #660033;">-z</span> <span style="color: #ff0000;">&quot;$1&quot;</span> <span style="color: #7a0874; font-weight: bold;">&#93;</span>; <span style="color: #000000; font-weight: bold;">then</span>
    <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;usage: $0 revision&quot;</span>
    <span style="color: #7a0874; font-weight: bold;">exit</span>
<span style="color: #000000; font-weight: bold;">fi</span>
&nbsp;
<span style="color: #007800;">REV</span>=$<span style="color: #000000;">1</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># Execute a svn diff on the revision, and search the output for deleted lines</span>
<span style="color: #666666; font-style: italic;"># (begin with a '-', but aren't followed by another immediately),</span>
<span style="color: #666666; font-style: italic;"># and likewise with any added lines</span>
<span style="color: #007800;">DIFF</span>=<span style="color: #ff0000;">&quot;<span style="color: #007800;">$(svn diff -c $REV --diff-cmd=/usr/bin/diff)</span>&quot;</span>
<span style="color: #007800;">DEL_COUNTS</span>=$<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #660033;">-e</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$DIFF</span>&quot;</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">egrep</span> <span style="color: #ff0000;">'^[-][^-]'</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">wc</span> -lm<span style="color: #7a0874; font-weight: bold;">&#41;</span>
<span style="color: #007800;">ADD_COUNTS</span>=$<span style="color: #7a0874; font-weight: bold;">&#40;</span><span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #660033;">-e</span> <span style="color: #ff0000;">&quot;<span style="color: #007800;">$DIFF</span>&quot;</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">egrep</span> <span style="color: #ff0000;">'^[+][^+]'</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">wc</span> -lm<span style="color: #7a0874; font-weight: bold;">&#41;</span>
&nbsp;
<span style="color: #000000; font-weight: bold;">function</span> get_field <span style="color: #7a0874; font-weight: bold;">&#123;</span>
    <span style="color: #666666; font-style: italic;"># given an input string with 2 fields delimited by spaces, possibly with</span>
    <span style="color: #666666; font-style: italic;"># leading whitespace, return the field in the position specified.</span>
    <span style="color: #666666; font-style: italic;"># so,  `get_field &quot;   123   45678&quot; 1`  would return &quot;122&quot;</span>
    <span style="color: #666666; font-style: italic;"># and  `get_field &quot;   123   45678&quot; 2`  would return &quot;45678&quot;</span>
    <span style="color: #007800;">INPUT</span>=$<span style="color: #000000;">1</span>
    <span style="color: #007800;">POSITION</span>=$<span style="color: #000000;">2</span>
    <span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #007800;">$INPUT</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">sed</span> <span style="color: #ff0000;">&quot;s/^[ ]*\([0-9]*\)[ ]*\([0-9]*\)/\<span style="color: #000099; font-weight: bold;">\$</span>POSITION/g&quot;</span>
<span style="color: #7a0874; font-weight: bold;">&#125;</span>
&nbsp;
<span style="color: #007800;">DEL_LINES</span>=$<span style="color: #7a0874; font-weight: bold;">&#40;</span>get_field <span style="color: #ff0000;">&quot;<span style="color: #007800;">$DEL_COUNTS</span>&quot;</span> <span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
<span style="color: #007800;">DEL_CHARS</span>=$<span style="color: #7a0874; font-weight: bold;">&#40;</span>get_field <span style="color: #ff0000;">&quot;<span style="color: #007800;">$DEL_COUNTS</span>&quot;</span> <span style="color: #000000;">2</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
&nbsp;
<span style="color: #007800;">ADD_LINES</span>=$<span style="color: #7a0874; font-weight: bold;">&#40;</span>get_field <span style="color: #ff0000;">&quot;<span style="color: #007800;">$ADD_COUNTS</span>&quot;</span> <span style="color: #000000;">1</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
<span style="color: #007800;">ADD_CHARS</span>=$<span style="color: #7a0874; font-weight: bold;">&#40;</span>get_field <span style="color: #ff0000;">&quot;<span style="color: #007800;">$ADD_COUNTS</span>&quot;</span> <span style="color: #000000;">2</span><span style="color: #7a0874; font-weight: bold;">&#41;</span>
&nbsp;
<span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;Removed <span style="color: #007800;">$DEL_LINES</span> lines (<span style="color: #007800;">$DEL_CHARS</span> characters)&quot;</span>
<span style="color: #7a0874; font-weight: bold;">echo</span> <span style="color: #ff0000;">&quot;Added <span style="color: #007800;">$ADD_LINES</span> lines (<span style="color: #007800;">$ADD_CHARS</span> characters)&quot;</span></pre></div></div>

<p>Save this as <code>$HOME/bin/svnstat</code>, then execute it passing in a revision, e.g. <code>$ svnstat 1001</code></p>

<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">$ svnstat 61765
Removed 113 lines (4913 characters)
Added 63 lines (2975 characters)</pre></div></div>

<p>If you want to then see your daily net lines of code, hook this up to an <code>egrep</code>&#8216;d <code>svn log</code>, piped into <code>xargs</code>.</p>
<p><strong>Update 1/13/2010:</strong><br />
I cleaned up the &#8220;get_field&#8221; function to use a single <code>sed</code> command with backreferences rather than piping the string through <code>tr</code>, <code>sed</code>, and <code>cut</code>.</p>
<p>Also updated the initial calculation of <code>DIFF_COUNTS</code> and <code>ADD_COUNTS</code> to only require 1 single <code>svn diff</code> execution.  Used <code>echo -e</code> to solve the newline issue I was having on a first failed attempt.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/01/recent-svn-commit-statistics/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Developing AIR apps for the desktop using the Flex 3 SDK for FREE</title>
		<link>http://www.mike-griffith.com/blog/2010/01/developing-air-apps-for-the-desktop-using-the-flex-3-sdk-for-free/</link>
		<comments>http://www.mike-griffith.com/blog/2010/01/developing-air-apps-for-the-desktop-using-the-flex-3-sdk-for-free/#comments</comments>
		<pubDate>Fri, 08 Jan 2010 22:03:12 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
				<category><![CDATA[software development]]></category>
		<category><![CDATA[flex]]></category>

		<guid isPermaLink="false">http://www.mike-griffith.com/blog/?p=308</guid>
		<description><![CDATA[One of the greatest things about Flex and Air is the low barrier to entry for us software geeks.  There&#8217;s nothing stopping any non-flash software dev from writing some kickass desktop apps.  There&#8217;s no reason you have to buy anything to start writing, testing, and distributing Adobe Air apps.  You don&#8217;t need [...]]]></description>
			<content:encoded><![CDATA[<p>One of the greatest things about Flex and Air is the low barrier to entry for us software geeks.  There&#8217;s nothing stopping any non-flash software dev from writing some kickass desktop apps.  There&#8217;s no reason you have to buy anything to start writing, testing, and distributing Adobe Air apps.  You don&#8217;t need their <a href="http://www.adobe.com/go/buyflexbuilder_std" target="_blank">fancy (unsupported) IDE, FlexBuilder</a>.  You don&#8217;t need to purchase a code signing certificate.  You don&#8217;t have to pay fees to get into a app store.  All you need is a terminal, some links to good samples and tutorials, and this article to get you moving in the right (hopefully) direction.</p>
<p><strong>Disclaimer</strong><br />
I&#8217;ve only spent a grand total of about 3 hours researching and coding anything flash-related.  Any information distributed here could lead you down a long and lonesome path &#8212; however, more likely, it&#8217;ll get your feet wet enough so that you can take off on your own adventure.</p>
<p><strong>Prerequisistes:</strong></p>
<ol>
<li>Download and install the <a href="http://www.adobe.com/cfusion/entitlement/index.cfm?e=flex3sdk" target="_blank">Adobe Flex SDK</a>.  I&#8217;m using Adobe Flex version 3.4 on Ubuntu 9.10</li>
<li>Have basic familiarity with a command line terminal (Linux, cygwin, etc.)</li>
</ol>
<p>Good so far?  Great.  Here&#8217;s what I hope to help you understand, because it was what I struggled with.</p>
<ol>
<li>Filesystem layout for your application</li>
<li>Writing your application, coding mxml and ActionScript</li>
<li>Packaging and signing your application</li>
<li>Installing and running your new application</li>
</ol>
<p>There are a lot of other more advanced topics that I&#8217;d like to learn myself, including but not limited to:</p>
<ol>
<li>Using third party libraries (.swc&#8217;s)</li>
<li>Unit testing your application with ASUnit</li>
<li>Upgrading your life with ANT buildscripts</li>
</ol>
<p><strong>Steps to writing your first air application:</strong></p>
<ol>
<li>Create a directory to hold this project and its source code.

<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">    $ mkdir myairapp
    $ mkdir myairapp/src</pre></div></div>

</li>
<li>Generate a new .mxml file inside your source code folder.

<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">    $ vim myairapp/src/MyAirApp.mxml</pre></div></div>

<p>FlexBuilder admittedly helps a ton here, but you don&#8217;t need it.  This file defines what your application will look like, using XML markup to indicate panes, widgets, and all sorts of fun stuff.  However, there&#8217;s no reason it can&#8217;t be done in VIM or your favorite text editor.  This file (along with your actionscript files) will likely be the most volatile part of the project as you continue to code.  Here&#8217;s a basic template you can download and use.  Stick this in your root source folder.</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">&quot;1.0&quot;</span> <span style="color: #000066;">encoding</span>=<span style="color: #ff0000;">&quot;utf-8&quot;</span><span style="color: #000000; font-weight: bold;">?&gt;</span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:WindowedApplication</span> <span style="color: #000066;">xmlns:mx</span>=<span style="color: #ff0000;">&quot;http://www.adobe.com/2006/mxml&quot;</span> <span style="color: #000066;">xmlns:ns1</span>=<span style="color: #ff0000;">&quot;*&quot;</span></span>
<span style="color: #009900;">        <span style="color: #000066;">layout</span>=<span style="color: #ff0000;">&quot;absolute&quot;</span> <span style="color: #000066;">width</span>=<span style="color: #ff0000;">&quot;300&quot;</span> <span style="color: #000066;">height</span>=<span style="color: #ff0000;">&quot;200&quot;</span> <span style="color: #000066;">showTitleBar</span>=<span style="color: #ff0000;">&quot;false&quot;</span> <span style="color: #000066;">showStatusBar</span>=<span style="color: #ff0000;">&quot;false&quot;</span></span>
<span style="color: #009900;">        <span style="color: #000066;">currentState</span>=<span style="color: #ff0000;">&quot;load_state&quot;</span> <span style="color: #000066;">creationComplete</span>=<span style="color: #ff0000;">&quot;init()&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:states<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:State</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;load_state&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span><span style="color: #000000; font-weight: bold;">&lt;/mx:State<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/mx:states<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:Script<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #339933;">&lt;![CDATA[</span>
<span style="color: #339933;">        private function init():void {</span>
<span style="color: #339933;">        }</span>
<span style="color: #339933;">    ]]&gt;</span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/mx:Script<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
<span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/mx:WindowedApplication<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

</li>
<li>Update the &#8220;mx:states&#8221;, which will be used to represent different viewports/panes within this app.  Each state will have a button, and it will be a different color for each.  The first, named &#8220;load_state&#8221;, has a green button as defined by the &#8220;fillColors&#8221; attribute.  The second, named &#8220;clicked_state&#8221;, has a blue button.

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">     <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:states<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:State</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;load_state&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:AddChild</span> <span style="color: #000066;">position</span>=<span style="color: #ff0000;">&quot;lastChild&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
                <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:Button</span> <span style="color: #000066;">x</span>=<span style="color: #ff0000;">&quot;24&quot;</span> <span style="color: #000066;">y</span>=<span style="color: #ff0000;">&quot;36&quot;</span> <span style="color: #000066;">width</span>=<span style="color: #ff0000;">&quot;250&quot;</span> <span style="color: #000066;">height</span>=<span style="color: #ff0000;">&quot;122&quot;</span> <span style="color: #000066;">fillAlphas</span>=<span style="color: #ff0000;">&quot;[1.0, 1.0]&quot;</span></span>
<span style="color: #009900;">                            <span style="color: #000066;">fillColors</span>=<span style="color: #ff0000;">&quot;[#00FF00, #00FF00]&quot;</span> <span style="color: #000066;">label</span>=<span style="color: #ff0000;">&quot;Page 1&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/mx:AddChild<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/mx:State<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:State</span> <span style="color: #000066;">name</span>=<span style="color: #ff0000;">&quot;clicked_state&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:AddChild</span> <span style="color: #000066;">position</span>=<span style="color: #ff0000;">&quot;lastChild&quot;</span><span style="color: #000000; font-weight: bold;">&gt;</span></span>
                <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:Button</span> <span style="color: #000066;">x</span>=<span style="color: #ff0000;">&quot;24&quot;</span> <span style="color: #000066;">y</span>=<span style="color: #ff0000;">&quot;36&quot;</span> <span style="color: #000066;">width</span>=<span style="color: #ff0000;">&quot;250&quot;</span> <span style="color: #000066;">height</span>=<span style="color: #ff0000;">&quot;122&quot;</span> <span style="color: #000066;">fillAlphas</span>=<span style="color: #ff0000;">&quot;[1.0, 1.0]&quot;</span></span>
<span style="color: #009900;">                            <span style="color: #000066;">fillColors</span>=<span style="color: #ff0000;">&quot;[#0000FF, #0000FF]&quot;</span> <span style="color: #000066;">label</span>=<span style="color: #ff0000;">&quot;Page 2&quot;</span><span style="color: #000000; font-weight: bold;">/&gt;</span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/mx:AddChild<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
        <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/mx:state<span style="color: #000000; font-weight: bold;">&gt;</span></span></span>
    <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;/mx:states<span style="color: #000000; font-weight: bold;">&gt;</span></span></span></pre></div></div>

</li>
<li>Write some actionscript code to toggle between states when you click the buttons.  The currently visible one can be read and changed based on a global variable, `currentState`.

<div class="wp_syntax"><div class="code"><pre class="javascript" style="font-family:monospace;">        <span style="color: #003366; font-weight: bold;">private</span> <span style="color: #003366; font-weight: bold;">function</span> onState1ButtonClick<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">void</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #006600; font-style: italic;">// Go to the 2nd &quot;state&quot; when this button is clicked</span>
            currentState <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;clicked_state&quot;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #003366; font-weight: bold;">private</span> <span style="color: #003366; font-weight: bold;">function</span> onState2ButtonClick<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">:</span><span style="color: #000066; font-weight: bold;">void</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #006600; font-style: italic;">// Return to the initial &quot;state&quot; when this button is clicked</span>
            currentState <span style="color: #339933;">=</span> <span style="color: #3366CC;">&quot;load_state&quot;</span><span style="color: #339933;">;</span>
        <span style="color: #009900;">&#125;</span></pre></div></div>

<p>    Then, wire-up the events so that clicking will actually trigger the code.  Add a &#8220;click&#8221; attribute to the &#8220;mx:Button&#8221; element to match</p>

<div class="wp_syntax"><div class="code"><pre class="xml" style="font-family:monospace;">            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:Button</span> ... <span style="color: #000066;">click</span>=<span style="color: #ff0000;">&quot;onState1ButtonClick()&quot;</span> ... <span style="color: #000000; font-weight: bold;">/&gt;</span></span>
            <span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;mx:Button</span> ... <span style="color: #000066;">click</span>=<span style="color: #ff0000;">&quot;onState2ButtonClick()&quot;</span> ... <span style="color: #000000; font-weight: bold;">/&gt;</span></span></pre></div></div>

</li>
<li>The last thing you&#8217;ll need before you can play with your masterpiece is your &#8220;application.xml&#8221; file. Stick this in the main project directory, rather than in with your source code.

<div class="wp_syntax"><div class="code"><pre class="text" style="font-family:monospace;">    $ vim myairapp/MyAirApp-app.xml</pre></div></div>

<p>See the <a href="http://help.adobe.com/en_US/AIR/1.5/devappshtml/WS5b3ccc516d4fbf351e63e3d118666ade46-7ecc.html#WS5b3ccc516d4fbf351e63e3d118666ade46-7fad" target="_blank">Adobe AIR documentation</a> for an example.
</li>
</ol>
<p><strong>Packaging, code signing, generating .air, and installing</strong><br />
Your app is *almost* ready to run.  The basic steps to get the app transformeed from source to binary are:</p>
<ol>
<li>Generate .swf with amxmlc</li>
<li>Generate code signing certificate with adt (alternatively, if you&#8217;re a linux whizbang you can use openssl)</li>
<li>Tweak your application .xml.  Link to .swf from step #1</li>
<li>Package it up into .air with adt</li>
</ol>
<p>I&#8217;ve created a shell script to automate the package, code signing, and generating air step. Copy this into &#8220;myairapp/build.sh&#8221;</p>

<div class="wp_syntax"><div class="code"><pre class="bash" style="font-family:monospace;"><span style="color: #666666; font-style: italic;">#!/bin/bash</span>
<span style="color: #007800;">APP_NAME</span>=MyAirApp
<span style="color: #007800;">FLEX_BIN</span>=<span style="color: #000000; font-weight: bold;">/</span>opt<span style="color: #000000; font-weight: bold;">/</span>flex_sdk_3.4<span style="color: #000000; font-weight: bold;">/</span>bin
&nbsp;
<span style="color: #666666; font-style: italic;"># build a .swf</span>
<span style="color: #c20cb9; font-weight: bold;">mkdir</span> release
<span style="color: #007800;">$FLEX_BIN</span><span style="color: #000000; font-weight: bold;">/</span>amxmlc .<span style="color: #000000; font-weight: bold;">/</span>src<span style="color: #000000; font-weight: bold;">/</span><span style="color: #007800;">$APP_NAME</span>.mxml <span style="color: #660033;">-output</span> .<span style="color: #000000; font-weight: bold;">/</span>release<span style="color: #000000; font-weight: bold;">/</span><span style="color: #007800;">$APP_NAME</span>.swf
&nbsp;
<span style="color: #666666; font-style: italic;"># generate code signing certificate</span>
<span style="color: #007800;">$FLEX_BIN</span><span style="color: #000000; font-weight: bold;">/</span>adt <span style="color: #660033;">-certificate</span> <span style="color: #660033;">-cn</span> <span style="color: #007800;">$APP_NAME</span> <span style="color: #000000;">1024</span>-RSA cert.pfx <span style="color: #007800;">$APP_NAME</span>
&nbsp;
<span style="color: #666666; font-style: italic;"># package into .air</span>
<span style="color: #007800;">$FLEX_BIN</span><span style="color: #000000; font-weight: bold;">/</span>adt <span style="color: #660033;">-package</span> <span style="color: #660033;">-storetype</span> pkcs12 <span style="color: #660033;">-keystore</span> cert.pfx \
    <span style="color: #660033;">-storepass</span> <span style="color: #007800;">$APP_NAME</span> \
    .<span style="color: #000000; font-weight: bold;">/</span>release<span style="color: #000000; font-weight: bold;">/</span><span style="color: #007800;">$APP_NAME</span>.air .<span style="color: #000000; font-weight: bold;">/</span><span style="color: #007800;">$APP_NAME</span>-app.xml .<span style="color: #000000; font-weight: bold;">/</span>release<span style="color: #000000; font-weight: bold;">/</span><span style="color: #007800;">$APP_NAME</span>.swf \
    <span style="color: #666666; font-style: italic;"># Uncomment the following lines if you have icons that you have created</span>
    <span style="color: #666666; font-style: italic;">#-C assets icon_16.png \</span>
    <span style="color: #666666; font-style: italic;">#-C assets icon_32.png \</span>
    <span style="color: #666666; font-style: italic;">#-C assets icon_48.png</span></pre></div></div>

<p>Now execute the build script. Assuming there were no errors, your new application is ready to be installed.  Open up the newly generated .air file in the release directory, and the air installer should walk you through the install process.</p>
<p>If you want to start getting fancier, the next step you&#8217;ll want to do is externalize your ActionScript code.  All you have to do is create a &#8220;.as&#8221; file (or an entire directory if you want to break up your code into namespaces), stick it next to the .mxml, and then you&#8217;ll be able to &#8220;import&#8221; it and use functions, classes, etc from it.  I prefer to keep the actual event wiring within the .mxml code, but don&#8217;t let it get too cluttered.</p>
<p>Additionally, you&#8217;ll want to start adding assets (icons, images, sound files, etc) to your project.  Create a separate &#8220;assets&#8221; directory to hold them.</p>
<p>Additional Flex development references</p>
<ul>
<li><a href="http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK">http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK</a></li>
<li><a href="http://www.adobe.com/devnet/flex/">http://www.adobe.com/devnet/flex/</a></li>
<li><a href="http://flex.org/">http://flex.org/</a></li>
<li>To find great examples, search Google for &#8220;flex site:googlecode.com&#8221;</li>
</ul>
<p>More help with compiling and packaging:</p>
<ul>
<li><a href="http://www.adobe.com/devnet/air/articles/signing_air_applications_print.html">http://www.adobe.com/devnet/air/articles/signing_air_applications_print.html</a></li>
<li><a href="http://blog.devsandbox.co.uk/?p=163">http://blog.devsandbox.co.uk/?p=163</a></li>
<li><a href="http://livedocs.adobe.com/flex/3/html/help.html?content=compilers_13.html#150640">http://livedocs.adobe.com/flex/3/html/help.html?content=compilers_13.html#150640</a></li>
</ul>
<p>Best of luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.mike-griffith.com/blog/2010/01/developing-air-apps-for-the-desktop-using-the-flex-3-sdk-for-free/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

