<?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>Steven Luscher's blog</title>
	<atom:link href="http://steveluscher.com/feed" rel="self" type="application/rss+xml" />
	<link>http://steveluscher.com</link>
	<description></description>
	<lastBuildDate>Mon, 13 Apr 2009 08:06:30 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>The free software movement, and the GNU GPL, in the age of Javascript</title>
		<link>http://steveluscher.com/archives/the-free-software-movement-and-the-gnu-gpl-in-the-age-of-javascript</link>
		<comments>http://steveluscher.com/archives/the-free-software-movement-and-the-gnu-gpl-in-the-age-of-javascript#comments</comments>
		<pubDate>Mon, 13 Apr 2009 08:02:03 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Culture]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[law]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=272</guid>
		<description><![CDATA[In February 2009, I enjoyed the privilege of attending a lecture series by Dr. Richard Stallman, founder of the free software movement, and main author of the GNU General Public License – a copyleft free software license. With his lectures still fresh in my mind, I would like to wrestle with a lingering question, for [...]]]></description>
			<content:encoded><![CDATA[<p>In February 2009, I enjoyed the privilege of attending a lecture series by Dr. <a href="http://en.wikipedia.org/wiki/Richard_Stallman">Richard Stallman</a>, founder of the <a href="http://en.wikipedia.org/wiki/Free_software_movement">free software movement</a>, and main author of the <a href="http://www.fsf.org/licensing/licenses/gpl.html">GNU General Public License</a> – a <a href="http://en.wikipedia.org/wiki/Copyleft">copyleft</a> free software license. With his lectures still fresh in my mind, I would like to wrestle with a lingering question, for which I have not yet found a satisfying answer.<span id="more-272"></span></p>
<h3>A backgrounder on the free software movement</h3>
<p>Understand, first of all, that the free software movement aims not to abolish the sale of software, but to to protect a person&#8217;s freedom to use, inspect, modify, and share software. As they say: <a href="http://en.wikipedia.org/wiki/Gratis_versus_Libre">free, as in speech, not free, as in beer</a>. In order for a piece of software to conform to the free software foundation&#8217;s definition of free, its license must grant users of the software the following four freedoms:</p>
<blockquote>
<ul>
<li>The freedom to run the program, for any purpose (freedom 0).</li>
<li>The freedom to study how the program works, and adapt it to your needs (freedom 1). Access to the source code is a precondition for this.</li>
<li>The freedom to redistribute copies so you can help your neighbor (freedom 2).</li>
<li>The freedom to improve the program, and release your improvements (and modified versions in general) to the public, so that the whole community benefits (freedom 3). Access to the source code is a precondition for this.</li>
</ul>
<p><cite>Source: <a href="http://www.gnu.org/philosophy/free-sw.html">&#8220;The Free Software Definition&#8221;</a></cite></p></blockquote>
<p>The GNU <abbr title="General Public License">GPL</abbr> goes one step further to say that any time you redistribute software that is derivative or comprehensive of free software, your software must be redistributed under a GPL-compatible license. It is this share-alike provision and its implication for software based on interpreted programming languages like Javascript, whose usefulness is only truly realized when the source code is distributed over a network and executed on a client computer, that forms the subject of this essay.</p>
<h3>When use means use, and distribution means distribution</h3>
<p>The share-alike provision of the GNU <abbr title="General Public License">GPL</abbr> is designed to prevent middlemen from stripping away any of the four freedoms from free software, on its way to the end user. To be clear, a person is free to build a piece of software atop someone else&#8217;s GPL code, and to use that software to conduct business activity for profit; this would constitute the exercising of freedoms 0 and 1 – the freedom to run a program, for any purpose, and the freedom to adapt it to their needs. To subsequently exercise freedoms 2 and 3 is purely optional, but if the user chose to exercise their freedom to distribute copies of their modified software, and distributed those copies under a license that restricts any of the four freedoms that they themselves were treated to, they would be in violation of the GNU <abbr title="General Public License">GPL</abbr>&#8216;s share-alike provision.</p>
<h3>When use and distribution go hand in hand</h3>
<p>Considering the scenario I&#8217;ve just mentioned, what if the software in question is written in an interpreted language like Javascript, and built atop GNU <abbr title="General Public License">GPL</abbr> licensed Javascript code? Further, what if the person&#8217;s business is the provision of a web application – a type of application which, by virtue of its architecture, requires that Javascript source code travel over-the-wire to be executed on the client side, in order for the web application to be usable? Is the business owner exercising freedom 0, to run free software for any purpose, or do we say that she has exercised freedom 3, to distribute a modified copy of the software – her modifications now being subject to the share-alike provision of the GNU <abbr title="General Public License">GPL</abbr>?</p>
<h3>Intent only to use</h3>
<p>One might say that the business owner&#8217;s intent was to exercise freedoms 0 and 1 exclusively. The business owner may think of herself as the sole end user of the Javascript software – software that they use to provide a service to their customers over the Internet. Said service might be a dating service, or perhaps a service for ordering pizza delivery.</p>
<h3>De facto, though, the distribution is</h3>
<p>Whatever the business owner&#8217;s intent, the fact is that the architecture of a Javascript-based web application requires Javascript source code to be distributed over-the-wire in order for it to be useful; de facto distribution of Javascript source code occurs every time a web application is accessed. The users of the business&#8217; service are, in fact, the end users of the business&#8217; modified version of the original, free program.</p>
<h3>Freedom isn&#8217;t free</h3>
<p>Chances are that one of those arguments will have seen readers get a little hot under the collar, so let&#8217;s relax for a moment. By now we all understand the principles of free software, and our business owner surely wants to uphold the agreement that she entered into when she chose to build her software atop GNU <abbr title="General Public License">GPL</abbr> code. We&#8217;re all essentially on the same side, but because of the nature of the web, we find that we&#8217;ve been stripped of our agency to decide when to exercise our freedom to <em>use</em> free software, and when to exercise our freedom to <em>distribute</em> it. The two have become inseparable; client side Javascript is only usable when distributed.</p>
<p>There exist some edge cases where Javascript code need not be distributed to be useful. You could wrap it in an Adobe AIR or Silverlight application for your own personal use. You could use Rhino to embed it in a Java application for your own personal use. You could even build an application that runs in a browser, but only ever load it locally. These examples are specious, though. However plausible, the choice to use Javascript in these instances would likely not be the best available one.</p>
<p>For the remaining, typical, client-server, web-centric uses of Javascript, it would seem to me that <strong>there exists no opportunity for the creator of a derivative work based on GNU <abbr title="General Public License">GPL</abbr> Javascript code to derive value from their program without being forced to exercise freedom 3 by virtue of the consequential distribution of Javascript source code</strong>. Proponents of free software <a href="http://www.gnu.org/philosophy/javascript-trap.html">call Javascript a trap</a>. I sense that &#8220;trapped&#8221; is how most of us feel about the deadlock between the Javascript community and the free software movement.</p>
<h3>Repercussions and implications</h3>
<p>The licenses of the <a href="http://www.fsf.org/">free software foundation</a> have found themselves the target of a number of Javascript developers&#8217; ire. When Jack Slocum&#8217;s ExtJS project relicensed their Javascript library under the GNU <abbr title="General Public License">GPL</abbr>, <a href="http://extjs.com/blog/2008/04/21/ext-js-21-and-ext-gwt-10-released-preview-of-ext-js-30/#comments">their community fractured</a> (of interest is <a href="http://www.codemonkeyism.com/archives/2008/04/28/more-on-extjs-the-gpl-fiasco-and-open-source-community-style/#comments">this conversation between Jack Slocum and jQuery&#8217;s John Resig</a>). Google around and you will find Javascript developers blogging the GNU <abbr title="General Public License">GPL</abbr> as &#8220;sneaky,&#8221; or &#8220;a virus.&#8221; This is likely a consequence of people failing to recognize that the explicit aim of the GNU <abbr title="General Public License">GPL</abbr> is not to sneakily restrict the freedoms of <em>developers</em>, but to guarantee the freedoms of <em>users</em>, of whom developers (don&#8217;t forget) are a subset. Put another way, copyleft, share-alike licenses like the GNU <abbr title="General Public License">GPL</abbr> concern themselves with user freedoms, while more permissive, BSD style licenses concern themselves with developer freedoms. Since BSD style licenses permit the total appropriation of free software by middlemen, and the GNU <abbr title="General Public License">GPL</abbr> doesn&#8217;t, one might argue that GNU <abbr title="General Public License">GPL</abbr> licensed projects are more sustainable. However true elsewhere, this has <a href="http://en.wikipedia.org/wiki/Comparison_of_JavaScript_frameworks">not proven to be the case with Javascript libraries</a>.</p>
<p>Perhaps the decision facing the Javascript developer who wants to build software atop GNU <abbr title="General Public License">GPL</abbr> Javascript code involves choosing the lesser of two evils.</p>
<p>Consider the case of the pizza parlor, who uses a web application based on GNU <abbr title="General Public License">GPL</abbr> Javascript, with custom modifications, to improve the efficiency with which they can deliver their service, namely: tasty pizza to your door in 30 minutes or less. If the employees of the pizza parlor used the web application to process the order of someone who called in over the telephone, there would be no problem – the parlor is exercising their freedom to run the program without distributing it. The moment they cut out the telephone, and expose that web application to the public so that customers can key in their own orders, de facto distribution has occurred, and the pizza parlor&#8217;s modifications are subject to the share-alike provision of the GNU <abbr title="General Public License">GPL</abbr>.</p>
<p>That being the case, it&#8217;s not clear that this is anything to fear. If the core business of the pizza parlor is the delivery of tasty pizza, and not the provision of software to manage a pizza parlor, should they be afraid of sharing their modifications? If the user&#8217;s core business is something other than the provision of software, it&#8217;s likely that their software modifications are trivial, or at least peripheral to their core competency – an animation class that makes a wedge of pizza fly across the screen, or a validation class that validates form input on the client side. In this case, the lesser evil may be to embrace the conditions that force her to share-alike, in exchange for the benefits the business owner enjoyed by having access to the GNU <abbr title="General Public License">GPL</abbr> Javascript upon which she based her application in the first place.</p>
<p>On the other hand, if your business actually is the provision of an online pizza parlor management service, then you&#8217;re probably in the business of providing software as a service, and ultimately, because of the nature of the web, the business of distributing software; a move that will reliably trigger the share-alike provision of the GNU <abbr title="General Public License">GPL</abbr>. In this form, we find that the GNU <abbr title="General Public License">GPL</abbr> functions as intended: to discourage the incorporation of free Javascript software into proprietary Javascript software, of which software as a service is a part. No surprises there.</p>
<h3>A world where all users are free, except those who happen to be less free than the rest</h3>
<p>A creator of a web application which incorporates client-side GNU <abbr title="General Public License">GPL</abbr> Javascript code may think of themselves as the end user of that web application, but they&#8217;re not; the architecture of the network forces them to distribute their modified Javascript source code in order for their application to deliver value to them. Though circumstance alone triggers the share-alike provision, it&#8217;s probably also the case that the creator should not fear having to share their client-side code, since their competitive advantage should come from the work they do in the back office (or, in the case of the pizza parlor, the kitchen).</p>
<p>If distribution, as a precondition of use, is going to trigger the share-alike provision for all web-delivered Javascript software, then so be it. There&#8217;s something unsatisfying, though, about knowing that all users of free software are free to exercise their freedom to adapt free software, and to run that software for any purpose, without being required to exercise their freedom to distribute their modified version… except if the purpose requires that it execute on a remote, client runtime.</p>
<p>But after all, it&#8217;s about being free, as in speech, not free, as in a decaf grande half-soy, half-low fat, iced vanilla, double-shot, gingerbread cappuccino, extra dry, light ice, with one Sweet-n&#8217;-Low and one NutraSweet.</p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/the-free-software-movement-and-the-gnu-gpl-in-the-age-of-javascript/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Scheduling the recording of an Internet radio stream with VLC</title>
		<link>http://steveluscher.com/archives/scheduling-the-recording-of-an-internet-radio-stream-with-vlc</link>
		<comments>http://steveluscher.com/archives/scheduling-the-recording-of-an-internet-radio-stream-with-vlc#comments</comments>
		<pubDate>Fri, 20 Mar 2009 23:47:14 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[music]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=356</guid>
		<description><![CDATA[I played a show on UBC&#8217;s CiTR radio yesterday with my band, Lakefield. I was planning to record it with something like Rogue Amoeba&#8217;s Radioshift, until my brother asked me for &#8220;the magic UNIX incantation that will make VLC record the broadcast.&#8221; I didn&#8217;t know that you can schedule recordings with VLC! Opening the door [...]]]></description>
			<content:encoded><![CDATA[<p>I played a show on UBC&#8217;s <a href="http://citr.ca">CiTR radio</a> yesterday with my band, <a href="http://lakefieldmusic.com">Lakefield</a>. I was planning to record it with something like Rogue Amoeba&#8217;s <a href="http://rogueamoeba.com/radioshift/">Radioshift</a>, until my brother asked me for &#8220;the magic UNIX incantation that will make <a href="http://www.videolan.org/vlc/">VLC</a> record the broadcast.&#8221; I didn&#8217;t know that you can schedule recordings with VLC!<span id="more-356"></span></p>
<h2>Opening the door to VLC&#8217;s secret telnet interface</h2>
<p>Alright, it&#8217;s not so secret. For my purposes, though, it was rather poorly documented.</p>
<p><div id="attachment_371" class="wp-caption alignright" style="width: 249px"><a href="http://steveluscher.com/wp-content/uploads/2009/03/enable-vlcs-telnet-interface.png"><img class="size-medium wp-image-371" title="enable-vlcs-telnet-interface" src="http://steveluscher.com/wp-content/uploads/2009/03/enable-vlcs-telnet-interface-239x165.png" alt="Enabling VLC's Telnet Interface on Mac" width="239" height="165" /></a><p class="wp-caption-text">Enabling VLC&#39;s Telnet Interface on Mac</p></div></p>
<p>First thing to do, is to launch VLC, and choose (VLC > Add Interface > Telnet Interface) from the menu bar. For non-Mac users, I believe this option lives under the &#8220;Settings&#8221; menu.</p>
<p><strong>Don&#8217;t quit VLC at this point!</strong> You will need to keep it running from now until the end of your scheduled recording. Also, make sure that your computer is not set to sleep after a period of idle time, and that your housemates know not to shut the computer down or put it to sleep.</p>
<h2>Connecting to VLC through telnet</h2>
<p>VLC opens a telnet interface at port 4212 of your local machine. Mac users can launch the Terminal application (typically found in the &#8220;Utilities&#8221; subfolder of your &#8220;Applications&#8221; folder) to make a connection.</p>
<p>Open Terminal and type the following:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">$ telnet localhost 4212</pre></div></div>

<p>The password, by default, is <code>admin</code>.</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">Trying ::1...
Connected to localhost.
Escape character is '^]'.
Password:
Welcome, Master
&gt;</pre></div></div>

<p>Once you&#8217;re in, you can start issuing commands directly to the VLC client.</p>
<h2>Defining a broadcast</h2>
<p>The &#8220;magic incantation&#8221; to define a broadcast, and the file to which you would like it saved, is as follows:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">&gt; new {name_of_broadcast} broadcast enabled input {url_of_broadcast} output #std{access=file,mux=raw,dst={path_to_which_to_save_broadcast}}</pre></div></div>

<p>Phew! In my case, where the broadcast I wanted to save streams from <a href="http://live.citr.ca:8000/stream.mp3">http://live.citr.ca:8000/stream.mp3</a>, and I wanted VLC to save the recording to my Desktop folder, the incantation went as follows:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">&gt; new citr_radio broadcast enabled input http://live.citr.ca:8000/stream.mp3 output #std{access=file,mux=raw,dst=/Users/sluscher/Desktop/lakefield_show_stream.mp3}</pre></div></div>

<p>I called my broadcast <code>citr_radio</code>, but you can call yours whatever you like. Also, make sure that the path to your destination file exists.</p>
<h2>Testing, testing – 1-2-3</h2>
<p>At this point, the recording is ready for scheduling, but before we do that, let&#8217;s make sure that we spoke the magic words correctly. Here, we&#8217;ll use the <code>control</code> directive to test out our broadcast recorder. Incant the following:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">&gt; control citr_radio play</pre></div></div>

<p>A file should appear at your destination path, and VLC should be dumping your broadcast into it. Let the tape roll for a few seconds, and then type this to stop it:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">&gt; control citr_radio stop</pre></div></div>

<p>If all went well (and VLC doesn&#8217;t report any errors when you bring it to the foreground) then you should be able to open the file that was created with VLC and listen to what you&#8217;ve recorded!</p>
<h2>Scheduling the recording</h2>
<p>Once you&#8217;ve demonstrated that you can manually start and stop the recording, it&#8217;s time to schedule a recording. The scheduling syntax looks like so:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">&gt; new {name_of_schedule} schedule enabled date YYYY/MM/DD-HH:MM:SS append {command_to_run}</pre></div></div>

<p>The time is in 24 hour clock, based on your system clock, and <code>name_of_schedule</code> can be anything you like. In my case, I wanted to schedule a start and a stop coincident with my band&#8217;s performance on the radio:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">&gt; new lakefield_show_start schedule enabled date 2009/03/19-21:00:00 append control citr_radio play
&gt; new lakefield_show_end schedule enabled date 2009/03/19-23:00:30 append control citr_radio stop</pre></div></div>

<p>Once you&#8217;ve set the trap, you can conclude your telnet conversation with VLC by typing:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">&gt; exit</pre></div></div>

<p>Feel free to quit Terminal at this point, but leave VLC (and your computer) running until the end of the broadcast.</p>
<h2>That&#8217;s that!</h2>
<p>Here&#8217;s the whole magic spell:</p>

<div class="wp_syntax"><div class="code"><pre class="terminal" style="font-family:monospace;">$ telnet localhost 4212
Trying ::1...
Connected to localhost.
Escape character is '^]'.
Password: [the password is &quot;admin&quot;]
Welcome, Master
&gt; new citr_radio broadcast enabled input http://live.citr.ca:8000/stream.mp3 output #std{access=file,mux=raw,dst=/Users/sluscher/Desktop/lakefield_show_stream.mp3}
&gt; new lakefield_show_start schedule enabled date 2009/03/19-21:00:00 append control citr_radio play
&gt; new lakefield_show_end schedule enabled date 2009/03/19-23:00:30 append control citr_radio stop
&gt; exit
Connection closed by foreign host.
$</pre></div></div>

<p>…and for your listening pleasure, here is Lakefield on CiTR&#8217;s Thunderbird Radio Hell!</p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/scheduling-the-recording-of-an-internet-radio-stream-with-vlc/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
<enclosure url="http://steveluscher.com/wp-content/uploads/2009/03/live-on-citrs-thunderbird-radio-hell-march-19-2009.mp3" length="" type="" />
		</item>
		<item>
		<title>Changing a time&#8217;s zone in Rails, keeping the same local representation</title>
		<link>http://steveluscher.com/archives/changing-a-times-zone-in-rails-keeping-the-same-local-representation</link>
		<comments>http://steveluscher.com/archives/changing-a-times-zone-in-rails-keeping-the-same-local-representation#comments</comments>
		<pubDate>Sun, 22 Feb 2009 08:47:01 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=271</guid>
		<description><![CDATA[The TimeWithZone class in Rails is great to work with, but I found it to be missing one thing. I needed a method for those &#8220;whoops, I meant 3:00am PST&#8221; moments. Let&#8217;s say that you&#8217;ve ended up with a time like March 8 2009 03:00 UTC, where you meant to say PDT instead of UTC. [...]]]></description>
			<content:encoded><![CDATA[<p>The TimeWithZone class in Rails is great to work with, but I found it to be missing one thing. I needed a method for those &#8220;whoops, I meant 3:00am <em>PST</em>&#8221; moments.<span id="more-271"></span></p>
<p>Let&#8217;s say that you&#8217;ve ended up with a time like March 8 2009 03:00 UTC, where you meant to say PDT instead of UTC. Wouldn&#8217;t it be nice to be able to change only the zone part of the time, without changing the local representation, like so:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby ruby" style="font-family:monospace;"><span style="color:#006600; font-weight:bold;">&gt;&gt;</span> t = <span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">zone</span>.<span style="color:#9900CC;">parse</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#996600;">'March 8 3am'</span><span style="color:#006600; font-weight:bold;">&#41;</span>
<span style="color:#006600; font-weight:bold;">=&gt;</span> Sun, 08 Mar <span style="color:#006666;">2009</span> 03:00:00 UTC <span style="color:#006600; font-weight:bold;">+</span>00:00
<span style="color:#006600; font-weight:bold;">&gt;&gt;</span> t.<span style="color:#9900CC;">zone</span> = <span style="color:#996600;">'America/Los_Angeles'</span>
<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">&quot;America/Los_Angeles&quot;</span>
&nbsp;
<span style="color:#006600; font-weight:bold;">&gt;&gt;</span> t                            <span style="color:#008000; font-style:italic;"># It's 3:00am in Vancouver!</span>
<span style="color:#006600; font-weight:bold;">=&gt;</span> Sun, 08 Mar <span style="color:#006666;">2009</span> 03:00:00 PDT <span style="color:#006600; font-weight:bold;">-</span>07:00
<span style="color:#006600; font-weight:bold;">&gt;&gt;</span> t.<span style="color:#9900CC;">zone</span> = <span style="color:#996600;">'America/Denver'</span>; t <span style="color:#008000; font-style:italic;"># It's 3:00am in Edmonton!</span>
<span style="color:#006600; font-weight:bold;">=&gt;</span> Sun, 08 Mar <span style="color:#006666;">2009</span> 03:00:00 MDT <span style="color:#006600; font-weight:bold;">-</span>06:00
<span style="color:#006600; font-weight:bold;">&gt;&gt;</span> t.<span style="color:#9900CC;">zone</span> = <span style="color:#996600;">'Saskatchewan'</span>; t   <span style="color:#008000; font-style:italic;"># It's 3:00am everywhere!</span>
<span style="color:#006600; font-weight:bold;">=&gt;</span> Sun, 08 Mar <span style="color:#006666;">2009</span> 03:00:00 CST <span style="color:#006600; font-weight:bold;">-</span>06:00</pre></div></div>

<p>You can, if you add this method to ActiveSupport::TimeWithZone:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby ruby" style="font-family:monospace;"><span style="color:#9966CC; font-weight:bold;">module</span> ActiveSupport
  <span style="color:#9966CC; font-weight:bold;">class</span> TimeWithZone
    <span style="color:#9966CC; font-weight:bold;">def</span> zone=<span style="color:#006600; font-weight:bold;">&#40;</span>new_zone = ::<span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">zone</span><span style="color:#006600; font-weight:bold;">&#41;</span>
      <span style="color:#008000; font-style:italic;"># Reinitialize with the new zone and the local time</span>
      initialize<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">nil</span>, ::<span style="color:#CC00FF; font-weight:bold;">Time</span>.__send__<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:get_zone</span>, new_zone<span style="color:#006600; font-weight:bold;">&#41;</span>, time<span style="color:#006600; font-weight:bold;">&#41;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>In my application, my users set start and end times for their events. I was able to make use of this new <code>zone=</code> method when they choose a new time zone for their event; it lets me preserve the local representation of the existing start and end times between time zone changes.</p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/changing-a-times-zone-in-rails-keeping-the-same-local-representation/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Localized range formats with Rails</title>
		<link>http://steveluscher.com/archives/localized-range-formats-with-rails</link>
		<comments>http://steveluscher.com/archives/localized-range-formats-with-rails#comments</comments>
		<pubDate>Tue, 17 Feb 2009 05:16:18 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=294</guid>
		<description><![CDATA[Here&#8217;s an implementation of Range#to_formatted_s that lets you retrieve range formats from your locale bank. It&#8217;s true that you can write your own range conversions and add them to ::Range::RANGE_FORMATS. Those conversions could refer to translations in your locale bank, but I&#8217;d prefer to see what the range formatter is doing without having to look [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s an implementation of <code>Range#to_formatted_s</code> that lets you retrieve range formats from your locale bank.<span id="more-294"></span></p>
<p>It&#8217;s true that you can <a href="http://railspikes.com/2008/3/1/cleaner-code-with-conversions">write your own range conversions</a> and add them to <code>::Range::RANGE_FORMATS</code>. Those conversions could refer to translations in your locale bank, but I&#8217;d prefer to see what the range formatter is doing without having to look in two places. I wanted a formatter that would do something like this, where <code>:fancy</code> can represent a different <code>Proc</code> depending on the current locale:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby ruby" style="font-family:monospace;"><span style="color:#006600; font-weight:bold;">&gt;&gt;</span> <span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">current</span>..<span style="color:#CC00FF; font-weight:bold;">Time</span>.<span style="color:#9900CC;">current</span><span style="color:#006600; font-weight:bold;">+</span>1.<span style="color:#9900CC;">hour</span><span style="color:#006600; font-weight:bold;">&#41;</span>.<span style="color:#9900CC;">to_s</span> <span style="color:#ff3333; font-weight:bold;">:fancy</span>
<span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#996600;">&quot;Monday, February 16th, 2009 between 6:00pm and 7:00pm PST&quot;</span></pre></div></div>

<p>First, I implemented a version of <code>Range#to_formatted_s</code> that&#8217;s I18n aware (thanks <a href="http://github.com/clemens/localized_dates/blob/8bc85aa33b9b4d96d42957e1a8c9e97be1d756cf/lib/core_ext/date.rb">clemens</a>!).</p>
<p><em>config/initializers/range_conversions.rb:</em></p>

<div class="wp_syntax"><div class="code"><pre class="ruby ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># Allows you to do things like (Time.current..Time.current+1.day).to_s :local_format</span>
<span style="color:#008000; font-style:italic;"># where range.format.local_format is an entry in one of your locale files</span>
::<span style="color:#CC00FF; font-weight:bold;">Range</span>.<span style="color:#9900CC;">class_eval</span> <span style="color:#9966CC; font-weight:bold;">do</span>
  <span style="color:#9966CC; font-weight:bold;">def</span> to_formatted_s<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#CC0066; font-weight:bold;">format</span> = <span style="color:#ff3333; font-weight:bold;">:default</span><span style="color:#006600; font-weight:bold;">&#41;</span>
    formats = ::<span style="color:#6666ff; font-weight:bold;">Range::RANGE_FORMATS</span>
    formatter = formats<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#CC0066; font-weight:bold;">format</span><span style="color:#006600; font-weight:bold;">&#93;</span>
&nbsp;
    <span style="color:#9966CC; font-weight:bold;">unless</span> formatter
      formatters = I18n.<span style="color:#9900CC;">translate</span><span style="color:#006600; font-weight:bold;">&#40;</span>:<span style="color:#996600;">'range.formats'</span>, <span style="color:#ff3333; font-weight:bold;">:raise</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#0000FF; font-weight:bold;">true</span><span style="color:#006600; font-weight:bold;">&#41;</span> <span style="color:#9966CC; font-weight:bold;">rescue</span> <span style="color:#006600; font-weight:bold;">&#123;</span><span style="color:#006600; font-weight:bold;">&#125;</span>
      formatter  = formatters<span style="color:#006600; font-weight:bold;">&#91;</span><span style="color:#CC0066; font-weight:bold;">format</span><span style="color:#006600; font-weight:bold;">&#93;</span>
    <span style="color:#9966CC; font-weight:bold;">end</span>
&nbsp;
    formatter.<span style="color:#9900CC;">respond_to</span>?<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#ff3333; font-weight:bold;">:call</span><span style="color:#006600; font-weight:bold;">&#41;</span> ? formatter.<span style="color:#9900CC;">call</span><span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9966CC; font-weight:bold;">begin</span>, <span style="color:#0000FF; font-weight:bold;">self</span>.<span style="color:#9966CC; font-weight:bold;">end</span><span style="color:#006600; font-weight:bold;">&#41;</span> : to_default_s
  <span style="color:#9966CC; font-weight:bold;">end</span>
  alias_method <span style="color:#ff3333; font-weight:bold;">:to_s</span>, <span style="color:#ff3333; font-weight:bold;">:to_formatted_s</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

<p>Next, I edited my locale file:</p>
<p><em>config/locales/en.rb:</em></p>

<div class="wp_syntax"><div class="code"><pre class="ruby ruby" style="font-family:monospace;"><span style="color:#006600; font-weight:bold;">&#123;</span> <span style="color:#ff3333; font-weight:bold;">:en</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
&nbsp;
  <span style="color:#008000; font-style:italic;"># Range</span>
  <span style="color:#ff3333; font-weight:bold;">:range</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
    <span style="color:#ff3333; font-weight:bold;">:formats</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#006600; font-weight:bold;">&#123;</span>
      <span style="color:#ff3333; font-weight:bold;">:fancy</span> <span style="color:#006600; font-weight:bold;">=&gt;</span> <span style="color:#CC0066; font-weight:bold;">lambda</span> <span style="color:#006600; font-weight:bold;">&#123;</span> |start_time, finish_time|
        start_format = <span style="color:#996600;">&quot;%B #{start_time.day.ordinalize}, %Y between %l:%M%p&quot;</span>
        finish_format = <span style="color:#996600;">&quot;%l:%M%p %Z&quot;</span>
&nbsp;
         <span style="color:#996600;">&quot;#{I18n.localize(start_time, :format =&gt; start_format)} and #{I18n.localize(finish_time, :format =&gt; finish_format)}&quot;</span>
      <span style="color:#006600; font-weight:bold;">&#125;</span>
    <span style="color:#006600; font-weight:bold;">&#125;</span>
  <span style="color:#006600; font-weight:bold;">&#125;</span>
&nbsp;
<span style="color:#006600; font-weight:bold;">&#125;</span> <span style="color:#006600; font-weight:bold;">&#125;</span></pre></div></div>

<p>And there you have it; fancy-formatted date ranges with localization.</p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/localized-range-formats-with-rails/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Enumerating annotations in directories of your choosing with rake:notes</title>
		<link>http://steveluscher.com/archives/enumerating-annotations-in-directories-of-your-choosing-with-rakenotes</link>
		<comments>http://steveluscher.com/archives/enumerating-annotations-in-directories-of-your-choosing-with-rakenotes#comments</comments>
		<pubDate>Sun, 08 Feb 2009 02:37:36 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=242</guid>
		<description><![CDATA[By default, rake:notes searches for annotations in the app, lib, and test directories. If you have code in other directories that you would like to be searched for annotations, you can override rake:notes&#8216; defaults by overriding SourceAnnotationExtractor#find. I use RSpec and Cucumber, and wanted rake:notes to traverse my spec and features directories. To achieve this, [...]]]></description>
			<content:encoded><![CDATA[<p>By default, <code>rake:notes</code> searches for annotations in the <code>app</code>, <code>lib</code>, and <code>test</code> directories. If you have code in <em>other</em> directories that you would like to be searched for annotations, you can override <code>rake:notes</code>&#8216; defaults by overriding <code>SourceAnnotationExtractor#find</code>.<span id="more-242"></span></p>
<p>I use <a href="http://rspec.info/">RSpec</a> and <a href="http://cukes.info/">Cucumber</a>, and wanted <code>rake:notes</code> to traverse my <code>spec</code> and <code>features</code> directories. To achieve this, I created a file called <code>lib/tasks/notes.rake</code>:</p>

<div class="wp_syntax"><div class="code"><pre class="ruby ruby" style="font-family:monospace;"><span style="color:#008000; font-style:italic;"># Override the directories that the</span>
<span style="color:#008000; font-style:italic;"># SourceAnnotationExtractor traverses</span>
<span style="color:#008000; font-style:italic;"># when you call rake notes</span>
<span style="color:#9966CC; font-weight:bold;">class</span> SourceAnnotationExtractor
  <span style="color:#9966CC; font-weight:bold;">def</span> find_with_custom_directories
    find_without_custom_directories<span style="color:#006600; font-weight:bold;">&#40;</span><span style="color:#006600; font-weight:bold;">%</span>w<span style="color:#006600; font-weight:bold;">&#40;</span>app lib spec features<span style="color:#006600; font-weight:bold;">&#41;</span><span style="color:#006600; font-weight:bold;">&#41;</span>
  <span style="color:#9966CC; font-weight:bold;">end</span>
  alias_method_chain <span style="color:#ff3333; font-weight:bold;">:find</span>, <span style="color:#ff3333; font-weight:bold;">:custom_directories</span>
<span style="color:#9966CC; font-weight:bold;">end</span></pre></div></div>

]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/enumerating-annotations-in-directories-of-your-choosing-with-rakenotes/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AJAX requests, X-Requested-With headers, and unexpected cache contents</title>
		<link>http://steveluscher.com/archives/ajax-requests-x-requested-with-headers-and-unexpected-cache-contents</link>
		<comments>http://steveluscher.com/archives/ajax-requests-x-requested-with-headers-and-unexpected-cache-contents#comments</comments>
		<pubDate>Mon, 29 Dec 2008 10:36:33 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[ajax]]></category>
		<category><![CDATA[bugs]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=196</guid>
		<description><![CDATA[I stumbled upon some curious browser behavior yesterday, and would like to share it here, lest it save someone a few hours of debugging. This story is about JSON requests, HTTP header based context switching, and browser caching. The scenario I was working on a live search plugin for WordPress; one that displays matching results [...]]]></description>
			<content:encoded><![CDATA[<p>I stumbled upon some curious browser behavior yesterday, and would like to share it here, lest it save someone a few hours of debugging. This story is about JSON requests, HTTP header based context switching, and browser caching.<span id="more-196"></span></p>
<h3>The scenario</h3>
<p>I was working on a live search plugin for WordPress; one that displays matching results below the search box as you type. The PHP part of the plugin was designed to hook into WordPress&#8217; search facility and make it output JSON, for the Javascript part of the plugin to consume and display. The hook was designed to run only if a search was initiated by an AJAX request. To detect such a request, and implement the context switch, it made use of <a href="http://github.com/mootools/mootools-core/tree/23f4c1f09e0034393454e527c04a2697ccde87bb/Source/Request/Request.js#L23">the &#8220;<code>X-Requested-With</code>&#8221; header sent by the MooTools framework</a>:</p>

<div class="wp_syntax"><div class="code"><pre class="php php" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">function</span> json_requested<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #b1b100;">return</span>  <span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="">'HTTP_X_REQUESTED_WITH'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span>
          <span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="">'HTTP_X_REQUESTED_WITH'</span><span style="color: #009900;">&#93;</span>  <span style="color: #339933;">==</span> <span style="">'XMLHttpRequest'</span> <span style="color: #339933;">&amp;&amp;</span>
&nbsp;
          <span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="">'HTTP_ACCEPT'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">&amp;&amp;</span>
          <span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="">'HTTP_ACCEPT'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">==</span> <span style="">'application/json'</span>;
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">json_requested</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #666666; font-style: italic;">// Output the query as JSON</span>
  add_action<span style="color: #009900;">&#40;</span><span style="">'wp'</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$this</span><span style="color: #339933;">,</span> <span style="">'output_search_results_as_json'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>;
<span style="color: #009900;">&#125;</span></pre></div></div>

<h3>The bug</h3>
<p><div id="attachment_199" class="wp-caption alignright" style="width: 250px"><a href="http://steveluscher.com/wp-content/uploads/2008/12/json_response_cached.png"><img class="size-medium wp-image-199" title="Cached JSON response instead of expected HTML response" src="http://steveluscher.com/wp-content/uploads/2008/12/json_response_cached-240x191.png" alt="Revisiting the search results page using the browser's back button results in the regurgitation of the content that last responded at that URL, expected format or not." width="240" height="191" /></a><p class="wp-caption-text">An unexpected response received upon revisiting the search results page using the back button.</p></div></p>
<p>This worked quite well, but my client began to notice an intermittent bug. On occasion, when making a non-AJAX request for the search results page, then leaving the search results page by following a link, then clicking the browser&#8217;s back button to return, the browser would display (Internet Explorer 6) or begin to download (Firefox 3) the <em>JSON</em> version of the search results, instead of the expected <em>HTML</em> response. When I heard the word &#8220;intermittent,&#8221; I smelled a race condition, opened up my trusty <a href="http://www.charlesproxy.com">Charles web debugging proxy</a>, and set to work trying to recreate the error.</p>
<h3>The problem</h3>
<p>What I found was that the last response to return for a given URL would be the one that the browser would cache, no matter what kind of response you were expecting given a certain set of request headers.</p>
<p>Here is what Charles says about the order of server responses with the bug absent. You&#8217;re looking at a set of four AJAX requests initiated by the Javascript application, and responded to by the server, followed by a non-AJAX request initiated when the user clicked submit on the search form. </p>
<p><div id="attachment_198" class="wp-caption aligncenter" style="width: 462px"><a href="http://steveluscher.com/wp-content/uploads/2008/12/html_response_last.png"><img class="size-large wp-image-198 " title="HTML response arrives last" src="http://steveluscher.com/wp-content/uploads/2008/12/html_response_last-452x169.png" alt="html_response_last" width="452" height="169" /></a><p class="wp-caption-text">Last response to arrive is the HTML one.</p></div></p>
<p>Next, what we have is Charles&#8217; report at the time the bug was discovered. Three AJAX requests receive responses, and while a fourth one is in progress, the user submits the search form. As we can see, the fourth JSON request returns <em>after</em> the HTML search page returns. The last response across the finish line, is the one that the browser will remember for the URL <code>/?s=food</code>.</p>
<p><div id="attachment_200" class="wp-caption aligncenter" style="width: 462px"><a href="http://steveluscher.com/wp-content/uploads/2008/12/json_response_last.png"><img class="size-large wp-image-200" title="JSON response arrives last" src="http://steveluscher.com/wp-content/uploads/2008/12/json_response_last-452x169.png" alt="json_response_last" width="452" height="169" /></a><p class="wp-caption-text">Last response to arrive is the JSON one.</p></div></p>
<h3>Solutions</h3>
<p>Turns out, you have at least two simple solutions in this case:</p>
<ol>
<li>Cancel all of the AJAX requests at the time the form is submitted</li>
<li>Use a different URL according to the response you expect</li>
</ol>
<p>In my case, I opted to make use of the <code><a href="http://mootools.net/docs/Request/Request#Request:cancel">cancel()</a></code><a href="http://mootools.net/docs/Request/Request#Request:cancel"> method</a> on my instance of the MooTools <code>Request</code> class to ensure that any AJAX requests for JSON would be killed before a request for the HTML version of the search results page even got rolling.</p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/ajax-requests-x-requested-with-headers-and-unexpected-cache-contents/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Farewell splash page, hello blog</title>
		<link>http://steveluscher.com/archives/farewell-splash-page-hello-blog</link>
		<comments>http://steveluscher.com/archives/farewell-splash-page-hello-blog#comments</comments>
		<pubDate>Wed, 17 Dec 2008 01:23:32 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Personal]]></category>
		<category><![CDATA[blogs]]></category>
		<category><![CDATA[redesigns]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=137</guid>
		<description><![CDATA[You would think that, as someone who lives and works on the internet, I would have jumped on the blog bandwagon the moment it first hit the trail. The reality is that my personal domain has been rocking the same splash page since it was registered over 8 years ago. Today, the splash page finally [...]]]></description>
			<content:encoded><![CDATA[<p>You would think that, as someone who lives and works on the internet, I would have jumped on the blog bandwagon the moment it first hit the trail. The reality is that my personal domain has been rocking the same splash page since it was registered over 8 years ago. Today, the splash page finally gives way to the almighty blog.<span id="more-137"></span></p>
<p><div id="attachment_142" class="wp-caption alignleft" style="width: 250px"><a href="http://steveluscher.com/wp-content/uploads/2008/12/picture-1.png"><img class="size-medium wp-image-142" title="steveluscher.com splash page" src="http://steveluscher.com/wp-content/uploads/2008/12/picture-1-300x190.png" alt="steveluscher.com splash page" width="240" height="152" /></a><p class="wp-caption-text">steveluscher.com from 2001–2008</p></div></p>
<p>It&#8217;s unclear what finally pushed me into the blogosphere. I&#8217;m not sure that I can point to any one trigger; it&#8217;s more likely the work of a lifetime of backburnered  thoughts, unsung opinions, and dusty ideas that led me to hollow out this piece of the internet, erect an <code>.htaccess</code> file, and call it my own.</p>
<p>I&#8217;m starting with only a handful of goals. I intend to spend hours, not seconds, crafting publishable material, so that would-be readers receive carefully considered prose more often than verbal diarrhea. Also, being a designer, I would like each and every post I publish to be accompanied by at least one editorial photograph; if it&#8217;s not worth illustrating, it&#8217;s not worth publishing. As for how to categorize and tag posts, and what sort of navigation to make available to visitors… I&#8217;ll work that out as I go.</p>
<p><div id="attachment_162" class="wp-caption alignright" style="width: 250px"><a href="http://steveluscher.com/wp-content/uploads/2008/12/picture-11.png"><img class="size-medium wp-image-162" title="The Aggregator" src="http://steveluscher.com/wp-content/uploads/2008/12/picture-11-240x72.png" alt="The Aggregator" width="240" height="72" /></a><p class="wp-caption-text">The Aggregator</p></div></p>
<p>The one navigational device that I&#8217;ve provided off the bat, other than tags and topics, is something that I call &#8220;The Aggregator.&#8221; Each time I publish a post, I will describe, in one short sentence, what I think that post says about me. The Aggregator will pull these sentences at random and display them as links to the posts they belong to.</p>
<p>Being late to the game, I&#8217;ve ported some of my old writing to this blog to fatten it up for launch. Here&#8217;s to all of the words I have yet to write.</p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/farewell-splash-page-hello-blog/feed</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>How to manage project requirements (for fun and profit)</title>
		<link>http://steveluscher.com/archives/how-to-manage-project-requirements-for-fun-and-profit</link>
		<comments>http://steveluscher.com/archives/how-to-manage-project-requirements-for-fun-and-profit#comments</comments>
		<pubDate>Sun, 14 Dec 2008 19:59:53 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Code]]></category>
		<category><![CDATA[Work]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=41</guid>
		<description><![CDATA[Your ability to deliver a web/software project successfully is largely dependent on how well you manage that project&#8217;s requirements. I&#8217;ve been repeatedly schooled on the how and why of great requirements management as I&#8217;ve watched so many good projects turn into nightmares. Being taken down with the ship is about as fun as it sounds, [...]]]></description>
			<content:encoded><![CDATA[<p>Your ability to deliver a web/software project successfully is largely dependent on how well you manage that project&#8217;s requirements. I&#8217;ve been repeatedly schooled on the how and why of great requirements management as I&#8217;ve watched so many good projects turn into nightmares. Being taken down with the ship is about as fun as it sounds, but if the things that I&#8217;ve learned can help you steer clear of the icebergs, I&#8217;d be happy to share. Let&#8217;s go to school.<span id="more-41"></span></p>
<h3>The bell rings:</h3>
<p>Let&#8217;s frame the discussion; A client hires you to build a web application, they bring a wish list of things they want it to do, and they ask you how much it&#8217;s going to cost. The more you know about what&#8217;s required, the more accurate your estimates will be, so you begin to size up the scope of the work by putting the project&#8217;s requirements down in writing. You can gather and store those requirements in a ticketing system like <a href="http://lighthouseapp.com">Lighthouse</a> or <a href="http://www.atlassian.com/software/jira/">Jira</a>, but you can be similarly effective using Post-it notes and a smooth wall. Let&#8217;s bracket out the discussion about tooling, assume that we&#8217;re going to use the term &#8220;ticket&#8221; to describe a distilled requirement, and take a look at the component parts that make up a requirement:</p>
<ol>
<li>Ticket title</li>
<li>Ticket description</li>
<li>Ticket milestone</li>
</ol>
<h3>1 – Ticket title</h3>
<p>The title of a ticket should strike to the very heart of the requirement, summarizing it succinctly and unequivocally. As is true of great ticket content, a great ticket title should be:</p>
<ol>
<li>Testable <em>– you&#8217;ve made an assertion about what outcome you desire</em></li>
<li>Contextual <em>– you&#8217;ve described the conditions and nature of the players involved</em></li>
<li>Granular <em>– you can&#8217;t break this requirement into smaller requirements</em></li>
</ol>
<p>The pro requirement gatherers that I&#8217;ve come to know develop formulas for their ticket titles, and stick to them. If this was your formula:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">{persona} should {imperative} when {condition}</pre></div></div>

<p>…you might expect to see the following ticket titles:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">The system should present visitors with a newsletter
subscription form when the signup link is clicked
&nbsp;
The system should present an error message when
a visitor submits a subscription form with errors
&nbsp;
A visitor should be added to the mailing list
upon signing up for the newsletter
&nbsp;
A visitor should be presented with a confirmation
message upon signing up for the newsletter
&nbsp;
The system should send monthly subscription
reminders to those on the mailing list
&nbsp;
An administrator should be able to send
a plain text email to mailing list subscribers
&nbsp;
An administrator should be able to send
an HTML email to mailing list subscribers
&nbsp;
The system should remove a subscriber from the
mailing list when their emails are found to bounce
&nbsp;
A subscriber should be removed from the mailing list
when they click the “unsubscribe” link
&nbsp;
A subscriber should be presented with a confirmation
that they've been unsubscribed when they click the
“unsubscribe” link</pre></div></div>

<p>Things to notice:</p>
<ol>
<li>Each ticket title describes a discrete user/system interaction. Note the splitting of plain text and HTML emails into two separate tickets. From a systems perspective, they may involve two very different pieces of code and, as such, constitute two requirements.</li>
<li>The development/design activity represented by each ticket can be tested for completeness by title alone, by asking the question “did the {persona} {imperative} when {condition}?”</li>
<li>As I wrote the ticket titles, a set of personas emerged; they are: “the system,” “a visitor,” “a subscriber,” and “an administrator.” Like good characters in a book, these personas will develop as the story unfolds, and should follow you around for the entire duration of the project.</li>
</ol>
<p>If your instincts told you to bundle all of those tickets into one ticket called “Newsletter,” you may have a medical condition called “epiccitis” – a propensity to write Epic Stories. Epics are a great way to bury complexity where it can&#8217;t be seen, leading to underestimation, and ensuring that your projects will run over time and blow your budgets. Write Epic fiction, not Epic Requirements.</p>
<h3>2 – Ticket description</h3>
<p>If your ticket description is half as good as your title, you&#8217;re still well on your way. Let&#8217;s extend the system that we used to write our titles, and write example ticket content for the ticket titled “The system should present visitors with a newsletter subscription form when the signup link is clicked.”</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">TICKET TITLE:
The system should present visitors with a newsletter
subscription form when the signup link is clicked
&nbsp;
TICKET DESCRIPTION:
Upon clicking on the “subscribe now” button,
a user should be required to supply
the following information:
&nbsp;
  * Name
  * Email (validate against RFC 2822)
  * Acceptance of privacy policy
&nbsp;
…and may optionally provide:
&nbsp;
  * Occupation
  * Telephone number</pre></div></div>

<p>Now <em>there&#8217;s</em> a requirement that designers and developers alike can sink their teeth into, right away!</p>
<h3>3 – Ticket milestone</h3>
<p>I&#8217;ll tell you how I feel about this, then you can develop a system that works for you.</p>
<p>Milestones should occur regularly, like a heartbeat. For simple projects, set a milestone every week. For complicated ones, three or four weeks.</p>
<p>Milestones should never move; the only thing that should move are requirements – in and out of milestones. When surprises come up, write the surprises down as new requirements (new tickets) and do one of three things:</p>
<ol>
<li>Schedule the new requirements in a future milestone</li>
<li>Move requirements out of the current milestone and into a future milestone, to make room for the new ones</li>
<li>Add the new requirements to the current milestone and assign additional personnel to that milestone; the more people working on a milestone, the more requirements it can hold</li>
</ol>
<p>When a milestone is reached, a snapshot of the project should be deployed, unfinished tickets (tsk, tsk) should be carried forward, and beer should be un-fridged.</p>
<p>Therefore, good milestone names:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">Iteration 1
Iteration 2
Iteration 3 (Internal Launch)
Iteration 4
Iteration 5 (Public Launch)
Iteration 6</pre></div></div>

<p>Bad milestone names:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">After Friday
Whenever you get time
Yesterday</pre></div></div>

<h3>School&#8217;s out</h3>
<p>If writing requirements in this way seems overly complicated and time-consuming, there&#8217;s good news: you can reuse them. The first time you write a series of stories for “new user registration” it will seem like time is standing still, yes, but if you write them in an abstract way, you should be able to drop them into all subsequent projects that require “new user registrations” and customize them from there.</p>
<p>Oh… I almost forgot; here are the answers to the test:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">  great requirements
= testable requirements
= clarity
= accurate estimates
= less rework
= more met deadlines
= happier clients
= happier staff
= ???
= profit!</pre></div></div>

<p>So next time you sit down to write a ticket, if you can&#8217;t think of a good title, chances are you don&#8217;t really know what&#8217;s required. And, as we&#8217;ve learned from G.I. JOE, knowing is half the battle.</p>
<p><object type="application/x-shockwave-flash" style="width:448px;height:386px" data="http://www.youtube.com/v/_KXCD5BQiM4&amp;fmt=18"><param name="allowfullscreen" value="true" /><param name="quality" value="best" /><param name="wmode" value="transparent" /><param name="movie" value="http://www.youtube.com/v/_KXCD5BQiM4&amp;fmt=18" /><param name="pluginspage" value="http://www.macromedia.com/go/getflashplayer" /></object><br/>
		<!-- Valid XHTML flash object delivered by XHTML Video Embed. Get it at: http://saltwaterc.net/xhtml-video-embed -->
		</p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/how-to-manage-project-requirements-for-fun-and-profit/feed</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>A user&#8217;s guide to Point Roberts, Washington</title>
		<link>http://steveluscher.com/archives/a-users-guide-to-point-roberts-washington</link>
		<comments>http://steveluscher.com/archives/a-users-guide-to-point-roberts-washington#comments</comments>
		<pubDate>Tue, 22 Jul 2008 10:22:13 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[General]]></category>
		<category><![CDATA[shopping]]></category>
		<category><![CDATA[tutorials]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=106</guid>
		<description><![CDATA[This is &#8220;A User&#8217;s Guide to Point Roberts, Washington,&#8221; or &#8220;How-to Get Stuff Shipped From The USA on a Shoestring.&#8221; Step 1: Want to order something from a US retailer, likely because it&#8217;s being sold for less than the cheapest Canadian retailer will sell it for, or because it&#8217;s just plum not available above the [...]]]></description>
			<content:encoded><![CDATA[<p>This is &#8220;A User&#8217;s Guide to Point Roberts, Washington,&#8221; or &#8220;How-to Get Stuff Shipped From The USA on a Shoestring.&#8221;<span id="more-106"></span></p>
<p><strong>Step 1</strong>:<br />
Want to order something from a US retailer, likely because it&#8217;s being sold for less than the cheapest Canadian retailer will sell it for, or because it&#8217;s just plum not available above the 49th parallel.</p>
<p><strong>Step 2</strong>:<br />
Notice that said US retailer either won&#8217;t ship internationally, charges damn near the original value of the item for shipping to Canada, or offers really attractive free shipping to US addresses. Now is the time to pound your fist against the desk, swear, or grumble something about living in a country that&#8217;s understood only as the land of &#8220;igloos&#8221; and &#8220;eskimos.&#8221;</p>
<p><strong>Step 3</strong>:<br />
Calm down. Click that &#8220;checkout&#8221; button. When it comes time to put in your shipping address, try this little number:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">{Your Name Here}
1591 McKenzie Way
Point Roberts, WA
98281</pre></div></div>

<p><strong>Step 4</strong>:<br />
<a href="http://www.p2pparcel.com/register.html">Register yourself</a> with Point To Point parcel, so they know to notify you when your package arrives.</p>
<p><strong>Step 5</strong>:<br />
You&#8217;ve received notification that your parcel has landed in Point Roberts! Grab your passport, a knapsack, your bike and get on the <a href="http://www.translink.bc.ca/bus/47/timetables/tt601.pdf">601 bus</a>. Take it all the way to the end of the line (52A street and 2nd), ride through Diefenbaker park, and locate the big orange square in the sky. That&#8217;s the border crossing.</p>
<p><strong>Step 6</strong>:<br />
Visit your good friends at Point To Point parcel, show your ID, and grab your goods!</p>
<p><strong>Step 7</strong>: Ride back across the border and pay your taxes. Y&#8217;know, the ones that paid for the bus ride down?</p>
<p>There you have it! Enjoy near-free shipping, competitive prices, a whole new world of selection, and good exercise. It&#8217;s the Point Roberts difference.</p>
<p><small><em>Other Point Roberts depots include <a href="http://tsbshipping.com">TSB Shipping</a> and <a href="http://thelettercarrier.com">The Letter Carrier</a>. These instructions are intended as guidelines only – always plan ahead, read the depot&#8217;s terms and conditions, check the latest transit schedules, and make sure that you understand the implications of cross-border travel and trade.<br />
</em></small></p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/a-users-guide-to-point-roberts-washington/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>The origins of car-love</title>
		<link>http://steveluscher.com/archives/the-origins-of-car-love</link>
		<comments>http://steveluscher.com/archives/the-origins-of-car-love#comments</comments>
		<pubDate>Fri, 24 Aug 2007 00:21:06 +0000</pubDate>
		<dc:creator>Steven Luscher</dc:creator>
				<category><![CDATA[Environmentalism]]></category>
		<category><![CDATA[opinions]]></category>
		<category><![CDATA[transportation]]></category>

		<guid isPermaLink="false">http://steveluscher.com/?p=115</guid>
		<description><![CDATA[I grew up in Oshawa, Ontario; longtime home of Robert McLaughlin&#8217;s 1878 Oshawa Carriage Works, the self-proclaimed city that &#8220;moto-vates&#8221; Canada, and current host to the General Motors of Canada headquarters. Having grown up in Canada&#8217;s version of Flint, Michigan, you might guess that I harbor a deep emotional connection to the North American auto [...]]]></description>
			<content:encoded><![CDATA[<p>I grew up in Oshawa, Ontario; longtime home of <a href="http://en.wikipedia.org/wiki/Robert_McLaughlin">Robert McLaughlin&#8217;s</a> 1878 Oshawa Carriage Works, the self-proclaimed city that &#8220;moto-vates&#8221; Canada, and current host to the <a href="http://en.wikipedia.org/wiki/General_Motors_Canada">General Motors of Canada</a> headquarters. Having grown up in Canada&#8217;s version of Flint, Michigan, you might guess that I harbor a deep emotional connection to the North American auto industry, that I drive an <a href="http://en.wikipedia.org/wiki/Oshawa_Truck_Assembly">Oshawa-built 2003 Chevrolet Silverado SS</a> (extended-cab, short-bed), and that I&#8217;m saving up to buy a brand new <a href="http://en.wikipedia.org/wiki/Oshawa_Car_Assembly">Oshawa-built fifth-generation Camaro</a> muscle car.</p>
<p>None of the above. I&#8217;m a sustainable transportation advocate!</p>
<p>I&#8217;m not sure how I escaped the gravitational pull of suburban cartown, with all of its union-wage factory jobs, cheap suburban homes, and open roads. Maybe it had to do with the world-view I inherited from my first-generation European parents that inspired me to pursue the arts, world-travel, and a University education instead. Enough about me though; this article is about the countless among us in love with – nay, <em>addicted</em> to – the automobile.</p>
<p>To help you understand where car-love comes from let me take you on a trip, back to my own childhood in suburban Oshawa. Picture it – the bungalow where I grew up sits on a corner lot with two cars in the driveway. My neighborhood of 80 single family homes has no sidewalks, partially because traffic density is low, but mostly because there&#8217;s nowhere to walk to within a 30 minute radius. My school is over an hour&#8217;s walk away, more than double that in the winter through snow up to your knees in places. Most of my friends live nearer to school, or in the neighboring suburbs of Whitby and Ajax, 10 or more kilometers away. Needless to say, I never walked to school or to my friends&#8217; houses; this is cartown! We drove!</p>
<p>The physical nature of suburbia makes everyone dependent on the automobile, and those who can&#8217;t afford cars or are too young to obtain a driver&#8217;s license are <em>doubly</em> dependent. While I was under 16 years of age, I relied on my parents to drive me <em>everywhere</em>. The hour, minute, and second that I turned 16 years of age I rushed to the local DriveTest centre to apply for my &#8220;G1&#8243; learner&#8217;s permit. According to Ontario&#8217;s graduated licensing system I had to put in twelve long, torturous months of parent-accompanied driving under my &#8220;G1&#8243; before I would be eligible to apply for the &#8220;G2&#8243; license – the holy grail of licenses which allows the holder to drive <em>unaccompanied</em>.</p>
<p>You have to understand – for a suburban seventeen year old, obtaining that &#8220;G2&#8243; license card wasn&#8217;t just a little logistical detail in a system of automobile licensing designed to keep the populace mobile and safe, it was 45 square centimeters of <em>pure unadulterated independence</em>, forged in plastic.</p>
<p><a href="http://en.wikipedia.org/wiki/Chris_Bradshaw">Chris Bradshaw</a>, Vancouver native and co-founder of Ottawa&#8217;s <a href="http://vrtucar.com/">Vrtucar</a> car-share, once explained car-love to me in a way that makes a lot of sense. Thinking back to my own relationship with the car as a kid growing up in suburbia I could see Chris&#8217; point that we have let the car become so much more than just a machine for connecting point A to point B. Access to a car has become a bargaining chip in parent-teenager negotiations; something which can be awarded for good behavior, or taken away for disobedience. As such, car-ownership has become a symbol of independence, from your parents first, and from social transit solutions second. Somehow, we&#8217;ve let the right to drive become entangled with one&#8217;s sense of self-reliance and freedom. We love cars like we love feeling free!</p>
<p>This seems to me to be a classic case of yearning for that which you have historically had limited access to. You never want to give up what you&#8217;ve worked so hard to obtain!</p>
<p>One proposal to change this dynamic is to lower the barrier to automobile access for young people; to frame automobile access not as some sort of reward, status-symbol, or light at the end of a dark tunnel of mobility-oppression, but as something commonplace which everyone has access to equally. Take away the prestige and the car-lust will go with it. Car-sharing services like Ottawa&#8217;s Vrtucar and Vancouver&#8217;s <a href="http://cooperativeauto.net/">Co-operative Auto Network</a> seem like ideal ways to improve automobile access but they are being hampered by the insurance industry; the insurance provider which insures Vancouver&#8217;s car co-op requires that drivers be at least 19 years of age, and in Ontario at Vrtucar the minimum age is 23 – much too late to have any significant impact on a young person&#8217;s perception of car-ownership. Unless we organize to effect change at the level of automobile insurance, I don&#8217;t see this dynamic changing on its own.</p>
<p>But wait – there&#8217;s new hope! Improvements in battery technology, electric motors, and in the legislation regulating the use of electric vehicles have spurred a groundswell of EV availability and ridership. Electric skateboards, power-assisted bicycles, and low-speed electric motorcycles have become affordable, available, and accessible to young people longing for personal mobility. The legislation is such in BC that you need only to be 16 years of age and be wearing a helmet to drive all but the largest and fastest EV two-wheelers. EVs are fast, fun, have storage and style, and are easy on the environment – and the pocketbook!</p>
<p>If you ride an EV of any kind, or if you simply want to learn more about EVs, come out to participate in Vancouver&#8217;s own monthly EV ride, &#8220;The Kilowatt Hour.&#8221; RSVP for the ride today at <a href="http://ev.meetup.com/1">http://ev.meetup.com/1</a></p>
<p><small><em>This was originally published as a </em><a href="http://www.changeeverything.ca/guest-columns/steven-luscher-vancouver-electric-vehicle-meetup-and-kilowatt-hour"><em>guest column</em></a><em> at </em><em><a href="http://changeeverything.ca">ChangeEverything.ca</a></em></small></p>
<p><em> </em></p>
]]></content:encoded>
			<wfw:commentRss>http://steveluscher.com/archives/the-origins-of-car-love/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

